[{"content":"If you have just arrived in Japan — or have lived here for years but never gotten around to investing — the first question is always the same: which account should I open first?\nJapan offers three main account types for retail investors: the tax-advantaged NISA, the pension-focused iDeCo, and a plain taxable brokerage account. Each serves a different purpose, and picking the wrong one first can cost you years of compound growth or lock away money you actually need.\nThis guide explains all three side by side, walks you through a decision flowchart, simulates 20-year growth in each account, and then shows the exact steps to open a Rakuten Securities account with your My Number card.\n1. Three Account Types Explained Side by Side NISA (Nippon Individual Savings Account) NISA is Japan\u0026rsquo;s flagship tax-free investment wrapper, redesigned and made permanent in 2024. The key benefit is simple: all capital gains and dividends earned inside NISA are completely tax-free. In a regular account you would pay 20.315% on every yen of profit; inside NISA, that tax disappears entirely.\nFeature Details Annual contribution limit ¥3,600,000 (¥1.2M Growth + ¥2.4M Tsumitate) Lifetime limit ¥18,000,000 Tax on capital gains 0% Tax on dividends 0% Withdrawal Anytime, any amount — fully liquid Who qualifies Residents of Japan aged 18 and older Account lifespan Permanent (no expiry under 2024 rules) One important nuance: when you sell investments inside NISA, the \u0026ldquo;used\u0026rdquo; lifetime allowance is restored the following year. This means your ¥18M lifetime cap is not a one-way door — it replenishes over time as you sell, giving you long-term flexibility.\niDeCo (Individual Defined Contribution Pension) iDeCo is Japan\u0026rsquo;s self-directed pension scheme. It comes with two tax benefits stacked on top of each other: contributions are fully tax-deductible (reducing your income tax and residence tax each year) and all investment growth is tax-free while the money remains in the account.\nThe trade-off is liquidity. Money inside iDeCo is locked until age 60 — no exceptions. If you withdraw early due to financial hardship, the rules are extremely strict and withdrawals are generally not permitted until the minimum age requirement is met.\nFeature Details Monthly contribution limit (company employee) ¥23,000 Monthly contribution limit (self-employed) ¥68,000 Tax on contributions Deductible from taxable income Tax on capital gains 0% during accumulation phase Withdrawal Locked until age 60 (60–75 payout phase) Who qualifies Working-age residents paying into public pension The contribution deduction is genuinely powerful. An employee earning ¥6M annually and contributing the maximum ¥23,000/month (¥276,000/year) would save roughly ¥55,000–¥83,000 per year in taxes depending on their marginal rate. Self-employed individuals contributing ¥68,000/month save even more.\nRegular Taxable Brokerage Account (Tokutei Koza) A standard brokerage account in Japan — called a tokutei koza (特定口座) with \u0026ldquo;withholding\u0026rdquo; selected — has no contribution limits and no restrictions on withdrawal, but all profits are taxed.\nFeature Details Annual contribution limit None Lifetime limit None Tax on capital gains 20.315% (15% income tax + 0.315% special reconstruction tax + 5% residence tax) Tax on dividends 20.315% Withdrawal Anytime, no restrictions Who qualifies Anyone The 20.315% effective rate means that for every ¥100,000 profit, you hand ¥20,315 to the government. Over a 20-year compounding period, this drag is substantial — which is why most beginners should exhaust their NISA room before using a taxable account.\nAt-a-Glance Comparison Table NISA iDeCo Taxable Tax-free growth Yes Yes No (20.315%) Contribution deduction No Yes No Annual limit ¥3.6M ¥23K–¥68K/mo Unlimited Lifetime limit ¥18M No cap No cap Liquidity Full Locked to age 60 Full Best for Long-term tax-free wealth Retirement pension savings Overflow / tactical use 2. Decision Flowchart: Which Account Should You Open First? Work through these questions in order.\nStep 1: Do you have an emergency fund? Before investing anything, you need 3–6 months of living expenses in a liquid savings or high-yield deposit account. If not, build that first. No flowchart needed.\nStep 2: Are you employed and does your employer offer a corporate DC (企業型DC) plan? If yes, check whether your employer allows simultaneous iDeCo contributions. As of 2022, most employees can join iDeCo even with a corporate DC, but the combined limit applies. Confirm this with your HR department first.\nStep 3: Do you have stable income and won\u0026rsquo;t need this money before age 60?\nYes, and you pay income tax: Open iDeCo first for the contribution deduction, then NISA. The deduction gives you an immediate return on top of investment growth. No — you might need the money before age 60: Skip iDeCo for now. Open NISA first. Full liquidity at zero tax cost is the priority. Step 4: Have you maxed out your NISA for the year?\nIf you still have NISA room: put new money there before any taxable account. If NISA is full and you have more to invest: open a regular tokutei koza. The most common beginner path: NISA first → iDeCo second (for the tax deduction) → taxable account only if both are maxed.\nSelf-employed (freelancer / sole proprietor): iDeCo first. The ¥68,000/month deduction (¥816,000/year) is one of the largest legal tax reduction tools available to you. Then NISA.\nPart-time worker or low income (below the income tax threshold): NISA only. The iDeCo deduction has no value if you are not paying income tax.\n3. Simulating 20-Year Growth Across Each Account Type Let\u0026rsquo;s make this concrete. Suppose you invest ¥30,000 per month (¥360,000/year) for 20 years into a broad global index fund averaging 5% annual growth.\nBase Assumptions Parameter Value Monthly investment ¥30,000 Annual return (pre-tax) 5.0% Investment period 20 years Total invested ¥7,200,000 Scenario A: NISA Account Inside NISA, every yen of growth is tax-free. After 20 years:\nFinal portfolio value: approximately ¥12,330,000 Tax owed on withdrawal: ¥0 Net gain over contributions: approximately ¥5,130,000 The entire ¥5.13M gain is yours to keep.\nScenario B: Regular Taxable Account In a taxable account, the 20.315% rate applies to all realised gains at withdrawal. Using a simplified end-of-period tax calculation:\nGross portfolio value: approximately ¥12,330,000 Capital gain: approximately ¥5,130,000 Tax on gain (20.315%): approximately ¥1,042,000 Net after-tax value: approximately ¥11,288,000 You lose approximately ¥1,042,000 to tax compared to NISA — purely because of account choice.\nScenario C: iDeCo Account (Employee, ¥23,000/month) iDeCo\u0026rsquo;s monthly limit for employees is ¥23,000, so this scenario uses ¥23,000/month (¥276,000/year).\nFinal portfolio value: approximately ¥9,475,000 Tax owed: deferred until payout (lump-sum deduction applies — first ~¥15M is tax-free under retirement income deduction for 20+ years) Annual tax saving during accumulation (at 20% marginal rate): approximately ¥55,200/year = ¥1,104,000 over 20 years The iDeCo comparison is more complex because the deduction savings compound in your other investments, and the retirement income deduction largely shields the payout. For most middle-income employees, iDeCo + NISA together handily beats NISA alone.\nWalk Through the NISA Simulator You can model your own numbers using the NISA Simulator on this site. Here is the exact process:\nOpen the NISA Simulator . Enter your monthly investment amount — for example, ¥30,000. Set the expected annual return — 5% is a reasonable long-run assumption for a diversified global equity index fund, but you can test 3%, 5%, and 7% scenarios. Set the investment period in years. Try 20 years, then adjust for your actual retirement timeline. The simulator shows your final NISA balance alongside a taxable account scenario, so you can see the exact yen difference tax-free compounding makes. Click \u0026ldquo;Compare with iDeCo\u0026rdquo; to run a parallel iDeCo projection. Enter your monthly contribution limit and marginal tax rate. Also try the iDeCo Simulator to model the tax-deduction savings year by year, and the Compound Interest Calculator to experiment with different return assumptions.\nFor a full picture of how these accounts feed into retirement, the Retirement Calculator lets you combine your NISA, iDeCo, and public pension (nenkin) into a single retirement income projection.\n4. Opening a Rakuten Securities Account: Exact Steps for Residents with a My Number Card Rakuten Securities (楽天証券) is one of Japan\u0026rsquo;s two largest online brokers alongside SBI Securities. It offers a clean interface in both Japanese and partial English, integrates with Rakuten Bank for easy cash transfers, and covers both NISA and iDeCo in the same login.\nWhat you need before you start:\nMy Number card (個人番号カード) — the physical card with the IC chip, not the paper notification slip Your smartphone with NFC capability (for card scanning) Japanese bank account or Rakuten Bank account Registered address in Japan (juusho) Email address Step-by-Step Account Opening Step 1: Go to the Rakuten Securities application page. Visit rakuten-sec.co.jp and click \u0026ldquo;口座開設\u0026rdquo; (Open an Account). You can also apply via the Rakuten Securities app.\nOpen a Rakuten Securities account and start investing tax-free: Apply via Rakuten Securities affiliate link Step 2: Enter your basic information. Fill in your name (exactly as it appears on your residence card or My Number card), date of birth, address, phone number, and email address. For foreign residents, use your registered address exactly as filed with your local municipal office.\nStep 3: Select your account types. You will be asked which accounts to open simultaneously. Select:\nTokutei koza (特定口座) with withholding (源泉徴収あり) — this automates your tax reporting NISA account — check the box to apply for NISA at the same time Optionally: iDeCo can be added later from the member portal Step 4: Identity verification with your My Number card. Choose \u0026ldquo;My Number card\u0026rdquo; as your identity document. The app will prompt you to:\nPhotograph the front of your My Number card Scan the IC chip via NFC (hold the card to the back of your phone) Enter your 4-digit My Number card PIN (the one you set at the municipal office) This e-KYC process typically completes in minutes. If you do not have a My Number card, you can use a combination of your residence card and a bank statement, but the process takes longer (postal verification).\nStep 5: Fund your account. Once approved (usually 1–3 business days), log in and link your bank account. Rakuten Bank users get zero-transfer-fee top-ups and a higher savings interest rate when both accounts are linked (Maneo Program). Set up a monthly auto-sweep to automate your NISA contributions.\nStep 6: Choose your first fund. For most beginners, a single all-world index fund is the simplest and most evidence-based starting point. Search for \u0026ldquo;eMAXIS Slim 全世界株式\u0026rdquo; (eMAXIS Slim All Country) in the fund search. It has a very low annual cost (TER around 0.057%) and covers over 2,800 companies across 47 countries.\nSet up a monthly automatic purchase (積立設定) aligned with your NISA tsumitate limit. Done.\nKey Takeaways NISA is the default first account for most residents — zero tax on growth, fully liquid, up to ¥3.6M/year. iDeCo is powerful if you pay income tax and can commit money until age 60 — the deduction gives you an instant return before any market movement. Regular brokerage is for overflow once you have maxed NISA; the 20.315% tax drag is real over 20+ years. The 20-year simulation shows roughly ¥1M in tax savings just by using NISA instead of a taxable account on the same ¥30,000/month plan. Open a Rakuten Securities account with your My Number card — the e-KYC process takes under 10 minutes. Run your own numbers with the NISA Simulator , the iDeCo Simulator , and the Retirement Calculator before you decide on a contribution amount.\nReady to start? Open a Rakuten Securities account today: Apply for a Rakuten Securities Account Related Tools Calculate compound interest → Compound Interest Calculator Plan your retirement → Retirement Calculator Estimate your tax bracket → Tax Bracket Calculator See how inflation affects your money → Inflation Calculator Disclaimer This article contains affiliate links. If you open an account via the links on this page, we may receive a commission at no extra cost to you. All opinions are our own. This article is for informational purposes only and does not constitute investment advice. Investment returns are not guaranteed, and the value of your investments can go down as well as up. Tax rules described reflect Japanese tax law as understood in May 2026 — always verify current rules with a tax professional or the NTA (国税庁) website before making decisions. iDeCo rules and contribution limits are subject to change.\nYou May Also Like iDeCo Tax Deduction Calculator: How Much Can You Actually Save in Japan? Best Index Funds for Beginners 2026 Beginner Investing Guide 2026: How to Start Building Wealth Today ","permalink":"https://productivity-works.com/posts/best-investment-account-japan-nisa-ideco/","summary":"\u003cp\u003eIf you have just arrived in Japan — or have lived here for years but never gotten around to investing — the first question is always the same: \u003cstrong\u003ewhich account should I open first?\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eJapan offers three main account types for retail investors: the tax-advantaged NISA, the pension-focused iDeCo, and a plain taxable brokerage account. Each serves a different purpose, and picking the wrong one first can cost you years of compound growth or lock away money you actually need.\u003c/p\u003e","title":"Best Investment Account for Beginners in Japan: NISA vs. iDeCo vs. Regular Brokerage"},{"content":"For salaried workers in Japan, building wealth beyond your monthly paycheck feels harder than it should be. The tax system is not exactly designed to reward passive income — but it does offer several well-structured pathways if you know how to use them together.\nMost foreign professionals in Japan start with NISA or iDeCo. Both are excellent. But there is a third pillar that many overlook entirely: rental real estate investment. When combined thoughtfully with tax-advantaged accounts, real estate can do things that stock market investing simply cannot — most notably, it can reduce the income tax you pay on your salary right now, not decades from now.\nThis article walks through all three pillars, explains how they interact, and helps you figure out where to focus based on your income level. If you have already set up your NISA and iDeCo accounts, or want to model your retirement projections first, the tools below are a good starting point before you continue reading.\nNISA Simulator — model your tax-free investment returns iDeCo Simulator — calculate your annual tax deduction Retirement Calculator — project your total retirement assets 1. Three Pillars of Wealth Building in Japan: NISA, iDeCo, and Real Estate Japan offers salaried workers three distinct approaches to building long-term financial security. Each has a different mechanism, a different tax treatment, and a different role in your overall portfolio.\nNISA: Tax-Free Capital Gains The new NISA system, introduced in its current form in 2024, allows you to invest up to 3.6 million yen per year across two accounts (a growth investment account and a flexible account), with a lifetime cap of 18 million yen. Any capital gains, dividends, or distributions generated within this account are completely tax-free.\nThis is a significant benefit. Under the normal taxation rules, capital gains on investments are taxed at 20.315% (15% national income tax + 5% local tax + 0.315% reconstruction surtax). If you hold assets outside NISA and sell them at a gain, that tax applies automatically. Inside NISA, it does not.\nNISA is best suited for:\nLong-horizon index fund investing (especially global equity index funds) Investors who want simplicity and liquidity People who may not want to lock up capital until age 60 You can withdraw from NISA at any time, which is a meaningful distinction from iDeCo.\niDeCo: Deductible Contributions, Tax-Deferred Growth iDeCo (Individual-type Defined Contribution pension) works differently. Your contributions are deducted from your taxable income in the year you make them. If you are a company employee earning 8 million yen a year and you contribute 276,000 yen to iDeCo (the maximum for most company employees), you reduce your taxable income by that amount immediately.\nThe assets inside iDeCo also grow tax-deferred, and when you withdraw them at retirement, favorable tax treatment applies (through the retirement income deduction and the public annuity deduction).\nThe tradeoff is illiquidity: you cannot access iDeCo funds until age 60. This makes iDeCo a true retirement savings instrument, not a general investment account.\nFor a quick calculation of how much iDeCo saves you this year, use the iDeCo Simulator .\nReal Estate: Current Income, Tax Offsets, and Leverage Rental real estate is categorically different from both NISA and iDeCo. It is not a paper asset. It involves physical property, leverage (mortgage debt), ongoing management, and the real risk of vacancy. But it also offers something the other two cannot: the ability to offset your salary income with real estate losses in the current tax year.\nThis mechanism — called 損益通算 (loss aggregation) in Japanese — is the cornerstone of why high-income salaried workers in Japan find real estate attractive. When your rental property generates a net loss on paper (typically due to depreciation), that loss reduces your reported income, which reduces both your income tax and your residence tax.\n2. How Rental Property Investment Works in Japan: Tax Benefits, Depreciation, and Loss Offset Understanding Japanese real estate tax rules is essential before you invest. The following sections cover the core mechanics.\nRental Income Is Taxed as Comprehensive Income (総合課税) Unlike capital gains from stocks — which are taxed separately at a flat 20.315% — rental income is added to your other income and taxed at your marginal income tax rate. This means:\nIf your salary already puts you in the 33% or 40% tax bracket, rental income on top of that is also taxed at those rates. However, the flip side is equally true: rental losses reduce your overall taxable income, and the tax saving scales with your marginal rate. This is why high earners benefit most from the depreciation strategy in real estate.\nDepreciation: The Core Tax Tool Japanese tax law allows property owners to depreciate the building portion of a property (not the land) over its legal useful life. The annual depreciation amount is:\nBuilding value / Useful life = Annual depreciation expense\nLegal useful lives under Japanese tax law:\nReinforced concrete (RC): 47 years Steel-frame construction: 34 years Wooden construction: 22 years For used (古) buildings, the remaining useful life is calculated differently, and in some cases it is very short — which creates an accelerated depreciation effect that investors deliberately seek out.\nExample: You purchase a used wooden apartment (木造) for 30 million yen, of which 15 million yen is attributed to the building. If the building has 5 remaining years of useful life under the tax formula, your annual depreciation is 3 million yen. That 3 million yen reduces your taxable income every year for 5 years — without being a cash outflow.\nIf you are in the 33% bracket, this saves you roughly 1 million yen in taxes annually. Over 5 years, that is 5 million yen in tax savings from a non-cash deduction.\nDeductible Expenses in Rental Real Estate Beyond depreciation, the following costs are deductible against rental income:\nExpense Category Notes Property tax (固定資産税) Annual land and building tax paid to the municipality Loan interest (ローン利息) Only the interest portion, not principal repayment Management fees (管理費) Paid to property management company Repair reserves (修繕積立金) Monthly reserves for future major repairs Repair costs (修繕費) Actual repairs and maintenance Insurance premiums (火災保険) Fire and earthquake insurance Depreciation (減価償却費) Non-cash but deductible Travel and administrative costs For property-related business purposes How Rental Losses Offset Salary: A Practical Example Let\u0026rsquo;s say you are a salaried worker earning 9 million yen per year and you own a single investment property. Here is a simplified year-end picture:\nItem Amount Gross rental income +1,200,000 yen Depreciation -1,800,000 yen Loan interest -400,000 yen Property tax + management fees -300,000 yen Net rental income (reported) -1,300,000 yen That -1,300,000 yen (net rental loss) is aggregated with your salary income, reducing your taxable income from 9 million to 7.7 million yen. In the 23-33% marginal bracket range, this could reduce your income and residence tax liability by 350,000 to 450,000 yen that year.\nTo confirm your take-home pay under different income scenarios, try the Take-home Pay Calculator .\nThe Important Caveat: Depreciation Runs Out This strategy works well during the depreciation period. Once depreciation runs out, the same property may generate taxable rental income, which now increases your tax burden. Smart investors either sell the property before that point or refinance and restructure. The exit strategy matters as much as the entry.\nProperty Value Risk and Capital Gains Tax When you sell:\nIf held for more than 5 years: capital gains tax is approximately 20% (long-term gains rate) If held for 5 years or less: approximately 39% (short-term gains rate) Note that the holding period is measured as of January 1 of the year of sale in Japan. This is different from many other countries. Plan your exit timeline accordingly.\n3. AI-Powered Property Investment Services Like RENOSY The barrier to real estate investing in Japan has historically been high for salaried workers and especially for foreign residents: language difficulty, complex legal processes, finding trustworthy agents, and ongoing management. A new category of technology-driven investment property services has emerged to reduce these friction points.\nWhat Is RENOSY? RENOSY is an AI-powered real estate investment platform operated by GA Technologies. It targets salaried workers who want to invest in single-room condominium units (区分マンション) in major urban markets — primarily Tokyo — without becoming hands-on landlords.\nThe platform\u0026rsquo;s key propositions include:\n1. AI-assisted property selection RENOSY uses data analysis to match investors with properties based on their financial situation, income level, and investment goals. The model draws on transaction data, rental yield histories, and vacancy rate trends by location.\n2. Integrated management After purchase, RENOSY arranges property management so that investors do not need to handle tenant communication, repairs coordination, or vacancy management themselves. This is particularly relevant for professionals who do not speak Japanese fluently.\n3. Loan coordination support RENOSY works with partner financial institutions to help investors secure real estate investment loans. For salaried workers with stable income histories, this is often achievable even if they are not Japanese citizens, though individual qualification varies by bank.\n4. Transparent online dashboard Investors can monitor rental income, expense summaries, and occupancy status through an online portal.\nIs RENOSY Right for You? RENOSY focuses primarily on Tokyo-area condominium units, which have relatively stable rental demand but also relatively low gross yields (typically 3-5% before expenses). The value proposition is not primarily about rental yield — it is about the tax optimization strategy and capital appreciation potential in central urban areas.\nThis model may suit you if:\nYou earn 6 million yen or more annually (the tax offset benefit scales with income) You want passive management rather than active landlord involvement You plan to hold for at least 5-10 years You are primarily interested in the income tax reduction angle It may not suit you if:\nYou prefer higher-yield properties in regional markets You want to be more hands-on with your portfolio You are in a lower tax bracket where the depreciation offset is minimal Alternative Approaches Other platforms and approaches in the Japanese real estate investment space include:\nTraditional real estate agents specializing in investment properties (prices typically higher, less data transparency) Property sourcing services for regional high-yield apartments (higher gross yield, higher vacancy risk) REITs (J-REIT) for indirect real estate exposure through the stock market (can be held inside NISA) Crowdfunding platforms like COZUCHI and Funds for smaller ticket real estate investment For accounting and bookkeeping when managing your rental income and tax filings, consider using freee — which supports rental income tracking alongside business and personal accounts:\nTry freee for rental income accounting 4. Building a Combined Portfolio: Which to Prioritize by Income Level There is no single correct order for combining NISA, iDeCo, and real estate. The right priority depends heavily on your current income level, your marginal tax rate, your liquidity needs, and your time horizon. Here is a practical framework.\nUnder 5 Million Yen Annual Income: NISA First At this income level, your marginal income tax rate is relatively low (5-20%). The tax-offset benefit from real estate depreciation is limited, and the capital and risk required for property investment are disproportionate to the benefit.\nRecommended priority:\nMaximize NISA (up to 3.6 million yen per year into low-cost global index funds) Contribute to iDeCo (even a modest amount captures meaningful deductions at this bracket) Real estate: monitor and consider later as income grows Use the NISA Simulator to model how consistent contributions compound over 20-30 years. The results are often more compelling than people expect.\n5-8 Million Yen Annual Income: Build All Three in Parallel This is the range where the combination begins to shine. Your marginal rate is in the 20-33% range, iDeCo deductions become meaningfully valuable, and the depreciation strategy in real estate starts to generate real tax savings.\nRecommended priority:\nMaximize iDeCo contributions (the annual deduction at this bracket is substantial) Fully fund NISA for long-term equity exposure Explore one investment property purchase — focus on the depreciation mechanics and loan eligibility At this income level, a single well-selected investment property in Tokyo, financed with a 20-25 year loan, can reduce your annual tax bill by 200,000-500,000 yen during the depreciation period while building an asset over time.\nCheck your Retirement Calculator to see how these contributions affect your projected retirement balance.\n8-15 Million Yen Annual Income: Real Estate Becomes High Priority Above 8 million yen, your marginal rate reaches 33% and above. At this level, every yen of depreciation deduction is saving you at least 33 sen in income tax plus roughly 10% in residence tax — approximately 43 yen of tax saving per 100 yen of depreciation.\nRecommended priority:\nMax out iDeCo (the deduction at 33-40% is very powerful) Use NISA as your liquid investment buffer Purchase one or more investment properties to aggressively use depreciation for tax reduction Consider a holding company structure after your property portfolio grows (consult a tax accountant) At this income level, real estate is not just an investment — it is an active tax management tool. Many professionals in this bracket work with a tax accountant (税理士) who specializes in real estate to structure their properties, filings, and eventual exits.\nAbove 15 Million Yen: Advanced Structuring At very high income levels, the discussion shifts to entity structure, multiple properties, and long-term exit strategies. A personal consultation with a tax specialist is essential. The general pillars — NISA, iDeCo, real estate — remain the same, but the sequencing and optimization become more sophisticated.\nKey Risks You Should Not Ignore Real estate investment is not passive income in the simple sense. These are the primary risks every investor needs to understand:\nVacancy Risk (空室リスク) If your unit sits empty, you receive no rental income but continue paying loan interest, management fees, and property tax. Single-unit investors are especially exposed — a 100% vacancy means 100% income loss. Tokyo central areas have historically low vacancy rates, but this is not guaranteed.\nInterest Rate Risk (金利リスク) Most investment property loans in Japan are variable-rate. As of early 2026, the Bank of Japan has begun normalizing interest rates after decades of near-zero policy. Rising rates increase your loan repayment costs and reduce net rental income. Model your finances under a scenario where your rate rises by 1-2 percentage points.\nProperty Value Decline (物件価格の下落リスク) Japan\u0026rsquo;s urban real estate market, particularly Tokyo, has been resilient. But values are not guaranteed. An aging building, declining neighborhood demographics, or a broader economic shock could reduce your property value below your outstanding loan balance — a negative equity situation.\nLiquidity Risk (流動性リスク) Real estate is illiquid. You cannot sell a portion of your apartment the way you can sell shares. If you need cash urgently, your exit options are limited and potentially costly. Do not invest money in real estate that you may need access to within 3-5 years.\nRegulatory and Tax Rule Changes Japan\u0026rsquo;s tax rules around depreciation and rental income deductions could change. The favorable treatment of short-depreciation-life used properties is a known policy item that regulators have discussed. Strategies that depend heavily on current rules carry regulatory risk.\nSummary: The Three-Pillar Approach in Practice NISA iDeCo Real Estate Annual limit 3.6M yen ~276K yen (employees) No limit Tax benefit Tax-free gains Contribution deduction Salary income offset Liquidity High (withdraw anytime) Very low (locked until 60) Very low Complexity Low Low-Medium High Best income range All levels All levels 6M yen+ Best for Long-term equity growth Retirement savings Current tax reduction For most salaried professionals in Japan, the practical starting point is:\nOpen a NISA account and start investing in a low-cost global index fund. Enroll in iDeCo and contribute the maximum your situation allows. Once you have 3-6 months of expenses saved and your tax-advantaged accounts are active, research your eligibility for investment property loans and consult a professional about whether real estate fits your income level and goals. And use these tools to anchor your planning with real numbers:\nNISA Simulator — project your NISA account growth iDeCo Simulator — calculate your annual iDeCo tax saving Retirement Calculator — combine all sources into a retirement projection Take-home Pay Calculator — understand your net income baseline Calculate your mortgage payment → Mortgage Calculator Disclaimer Related Tools Estimate your mortgage payments → Mortgage Calculator Calculate your net worth → Net Worth Calculator Calculate compound interest → Compound Interest Calculator This article is for informational purposes only and does not constitute financial, tax, or legal advice. Tax rules in Japan are complex and change over time. Please consult a qualified tax accountant (税理士) or financial advisor before making investment decisions. Individual circumstances vary significantly.\nThis article contains affiliate links. Productivity Works may receive compensation if you sign up for services through these links. This does not affect our editorial position or the information provided.\nYou May Also Like RENOSY Review for Expats: Is Real Estate Investment in Japan Worth It? (2026) How to Start Investing with $100 in 2026 Passive Income Ideas That Actually Work 2026 ","permalink":"https://productivity-works.com/posts/real-estate-investment-japan-salaried-worker/","summary":"\u003cp\u003eFor salaried workers in Japan, building wealth beyond your monthly paycheck feels harder than it should be. The tax system is not exactly designed to reward passive income — but it does offer several well-structured pathways if you know how to use them together.\u003c/p\u003e\n\u003cp\u003eMost foreign professionals in Japan start with NISA or iDeCo. Both are excellent. But there is a third pillar that many overlook entirely: \u003cstrong\u003erental real estate investment\u003c/strong\u003e. When combined thoughtfully with tax-advantaged accounts, real estate can do things that stock market investing simply cannot — most notably, it can reduce the income tax you pay on your salary right now, not decades from now.\u003c/p\u003e","title":"Real Estate Investment in Japan for Salaried Workers: How It Compares to NISA and iDeCo"},{"content":"This article contains affiliate links.\nAsset Allocation Simulator Enter your assets (cash, investments, real estate, etc.) and liabilities (loans, debt, etc.) to instantly calculate your net worth. Compare against age-group averages and set a personal goal.\nAssets Cash \u0026 Deposits (checking, savings, CDs, emergency fund) 万円 (¥10K units) Investment Assets (stocks, funds, NISA, iDeCo, crypto) 万円 (¥10K units) Real Estate (primary residence value, investment properties) 万円 (¥10K units) Other (vehicles, precious metals, insurance cash value, business assets) 万円 (¥10K units) Total Assets ¥0 Liabilities Mortgage Balance 万円 (¥10K units) Auto Loan 万円 (¥10K units) Student Loan / Education Debt 万円 (¥10K units) Credit Card Balance 万円 (¥10K units) Other Debt 万円 (¥10K units) Total Liabilities ¥0 Net Worth (Total Assets − Total Liabilities) ¥0 Debt ratio: 0% Asset Breakdown Cash \u0026 Deposits Investments Real Estate Other Liability Breakdown Mortgage Auto Student Credit Card Other Compare Against Age-Group Averages Your Age Group Select your age group 20s 30s 40s 50s 60s 70+ Age Group Median Net Worth (2+ person HH) vs. You 20s¥1,650,000— 30s¥5,260,000— 40s¥8,250,000— 50s¥12,530,000— 60s¥18,190,000— 70+¥19,050,000— Source: Survey on Household Financial Behavior (Central Council for Financial Services Information, Japan). Values shown in JPY.\nSet a Net Worth Goal Target Net Worth (¥10K units) 万円 (¥10K units) Goal Progress 0% What Is Net Worth and Why Does It Matter? Net worth is the value of everything you own minus everything you owe.\nNet Worth = Total Assets − Total Liabilities\nIncome and salary measure flow (what comes in each period), while net worth measures stock (what you have accumulated). Knowing your net worth helps you understand:\nFinancial resilience — A higher net worth means greater ability to weather income disruptions Retirement readiness — How much wealth you have beyond future pension payments Borrowing capacity — Lenders assess net worth when approving mortgages and business loans Progress tracking — Measuring annually shows whether your wealth-building is on track If your debt ratio (total liabilities ÷ total assets) exceeds 50%, consider prioritizing repayment of high-interest debt.\n5 Ways to Grow Your Net Worth 1. Increase income Side work, skill development, job changes, or negotiating a raise are the most direct levers. Channel extra income straight into assets.\n2. Reduce expenses Auditing fixed costs (rent, phone, insurance, subscriptions) can free tens of thousands of yen per year — often faster than a pay raise.\n3. Pay off high-interest debt first Credit card revolving debt (15–18% p.a.) erodes wealth faster than almost any investment can grow it. Eliminate these balances before investing aggressively.\n4. Start investing Cash alone loses purchasing power to inflation. Use NISA or iDeCo to invest in index funds for long-term, diversified, low-cost growth. Even 3–5% annual returns compound dramatically over decades.\n5. Refinance or renegotiate fixed costs Refinancing a mortgage, switching to a budget phone plan, or canceling unused insurance can permanently reduce monthly outflows.\nRelated Tools Savings Goal Calculator — Calculate how long to reach any savings target iDeCo Simulator — Model iDeCo tax savings and projected payout Dividend Growth Simulator — Project dividend income from your investment portfolio ","permalink":"https://productivity-works.com/tools/asset-allocation-simulator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"asset-allocation-simulator\"\u003eAsset Allocation Simulator\u003c/h1\u003e\n\u003cp\u003eEnter your \u003cstrong\u003eassets\u003c/strong\u003e (cash, investments, real estate, etc.) and \u003cstrong\u003eliabilities\u003c/strong\u003e (loans, debt, etc.) to instantly calculate your \u003cstrong\u003enet worth\u003c/strong\u003e. Compare against age-group averages and set a personal goal.\u003c/p\u003e\n\u003cdiv id=\"ss-calc\" style=\"max-width:720px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;color:#1e293b;\"\u003e\n\u003c!-- Assets section --\u003e\n\u003cdiv style=\"padding:24px;border:2px solid #059669;border-radius:12px;background:#f0fdf4;margin-bottom:20px;\"\u003e\n\u003ch2 style=\"margin:0 0 20px 0;font-size:20px;color:#059669;\"\u003eAssets\u003c/h2\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:14px;\"\u003eCash \u0026 Deposits \u003cspan style=\"font-weight:normal;color:#64748b;\"\u003e(checking, savings, CDs, emergency fund)\u003c/span\u003e\u003c/label\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cinput type=\"number\" id=\"ssCash\" min=\"0\" step=\"1\" value=\"0\" oninput=\"calcSS()\" style=\"flex:1;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:16px;\"\u003e\n\u003cspan style=\"color:#64748b;white-space:nowrap;\"\u003e万円 (¥10K units)\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:14px;\"\u003eInvestment Assets \u003cspan style=\"font-weight:normal;color:#64748b;\"\u003e(stocks, funds, NISA, iDeCo, crypto)\u003c/span\u003e\u003c/label\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cinput type=\"number\" id=\"ssInvest\" min=\"0\" step=\"1\" value=\"0\" oninput=\"calcSS()\" style=\"flex:1;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:16px;\"\u003e\n\u003cspan style=\"color:#64748b;white-space:nowrap;\"\u003e万円 (¥10K units)\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:14px;\"\u003eReal Estate \u003cspan style=\"font-weight:normal;color:#64748b;\"\u003e(primary residence value, investment properties)\u003c/span\u003e\u003c/label\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cinput type=\"number\" id=\"ssRealty\" min=\"0\" step=\"1\" value=\"0\" oninput=\"calcSS()\" style=\"flex:1;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:16px;\"\u003e\n\u003cspan style=\"color:#64748b;white-space:nowrap;\"\u003e万円 (¥10K units)\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:14px;\"\u003eOther \u003cspan style=\"font-weight:normal;color:#64748b;\"\u003e(vehicles, precious metals, insurance cash value, business assets)\u003c/span\u003e\u003c/label\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cinput type=\"number\" id=\"ssOtherAsset\" min=\"0\" step=\"1\" value=\"0\" oninput=\"calcSS()\" style=\"flex:1;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:16px;\"\u003e\n\u003cspan style=\"color:#64748b;white-space:nowrap;\"\u003e万円 (¥10K units)\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:16px;background:#dcfce7;border-radius:8px;display:flex;justify-content:space-between;align-items:center;\"\u003e\n\u003cspan style=\"font-weight:bold;font-size:16px;color:#059669;\"\u003eTotal Assets\u003c/span\u003e\n\u003cspan id=\"ssTotalAsset\" style=\"font-size:28px;font-weight:bold;color:#059669;\"\u003e¥0\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Liabilities section --\u003e\n\u003cdiv style=\"padding:24px;border:2px solid #dc2626;border-radius:12px;background:#fef2f2;margin-bottom:20px;\"\u003e\n\u003ch2 style=\"margin:0 0 20px 0;font-size:20px;color:#dc2626;\"\u003eLiabilities\u003c/h2\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:14px;\"\u003eMortgage Balance\u003c/label\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cinput type=\"number\" id=\"ssHomeLoan\" min=\"0\" step=\"1\" value=\"0\" oninput=\"calcSS()\" style=\"flex:1;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:16px;\"\u003e\n\u003cspan style=\"color:#64748b;white-space:nowrap;\"\u003e万円 (¥10K units)\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:14px;\"\u003eAuto Loan\u003c/label\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cinput type=\"number\" id=\"ssCarLoan\" min=\"0\" step=\"1\" value=\"0\" oninput=\"calcSS()\" style=\"flex:1;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:16px;\"\u003e\n\u003cspan style=\"color:#64748b;white-space:nowrap;\"\u003e万円 (¥10K units)\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:14px;\"\u003eStudent Loan / Education Debt\u003c/label\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cinput type=\"number\" id=\"ssEduLoan\" min=\"0\" step=\"1\" value=\"0\" oninput=\"calcSS()\" style=\"flex:1;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:16px;\"\u003e\n\u003cspan style=\"color:#64748b;white-space:nowrap;\"\u003e万円 (¥10K units)\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:14px;\"\u003eCredit Card Balance\u003c/label\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cinput type=\"number\" id=\"ssCreditCard\" min=\"0\" step=\"1\" value=\"0\" oninput=\"calcSS()\" style=\"flex:1;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:16px;\"\u003e\n\u003cspan style=\"color:#64748b;white-space:nowrap;\"\u003e万円 (¥10K units)\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:14px;\"\u003eOther Debt\u003c/label\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cinput type=\"number\" id=\"ssOtherDebt\" min=\"0\" step=\"1\" value=\"0\" oninput=\"calcSS()\" style=\"flex:1;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:16px;\"\u003e\n\u003cspan style=\"color:#64748b;white-space:nowrap;\"\u003e万円 (¥10K units)\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:16px;background:#fee2e2;border-radius:8px;display:flex;justify-content:space-between;align-items:center;\"\u003e\n\u003cspan style=\"font-weight:bold;font-size:16px;color:#dc2626;\"\u003eTotal Liabilities\u003c/span\u003e\n\u003cspan id=\"ssTotalDebt\" style=\"font-size:28px;font-weight:bold;color:#dc2626;\"\u003e¥0\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Results panel --\u003e\n\u003cdiv id=\"ssResults\" style=\"padding:24px;border-radius:12px;background:#eef2ff;border:2px solid #4f46e5;margin-bottom:20px;\"\u003e\n\u003cdiv style=\"text-align:center;margin-bottom:24px;\"\u003e\n\u003cdiv style=\"font-size:14px;color:#64748b;margin-bottom:4px;\"\u003eNet Worth (Total Assets − Total Liabilities)\u003c/div\u003e\n\u003cdiv id=\"ssNetWorth\" style=\"font-size:48px;font-weight:bold;color:#4f46e5;line-height:1.1;\"\u003e¥0\u003c/div\u003e\n\u003cdiv id=\"ssDebtRatio\" style=\"font-size:14px;color:#64748b;margin-top:8px;\"\u003eDebt ratio: 0%\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Asset breakdown bar --\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003cdiv style=\"font-weight:bold;font-size:14px;margin-bottom:8px;\"\u003eAsset Breakdown\u003c/div\u003e\n\u003cdiv style=\"height:28px;background:#e2e8f0;border-radius:14px;overflow:hidden;display:flex;\"\u003e\n\u003cdiv id=\"ssBarCash\" style=\"background:#059669;height:100%;transition:width 0.3s;width:0%;\" title=\"Cash \u0026 Deposits\"\u003e\u003c/div\u003e\n\u003cdiv id=\"ssBarInvest\" style=\"background:#10b981;height:100%;transition:width 0.3s;width:0%;\" title=\"Investment Assets\"\u003e\u003c/div\u003e\n\u003cdiv id=\"ssBarRealty\" style=\"background:#34d399;height:100%;transition:width 0.3s;width:0%;\" title=\"Real Estate\"\u003e\u003c/div\u003e\n\u003cdiv id=\"ssBarOtherAsset\" style=\"background:#6ee7b7;height:100%;transition:width 0.3s;width:0%;\" title=\"Other\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;flex-wrap:wrap;gap:12px;font-size:12px;color:#64748b;margin-top:8px;\"\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#059669;border-radius:2px;\"\u003e\u003c/span\u003e Cash \u0026 Deposits\u003c/span\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#10b981;border-radius:2px;\"\u003e\u003c/span\u003e Investments\u003c/span\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#34d399;border-radius:2px;\"\u003e\u003c/span\u003e Real Estate\u003c/span\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#6ee7b7;border-radius:2px;\"\u003e\u003c/span\u003e Other\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Debt breakdown bar --\u003e\n\u003cdiv id=\"ssDebtBreakdownWrap\"\u003e\n\u003cdiv style=\"font-weight:bold;font-size:14px;margin-bottom:8px;\"\u003eLiability Breakdown\u003c/div\u003e\n\u003cdiv style=\"height:28px;background:#e2e8f0;border-radius:14px;overflow:hidden;display:flex;\"\u003e\n\u003cdiv id=\"ssBarHomeLoan\" style=\"background:#dc2626;height:100%;transition:width 0.3s;width:0%;\" title=\"Mortgage\"\u003e\u003c/div\u003e\n\u003cdiv id=\"ssBarCarLoan\" style=\"background:#ef4444;height:100%;transition:width 0.3s;width:0%;\" title=\"Auto Loan\"\u003e\u003c/div\u003e\n\u003cdiv id=\"ssBarEduLoan\" style=\"background:#f87171;height:100%;transition:width 0.3s;width:0%;\" title=\"Student Loan\"\u003e\u003c/div\u003e\n\u003cdiv id=\"ssBarCredit\" style=\"background:#fca5a5;height:100%;transition:width 0.3s;width:0%;\" title=\"Credit Card\"\u003e\u003c/div\u003e\n\u003cdiv id=\"ssBarOtherDebt\" style=\"background:#fecaca;height:100%;transition:width 0.3s;width:0%;\" title=\"Other Debt\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;flex-wrap:wrap;gap:12px;font-size:12px;color:#64748b;margin-top:8px;\"\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#dc2626;border-radius:2px;\"\u003e\u003c/span\u003e Mortgage\u003c/span\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#ef4444;border-radius:2px;\"\u003e\u003c/span\u003e Auto\u003c/span\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#f87171;border-radius:2px;\"\u003e\u003c/span\u003e Student\u003c/span\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#fca5a5;border-radius:2px;\"\u003e\u003c/span\u003e Credit Card\u003c/span\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#fecaca;border-radius:2px;\"\u003e\u003c/span\u003e Other\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Age comparison --\u003e\n\u003cdiv style=\"padding:24px;border-radius:12px;background:#f8fafc;border:1px solid #e2e8f0;margin-bottom:20px;\"\u003e\n\u003ch3 style=\"margin:0 0 16px 0;font-size:18px;\"\u003eCompare Against Age-Group Averages\u003c/h3\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;font-size:14px;\"\u003eYour Age Group\u003c/label\u003e\n\u003cselect id=\"ssAge\" onchange=\"calcSS()\" style=\"width:100%;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:15px;\"\u003e\n\u003coption value=\"\"\u003eSelect your age group\u003c/option\u003e\n\u003coption value=\"20\"\u003e20s\u003c/option\u003e\n\u003coption value=\"30\"\u003e30s\u003c/option\u003e\n\u003coption value=\"40\"\u003e40s\u003c/option\u003e\n\u003coption value=\"50\"\u003e50s\u003c/option\u003e\n\u003coption value=\"60\"\u003e60s\u003c/option\u003e\n\u003coption value=\"70\"\u003e70+\u003c/option\u003e\n\u003c/select\u003e\n\u003c/div\u003e\n\u003ctable style=\"width:100%;border-collapse:collapse;font-size:14px;\"\u003e\n\u003cthead\u003e\n\u003ctr style=\"background:#4f46e5;color:white;\"\u003e\n\u003cth style=\"padding:10px 12px;text-align:left;border-radius:8px 0 0 0;\"\u003eAge Group\u003c/th\u003e\n\u003cth style=\"padding:10px 12px;text-align:right;\"\u003eMedian Net Worth (2+ person HH)\u003c/th\u003e\n\u003cth style=\"padding:10px 12px;text-align:center;border-radius:0 8px 0 0;\"\u003evs. You\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody id=\"ssAgeTable\"\u003e\n\u003ctr style=\"background:white;\"\u003e\u003ctd style=\"padding:10px 12px;border-bottom:1px solid #e2e8f0;\"\u003e20s\u003c/td\u003e\u003ctd style=\"padding:10px 12px;text-align:right;border-bottom:1px solid #e2e8f0;\"\u003e¥1,650,000\u003c/td\u003e\u003ctd style=\"padding:10px 12px;text-align:center;border-bottom:1px solid #e2e8f0;\" id=\"ssCmp20\"\u003e—\u003c/td\u003e\u003c/tr\u003e\n\u003ctr style=\"background:#f8fafc;\"\u003e\u003ctd style=\"padding:10px 12px;border-bottom:1px solid #e2e8f0;\"\u003e30s\u003c/td\u003e\u003ctd style=\"padding:10px 12px;text-align:right;border-bottom:1px solid #e2e8f0;\"\u003e¥5,260,000\u003c/td\u003e\u003ctd style=\"padding:10px 12px;text-align:center;border-bottom:1px solid #e2e8f0;\" id=\"ssCmp30\"\u003e—\u003c/td\u003e\u003c/tr\u003e\n\u003ctr style=\"background:white;\"\u003e\u003ctd style=\"padding:10px 12px;border-bottom:1px solid #e2e8f0;\"\u003e40s\u003c/td\u003e\u003ctd style=\"padding:10px 12px;text-align:right;border-bottom:1px solid #e2e8f0;\"\u003e¥8,250,000\u003c/td\u003e\u003ctd style=\"padding:10px 12px;text-align:center;border-bottom:1px solid #e2e8f0;\" id=\"ssCmp40\"\u003e—\u003c/td\u003e\u003c/tr\u003e\n\u003ctr style=\"background:#f8fafc;\"\u003e\u003ctd style=\"padding:10px 12px;border-bottom:1px solid #e2e8f0;\"\u003e50s\u003c/td\u003e\u003ctd style=\"padding:10px 12px;text-align:right;border-bottom:1px solid #e2e8f0;\"\u003e¥12,530,000\u003c/td\u003e\u003ctd style=\"padding:10px 12px;text-align:center;border-bottom:1px solid #e2e8f0;\" id=\"ssCmp50\"\u003e—\u003c/td\u003e\u003c/tr\u003e\n\u003ctr style=\"background:white;\"\u003e\u003ctd style=\"padding:10px 12px;border-bottom:1px solid #e2e8f0;\"\u003e60s\u003c/td\u003e\u003ctd style=\"padding:10px 12px;text-align:right;border-bottom:1px solid #e2e8f0;\"\u003e¥18,190,000\u003c/td\u003e\u003ctd style=\"padding:10px 12px;text-align:center;border-bottom:1px solid #e2e8f0;\" id=\"ssCmp60\"\u003e—\u003c/td\u003e\u003c/tr\u003e\n\u003ctr style=\"background:#f8fafc;\"\u003e\u003ctd style=\"padding:10px 12px;\"\u003e70+\u003c/td\u003e\u003ctd style=\"padding:10px 12px;text-align:right;\"\u003e¥19,050,000\u003c/td\u003e\u003ctd style=\"padding:10px 12px;text-align:center;\" id=\"ssCmp70\"\u003e—\u003c/td\u003e\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cdiv id=\"ssAgeComment\" style=\"margin-top:12px;padding:12px;border-radius:8px;font-size:14px;display:none;\"\u003e\u003c/div\u003e\n\u003cp style=\"font-size:12px;color:#94a3b8;margin-top:8px;margin-bottom:0;\"\u003eSource: Survey on Household Financial Behavior (Central Council for Financial Services Information, Japan). Values shown in JPY.\u003c/p\u003e","title":"Asset Allocation Simulator | Calculate Total Assets \u0026 Net Worth Automatically [2026]"},{"content":"This page contains affiliate links. We may earn a commission at no extra cost to you if you make a purchase through these links. Our editorial opinions remain independent.\nBMI Calculator Your Body Mass Index (BMI) is a simple number derived from your height and weight that helps gauge whether you are in a healthy weight range. Use this free calculator to find your BMI instantly — no sign-up required. Toggle between Imperial (lb/ft) and Metric (kg/cm), and get a color-coded result along with your healthy weight range and a personalized goal panel.\nImperial (lb / ft-in) Metric (kg / cm) \u0026lt;!-- Imperial Inputs --\u0026gt; \u0026lt;div id=\u0026quot;bmi-imperial-inputs\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;bmi-grid\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;bmi-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Height\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;bmi-height-imperial\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;imp-ft\u0026quot; placeholder=\u0026quot;5\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;8\u0026quot; inputmode=\u0026quot;decimal\u0026quot;\u0026gt; \u0026lt;span\u0026gt;ft\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;imp-in\u0026quot; placeholder=\u0026quot;7\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;11\u0026quot; inputmode=\u0026quot;decimal\u0026quot;\u0026gt; \u0026lt;span\u0026gt;in\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bmi-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Weight (lbs)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;imp-lbs\u0026quot; placeholder=\u0026quot;154\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;1000\u0026quot; inputmode=\u0026quot;decimal\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bmi-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Age (optional)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;imp-age\u0026quot; placeholder=\u0026quot;30\u0026quot; min=\u0026quot;2\u0026quot; max=\u0026quot;120\u0026quot; inputmode=\u0026quot;decimal\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bmi-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Gender\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;bmi-gender-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;\u0026lt;input type=\u0026quot;radio\u0026quot; name=\u0026quot;bmi-gender-imp\u0026quot; value=\u0026quot;male\u0026quot; checked\u0026gt; Male\u0026lt;/label\u0026gt; \u0026lt;label\u0026gt;\u0026lt;input type=\u0026quot;radio\u0026quot; name=\u0026quot;bmi-gender-imp\u0026quot; value=\u0026quot;female\u0026quot;\u0026gt; Female\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Metric Inputs --\u0026gt; \u0026lt;div id=\u0026quot;bmi-metric-inputs\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;bmi-grid\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;bmi-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Height (cm)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;met-cm\u0026quot; placeholder=\u0026quot;170\u0026quot; min=\u0026quot;50\u0026quot; max=\u0026quot;300\u0026quot; inputmode=\u0026quot;decimal\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bmi-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Weight (kg)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;met-kg\u0026quot; placeholder=\u0026quot;70\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;500\u0026quot; inputmode=\u0026quot;decimal\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bmi-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Age (optional)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;met-age\u0026quot; placeholder=\u0026quot;30\u0026quot; min=\u0026quot;2\u0026quot; max=\u0026quot;120\u0026quot; inputmode=\u0026quot;decimal\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bmi-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Gender\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;bmi-gender-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;\u0026lt;input type=\u0026quot;radio\u0026quot; name=\u0026quot;bmi-gender-met\u0026quot; value=\u0026quot;male\u0026quot; checked\u0026gt; Male\u0026lt;/label\u0026gt; \u0026lt;label\u0026gt;\u0026lt;input type=\u0026quot;radio\u0026quot; name=\u0026quot;bmi-gender-met\u0026quot; value=\u0026quot;female\u0026quot;\u0026gt; Female\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;bmi-calc-btn\u0026quot; onclick=\u0026quot;bmiCalculate()\u0026quot;\u0026gt;Calculate My BMI\u0026lt;/button\u0026gt; \u0026lt;!-- Big Number --\u0026gt; \u0026lt;div class=\u0026quot;bmi-card\u0026quot; style=\u0026quot;padding:0;overflow:hidden\u0026quot;\u0026gt; \u0026lt;div id=\u0026quot;bmi-number-wrap\u0026quot; class=\u0026quot;bmi-result-number-wrap\u0026quot; style=\u0026quot;margin:0;border-radius:0\u0026quot;\u0026gt; \u0026lt;div id=\u0026quot;bmi-number\u0026quot; class=\u0026quot;bmi-result-number\u0026quot;\u0026gt;--\u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;bmi-cat-text\u0026quot; class=\u0026quot;bmi-result-label\u0026quot;\u0026gt;--\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div style=\u0026quot;padding:1.25rem\u0026quot;\u0026gt; \u0026lt;!-- Scale Bar --\u0026gt; \u0026lt;div class=\u0026quot;bmi-scale-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;bmi-scale-label\u0026quot;\u0026gt;BMI Scale\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bmi-scale-track\u0026quot;\u0026gt; \u0026lt;div id=\u0026quot;bmi-pointer\u0026quot; class=\u0026quot;bmi-scale-pointer\u0026quot; style=\u0026quot;left:0%\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bmi-scale-ticks\u0026quot;\u0026gt; \u0026lt;span\u0026gt;10\u0026lt;/span\u0026gt; \u0026lt;span\u0026gt;18.5\u0026lt;/span\u0026gt; \u0026lt;span\u0026gt;25\u0026lt;/span\u0026gt; \u0026lt;span\u0026gt;30\u0026lt;/span\u0026gt; \u0026lt;span\u0026gt;40+\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Info Boxes --\u0026gt; \u0026lt;div class=\u0026quot;bmi-info-grid\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;bmi-info-box\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;bmi-info-box-title\u0026quot;\u0026gt;Your BMI\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bmi-info-box-value\u0026quot; id=\u0026quot;bmi-info-number\u0026quot;\u0026gt;--\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bmi-info-box\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;bmi-info-box-title\u0026quot;\u0026gt;Category\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bmi-info-box-value\u0026quot; id=\u0026quot;bmi-info-cat\u0026quot;\u0026gt;--\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bmi-info-box\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;bmi-info-box-title\u0026quot;\u0026gt;Healthy Weight Range\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bmi-info-box-value\u0026quot; id=\u0026quot;bmi-info-range\u0026quot;\u0026gt;--\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bmi-info-box\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;bmi-info-box-title\u0026quot;\u0026gt;Your Weight\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bmi-info-box-value\u0026quot; id=\u0026quot;bmi-info-weight\u0026quot;\u0026gt;--\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Goal Panel --\u0026gt; \u0026lt;div id=\u0026quot;bmi-goal-panel\u0026quot; class=\u0026quot;bmi-goal-panel bmi-on-track\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;bmi-goal-title\u0026quot; id=\u0026quot;bmi-goal-title\u0026quot;\u0026gt;Status\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bmi-goal-text\u0026quot; id=\u0026quot;bmi-goal-text\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Chart Reference --\u0026gt; \u0026lt;div class=\u0026quot;bmi-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;bmi-chart-title\u0026quot;\u0026gt;BMI Category Reference Chart\u0026lt;/div\u0026gt; \u0026lt;table class=\u0026quot;bmi-chart-table\u0026quot;\u0026gt; \u0026lt;thead\u0026gt; \u0026lt;tr\u0026gt;\u0026lt;th\u0026gt;Category\u0026lt;/th\u0026gt;\u0026lt;th\u0026gt;BMI Range\u0026lt;/th\u0026gt;\u0026lt;/tr\u0026gt; \u0026lt;/thead\u0026gt; \u0026lt;tbody id=\u0026quot;bmi-chart-body\u0026quot;\u0026gt; \u0026lt;tr data-cat=\u0026quot;underweight\u0026quot;\u0026gt;\u0026lt;td\u0026gt;Underweight\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;\u0026amp;lt; 18.5\u0026lt;/td\u0026gt;\u0026lt;/tr\u0026gt; \u0026lt;tr data-cat=\u0026quot;normal\u0026quot;\u0026gt;\u0026lt;td\u0026gt;Normal\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;18.5 – 24.9\u0026lt;/td\u0026gt;\u0026lt;/tr\u0026gt; \u0026lt;tr data-cat=\u0026quot;overweight\u0026quot;\u0026gt;\u0026lt;td\u0026gt;Overweight\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;25.0 – 29.9\u0026lt;/td\u0026gt;\u0026lt;/tr\u0026gt; \u0026lt;tr data-cat=\u0026quot;obese1\u0026quot;\u0026gt;\u0026lt;td\u0026gt;Obese Class I\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;30.0 – 34.9\u0026lt;/td\u0026gt;\u0026lt;/tr\u0026gt; \u0026lt;tr data-cat=\u0026quot;obese2\u0026quot;\u0026gt;\u0026lt;td\u0026gt;Obese Class II\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;35.0 – 39.9\u0026lt;/td\u0026gt;\u0026lt;/tr\u0026gt; \u0026lt;tr data-cat=\u0026quot;obese3\u0026quot;\u0026gt;\u0026lt;td\u0026gt;Obese Class III\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;\u0026amp;#8805; 40.0\u0026lt;/td\u0026gt;\u0026lt;/tr\u0026gt; \u0026lt;/tbody\u0026gt; \u0026lt;/table\u0026gt; \u0026lt;/div\u0026gt; BMI is a screening tool, not a diagnostic measure. It does not account for muscle mass, bone density, or fat distribution. Consult a qualified healthcare professional for personalized health and weight advice.\nRelated Tools Create a monthly budget → Budget Planner Calculate any percentage → Percentage Calculator ","permalink":"https://productivity-works.com/tools/bmi-calculator/","summary":"\u003cp\u003e\u003cem\u003eThis page contains affiliate links. We may earn a commission at no extra cost to you if you make a purchase through these links. Our editorial opinions remain independent.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"bmi-calculator\"\u003eBMI Calculator\u003c/h1\u003e\n\u003cp\u003eYour \u003cstrong\u003eBody Mass Index (BMI)\u003c/strong\u003e is a simple number derived from your height and weight that helps gauge whether you are in a healthy weight range. Use this free calculator to find your BMI instantly — no sign-up required. Toggle between Imperial (lb/ft) and Metric (kg/cm), and get a color-coded result along with your healthy weight range and a personalized goal panel.\u003c/p\u003e","title":"BMI Calculator | Check Your Body Mass Index Instantly"},{"content":" Character limit: 0 / 0 Limit exceeded Character limit: Twitter/X (280) Instagram (2200) Blog Title (60) Meta Description (160) LinkedIn Post (500) No Limit Copy UPPERCASE lowercase Title Case Clear Characters (with spaces) 0 incl. spaces Characters (no spaces) 0 excl. spaces Words 0 words Sentences 0 sentences Paragraphs 0 paragraphs Reading Time 0 min (200 wpm) Speaking Time 0 min (130 wpm) Byte Size 0 bytes (UTF-8) Top 10 Keywords by Frequency Rank Keyword Count Frequency Enter text above to see keyword frequency Copied to clipboard How to Use This Tool Paste or type any text into the box above. Character count, word count, reading time, and all other statistics update instantly as you type. The tool supports both English and Japanese text (CJK character detection included).\nUse the character limit mode to check against platform restrictions — simply click a preset (Twitter/X 280, Meta Description 160, etc.) or enter your own limit. A progress bar will show how close you are to the ceiling.\nThe keyword frequency table filters out common stop words and ranks meaningful terms by how often they appear — useful for checking SEO keyword density or spotting unintentional repetition.\nWhy Character Count Matters Every platform has different constraints. Twitter/X allows 280 characters, Instagram captions get truncated after a certain length, and Google typically displays 50–60 characters of a page title in search results. Knowing your exact character count before publishing prevents truncation surprises.\nFor academic and business writing, many submissions require a word or character count within a specific range. This tool supports both characters-with-spaces and characters-without-spaces counts to match any submission guideline.\nReading time and speaking time estimates help you calibrate content length for your audience — whether you\u0026rsquo;re writing a 5-minute blog post or a 10-minute speech.\nRelated Tools Stay focused while writing -\u0026gt; Compound Interest Calculator Calculate percentages instantly -\u0026gt; Percentage Calculator Simulate your NISA investment growth -\u0026gt; NISA Investment Simulator ","permalink":"https://productivity-works.com/tools/japanese-character-counter/","summary":"\u003cdiv id=\"wordcount-app\"\u003e\n\u003cstyle\u003e\n#wordcount-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans JP', sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 16px;\n  color: #1e1b4b;\n}\n\n#wordcount-app * {\n  box-sizing: border-box;\n}\n\n/* Textarea */\n#wc-textarea {\n  width: 100%;\n  min-height: 220px;\n  padding: 14px 16px;\n  font-size: 15px;\n  line-height: 1.7;\n  border: 2px solid #c4b5fd;\n  border-radius: 12px;\n  resize: vertical;\n  outline: none;\n  transition: border-color 0.2s;\n  background: #faf5ff;\n  color: #1e1b4b;\n  font-family: inherit;\n}\n\n#wc-textarea:focus {\n  border-color: #7c3aed;\n  background: #fff;\n  box-shadow: 0 0 0 3px rgba(124,58,237,0.12);\n}\n\n/* Limit bar */\n#wc-limit-bar-wrap {\n  margin-top: 10px;\n  display: none;\n}\n\n#wc-limit-label {\n  font-size: 13px;\n  color: #6d28d9;\n  margin-bottom: 4px;\n  display: flex;\n  justify-content: space-between;\n}\n\n#wc-progress-track {\n  width: 100%;\n  height: 10px;\n  background: #ede9fe;\n  border-radius: 99px;\n  overflow: hidden;\n}\n\n#wc-progress-bar {\n  height: 100%;\n  background: linear-gradient(90deg, #7c3aed, #a78bfa);\n  border-radius: 99px;\n  transition: width 0.3s, background 0.3s;\n  width: 0%;\n}\n\n#wc-progress-bar.over {\n  background: linear-gradient(90deg, #dc2626, #f87171);\n}\n\n/* Limit controls */\n#wc-limit-controls {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  align-items: center;\n  margin-top: 12px;\n}\n\n#wc-limit-controls label {\n  font-size: 13px;\n  color: #5b21b6;\n  white-space: nowrap;\n}\n\n#wc-limit-input {\n  width: 90px;\n  padding: 5px 8px;\n  border: 1px solid #c4b5fd;\n  border-radius: 6px;\n  font-size: 13px;\n  color: #1e1b4b;\n  background: #faf5ff;\n  outline: none;\n}\n\n#wc-limit-input:focus {\n  border-color: #7c3aed;\n}\n\n.wc-preset-btn {\n  padding: 4px 10px;\n  background: #ede9fe;\n  color: #5b21b6;\n  border: 1px solid #c4b5fd;\n  border-radius: 99px;\n  font-size: 12px;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s;\n  white-space: nowrap;\n}\n\n.wc-preset-btn:hover {\n  background: #7c3aed;\n  color: #fff;\n  border-color: #7c3aed;\n}\n\n/* Action buttons */\n#wc-action-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-top: 14px;\n}\n\n.wc-action-btn {\n  padding: 7px 14px;\n  border-radius: 8px;\n  border: none;\n  font-size: 13px;\n  cursor: pointer;\n  font-family: inherit;\n  font-weight: 600;\n  transition: background 0.15s, opacity 0.15s, transform 0.1s;\n}\n\n.wc-action-btn:active {\n  transform: scale(0.97);\n}\n\n.wc-btn-primary {\n  background: #7c3aed;\n  color: #fff;\n}\n\n.wc-btn-primary:hover {\n  background: #6d28d9;\n}\n\n.wc-btn-secondary {\n  background: #ede9fe;\n  color: #5b21b6;\n}\n\n.wc-btn-secondary:hover {\n  background: #ddd6fe;\n}\n\n.wc-btn-danger {\n  background: #fee2e2;\n  color: #b91c1c;\n}\n\n.wc-btn-danger:hover {\n  background: #fecaca;\n}\n\n/* Stats grid */\n#wc-stats-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));\n  gap: 12px;\n  margin-top: 20px;\n}\n\n.wc-stat-card {\n  background: linear-gradient(135deg, #faf5ff 0%, #ede9fe 100%);\n  border: 1px solid #c4b5fd;\n  border-radius: 12px;\n  padding: 14px 16px;\n  text-align: center;\n  transition: box-shadow 0.2s, transform 0.2s;\n}\n\n.wc-stat-card:hover {\n  box-shadow: 0 4px 16px rgba(124,58,237,0.15);\n  transform: translateY(-2px);\n}\n\n.wc-stat-label {\n  font-size: 11px;\n  color: #6d28d9;\n  font-weight: 600;\n  letter-spacing: 0.03em;\n  margin-bottom: 6px;\n  text-transform: uppercase;\n}\n\n.wc-stat-value {\n  font-size: 26px;\n  font-weight: 700;\n  color: #7c3aed;\n  line-height: 1.1;\n}\n\n.wc-stat-unit {\n  font-size: 11px;\n  color: #8b5cf6;\n  margin-top: 3px;\n}\n\n/* Section title */\n.wc-section-title {\n  font-size: 14px;\n  font-weight: 700;\n  color: #5b21b6;\n  margin: 24px 0 10px;\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n\n.wc-section-title::before {\n  content: '';\n  display: inline-block;\n  width: 4px;\n  height: 16px;\n  background: #7c3aed;\n  border-radius: 2px;\n}\n\n/* Frequency table */\n#wc-freq-table-wrap {\n  overflow-x: auto;\n}\n\n#wc-freq-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 13px;\n}\n\n#wc-freq-table th {\n  background: #7c3aed;\n  color: #fff;\n  padding: 8px 12px;\n  text-align: left;\n  font-weight: 600;\n}\n\n#wc-freq-table th:first-child { border-radius: 8px 0 0 0; }\n#wc-freq-table th:last-child  { border-radius: 0 8px 0 0; }\n\n#wc-freq-table td {\n  padding: 8px 12px;\n  border-bottom: 1px solid #ede9fe;\n  color: #1e1b4b;\n}\n\n#wc-freq-table tr:nth-child(even) td {\n  background: #faf5ff;\n}\n\n#wc-freq-table tr:hover td {\n  background: #ede9fe;\n}\n\n.wc-freq-bar-cell {\n  width: 140px;\n}\n\n.wc-freq-bar-bg {\n  background: #ede9fe;\n  border-radius: 99px;\n  height: 8px;\n  overflow: hidden;\n}\n\n.wc-freq-bar-fill {\n  height: 100%;\n  background: linear-gradient(90deg, #7c3aed, #a78bfa);\n  border-radius: 99px;\n  transition: width 0.4s;\n}\n\n/* Toast */\n#wc-toast {\n  position: fixed;\n  bottom: 28px;\n  left: 50%;\n  transform: translateX(-50%) translateY(20px);\n  background: #1e1b4b;\n  color: #fff;\n  padding: 10px 24px;\n  border-radius: 99px;\n  font-size: 14px;\n  opacity: 0;\n  pointer-events: none;\n  transition: opacity 0.3s, transform 0.3s;\n  z-index: 9999;\n  white-space: nowrap;\n}\n\n#wc-toast.show {\n  opacity: 1;\n  transform: translateX(-50%) translateY(0);\n}\n\n/* Responsive */\n@media (max-width: 600px) {\n  #wc-stats-grid {\n    grid-template-columns: repeat(2, 1fr);\n    gap: 8px;\n  }\n  .wc-stat-value { font-size: 20px; }\n  #wc-action-row { gap: 6px; }\n  .wc-action-btn { font-size: 12px; padding: 6px 10px; }\n}\n\u003c/style\u003e\n\u003c!-- Textarea --\u003e\n\u003ctextarea id=\"wc-textarea\" placeholder=\"Type or paste your text here. Character count, word count, reading time, and more are calculated in real time.\"\u003e\u003c/textarea\u003e\n\u003c!-- Character limit bar --\u003e\n\u003cdiv id=\"wc-limit-bar-wrap\"\u003e\n  \u003cdiv id=\"wc-limit-label\"\u003e\n    \u003cspan id=\"wc-limit-text\"\u003eCharacter limit: 0 / 0\u003c/span\u003e\n    \u003cspan id=\"wc-limit-over\" style=\"color:#dc2626;display:none;\"\u003eLimit exceeded\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv id=\"wc-progress-track\"\u003e\u003cdiv id=\"wc-progress-bar\"\u003e\u003c/div\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Limit controls --\u003e\n\u003cdiv id=\"wc-limit-controls\"\u003e\n  \u003clabel\u003eCharacter limit:\u003c/label\u003e\n  \u003cinput type=\"number\" id=\"wc-limit-input\" placeholder=\"Max chars\" min=\"1\" /\u003e\n  \u003cbutton class=\"wc-preset-btn\" data-limit=\"280\"\u003eTwitter/X (280)\u003c/button\u003e\n  \u003cbutton class=\"wc-preset-btn\" data-limit=\"2200\"\u003eInstagram (2200)\u003c/button\u003e\n  \u003cbutton class=\"wc-preset-btn\" data-limit=\"60\"\u003eBlog Title (60)\u003c/button\u003e\n  \u003cbutton class=\"wc-preset-btn\" data-limit=\"160\"\u003eMeta Description (160)\u003c/button\u003e\n  \u003cbutton class=\"wc-preset-btn\" data-limit=\"500\"\u003eLinkedIn Post (500)\u003c/button\u003e\n  \u003cbutton class=\"wc-preset-btn\" data-limit=\"0\"\u003eNo Limit\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Action buttons --\u003e\n\u003cdiv id=\"wc-action-row\"\u003e\n  \u003cbutton class=\"wc-action-btn wc-btn-primary\" id=\"wc-copy-btn\"\u003eCopy\u003c/button\u003e\n  \u003cbutton class=\"wc-action-btn wc-btn-secondary\" id=\"wc-upper-btn\"\u003eUPPERCASE\u003c/button\u003e\n  \u003cbutton class=\"wc-action-btn wc-btn-secondary\" id=\"wc-lower-btn\"\u003elowercase\u003c/button\u003e\n  \u003cbutton class=\"wc-action-btn wc-btn-secondary\" id=\"wc-title-btn\"\u003eTitle Case\u003c/button\u003e\n  \u003cbutton class=\"wc-action-btn wc-btn-danger\" id=\"wc-clear-btn\"\u003eClear\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Stats grid --\u003e\n\u003cdiv id=\"wc-stats-grid\"\u003e\n  \u003cdiv class=\"wc-stat-card\"\u003e\n    \u003cdiv class=\"wc-stat-label\"\u003eCharacters (with spaces)\u003c/div\u003e\n    \u003cdiv class=\"wc-stat-value\" id=\"wc-chars-with\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"wc-stat-unit\"\u003eincl. spaces\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wc-stat-card\"\u003e\n    \u003cdiv class=\"wc-stat-label\"\u003eCharacters (no spaces)\u003c/div\u003e\n    \u003cdiv class=\"wc-stat-value\" id=\"wc-chars-without\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"wc-stat-unit\"\u003eexcl. spaces\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wc-stat-card\"\u003e\n    \u003cdiv class=\"wc-stat-label\"\u003eWords\u003c/div\u003e\n    \u003cdiv class=\"wc-stat-value\" id=\"wc-words\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"wc-stat-unit\"\u003ewords\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wc-stat-card\"\u003e\n    \u003cdiv class=\"wc-stat-label\"\u003eSentences\u003c/div\u003e\n    \u003cdiv class=\"wc-stat-value\" id=\"wc-sentences\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"wc-stat-unit\"\u003esentences\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wc-stat-card\"\u003e\n    \u003cdiv class=\"wc-stat-label\"\u003eParagraphs\u003c/div\u003e\n    \u003cdiv class=\"wc-stat-value\" id=\"wc-paragraphs\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"wc-stat-unit\"\u003eparagraphs\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wc-stat-card\"\u003e\n    \u003cdiv class=\"wc-stat-label\"\u003eReading Time\u003c/div\u003e\n    \u003cdiv class=\"wc-stat-value\" id=\"wc-read-time\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"wc-stat-unit\"\u003emin (200 wpm)\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wc-stat-card\"\u003e\n    \u003cdiv class=\"wc-stat-label\"\u003eSpeaking Time\u003c/div\u003e\n    \u003cdiv class=\"wc-stat-value\" id=\"wc-speech-time\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"wc-stat-unit\"\u003emin (130 wpm)\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wc-stat-card\"\u003e\n    \u003cdiv class=\"wc-stat-label\"\u003eByte Size\u003c/div\u003e\n    \u003cdiv class=\"wc-stat-value\" id=\"wc-bytes\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"wc-stat-unit\"\u003ebytes (UTF-8)\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Keyword frequency --\u003e\n\u003cdiv class=\"wc-section-title\"\u003eTop 10 Keywords by Frequency\u003c/div\u003e\n\u003cdiv id=\"wc-freq-table-wrap\"\u003e\n  \u003ctable id=\"wc-freq-table\"\u003e\n    \u003cthead\u003e\n      \u003ctr\u003e\n        \u003cth\u003eRank\u003c/th\u003e\n        \u003cth\u003eKeyword\u003c/th\u003e\n        \u003cth\u003eCount\u003c/th\u003e\n        \u003cth class=\"wc-freq-bar-cell\"\u003eFrequency\u003c/th\u003e\n      \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody id=\"wc-freq-tbody\"\u003e\n      \u003ctr\u003e\u003ctd colspan=\"4\" style=\"color:#8b5cf6;text-align:center;padding:16px;\"\u003eEnter text above to see keyword frequency\u003c/td\u003e\u003c/tr\u003e\n    \u003c/tbody\u003e\n  \u003c/table\u003e\n\u003c/div\u003e\n\u003c!-- Toast --\u003e\n\u003cdiv id=\"wc-toast\"\u003eCopied to clipboard\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var ta = document.getElementById('wc-textarea');\n  var limitInput = document.getElementById('wc-limit-input');\n  var limitBarWrap = document.getElementById('wc-limit-bar-wrap');\n  var limitText = document.getElementById('wc-limit-text');\n  var limitOver = document.getElementById('wc-limit-over');\n  var progressBar = document.getElementById('wc-progress-bar');\n  var toast = document.getElementById('wc-toast');\n  var currentLimit = 0;\n\n  // English stop words\n  var stopWords = new Set([\n    'a','an','the','is','are','was','were','be','been','being','have','has','had',\n    'do','does','did','will','would','could','should','may','might','shall',\n    'and','or','but','if','in','on','at','to','for','of','with','by','from',\n    'this','that','these','those','it','its','we','our','you','your','they',\n    'their','i','my','me','he','she','his','her','him','us','not','no','so',\n    'up','out','as','into','about','than','then','when','where','which','who',\n    'what','how','all','each','every','both','few','more','most','other','some',\n    'such','only','own','same','too','very','just','because','while','through',\n    // Japanese stop words\n    'の','に','は','を','た','が','で','て','と','し','れ','さ','ある','いる','も',\n    'する','から','な','こと','として','い','や','れる','など','なり','ない','この',\n    'ため','その','あ','さ','せ','で','た','どの','という','とき','また','や'\n  ]);\n\n  // Byte length\n  function getByteLength(str) {\n    var b = 0;\n    for (var i = 0; i \u003c str.length; i++) {\n      var c = str.charCodeAt(i);\n      if (c \u003c= 0x7F) b += 1;\n      else if (c \u003c= 0x7FF) b += 2;\n      else if (c \u003c= 0xFFFF) b += 3;\n      else b += 4;\n    }\n    return b;\n  }\n\n  // Tokenize (English words + Japanese CJK blocks)\n  function tokenize(text) {\n    var tokens = [];\n    var en = text.match(/[a-zA-Z0-9]+/g) || [];\n    tokens = tokens.concat(en);\n    var ja = text.match(/[\\u3040-\\u309f\\u30a0-\\u30ff\\u4e00-\\u9fff\\uff66-\\uff9f]{2,}/g) || [];\n    tokens = tokens.concat(ja);\n    return tokens;\n  }\n\n  // Keyword frequency\n  function getFrequency(text) {\n    var tokens = tokenize(text);\n    var freq = {};\n    for (var i = 0; i \u003c tokens.length; i++) {\n      var w = tokens[i].toLowerCase();\n      if (stopWords.has(w)) continue;\n      if (w.length \u003c 2) continue;\n      freq[w] = (freq[w] || 0) + 1;\n    }\n    var arr = Object.keys(freq).map(function(k) { return { word: k, count: freq[k] }; });\n    arr.sort(function(a, b) { return b.count - a.count; });\n    return arr.slice(0, 10);\n  }\n\n  // Count paragraphs\n  function countParagraphs(text) {\n    if (!text.trim()) return 0;\n    var paras = text.split(/\\n\\s*\\n/).filter(function(p) { return p.trim().length \u003e 0; });\n    return paras.length || (text.trim() ? 1 : 0);\n  }\n\n  // Count sentences\n  function countSentences(text) {\n    if (!text.trim()) return 0;\n    var matches = text.match(/[.!?]+/g);\n    return matches ? matches.length : (text.trim() ? 1 : 0);\n  }\n\n  // Count words\n  function countWords(text) {\n    if (!text.trim()) return 0;\n    var enWords = (text.match(/[a-zA-Z0-9]+(?:['\\-][a-zA-Z0-9]+)*/g) || []).length;\n    var jaSegments = (text.match(/[\\u3040-\\u309f\\u30a0-\\u30ff\\u4e00-\\u9fff\\uff66-\\uff9f]+/g) || []);\n    var jaCount = 0;\n    for (var i = 0; i \u003c jaSegments.length; i++) {\n      jaCount += Math.ceil(jaSegments[i].length / 3);\n    }\n    return enWords + jaCount;\n  }\n\n  // Format time\n  function formatTime(minutes) {\n    if (minutes \u003c 1) return '\u003c 1';\n    return Math.ceil(minutes).toString();\n  }\n\n  // Update stats\n  function updateStats() {\n    var text = ta.value;\n    var withSpaces = text.length;\n    var withoutSpaces = text.replace(/\\s/g, '').length;\n    var words = countWords(text);\n    var sentences = countSentences(text);\n    var paragraphs = countParagraphs(text);\n    var readMin = words / 200;\n    var speechMin = words / 130;\n    var bytes = getByteLength(text);\n\n    document.getElementById('wc-chars-with').textContent = withSpaces.toLocaleString();\n    document.getElementById('wc-chars-without').textContent = withoutSpaces.toLocaleString();\n    document.getElementById('wc-words').textContent = words.toLocaleString();\n    document.getElementById('wc-sentences').textContent = sentences.toLocaleString();\n    document.getElementById('wc-paragraphs').textContent = paragraphs.toLocaleString();\n    document.getElementById('wc-read-time').textContent = formatTime(readMin);\n    document.getElementById('wc-speech-time').textContent = formatTime(speechMin);\n    document.getElementById('wc-bytes').textContent = bytes.toLocaleString();\n\n    // Limit bar\n    if (currentLimit \u003e 0) {\n      var pct = Math.min((withSpaces / currentLimit) * 100, 100);\n      progressBar.style.width = pct + '%';\n      limitText.textContent = 'Character limit: ' + withSpaces.toLocaleString() + ' / ' + currentLimit.toLocaleString();\n      if (withSpaces \u003e currentLimit) {\n        progressBar.classList.add('over');\n        limitOver.style.display = 'inline';\n      } else {\n        progressBar.classList.remove('over');\n        limitOver.style.display = 'none';\n      }\n    }\n\n    updateFreqTable(text);\n  }\n\n  // Update frequency table\n  function updateFreqTable(text) {\n    var tbody = document.getElementById('wc-freq-tbody');\n    var freq = getFrequency(text);\n    if (freq.length === 0) {\n      tbody.innerHTML = '\u003ctr\u003e\u003ctd colspan=\"4\" style=\"color:#8b5cf6;text-align:center;padding:16px;\"\u003eEnter text above to see keyword frequency\u003c/td\u003e\u003c/tr\u003e';\n      return;\n    }\n    var maxCount = freq[0].count;\n    var html = '';\n    for (var i = 0; i \u003c freq.length; i++) {\n      var pct = maxCount \u003e 0 ? Math.round((freq[i].count / maxCount) * 100) : 0;\n      html += '\u003ctr\u003e'\n        + '\u003ctd style=\"color:#8b5cf6;font-weight:700;\"\u003e' + (i + 1) + '\u003c/td\u003e'\n        + '\u003ctd style=\"font-weight:600;\"\u003e' + escHtml(freq[i].word) + '\u003c/td\u003e'\n        + '\u003ctd style=\"color:#7c3aed;font-weight:700;\"\u003e' + freq[i].count + '\u003c/td\u003e'\n        + '\u003ctd class=\"wc-freq-bar-cell\"\u003e\u003cdiv class=\"wc-freq-bar-bg\"\u003e\u003cdiv class=\"wc-freq-bar-fill\" style=\"width:' + pct + '%\"\u003e\u003c/div\u003e\u003c/div\u003e\u003c/td\u003e'\n        + '\u003c/tr\u003e';\n    }\n    tbody.innerHTML = html;\n  }\n\n  function escHtml(s) {\n    return s.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n  }\n\n  // Real-time update\n  ta.addEventListener('input', updateStats);\n\n  // Limit input\n  limitInput.addEventListener('input', function() {\n    var v = parseInt(limitInput.value, 10);\n    currentLimit = (isNaN(v) || v \u003c= 0) ? 0 : v;\n    limitBarWrap.style.display = currentLimit \u003e 0 ? 'block' : 'none';\n    updateStats();\n  });\n\n  // Preset buttons\n  document.querySelectorAll('.wc-preset-btn').forEach(function(btn) {\n    btn.addEventListener('click', function() {\n      var lim = parseInt(btn.getAttribute('data-limit'), 10);\n      currentLimit = lim;\n      limitInput.value = lim \u003e 0 ? lim : '';\n      limitBarWrap.style.display = currentLimit \u003e 0 ? 'block' : 'none';\n      updateStats();\n    });\n  });\n\n  // Copy\n  document.getElementById('wc-copy-btn').addEventListener('click', function() {\n    var text = ta.value;\n    if (!text) return;\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(text).then(showToast);\n    } else {\n      ta.select();\n      document.execCommand('copy');\n      showToast();\n    }\n  });\n\n  function showToast() {\n    toast.classList.add('show');\n    setTimeout(function() { toast.classList.remove('show'); }, 2000);\n  }\n\n  // Clear\n  document.getElementById('wc-clear-btn').addEventListener('click', function() {\n    ta.value = '';\n    updateStats();\n    ta.focus();\n  });\n\n  // UPPERCASE\n  document.getElementById('wc-upper-btn').addEventListener('click', function() {\n    ta.value = ta.value.toUpperCase();\n    updateStats();\n  });\n\n  // lowercase\n  document.getElementById('wc-lower-btn').addEventListener('click', function() {\n    ta.value = ta.value.toLowerCase();\n    updateStats();\n  });\n\n  // Title Case\n  document.getElementById('wc-title-btn').addEventListener('click', function() {\n    ta.value = ta.value.replace(/\\w\\S*/g, function(w) {\n      return w.charAt(0).toUpperCase() + w.slice(1).toLowerCase();\n    });\n    updateStats();\n  });\n\n  // Init\n  updateStats();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-to-use-this-tool\"\u003eHow to Use This Tool\u003c/h2\u003e\n\u003cp\u003ePaste or type any text into the box above. Character count, word count, reading time, and all other statistics update instantly as you type. The tool supports both English and Japanese text (CJK character detection included).\u003c/p\u003e","title":"Character Counter - Free Online Word \u0026 Character Count Tool"},{"content":" Coin Flipper Sound H T — Click to flip Flip Coin\nHeads label Tails label Statistics Reset 0 Heads 0 Tails — Heads % 0 Longest streak Flip History (last 50) No flips yet Flip Multiple Coins Number of coins Flip All Best of N Series Best of 3 Best of 5 Best of 7 New series Heads: 0 Tails: 0 Flip for Best of N More randomness tools:\nRoll dice → Dice Roller\nGenerate random numbers → Random Number Generator Related Tools Dice Roller Password Generator Random Number Generator ","permalink":"https://productivity-works.com/tools/coin-flipper/","summary":"\u003cdiv id=\"cf-app\"\u003e\n\u003cstyle\u003e\n#cf-app *,#cf-app *::before,#cf-app *::after{box-sizing:border-box;margin:0;padding:0}\n#cf-app{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;max-width:700px;margin:0 auto;padding:16px;color:#1e293b}\n#cf-app h2{font-size:1.15rem;font-weight:700;color:#0f172a;margin-bottom:14px}\n#cf-app .cf-section{background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:20px;margin-bottom:16px}\n\u003cp\u003e/* Coin */\n#cf-app .cf-coin-wrap{display:flex;justify-content:center;align-items:center;margin:8px 0 20px;perspective:600px}\n#cf-app .cf-coin{width:130px;height:130px;position:relative;transform-style:preserve-3d;transition:transform 0.05s;cursor:pointer;user-select:none}\n#cf-app .cf-coin.spinning{animation:cfSpin 0.8s ease-out forwards}\n#cf-app .cf-coin-face{position:absolute;width:100%;height:100%;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:2.6rem;font-weight:900;backface-visibility:hidden;box-shadow:0 4px 18px rgba(0,0,0,0.18)}\n#cf-app .cf-heads{background:linear-gradient(145deg,#f5c842,#e6a817);color:#7a4800;border:4px solid #c8860a}\n#cf-app .cf-tails{background:linear-gradient(145deg,#d4d4d4,#a0a0a0);color:#3a3a3a;border:4px solid #888;transform:rotateY(180deg)}\n@keyframes cfSpin{0%{transform:rotateY(0deg)}60%{transform:rotateY(900deg)}80%{transform:rotateY(1050deg)}90%{transform:rotateY(980deg)}100%{transform:rotateY(var(\u0026ndash;cf-end-rot,1080deg))}}\u003c/p\u003e\n\u003cp\u003e/* Result badge */\n#cf-app .cf-result{text-align:center;font-size:1.5rem;font-weight:800;margin-bottom:6px;min-height:36px;transition:color 0.3s}\n#cf-app .cf-result.heads{color:#b45309}\n#cf-app .cf-result.tails{color:#475569}\n#cf-app .cf-result-label{text-align:center;font-size:0.85rem;color:#64748b;margin-bottom:16px;min-height:20px}\u003c/p\u003e\n\u003cp\u003e/* Buttons */\n#cf-app .cf-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:11px 22px;border:none;border-radius:8px;font-size:1rem;font-weight:700;cursor:pointer;transition:background 0.18s,transform 0.1s}\n#cf-app .cf-btn:active{transform:scale(0.97)}\n#cf-app .cf-btn-primary{background:#2563eb;color:#fff;font-size:1.15rem;padding:14px 36px;border-radius:10px;width:100%;margin-bottom:10px;min-height:54px}\n#cf-app .cf-btn-primary:hover{background:#1d4ed8}\n#cf-app .cf-btn-primary:disabled{background:#93c5fd;cursor:not-allowed;transform:none}\n#cf-app .cf-btn-sm{background:#f1f5f9;color:#334155;font-size:0.85rem;padding:7px 14px;border-radius:7px;border:1px solid #e2e8f0}\n#cf-app .cf-btn-sm:hover{background:#e2e8f0}\n#cf-app .cf-btn-danger{background:#fee2e2;color:#b91c1c;border:1px solid #fecaca}\n#cf-app .cf-btn-danger:hover{background:#fecaca}\u003c/p\u003e","title":"Coin Flipper - Virtual Coin Toss"},{"content":"This article contains affiliate links. We may earn a commission at no extra cost to you.\nDebt Payoff Calculator Enter your debts and extra monthly payment to compare snowball (smallest balance first) vs avalanche (highest rate first) strategies.\nYour Debts + Add Debt\nExtra Monthly Payment ($) $0$200$2,000 Payoff Timeline Comparison Snowball vs Avalanche: Which Is Better? The two most popular debt payoff strategies are:\nStrategy How It Works Best For Snowball Pay off smallest balance first People who need quick wins for motivation Avalanche Pay off highest interest rate first Saving the most money on interest The avalanche method is mathematically optimal — you\u0026rsquo;ll always pay less interest. But the snowball method eliminates individual debts faster, which can keep you motivated.\nHow the Extra Payment Works Both strategies make minimum payments on all debts, then apply any extra money to the priority debt. Once that debt is paid off, the freed-up payment \u0026ldquo;rolls\u0026rdquo; into the next debt — creating a snowball effect.\nTips for Faster Debt Payoff Increase your extra payment — even $50 more per month makes a significant difference Use windfalls — tax refunds, bonuses, and side income can accelerate payoff Negotiate lower rates — call your credit card company and ask for a rate reduction Avoid new debt — pause credit card spending until existing balances are cleared Want to understand your full financial picture? Use our Budget Planner to find extra money for debt payments.\nPlanning for after you\u0026rsquo;re debt-free? Our Compound Interest Calculator shows how redirecting debt payments into investments can build wealth.\nRelated Tools\nLoan Repayment Calculator Budget Planner Emergency Fund Calculator US Salary Calculator Related Articles How to Pay Off Debt Fast: Best Strategies 2026 ","permalink":"https://productivity-works.com/tools/debt-payoff-calculator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links. We may earn a commission at no extra cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"debt-payoff-calculator\"\u003eDebt Payoff Calculator\u003c/h1\u003e\n\u003cp\u003eEnter your debts and extra monthly payment to compare \u003cstrong\u003esnowball\u003c/strong\u003e (smallest balance first) vs \u003cstrong\u003eavalanche\u003c/strong\u003e (highest rate first) strategies.\u003c/p\u003e\n\u003cdiv id=\"dp-calc\" style=\"max-width:720px;margin:0 auto;font-family:Inter,sans-serif;\"\u003e\n\u003cdiv style=\"padding:24px;border:2px solid #2563eb;border-radius:12px;background:#f8fafc;\"\u003e\n\u003ch3 style=\"margin:0 0 16px;color:#1e3a8a;font-size:16px;\"\u003eYour Debts\u003c/h3\u003e\n\u003cdiv id=\"debtList\"\u003e\u003c/div\u003e\n\u003cp\u003e\u003cbutton onclick=\"addDebt()\" style=\"margin-top:12px;padding:8px 20px;background:#2563eb;color:#fff;border:none;border-radius:8px;font-size:14px;cursor:pointer;\"\u003e+ Add Debt\u003c/button\u003e\u003c/p\u003e\n\u003cdiv style=\"margin-top:20px;padding-top:16px;border-top:1px solid #e2e8f0;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eExtra Monthly Payment ($)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"extraPay\" min=\"0\" max=\"2000\" step=\"25\" value=\"200\" oninput=\"calcDP()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e$0\u003c/span\u003e\u003cspan id=\"extraVal\" style=\"font-weight:bold;font-size:18px;color:#10b981;\"\u003e$200\u003c/span\u003e\u003cspan\u003e$2,000\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"dpResult\" style=\"margin-top:24px;\"\u003e\u003c/div\u003e\n\u003cdiv style=\"margin-top:24px;padding:24px;border:2px solid #8b5cf6;border-radius:12px;background:#faf5ff;\"\u003e\n\u003ch3 style=\"margin:0 0 16px;color:#6d28d9;font-size:16px;\"\u003ePayoff Timeline Comparison\u003c/h3\u003e\n\u003ccanvas id=\"dpChart\" width=\"680\" height=\"280\" style=\"width:100%;height:auto;\"\u003e\u003c/canvas\u003e\n\u003c/div\u003e\n\u003cdiv id=\"dpSchedule\" style=\"margin-top:24px;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nvar debts=[];\nvar debtId=0;\n\nfunction addDebt(name,bal,rate,minPay){\n  debts.push({id:debtId++,name:name||'Debt '+(debts.length+1),balance:bal||5000,rate:rate||18,minPay:minPay||150});\n  renderDebts();\n  calcDP();\n}\n\nfunction removeDebt(id){\n  debts=debts.filter(function(d){return d.id!==id;});\n  renderDebts();\n  calcDP();\n}\n\nfunction updateDebt(id,field,val){\n  for(var i=0;i\u003cdebts.length;i++){\n    if(debts[i].id===id){debts[i][field]=field==='name'?val:+val;break;}\n  }\n  calcDP();\n}\n\nfunction renderDebts(){\n  var html='';\n  for(var i=0;i\u003cdebts.length;i++){\n    var d=debts[i];\n    html+='\u003cdiv style=\"padding:12px;margin-bottom:8px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e';\n    html+='\u003cdiv style=\"display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;\"\u003e';\n    html+='\u003cinput type=\"text\" value=\"'+d.name+'\" onchange=\"updateDebt('+d.id+',\\'name\\',this.value)\" style=\"border:none;font-weight:bold;font-size:14px;color:#1e3a8a;width:60%;outline:none;\"\u003e';\n    html+='\u003cbutton onclick=\"removeDebt('+d.id+')\" style=\"background:none;border:none;color:#ef4444;cursor:pointer;font-size:18px;\"\u003ex\u003c/button\u003e';\n    html+='\u003c/div\u003e';\n    html+='\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;\"\u003e';\n    html+='\u003cdiv\u003e\u003clabel style=\"display:block;font-size:11px;color:#64748b;\"\u003eBalance ($)\u003c/label\u003e\u003cinput type=\"number\" value=\"'+d.balance+'\" onchange=\"updateDebt('+d.id+',\\'balance\\',this.value)\" style=\"width:100%;padding:6px;border:1px solid #cbd5e1;border-radius:4px;font-size:13px;\"\u003e\u003c/div\u003e';\n    html+='\u003cdiv\u003e\u003clabel style=\"display:block;font-size:11px;color:#64748b;\"\u003eAPR (%)\u003c/label\u003e\u003cinput type=\"number\" value=\"'+d.rate+'\" step=\"0.1\" onchange=\"updateDebt('+d.id+',\\'rate\\',this.value)\" style=\"width:100%;padding:6px;border:1px solid #cbd5e1;border-radius:4px;font-size:13px;\"\u003e\u003c/div\u003e';\n    html+='\u003cdiv\u003e\u003clabel style=\"display:block;font-size:11px;color:#64748b;\"\u003eMin Payment ($)\u003c/label\u003e\u003cinput type=\"number\" value=\"'+d.minPay+'\" onchange=\"updateDebt('+d.id+',\\'minPay\\',this.value)\" style=\"width:100%;padding:6px;border:1px solid #cbd5e1;border-radius:4px;font-size:13px;\"\u003e\u003c/div\u003e';\n    html+='\u003c/div\u003e\u003c/div\u003e';\n  }\n  document.getElementById('debtList').innerHTML=html;\n}\n\nfunction fmt(n){return Math.round(n).toLocaleString('en-US');}\n\nfunction simulate(sortFn,extra){\n  var ds=debts.map(function(d){return{name:d.name,balance:d.balance,rate:d.rate,minPay:d.minPay};});\n  var totalPaid=0,months=0,history=[getTotalBal(ds)];\n  var maxMonths=600;\n  while(getTotalBal(ds)\u003e0\u0026\u0026months\u003cmaxMonths){\n    months++;\n    ds.sort(sortFn);\n    var leftover=extra;\n    for(var i=0;i\u003cds.length;i++){\n      if(ds[i].balance\u003c=0)continue;\n      var interest=ds[i].balance*(ds[i].rate/100/12);\n      ds[i].balance+=interest;\n      var pay=Math.min(ds[i].balance,ds[i].minPay);\n      ds[i].balance-=pay;\n      totalPaid+=pay;\n    }\n    // Apply extra to priority debt\n    for(var i=0;i\u003cds.length;i++){\n      if(ds[i].balance\u003c=0||leftover\u003c=0)continue;\n      var ep=Math.min(ds[i].balance,leftover);\n      ds[i].balance-=ep;\n      leftover-=ep;\n      totalPaid+=ep;\n    }\n    history.push(getTotalBal(ds));\n  }\n  return{months:months,totalPaid:totalPaid,history:history};\n}\n\nfunction getTotalBal(ds){\n  var t=0;for(var i=0;i\u003cds.length;i++)t+=Math.max(0,ds[i].balance);return t;\n}\n\nfunction calcDP(){\n  if(debts.length===0)return;\n  var extra=+document.getElementById('extraPay').value;\n  document.getElementById('extraVal').textContent='$'+fmt(extra);\n\n  var totalBal=0,totalMin=0;\n  for(var i=0;i\u003cdebts.length;i++){totalBal+=debts[i].balance;totalMin+=debts[i].minPay;}\n\n  var snowball=simulate(function(a,b){return a.balance-b.balance;},extra);\n  var avalanche=simulate(function(a,b){return b.rate-a.rate;},extra);\n  var noExtra=simulate(function(a,b){return b.rate-a.rate;},0);\n\n  var snowInt=snowball.totalPaid-totalBal;\n  var avaInt=avalanche.totalPaid-totalBal;\n  var noExInt=noExtra.totalPaid-totalBal;\n  var best=avaInt\u003c=snowInt?'avalanche':'snowball';\n  var saved=Math.max(0,noExInt-Math.min(snowInt,avaInt));\n\n  var html='\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:16px;\"\u003e';\n\n  // Snowball card\n  var sBorder=best==='snowball'?'2px solid #10b981':'1px solid #e2e8f0';\n  var sBadge=best==='snowball'?'\u003cspan style=\"background:#dcfce7;color:#166534;font-size:11px;font-weight:700;padding:2px 8px;border-radius:4px;margin-left:8px;\"\u003eWINNER\u003c/span\u003e':'';\n  html+='\u003cdiv style=\"padding:20px;border-radius:12px;border:'+sBorder+';background:#fff;\"\u003e';\n  html+='\u003cdiv style=\"font-size:14px;font-weight:bold;color:#2563eb;margin-bottom:12px;\"\u003eSnowball Method'+sBadge+'\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eSmallest balance first\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:28px;font-weight:bold;margin:8px 0;\"\u003e'+snowball.months+'\u003cspan style=\"font-size:14px;\"\u003e months\u003c/span\u003e\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:13px;color:#64748b;\"\u003eTotal interest: \u003cstrong\u003e$'+fmt(snowInt)+'\u003c/strong\u003e\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:13px;color:#64748b;\"\u003eTotal paid: $'+fmt(snowball.totalPaid)+'\u003c/div\u003e';\n  html+='\u003c/div\u003e';\n\n  // Avalanche card\n  var aBorder=best==='avalanche'?'2px solid #10b981':'1px solid #e2e8f0';\n  var aBadge=best==='avalanche'?'\u003cspan style=\"background:#dcfce7;color:#166534;font-size:11px;font-weight:700;padding:2px 8px;border-radius:4px;margin-left:8px;\"\u003eWINNER\u003c/span\u003e':'';\n  html+='\u003cdiv style=\"padding:20px;border-radius:12px;border:'+aBorder+';background:#fff;\"\u003e';\n  html+='\u003cdiv style=\"font-size:14px;font-weight:bold;color:#8b5cf6;margin-bottom:12px;\"\u003eAvalanche Method'+aBadge+'\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eHighest rate first\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:28px;font-weight:bold;margin:8px 0;\"\u003e'+avalanche.months+'\u003cspan style=\"font-size:14px;\"\u003e months\u003c/span\u003e\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:13px;color:#64748b;\"\u003eTotal interest: \u003cstrong\u003e$'+fmt(avaInt)+'\u003c/strong\u003e\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:13px;color:#64748b;\"\u003eTotal paid: $'+fmt(avalanche.totalPaid)+'\u003c/div\u003e';\n  html+='\u003c/div\u003e';\n  html+='\u003c/div\u003e';\n\n  if(saved\u003e0){\n    html+='\u003cdiv style=\"margin-top:16px;padding:16px;background:#f0fdf4;border-radius:8px;text-align:center;\"\u003e';\n    html+='\u003cdiv style=\"font-size:13px;color:#64748b;\"\u003eExtra $'+fmt(extra)+'/month saves you\u003c/div\u003e';\n    html+='\u003cdiv style=\"font-size:24px;font-weight:bold;color:#10b981;\"\u003e$'+fmt(saved)+' in interest\u003c/div\u003e';\n    html+='\u003cdiv style=\"font-size:13px;color:#64748b;\"\u003eand '+(noExtra.months-Math.min(snowball.months,avalanche.months))+' months of payments\u003c/div\u003e';\n    html+='\u003c/div\u003e';\n  }\n\n  document.getElementById('dpResult').innerHTML=html;\n  drawDPChart(snowball,avalanche,noExtra);\n}\n\nfunction drawDPChart(snow,ava,noEx){\n  var c=document.getElementById('dpChart');\n  var ctx=c.getContext('2d');\n  var W=c.width,H=c.height;\n  ctx.clearRect(0,0,W,H);\n\n  var maxM=Math.max(snow.months,ava.months,noEx.months,12);\n  maxM=Math.ceil(maxM*1.1);\n  var maxVal=Math.max(snow.history[0],1);\n  var pad={t:20,r:20,b:45,l:70};\n  var cw=W-pad.l-pad.r,ch=H-pad.t-pad.b;\n\n  // Grid\n  ctx.strokeStyle='#e2e8f0';ctx.lineWidth=1;\n  for(var i=0;i\u003c=4;i++){\n    var y=pad.t+ch*(1-i/4);\n    ctx.beginPath();ctx.moveTo(pad.l,y);ctx.lineTo(W-pad.r,y);ctx.stroke();\n    ctx.fillStyle='#94a3b8';ctx.font='11px sans-serif';ctx.textAlign='right';\n    ctx.fillText('$'+fmt(maxVal*i/4),pad.l-6,y+4);\n  }\n\n  function drawLine(hist,color,dash){\n    ctx.beginPath();ctx.strokeStyle=color;ctx.lineWidth=2;ctx.setLineDash(dash||[]);\n    for(var m=0;m\u003chist.length\u0026\u0026m\u003c=maxM;m++){\n      var x=pad.l+cw*m/maxM,y=pad.t+ch*(1-hist[m]/maxVal);\n      if(m===0)ctx.moveTo(x,y);else ctx.lineTo(x,y);\n    }\n    ctx.stroke();ctx.setLineDash([]);\n  }\n\n  drawLine(noEx.history,'#94a3b8',[4,4]);\n  drawLine(snow.history,'#2563eb',[]);\n  drawLine(ava.history,'#8b5cf6',[]);\n\n  // Legend\n  var lx=pad.l+10,ly=pad.t+12;\n  [[' Snowball','#2563eb'],[' Avalanche','#8b5cf6'],[' Min only','#94a3b8']].forEach(function(item,i){\n    ctx.fillStyle=item[1];ctx.fillRect(lx,ly+i*16,12,3);\n    ctx.fillStyle='#334155';ctx.font='11px sans-serif';ctx.textAlign='left';\n    ctx.fillText(item[0],lx+16,ly+i*16+5);\n  });\n\n  // X-axis\n  ctx.fillStyle='#64748b';ctx.font='11px sans-serif';ctx.textAlign='center';\n  var step=maxM\u003c=12?1:maxM\u003c=24?3:maxM\u003c=48?6:12;\n  for(var m=0;m\u003c=maxM;m+=step){\n    ctx.fillText(m+'mo',pad.l+cw*m/maxM,H-pad.b+20);\n  }\n}\n\n// Initialize with sample debts\naddDebt('Credit Card',6500,22.9,200);\naddDebt('Car Loan',12000,6.5,350);\naddDebt('Personal Loan',3000,15,100);\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"snowball-vs-avalanche-which-is-better\"\u003eSnowball vs Avalanche: Which Is Better?\u003c/h2\u003e\n\u003cp\u003eThe two most popular debt payoff strategies are:\u003c/p\u003e","title":"Debt Payoff Calculator | Snowball vs Avalanche Strategy Comparison"},{"content":"Roll virtual dice for board games, RPGs, and tabletop gaming — right in your browser. Choose your dice type and count, watch the animated roll, and track your history.\nNumber of Dice 1 die 2 dice 3 dice 4 dice 5 dice 6 dice 7 dice 8 dice 9 dice 10 dice Sides d4 d6 d8 d10 d12 d20 🎲 Roll 🔊 Quick Roll: 2d6 1d20 3d6 4d6 drop lowest Select your dice and click Roll — Average — Min Roll — Max Roll 0 Total Rolls Roll History (last 20) No rolls yet. Start rolling! How to Use Choose your dice count — pick 1 to 10 dice from the dropdown. Pick the die type — d4, d6, d8, d10, d12, or d20. Click Roll — watch the animated result appear with individual values and a total. Use Quick Roll — one-click presets for the most common combinations. Common Dice Combinations Combo Use case 2d6 Standard board game roll, Monopoly 1d20 D\u0026amp;D attack rolls, skill checks 3d6 Attribute generation in many RPGs 4d6 drop lowest D\u0026amp;D 5e character stat generation 1d6 Simple random decision Related Tools Random numbers → Random Number Generator Board game timer → Countdown Timer ","permalink":"https://productivity-works.com/tools/dice-roller/","summary":"\u003cp\u003eRoll virtual dice for board games, RPGs, and tabletop gaming — right in your browser. Choose your dice type and count, watch the animated roll, and track your history.\u003c/p\u003e\n\u003cdiv id=\"dr-app\"\u003e\n\u003cstyle\u003e\n#dr-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 680px;\n  margin: 0 auto;\n  color: #1e293b;\n}\n#dr-app * { box-sizing: border-box; }\n\n#dr-app .dr-controls {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 16px;\n  align-items: flex-end;\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 20px;\n  margin-bottom: 20px;\n}\n#dr-app .dr-field {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n}\n#dr-app .dr-field label {\n  font-size: 12px;\n  font-weight: 600;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n#dr-app select {\n  padding: 10px 14px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 15px;\n  background: #fff;\n  color: #1e293b;\n  cursor: pointer;\n  outline: none;\n  transition: border-color 0.15s;\n}\n#dr-app select:focus { border-color: #6366f1; }\n\n#dr-app .dr-btn {\n  padding: 11px 28px;\n  border: none;\n  border-radius: 8px;\n  font-size: 16px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: transform 0.1s, background 0.15s;\n  user-select: none;\n}\n#dr-app .dr-btn:active { transform: scale(0.96); }\n#dr-app .dr-btn-roll {\n  background: #6366f1;\n  color: #fff;\n  font-size: 17px;\n  padding: 12px 36px;\n}\n#dr-app .dr-btn-roll:hover { background: #4f46e5; }\n#dr-app .dr-btn-sound {\n  background: #e2e8f0;\n  color: #475569;\n  padding: 11px 16px;\n  font-size: 18px;\n}\n#dr-app .dr-btn-sound:hover { background: #cbd5e1; }\n\n#dr-app .dr-quick-btns {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 20px;\n}\n#dr-app .dr-quick-btns span {\n  font-size: 12px;\n  font-weight: 600;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  align-self: center;\n  margin-right: 4px;\n}\n#dr-app .dr-btn-quick {\n  padding: 7px 16px;\n  background: #fff;\n  border: 1.5px solid #6366f1;\n  color: #6366f1;\n  border-radius: 20px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s;\n}\n#dr-app .dr-btn-quick:hover {\n  background: #6366f1;\n  color: #fff;\n}\n\n#dr-app .dr-result-area {\n  background: #fff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 24px;\n  margin-bottom: 20px;\n  min-height: 140px;\n  text-align: center;\n}\n#dr-app .dr-dice-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  justify-content: center;\n  margin-bottom: 16px;\n  min-height: 64px;\n  align-items: center;\n}\n#dr-app .dr-die {\n  width: 60px;\n  height: 60px;\n  background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);\n  border-radius: 10px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: #fff;\n  font-size: 22px;\n  font-weight: 800;\n  box-shadow: 0 4px 12px rgba(99,102,241,0.3), inset 0 1px 0 rgba(255,255,255,0.2);\n  transition: transform 0.15s;\n}\n#dr-app .dr-die.rolling {\n  animation: dr-spin 0.5s ease-out;\n}\n@keyframes dr-spin {\n  0%   { transform: rotateY(0deg) rotateX(0deg) scale(0.8); opacity: 0.4; }\n  40%  { transform: rotateY(180deg) rotateX(90deg) scale(1.1); opacity: 1; }\n  70%  { transform: rotateY(320deg) rotateX(40deg) scale(0.95); }\n  100% { transform: rotateY(360deg) rotateX(0deg) scale(1); opacity: 1; }\n}\n#dr-app .dr-total {\n  font-size: 32px;\n  font-weight: 800;\n  color: #4f46e5;\n}\n#dr-app .dr-total-label {\n  font-size: 13px;\n  color: #94a3b8;\n  margin-top: 2px;\n}\n#dr-app .dr-note {\n  font-size: 13px;\n  color: #64748b;\n  margin-top: 8px;\n  font-style: italic;\n}\n#dr-app .dr-placeholder {\n  color: #94a3b8;\n  font-size: 15px;\n  padding: 20px 0;\n}\n\n#dr-app .dr-stats {\n  display: flex;\n  gap: 12px;\n  margin-bottom: 20px;\n}\n#dr-app .dr-stat-box {\n  flex: 1;\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 14px;\n  text-align: center;\n}\n#dr-app .dr-stat-val {\n  font-size: 22px;\n  font-weight: 700;\n  color: #4f46e5;\n}\n#dr-app .dr-stat-lbl {\n  font-size: 12px;\n  color: #94a3b8;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-top: 2px;\n}\n\n#dr-app .dr-history-title {\n  font-size: 13px;\n  font-weight: 600;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 10px;\n}\n#dr-app .dr-history-list {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n  max-height: 240px;\n  overflow-y: auto;\n}\n#dr-app .dr-history-list li {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  padding: 8px 12px;\n  border-radius: 7px;\n  font-size: 13px;\n  color: #475569;\n  border-bottom: 1px solid #f1f5f9;\n}\n#dr-app .dr-history-list li:last-child { border-bottom: none; }\n#dr-app .dr-history-list li:nth-child(odd) { background: #f8fafc; }\n#dr-app .dr-history-total {\n  font-weight: 700;\n  color: #4f46e5;\n}\n#dr-app .dr-empty-history {\n  text-align: center;\n  color: #94a3b8;\n  font-size: 14px;\n  padding: 16px;\n}\n\n@media (max-width: 500px) {\n  #dr-app .dr-controls { flex-direction: column; }\n  #dr-app .dr-stats { flex-wrap: wrap; }\n  #dr-app .dr-stat-box { min-width: calc(50% - 6px); }\n  #dr-app .dr-die { width: 52px; height: 52px; font-size: 18px; }\n}\n\u003c/style\u003e\n\u003c!-- Controls --\u003e\n\u003cdiv class=\"dr-controls\"\u003e\n  \u003cdiv class=\"dr-field\"\u003e\n    \u003clabel\u003eNumber of Dice\u003c/label\u003e\n    \u003cselect id=\"dr-num-dice\"\u003e\n      \u003coption value=\"1\"\u003e1 die\u003c/option\u003e\n      \u003coption value=\"2\" selected\u003e2 dice\u003c/option\u003e\n      \u003coption value=\"3\"\u003e3 dice\u003c/option\u003e\n      \u003coption value=\"4\"\u003e4 dice\u003c/option\u003e\n      \u003coption value=\"5\"\u003e5 dice\u003c/option\u003e\n      \u003coption value=\"6\"\u003e6 dice\u003c/option\u003e\n      \u003coption value=\"7\"\u003e7 dice\u003c/option\u003e\n      \u003coption value=\"8\"\u003e8 dice\u003c/option\u003e\n      \u003coption value=\"9\"\u003e9 dice\u003c/option\u003e\n      \u003coption value=\"10\"\u003e10 dice\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"dr-field\"\u003e\n    \u003clabel\u003eSides\u003c/label\u003e\n    \u003cselect id=\"dr-sides\"\u003e\n      \u003coption value=\"4\"\u003ed4\u003c/option\u003e\n      \u003coption value=\"6\" selected\u003ed6\u003c/option\u003e\n      \u003coption value=\"8\"\u003ed8\u003c/option\u003e\n      \u003coption value=\"10\"\u003ed10\u003c/option\u003e\n      \u003coption value=\"12\"\u003ed12\u003c/option\u003e\n      \u003coption value=\"20\"\u003ed20\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n  \u003cbutton class=\"dr-btn dr-btn-roll\" id=\"dr-roll-btn\"\u003e🎲 Roll\u003c/button\u003e\n  \u003cbutton class=\"dr-btn dr-btn-sound\" id=\"dr-sound-btn\" title=\"Toggle sound\"\u003e🔊\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Quick Roll Buttons --\u003e\n\u003cdiv class=\"dr-quick-btns\"\u003e\n  \u003cspan\u003eQuick Roll:\u003c/span\u003e\n  \u003cbutton class=\"dr-btn-quick\" data-num=\"2\" data-sides=\"6\"\u003e2d6\u003c/button\u003e\n  \u003cbutton class=\"dr-btn-quick\" data-num=\"1\" data-sides=\"20\"\u003e1d20\u003c/button\u003e\n  \u003cbutton class=\"dr-btn-quick\" data-num=\"3\" data-sides=\"6\"\u003e3d6\u003c/button\u003e\n  \u003cbutton class=\"dr-btn-quick\" data-num=\"4\" data-sides=\"6\" data-drop-lowest=\"1\"\u003e4d6 drop lowest\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Result Area --\u003e\n\u003cdiv class=\"dr-result-area\"\u003e\n  \u003cdiv class=\"dr-dice-row\" id=\"dr-dice-row\"\u003e\n    \u003cdiv class=\"dr-placeholder\"\u003eSelect your dice and click Roll\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv id=\"dr-total-wrap\" style=\"display:none;\"\u003e\n    \u003cdiv class=\"dr-total\" id=\"dr-total\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"dr-total-label\" id=\"dr-total-label\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"dr-note\" id=\"dr-note\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Stats --\u003e\n\u003cdiv class=\"dr-stats\"\u003e\n  \u003cdiv class=\"dr-stat-box\"\u003e\n    \u003cdiv class=\"dr-stat-val\" id=\"dr-stat-avg\"\u003e—\u003c/div\u003e\n    \u003cdiv class=\"dr-stat-lbl\"\u003eAverage\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"dr-stat-box\"\u003e\n    \u003cdiv class=\"dr-stat-val\" id=\"dr-stat-min\"\u003e—\u003c/div\u003e\n    \u003cdiv class=\"dr-stat-lbl\"\u003eMin Roll\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"dr-stat-box\"\u003e\n    \u003cdiv class=\"dr-stat-val\" id=\"dr-stat-max\"\u003e—\u003c/div\u003e\n    \u003cdiv class=\"dr-stat-lbl\"\u003eMax Roll\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"dr-stat-box\"\u003e\n    \u003cdiv class=\"dr-stat-val\" id=\"dr-stat-count\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"dr-stat-lbl\"\u003eTotal Rolls\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- History --\u003e\n\u003cdiv\u003e\n  \u003cdiv class=\"dr-history-title\"\u003eRoll History (last 20)\u003c/div\u003e\n  \u003cul class=\"dr-history-list\" id=\"dr-history-list\"\u003e\n    \u003cli class=\"dr-empty-history\" id=\"dr-empty-hist\"\u003eNo rolls yet. Start rolling!\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  'use strict';\n\n  var numDiceEl  = document.getElementById('dr-num-dice');\n  var sidesEl    = document.getElementById('dr-sides');\n  var rollBtn    = document.getElementById('dr-roll-btn');\n  var soundBtn   = document.getElementById('dr-sound-btn');\n  var diceRow    = document.getElementById('dr-dice-row');\n  var totalWrap  = document.getElementById('dr-total-wrap');\n  var totalEl    = document.getElementById('dr-total');\n  var totalLabel = document.getElementById('dr-total-label');\n  var noteEl     = document.getElementById('dr-note');\n  var historyEl  = document.getElementById('dr-history-list');\n  var emptyHist  = document.getElementById('dr-empty-hist');\n  var statAvg    = document.getElementById('dr-stat-avg');\n  var statMin    = document.getElementById('dr-stat-min');\n  var statMax    = document.getElementById('dr-stat-max');\n  var statCount  = document.getElementById('dr-stat-count');\n\n  var soundOn = true;\n  var history = [];\n  var allTotals = [];\n  var rolling = false;\n\n  // Sound via Web Audio API\n  var audioCtx = null;\n  function getAudioCtx() {\n    if (!audioCtx) {\n      try { audioCtx = new (window.AudioContext || window.webkitAudioContext)(); } catch(e) {}\n    }\n    return audioCtx;\n  }\n  function playRollSound() {\n    if (!soundOn) return;\n    var ctx = getAudioCtx();\n    if (!ctx) return;\n    // Short percussive click cluster\n    for (var i = 0; i \u003c 3; i++) {\n      (function(delay) {\n        setTimeout(function() {\n          var osc = ctx.createOscillator();\n          var gain = ctx.createGain();\n          osc.connect(gain);\n          gain.connect(ctx.destination);\n          osc.type = 'triangle';\n          osc.frequency.setValueAtTime(280 + Math.random() * 180, ctx.currentTime);\n          osc.frequency.exponentialRampToValueAtTime(80, ctx.currentTime + 0.08);\n          gain.gain.setValueAtTime(0.18, ctx.currentTime);\n          gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.1);\n          osc.start(ctx.currentTime);\n          osc.stop(ctx.currentTime + 0.12);\n        }, delay);\n      })(i * 80);\n    }\n  }\n\n  soundBtn.addEventListener('click', function() {\n    soundOn = !soundOn;\n    soundBtn.textContent = soundOn ? '🔊' : '🔇';\n  });\n\n  // Quick roll buttons\n  document.querySelectorAll('#dr-app .dr-btn-quick').forEach(function(btn) {\n    btn.addEventListener('click', function() {\n      numDiceEl.value = btn.dataset.num;\n      sidesEl.value   = btn.dataset.sides;\n      doRoll(parseInt(btn.dataset.num), parseInt(btn.dataset.sides), parseInt(btn.dataset.dropLowest || '0'));\n    });\n  });\n\n  rollBtn.addEventListener('click', function() {\n    var n = parseInt(numDiceEl.value);\n    var s = parseInt(sidesEl.value);\n    doRoll(n, s, 0);\n  });\n\n  function randInt(min, max) {\n    return Math.floor(Math.random() * (max - min + 1)) + min;\n  }\n\n  function doRoll(numDice, sides, dropLowest) {\n    if (rolling) return;\n    rolling = true;\n    rollBtn.disabled = true;\n    playRollSound();\n\n    // Generate results\n    var results = [];\n    for (var i = 0; i \u003c numDice; i++) {\n      results.push(randInt(1, sides));\n    }\n\n    // Drop lowest if needed\n    var dropped = null;\n    var usedResults = results.slice();\n    if (dropLowest \u003e 0 \u0026\u0026 usedResults.length \u003e dropLowest) {\n      var sorted = usedResults.slice().sort(function(a, b) { return a - b; });\n      dropped = sorted.slice(0, dropLowest);\n      for (var d = 0; d \u003c dropped.length; d++) {\n        var idx = usedResults.indexOf(dropped[d]);\n        if (idx !== -1) usedResults.splice(idx, 1);\n      }\n    }\n    var total = usedResults.reduce(function(a, b) { return a + b; }, 0);\n\n    // Render dice (animate)\n    diceRow.innerHTML = '';\n    totalWrap.style.display = 'none';\n\n    results.forEach(function(val, idx) {\n      var die = document.createElement('div');\n      die.className = 'dr-die';\n      var isDropped = dropped \u0026\u0026 dropped.includes(val) \u0026\u0026 idx \u003e= usedResults.length;\n      die.textContent = '?';\n      if (isDropped) {\n        die.style.opacity = '0.35';\n        die.style.background = '#94a3b8';\n        die.title = 'Dropped';\n      }\n      diceRow.appendChild(die);\n\n      // Staggered reveal\n      (function(el, finalVal, delay) {\n        el.classList.add('rolling');\n        // Shuffle display during animation\n        var shuffleInterval = setInterval(function() {\n          el.textContent = randInt(1, sides);\n        }, 60);\n        setTimeout(function() {\n          clearInterval(shuffleInterval);\n          el.textContent = finalVal;\n          el.classList.remove('rolling');\n        }, delay + 400);\n      })(die, val, idx * 80);\n    });\n\n    // Show total after animation completes\n    var totalDelay = results.length * 80 + 500;\n    setTimeout(function() {\n      totalWrap.style.display = 'block';\n      totalEl.textContent = total;\n      var diceLabel = numDice + 'd' + sides;\n      if (dropLowest \u003e 0) diceLabel += ' drop lowest';\n      totalLabel.textContent = 'Total (' + diceLabel + ')';\n      if (results.length \u003e 1) {\n        var indivStr = results.map(function(v, i) {\n          if (dropped \u0026\u0026 i \u003e= usedResults.length) return '[' + v + ']';\n          return v;\n        }).join(' + ');\n        noteEl.textContent = 'Rolls: ' + indivStr;\n      } else {\n        noteEl.textContent = '';\n      }\n\n      // History\n      addHistory(results, total, sides, dropLowest, usedResults);\n      updateStats();\n\n      rolling = false;\n      rollBtn.disabled = false;\n    }, totalDelay);\n  }\n\n  function addHistory(results, total, sides, dropLowest, usedResults) {\n    allTotals.push(total);\n    var entry = {\n      label: results.length + 'd' + sides + (dropLowest \u003e 0 ? ' DL' : ''),\n      results: results,\n      total: total\n    };\n    history.unshift(entry);\n    if (history.length \u003e 20) history.pop();\n\n    // Re-render history\n    historyEl.innerHTML = '';\n    if (history.length === 0) {\n      var li = document.createElement('li');\n      li.id = 'dr-empty-hist';\n      li.className = 'dr-empty-history';\n      li.textContent = 'No rolls yet. Start rolling!';\n      historyEl.appendChild(li);\n      return;\n    }\n    history.forEach(function(h, idx) {\n      var li = document.createElement('li');\n      var left = document.createElement('span');\n      left.textContent = '#' + (history.length - idx) + '  ' + h.label + '  [' + h.results.join(', ') + ']';\n      var right = document.createElement('span');\n      right.className = 'dr-history-total';\n      right.textContent = '= ' + h.total;\n      li.appendChild(left);\n      li.appendChild(right);\n      historyEl.appendChild(li);\n    });\n  }\n\n  function updateStats() {\n    if (allTotals.length === 0) return;\n    var sum = allTotals.reduce(function(a, b) { return a + b; }, 0);\n    var avg = (sum / allTotals.length).toFixed(1);\n    var min = Math.min.apply(null, allTotals);\n    var max = Math.max.apply(null, allTotals);\n    statAvg.textContent = avg;\n    statMin.textContent = min;\n    statMax.textContent = max;\n    statCount.textContent = allTotals.length;\n  }\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-to-use\"\u003eHow to Use\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eChoose your dice count\u003c/strong\u003e — pick 1 to 10 dice from the dropdown.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ePick the die type\u003c/strong\u003e — d4, d6, d8, d10, d12, or d20.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eClick Roll\u003c/strong\u003e — watch the animated result appear with individual values and a total.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eUse Quick Roll\u003c/strong\u003e — one-click presets for the most common combinations.\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"common-dice-combinations\"\u003eCommon Dice Combinations\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eCombo\u003c/th\u003e\n          \u003cth\u003eUse case\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e2d6\u003c/td\u003e\n          \u003ctd\u003eStandard board game roll, Monopoly\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e1d20\u003c/td\u003e\n          \u003ctd\u003eD\u0026amp;D attack rolls, skill checks\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e3d6\u003c/td\u003e\n          \u003ctd\u003eAttribute generation in many RPGs\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e4d6 drop lowest\u003c/td\u003e\n          \u003ctd\u003eD\u0026amp;D 5e character stat generation\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e1d6\u003c/td\u003e\n          \u003ctd\u003eSimple random decision\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eRandom numbers → \u003ca href=\"https://productivity-works.com/tools/random-number-generator/\"\u003eRandom Number Generator\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003eBoard game timer → \u003ca href=\"https://productivity-works.com/tools/countdown-timer/\"\u003eCountdown Timer\u003c/a\u003e\n\u003c/li\u003e\n\u003c/ul\u003e","title":"Dice Roller - Virtual Dice Simulator"},{"content":"This article contains affiliate links.\nDividend Growth Simulator [2026] Enter your investment amount and dividend yield to automatically calculate your annual and monthly dividend income, the NISA tax-free benefit, and the compounding effect of reinvesting dividends.\nInvestment Amount (¥10K units) ¥100K¥5,000,000¥50M Dividend Yield (annual) 0.5%3.5%10% Account Type NISA (tax-free) Taxable account (20.315% tax) Dividend Reinvestment Yes (compound growth) No (receive dividends as cash) Investment Period 1 yr10 years30 yrs Popular High-Yield ETF \u0026 Stock Comparison NISA vs. Taxable Account: Dividend Take-Home Comparison Dividend Investing Basics What are dividends? Dividends are cash payments a company distributes to shareholders from its profits. Simply holding qualifying stocks or ETFs earns you payouts one to four times per year.\nHow is dividend yield calculated? D i v i d e n d Y i e l d ( % ) = A n n u a l D i v i d e n d P e r S h a r e / S h a r e P r i c e × 1 0 0 Example: A stock priced at ¥1,000 paying ¥40/year has a 4.0% yield.\nHigh-yield investing: pros and cons Pros Cons Regular passive income Share price can fall Dividend reinvestment compounds returns Dividends can be cut or suspended NISA makes dividends tax-free May underperform growth stocks Psychologically easier to hold High yield alone can signal risk Starting dividend investing with NISA The NISA growth investment quota (¥2,400,000/year) lets you receive dividends tax-free. On a 3.5% yield position, eliminating the usual 20.315% withholding effectively adds ~0.7 percentage points to your take-home yield.\nRelated Tools iDeCo Simulator — Model tax-deferred pension contributions alongside dividend income Savings Goal Calculator — Calculate how long to build your target investment principal Asset Allocation Simulator — Track dividend-generating investments as part of your overall net worth ","permalink":"https://productivity-works.com/tools/dividend-growth-simulator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"dividend-growth-simulator-2026\"\u003eDividend Growth Simulator [2026]\u003c/h1\u003e\n\u003cp\u003eEnter your investment amount and dividend yield to automatically calculate your \u003cstrong\u003eannual and monthly dividend income\u003c/strong\u003e, the \u003cstrong\u003eNISA tax-free benefit\u003c/strong\u003e, and the \u003cstrong\u003ecompounding effect of reinvesting dividends\u003c/strong\u003e.\u003c/p\u003e\n\u003cdiv id=\"div-calc\" style=\"max-width:700px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;\"\u003e\n\u003cdiv style=\"padding:24px;border:2px solid #2563eb;border-radius:12px;background:#f8fafc;\"\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:20px;\"\u003e\n\u003cdiv\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;font-size:14px;\"\u003eInvestment Amount (¥10K units)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"divAmount\" min=\"10\" max=\"5000\" step=\"10\" value=\"500\" oninput=\"calcDiv()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:12px;color:#64748b;\"\u003e\u003cspan\u003e¥100K\u003c/span\u003e\u003cspan id=\"divAmtVal\" style=\"font-weight:bold;font-size:20px;color:#2563eb;\"\u003e¥5,000,000\u003c/span\u003e\u003cspan\u003e¥50M\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;font-size:14px;\"\u003eDividend Yield (annual)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"divYield\" min=\"0.5\" max=\"10\" step=\"0.1\" value=\"3.5\" oninput=\"calcDiv()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:12px;color:#64748b;\"\u003e\u003cspan\u003e0.5%\u003c/span\u003e\u003cspan id=\"divYldVal\" style=\"font-weight:bold;font-size:20px;color:#10b981;\"\u003e3.5%\u003c/span\u003e\u003cspan\u003e10%\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-top:16px;\"\u003e\n\u003cdiv\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;font-size:14px;\"\u003eAccount Type\u003c/label\u003e\n\u003cselect id=\"divAccount\" onchange=\"calcDiv()\" style=\"width:100%;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:15px;\"\u003e\n\u003coption value=\"nisa\"\u003eNISA (tax-free)\u003c/option\u003e\n\u003coption value=\"tokutei\"\u003eTaxable account (20.315% tax)\u003c/option\u003e\n\u003c/select\u003e\n\u003c/div\u003e\n\u003cdiv\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;font-size:14px;\"\u003eDividend Reinvestment\u003c/label\u003e\n\u003cselect id=\"divReinvest\" onchange=\"calcDiv()\" style=\"width:100%;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:15px;\"\u003e\n\u003coption value=\"yes\"\u003eYes (compound growth)\u003c/option\u003e\n\u003coption value=\"no\"\u003eNo (receive dividends as cash)\u003c/option\u003e\n\u003c/select\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-top:16px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;font-size:14px;\"\u003eInvestment Period\u003c/label\u003e\n\u003cinput type=\"range\" id=\"divYears\" min=\"1\" max=\"30\" step=\"1\" value=\"10\" oninput=\"calcDiv()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:12px;color:#64748b;\"\u003e\u003cspan\u003e1 yr\u003c/span\u003e\u003cspan id=\"divYrsVal\" style=\"font-weight:bold;font-size:18px;color:#f59e0b;\"\u003e10 years\u003c/span\u003e\u003cspan\u003e30 yrs\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"divResult\" style=\"margin-top:24px;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"divTimeline\" style=\"margin-top:24px;padding:24px;border:2px solid #10b981;border-radius:12px;background:#f0fdf4;\"\u003e\u003c/div\u003e\n\u003cdiv style=\"margin-top:24px;padding:24px;border:2px solid #f59e0b;border-radius:12px;background:#fffbeb;\"\u003e\n\u003ch3 style=\"margin:0 0 12px;color:#92400e;font-size:16px;\"\u003ePopular High-Yield ETF \u0026 Stock Comparison\u003c/h3\u003e\n\u003cdiv id=\"divETF\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-top:24px;padding:24px;border:2px solid #8b5cf6;border-radius:12px;background:#f5f3ff;\"\u003e\n\u003ch3 style=\"margin:0 0 12px;color:#6d28d9;font-size:16px;\"\u003eNISA vs. Taxable Account: Dividend Take-Home Comparison\u003c/h3\u003e\n\u003cdiv id=\"divNISA\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nfunction fmt(n){return Math.round(n).toLocaleString();}\n\nfunction calcDiv(){\n  var amount=+document.getElementById('divAmount').value;\n  var yieldRate=+document.getElementById('divYield').value;\n  var account=document.getElementById('divAccount').value;\n  var reinvest=document.getElementById('divReinvest').value;\n  var years=+document.getElementById('divYears').value;\n\n  document.getElementById('divAmtVal').textContent='¥'+fmt(amount*10000);\n  document.getElementById('divYldVal').textContent=yieldRate.toFixed(1)+'%';\n  document.getElementById('divYrsVal').textContent=years+' year'+(years\u003e1?'s':'');\n\n  var principal=amount*10000;\n  var rate=yieldRate/100;\n  var taxRate=account==='nisa'?0:0.20315;\n\n  var annualDiv=principal*rate;\n  var annualAfterTax=annualDiv*(1-taxRate);\n  var monthlyAfterTax=annualAfterTax/12;\n\n  var html='\u003cdiv style=\"text-align:center;padding:20px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e';\n  html+='\u003cdiv style=\"font-size:13px;color:#64748b;\"\u003eAnnual Dividend Income (after tax)\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:40px;font-weight:bold;color:#10b981;\"\u003e¥'+fmt(annualAfterTax)+'\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:14px;color:#64748b;margin-top:4px;\"\u003eMonthly approx. \u003cstrong style=\"color:#2563eb;\"\u003e¥'+fmt(monthlyAfterTax)+'\u003c/strong\u003e\u003c/div\u003e';\n  html+='\u003c/div\u003e';\n\n  html+='\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;margin-top:16px;text-align:center;\"\u003e';\n  html+='\u003cdiv style=\"padding:12px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e\u003cdiv style=\"font-size:11px;color:#64748b;\"\u003ePre-tax dividend\u003c/div\u003e\u003cdiv style=\"font-size:18px;font-weight:bold;color:#1e293b;\"\u003e¥'+fmt(annualDiv)+'/yr\u003c/div\u003e\u003c/div\u003e';\n  var taxAmount=annualDiv*taxRate;\n  html+='\u003cdiv style=\"padding:12px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e\u003cdiv style=\"font-size:11px;color:#64748b;\"\u003eTax withheld\u003c/div\u003e\u003cdiv style=\"font-size:18px;font-weight:bold;color:'+(taxRate\u003e0?'#ef4444':'#10b981')+';\"\u003e¥'+fmt(taxAmount)+'/yr\u003c/div\u003e\u003c/div\u003e';\n  html+='\u003cdiv style=\"padding:12px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e\u003cdiv style=\"font-size:11px;color:#64748b;\"\u003eEffective yield (after tax)\u003c/div\u003e\u003cdiv style=\"font-size:18px;font-weight:bold;color:#f59e0b;\"\u003e'+(yieldRate*(1-taxRate)).toFixed(2)+'%\u003c/div\u003e\u003c/div\u003e';\n  html+='\u003c/div\u003e';\n\n  if(account==='nisa'){\n    html+='\u003cdiv style=\"margin-top:8px;text-align:center;font-size:12px;color:#10b981;font-weight:bold;\"\u003eNISA account: dividends are tax-free! Annual tax saving of ¥'+fmt(annualDiv*0.20315)+'\u003c/div\u003e';\n  }\n\n  document.getElementById('divResult').innerHTML=html;\n\n  var thtml='\u003ch3 style=\"margin:0 0 12px;color:#166534;font-size:16px;\"\u003eDividend Income Over Time ('+years+' year'+(years\u003e1?'s':'')+')\u003c/h3\u003e';\n  thtml+='\u003ctable style=\"width:100%;border-collapse:collapse;font-size:13px;\"\u003e';\n  thtml+='\u003ctr style=\"border-bottom:2px solid #10b981;\"\u003e\u003cth style=\"text-align:left;padding:6px;\"\u003eYear\u003c/th\u003e\u003cth style=\"text-align:right;padding:6px;\"\u003ePrincipal\u003c/th\u003e\u003cth style=\"text-align:right;padding:6px;\"\u003eAnnual Dividend\u003c/th\u003e\u003cth style=\"text-align:right;padding:6px;\"\u003eCumulative Dividends\u003c/th\u003e\u003c/tr\u003e';\n\n  var balance=principal;\n  var totalDiv=0;\n  var showYears=[];\n  if(years\u003c=10){for(var i=1;i\u003c=years;i++) showYears.push(i);}\n  else{showYears=[1,2,3,5];for(var i=10;i\u003c=years;i+=5) showYears.push(i);if(showYears[showYears.length-1]!==years) showYears.push(years);}\n\n  var yearData=[];\n  for(var y=1;y\u003c=years;y++){\n    var div=balance*rate;\n    var divNet=div*(1-taxRate);\n    totalDiv+=divNet;\n    if(reinvest==='yes') balance+=divNet;\n    yearData.push({year:y,balance:balance,div:divNet,total:totalDiv});\n  }\n\n  for(var i=0;i\u003cshowYears.length;i++){\n    var d=yearData[showYears[i]-1];\n    var bg=i%2===0?'#fff':'#f0fdf4';\n    var bold=showYears[i]===years?'font-weight:bold;':'';\n    thtml+='\u003ctr style=\"background:'+bg+';'+bold+'\"\u003e';\n    thtml+='\u003ctd style=\"padding:6px;\"\u003eYear '+d.year+'\u003c/td\u003e';\n    thtml+='\u003ctd style=\"padding:6px;text-align:right;\"\u003e¥'+fmt(d.balance)+'\u003c/td\u003e';\n    thtml+='\u003ctd style=\"padding:6px;text-align:right;\"\u003e¥'+fmt(d.div)+'\u003c/td\u003e';\n    thtml+='\u003ctd style=\"padding:6px;text-align:right;color:#10b981;\"\u003e¥'+fmt(d.total)+'\u003c/td\u003e';\n    thtml+='\u003c/tr\u003e';\n  }\n  thtml+='\u003c/table\u003e';\n\n  var finalData=yearData[years-1];\n  thtml+='\u003cdiv style=\"margin-top:12px;padding:12px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;text-align:center;\"\u003e';\n  thtml+='\u003cspan style=\"font-size:13px;color:#64748b;\"\u003ePortfolio value after '+years+' year'+(years\u003e1?'s':'')+': \u003c/span\u003e';\n  thtml+='\u003cspan style=\"font-size:20px;font-weight:bold;color:#166534;\"\u003e¥'+fmt(reinvest==='yes'?finalData.balance:principal)+'\u003c/span\u003e';\n  if(reinvest==='yes'){\n    thtml+='\u003cspan style=\"font-size:13px;color:#64748b;\"\u003e + cumulative dividends: \u003c/span\u003e';\n    thtml+='\u003cspan style=\"font-size:13px;font-weight:bold;color:#10b981;\"\u003ereinvested\u003c/span\u003e';\n  } else {\n    thtml+='\u003cspan style=\"font-size:13px;color:#64748b;\"\u003e + cumulative dividends received: \u003c/span\u003e';\n    thtml+='\u003cspan style=\"font-size:20px;font-weight:bold;color:#10b981;\"\u003e¥'+fmt(finalData.total)+'\u003c/span\u003e';\n  }\n  thtml+='\u003c/div\u003e';\n\n  if(reinvest==='yes'){\n    var noReinvestTotal=principal*rate*(1-taxRate)*years;\n    var diff=finalData.total-noReinvestTotal;\n    thtml+='\u003cdiv style=\"margin-top:8px;font-size:12px;color:#166534;text-align:center;\"\u003eReinvestment compounding bonus: \u003cstrong\u003e+¥'+fmt(diff)+'\u003c/strong\u003e (vs. no reinvestment)\u003c/div\u003e';\n  }\n\n  document.getElementById('divTimeline').innerHTML=thtml;\n\n  var etfs=[\n    {name:'VYM',desc:'Vanguard High Dividend Yield ETF',yield:2.8,type:'US ETF'},\n    {name:'HDV',desc:'iShares Core High Dividend ETF',yield:3.5,type:'US ETF'},\n    {name:'SPYD',desc:'SPDR Portfolio S\u0026P 500 High Dividend ETF',yield:4.5,type:'US ETF'},\n    {name:'1489',desc:'Nikkei High Dividend 50 ETF',yield:3.2,type:'JP ETF'},\n    {name:'2914',desc:'Japan Tobacco (JT)',yield:4.8,type:'JP Stock'},\n    {name:'8593',desc:'Mitsubishi HC Capital',yield:3.8,type:'JP Stock'}\n  ];\n\n  var ehtml='\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:10px;\"\u003e';\n  for(var i=0;i\u003cetfs.length;i++){\n    var annDiv=principal*etfs[i].yield/100*(account==='nisa'?1:0.79685);\n    ehtml+='\u003cdiv style=\"padding:10px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e';\n    ehtml+='\u003cdiv style=\"display:flex;justify-content:space-between;align-items:center;\"\u003e';\n    ehtml+='\u003cdiv\u003e\u003cspan style=\"font-weight:bold;font-size:14px;color:#1e293b;\"\u003e'+etfs[i].name+'\u003c/span\u003e\u003cspan style=\"font-size:11px;color:#64748b;margin-left:4px;\"\u003e'+etfs[i].type+'\u003c/span\u003e\u003c/div\u003e';\n    ehtml+='\u003cspan style=\"font-size:14px;font-weight:bold;color:#10b981;\"\u003e'+etfs[i].yield+'%\u003c/span\u003e';\n    ehtml+='\u003c/div\u003e';\n    ehtml+='\u003cdiv style=\"font-size:11px;color:#64748b;\"\u003e'+etfs[i].desc+'\u003c/div\u003e';\n    ehtml+='\u003cdiv style=\"font-size:12px;margin-top:4px;color:#2563eb;\"\u003eAnnual dividend: \u003cstrong\u003e¥'+fmt(annDiv)+'\u003c/strong\u003e (after tax)\u003c/div\u003e';\n    ehtml+='\u003c/div\u003e';\n  }\n  ehtml+='\u003c/div\u003e';\n  ehtml+='\u003cdiv style=\"margin-top:8px;font-size:11px;color:#92400e;text-align:center;\"\u003e*Dividend yields are reference values as of May 2026. Actual dividends fluctuate.\u003c/div\u003e';\n  document.getElementById('divETF').innerHTML=ehtml;\n\n  var nisaDiv=principal*rate;\n  var tokuteiDiv=nisaDiv*0.79685;\n  var diff10=nisaDiv*10-tokuteiDiv*10;\n  var nhtml='\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:16px;text-align:center;\"\u003e';\n  nhtml+='\u003cdiv style=\"padding:16px;background:#fff;border-radius:8px;border:2px solid #10b981;\"\u003e';\n  nhtml+='\u003cdiv style=\"font-size:12px;color:#166534;font-weight:bold;\"\u003eNISA (tax-free)\u003c/div\u003e';\n  nhtml+='\u003cdiv style=\"font-size:24px;font-weight:bold;color:#10b981;\"\u003e¥'+fmt(nisaDiv)+'/yr\u003c/div\u003e';\n  nhtml+='\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003e10-year total: ¥'+fmt(nisaDiv*10)+'\u003c/div\u003e';\n  nhtml+='\u003c/div\u003e';\n  nhtml+='\u003cdiv style=\"padding:16px;background:#fff;border-radius:8px;border:2px solid #94a3b8;\"\u003e';\n  nhtml+='\u003cdiv style=\"font-size:12px;color:#64748b;font-weight:bold;\"\u003eTaxable account\u003c/div\u003e';\n  nhtml+='\u003cdiv style=\"font-size:24px;font-weight:bold;color:#64748b;\"\u003e¥'+fmt(tokuteiDiv)+'/yr\u003c/div\u003e';\n  nhtml+='\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003e10-year total: ¥'+fmt(tokuteiDiv*10)+'\u003c/div\u003e';\n  nhtml+='\u003c/div\u003e';\n  nhtml+='\u003c/div\u003e';\n  nhtml+='\u003cdiv style=\"margin-top:12px;text-align:center;padding:12px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e';\n  nhtml+='\u003cdiv style=\"font-size:13px;color:#64748b;\"\u003eWith NISA, over 10 years you keep an extra\u003c/div\u003e';\n  nhtml+='\u003cdiv style=\"font-size:28px;font-weight:bold;color:#10b981;\"\u003e+¥'+fmt(diff10)+'\u003c/div\u003e';\n  nhtml+='\u003cdiv style=\"font-size:13px;color:#64748b;\"\u003ein dividend income\u003c/div\u003e';\n  nhtml+='\u003c/div\u003e';\n  document.getElementById('divNISA').innerHTML=nhtml;\n}\n\ndocument.addEventListener('DOMContentLoaded',function(){calcDiv();});\ncalcDiv();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"dividend-investing-basics\"\u003eDividend Investing Basics\u003c/h2\u003e\n\u003ch3 id=\"what-are-dividends\"\u003eWhat are dividends?\u003c/h3\u003e\n\u003cp\u003eDividends are cash payments a company distributes to shareholders from its profits. Simply holding qualifying stocks or ETFs earns you payouts one to four times per year.\u003c/p\u003e","title":"Dividend Growth Simulator | Calculate Annual Dividend Income from Investment Amount \u0026 Yield [2026]"},{"content":"This article contains affiliate links. We may earn a commission at no extra cost to you.\nDividend Income Calculator Enter your investment amount and dividend yield to estimate your annual and monthly dividend income. See the power of dividend reinvestment (DRIP) compounding over time.\nInvestment Amount ($) $1K$50,000$1M Dividend Yield (%) 0.5%3.0%10% Dividend Reinvestment (DRIP) Yes (compound growth) No (take cash) Tax Rate on Dividends 0% (Roth IRA / tax-free) 15% (qualified dividends) 20% (high income) 22% (ordinary income) Time Horizon (years) 1yr10 years30yr Popular Dividend ETFs How Dividend Investing Works What Are Dividends? Dividends are cash payments that companies make to shareholders from their profits. When you own dividend-paying stocks or ETFs, you receive regular income — typically quarterly — just for holding the shares.\nDividend Yield Explained D i v i d e n d Y i e l d ( % ) = A n n u a l D i v i d e n d p e r S h a r e / S h a r e P r i c e × 1 0 0 A stock priced at $100 that pays $3.50 annually has a 3.5% dividend yield.\nThe Power of DRIP (Dividend Reinvestment) Reinvesting dividends to buy more shares creates a compounding effect — your dividends generate their own dividends. Over long periods, DRIP can dramatically increase total returns compared to taking cash.\nTax Considerations Qualified dividends (most US stock dividends held 60+ days): Taxed at 0%, 15%, or 20% depending on income Ordinary dividends (REITs, some international stocks): Taxed at your regular income tax rate Tax-advantaged accounts (Roth IRA, 401k): Dividends grow tax-free or tax-deferred Want to see how your overall investment portfolio grows? Try our Compound Interest Calculator for a broader view.\nPlanning for retirement with dividends? Our Retirement Savings Calculator includes 401(k), IRA, and employer match projections.\nRelated Tools\nCompound Interest Calculator Retirement Savings Calculator US Salary Calculator Budget Planner ","permalink":"https://productivity-works.com/tools/dividend-income-calculator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links. We may earn a commission at no extra cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"dividend-income-calculator\"\u003eDividend Income Calculator\u003c/h1\u003e\n\u003cp\u003eEnter your investment amount and dividend yield to estimate your \u003cstrong\u003eannual and monthly dividend income\u003c/strong\u003e. See the power of \u003cstrong\u003edividend reinvestment (DRIP)\u003c/strong\u003e compounding over time.\u003c/p\u003e\n\u003cdiv id=\"dc-calc\" style=\"max-width:700px;margin:0 auto;font-family:Inter,sans-serif;\"\u003e\n\u003cdiv style=\"padding:24px;border:2px solid #2563eb;border-radius:12px;background:#f8fafc;\"\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:20px;\"\u003e\n\u003cdiv\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;font-size:14px;\"\u003eInvestment Amount ($)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"dcAmount\" min=\"1000\" max=\"1000000\" step=\"1000\" value=\"50000\" oninput=\"calcDC()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:12px;color:#64748b;\"\u003e\u003cspan\u003e$1K\u003c/span\u003e\u003cspan id=\"dcAmtVal\" style=\"font-weight:bold;font-size:20px;color:#2563eb;\"\u003e$50,000\u003c/span\u003e\u003cspan\u003e$1M\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;font-size:14px;\"\u003eDividend Yield (%)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"dcYield\" min=\"0.5\" max=\"10\" step=\"0.1\" value=\"3.0\" oninput=\"calcDC()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:12px;color:#64748b;\"\u003e\u003cspan\u003e0.5%\u003c/span\u003e\u003cspan id=\"dcYldVal\" style=\"font-weight:bold;font-size:20px;color:#10b981;\"\u003e3.0%\u003c/span\u003e\u003cspan\u003e10%\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-top:16px;\"\u003e\n\u003cdiv\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;font-size:14px;\"\u003eDividend Reinvestment (DRIP)\u003c/label\u003e\n\u003cselect id=\"dcDRIP\" onchange=\"calcDC()\" style=\"width:100%;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:15px;\"\u003e\n\u003coption value=\"yes\"\u003eYes (compound growth)\u003c/option\u003e\n\u003coption value=\"no\"\u003eNo (take cash)\u003c/option\u003e\n\u003c/select\u003e\n\u003c/div\u003e\n\u003cdiv\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;font-size:14px;\"\u003eTax Rate on Dividends\u003c/label\u003e\n\u003cselect id=\"dcTax\" onchange=\"calcDC()\" style=\"width:100%;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:15px;\"\u003e\n\u003coption value=\"0\"\u003e0% (Roth IRA / tax-free)\u003c/option\u003e\n\u003coption value=\"15\" selected\u003e15% (qualified dividends)\u003c/option\u003e\n\u003coption value=\"20\"\u003e20% (high income)\u003c/option\u003e\n\u003coption value=\"22\"\u003e22% (ordinary income)\u003c/option\u003e\n\u003c/select\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-top:16px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;font-size:14px;\"\u003eTime Horizon (years)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"dcYears\" min=\"1\" max=\"30\" step=\"1\" value=\"10\" oninput=\"calcDC()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:12px;color:#64748b;\"\u003e\u003cspan\u003e1yr\u003c/span\u003e\u003cspan id=\"dcYrsVal\" style=\"font-weight:bold;font-size:18px;color:#f59e0b;\"\u003e10 years\u003c/span\u003e\u003cspan\u003e30yr\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"dcResult\" style=\"margin-top:24px;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"dcTimeline\" style=\"margin-top:24px;padding:24px;border:2px solid #10b981;border-radius:12px;background:#f0fdf4;\"\u003e\u003c/div\u003e\n\u003cdiv style=\"margin-top:24px;padding:24px;border:2px solid #f59e0b;border-radius:12px;background:#fffbeb;\"\u003e\n\u003ch3 style=\"margin:0 0 12px;color:#92400e;font-size:16px;\"\u003ePopular Dividend ETFs\u003c/h3\u003e\n\u003cdiv id=\"dcETF\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nfunction fmtD(n){return Math.round(n).toLocaleString('en-US');}\n\nfunction calcDC(){\n  var amount=+document.getElementById('dcAmount').value;\n  var yieldRate=+document.getElementById('dcYield').value;\n  var drip=document.getElementById('dcDRIP').value;\n  var taxPct=+document.getElementById('dcTax').value;\n  var years=+document.getElementById('dcYears').value;\n\n  document.getElementById('dcAmtVal').textContent='$'+fmtD(amount);\n  document.getElementById('dcYldVal').textContent=yieldRate.toFixed(1)+'%';\n  document.getElementById('dcYrsVal').textContent=years+(years===1?' year':' years');\n\n  var rate=yieldRate/100;\n  var taxRate=taxPct/100;\n\n  var annualDiv=amount*rate;\n  var annualNet=annualDiv*(1-taxRate);\n  var monthlyNet=annualNet/12;\n\n  // Result summary\n  var html='\u003cdiv style=\"text-align:center;padding:20px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e';\n  html+='\u003cdiv style=\"font-size:13px;color:#64748b;\"\u003eAnnual Dividend Income (after tax)\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:40px;font-weight:bold;color:#10b981;\"\u003e$'+fmtD(annualNet)+'\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:14px;color:#64748b;margin-top:4px;\"\u003eMonthly: \u003cstrong style=\"color:#2563eb;\"\u003e$'+fmtD(monthlyNet)+'\u003c/strong\u003e\u003c/div\u003e';\n  html+='\u003c/div\u003e';\n\n  html+='\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;margin-top:16px;text-align:center;\"\u003e';\n  html+='\u003cdiv style=\"padding:12px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e\u003cdiv style=\"font-size:11px;color:#64748b;\"\u003eGross Dividend\u003c/div\u003e\u003cdiv style=\"font-size:18px;font-weight:bold;color:#1e293b;\"\u003e$'+fmtD(annualDiv)+'\u003cspan style=\"font-size:11px;\"\u003e/yr\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e';\n  var taxAmt=annualDiv*taxRate;\n  html+='\u003cdiv style=\"padding:12px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e\u003cdiv style=\"font-size:11px;color:#64748b;\"\u003eTax Withheld\u003c/div\u003e\u003cdiv style=\"font-size:18px;font-weight:bold;color:'+(taxRate\u003e0?'#ef4444':'#10b981')+';\"\u003e$'+fmtD(taxAmt)+'\u003cspan style=\"font-size:11px;\"\u003e/yr\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e';\n  html+='\u003cdiv style=\"padding:12px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e\u003cdiv style=\"font-size:11px;color:#64748b;\"\u003eAfter-Tax Yield\u003c/div\u003e\u003cdiv style=\"font-size:18px;font-weight:bold;color:#f59e0b;\"\u003e'+(yieldRate*(1-taxRate)).toFixed(2)+'\u003cspan style=\"font-size:11px;\"\u003e%\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e';\n  html+='\u003c/div\u003e';\n\n  document.getElementById('dcResult').innerHTML=html;\n\n  // Timeline\n  var thtml='\u003ch3 style=\"margin:0 0 12px;color:#166534;font-size:16px;\"\u003eDividend Income Over '+years+' Years\u003c/h3\u003e';\n  thtml+='\u003ctable style=\"width:100%;border-collapse:collapse;font-size:13px;\"\u003e';\n  thtml+='\u003ctr style=\"border-bottom:2px solid #10b981;\"\u003e\u003cth style=\"text-align:left;padding:6px;\"\u003eYear\u003c/th\u003e\u003cth style=\"text-align:right;padding:6px;\"\u003ePortfolio\u003c/th\u003e\u003cth style=\"text-align:right;padding:6px;\"\u003eAnnual Div\u003c/th\u003e\u003cth style=\"text-align:right;padding:6px;\"\u003eCumulative\u003c/th\u003e\u003c/tr\u003e';\n\n  var balance=amount;\n  var totalDiv=0;\n  var showYears=[];\n  if(years\u003c=10){for(var i=1;i\u003c=years;i++) showYears.push(i);}\n  else{showYears=[1,2,3,5];for(var i=10;i\u003c=years;i+=5) showYears.push(i);if(showYears[showYears.length-1]!==years) showYears.push(years);}\n\n  var yearData=[];\n  for(var y=1;y\u003c=years;y++){\n    var div=balance*rate;\n    var divNet=div*(1-taxRate);\n    totalDiv+=divNet;\n    if(drip==='yes') balance+=divNet;\n    yearData.push({year:y,balance:balance,div:divNet,total:totalDiv});\n  }\n\n  for(var i=0;i\u003cshowYears.length;i++){\n    var d=yearData[showYears[i]-1];\n    var bg=i%2===0?'#fff':'#f0fdf4';\n    var bold=showYears[i]===years?'font-weight:bold;':'';\n    thtml+='\u003ctr style=\"background:'+bg+';'+bold+'\"\u003e';\n    thtml+='\u003ctd style=\"padding:6px;\"\u003eYear '+d.year+'\u003c/td\u003e';\n    thtml+='\u003ctd style=\"padding:6px;text-align:right;\"\u003e$'+fmtD(d.balance)+'\u003c/td\u003e';\n    thtml+='\u003ctd style=\"padding:6px;text-align:right;\"\u003e$'+fmtD(d.div)+'\u003c/td\u003e';\n    thtml+='\u003ctd style=\"padding:6px;text-align:right;color:#10b981;\"\u003e$'+fmtD(d.total)+'\u003c/td\u003e';\n    thtml+='\u003c/tr\u003e';\n  }\n  thtml+='\u003c/table\u003e';\n\n  var finalData=yearData[years-1];\n  thtml+='\u003cdiv style=\"margin-top:12px;padding:12px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;text-align:center;\"\u003e';\n  thtml+='\u003cspan style=\"font-size:13px;color:#64748b;\"\u003eAfter '+years+' years — Portfolio: \u003c/span\u003e';\n  thtml+='\u003cspan style=\"font-size:20px;font-weight:bold;color:#166534;\"\u003e$'+fmtD(drip==='yes'?finalData.balance:amount)+'\u003c/span\u003e';\n  if(drip==='no'){\n    thtml+='\u003cspan style=\"font-size:13px;color:#64748b;\"\u003e + Cash Dividends: \u003c/span\u003e';\n    thtml+='\u003cspan style=\"font-size:20px;font-weight:bold;color:#10b981;\"\u003e$'+fmtD(finalData.total)+'\u003c/span\u003e';\n  }\n  thtml+='\u003c/div\u003e';\n\n  if(drip==='yes'){\n    var noReinvest=amount*rate*(1-taxRate)*years;\n    var diff=finalData.total-noReinvest;\n    thtml+='\u003cdiv style=\"margin-top:8px;font-size:12px;color:#166534;text-align:center;\"\u003eDRIP compounding bonus: \u003cstrong\u003e+$'+fmtD(diff)+'\u003c/strong\u003e vs taking cash dividends\u003c/div\u003e';\n  }\n\n  document.getElementById('dcTimeline').innerHTML=thtml;\n\n  // ETF comparison\n  var etfs=[\n    {name:'VYM',desc:'Vanguard High Dividend Yield',yield:2.8,expense:0.06},\n    {name:'HDV',desc:'iShares Core High Dividend',yield:3.5,expense:0.08},\n    {name:'SPYD',desc:'SPDR S\u0026P 500 High Dividend',yield:4.5,expense:0.07},\n    {name:'SCHD',desc:'Schwab US Dividend Equity',yield:3.4,expense:0.06},\n    {name:'VIG',desc:'Vanguard Dividend Appreciation',yield:1.8,expense:0.06},\n    {name:'JEPI',desc:'JPMorgan Equity Premium Income',yield:7.5,expense:0.35}\n  ];\n\n  var ehtml='\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:10px;\"\u003e';\n  for(var i=0;i\u003cetfs.length;i++){\n    var annDivE=amount*etfs[i].yield/100*(1-taxRate);\n    ehtml+='\u003cdiv style=\"padding:10px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e';\n    ehtml+='\u003cdiv style=\"display:flex;justify-content:space-between;align-items:center;\"\u003e';\n    ehtml+='\u003cspan style=\"font-weight:bold;font-size:14px;color:#1e293b;\"\u003e'+etfs[i].name+'\u003c/span\u003e';\n    ehtml+='\u003cspan style=\"font-size:14px;font-weight:bold;color:#10b981;\"\u003e'+etfs[i].yield+'%\u003c/span\u003e';\n    ehtml+='\u003c/div\u003e';\n    ehtml+='\u003cdiv style=\"font-size:11px;color:#64748b;\"\u003e'+etfs[i].desc+' (ER: '+etfs[i].expense+'%)\u003c/div\u003e';\n    ehtml+='\u003cdiv style=\"font-size:12px;margin-top:4px;color:#2563eb;\"\u003eAnnual income: \u003cstrong\u003e$'+fmtD(annDivE)+'\u003c/strong\u003e (after tax)\u003c/div\u003e';\n    ehtml+='\u003c/div\u003e';\n  }\n  ehtml+='\u003c/div\u003e';\n  ehtml+='\u003cdiv style=\"margin-top:8px;font-size:11px;color:#92400e;text-align:center;\"\u003eYields as of May 2026 (approximate). Actual dividends vary. Past performance does not guarantee future results.\u003c/div\u003e';\n  document.getElementById('dcETF').innerHTML=ehtml;\n}\n\ndocument.addEventListener('DOMContentLoaded',function(){calcDC();});\ncalcDC();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-dividend-investing-works\"\u003eHow Dividend Investing Works\u003c/h2\u003e\n\u003ch3 id=\"what-are-dividends\"\u003eWhat Are Dividends?\u003c/h3\u003e\n\u003cp\u003eDividends are cash payments that companies make to shareholders from their profits. When you own dividend-paying stocks or ETFs, you receive regular income — typically quarterly — just for holding the shares.\u003c/p\u003e","title":"Dividend Income Calculator | Estimate Annual \u0026 Monthly Dividend Earnings"},{"content":"This article contains affiliate links.\nEducation Cost Calculator [2026] Just select a schooling path from kindergarten through university to automatically calculate the total education cost for one child. Compare savings requirements and see the impact of child benefit funds.\nChild's Current Age 0Age 318 Schooling Path Kindergarten (3 years) Public (¥170,000/yr) Private (¥310,000/yr) Elementary School (6 years) Public (¥350,000/yr) Private (¥1,670,000/yr) Middle School (3 years) Public (¥540,000/yr) Private (¥1,440,000/yr) High School (3 years) Public (¥510,000/yr) Private (¥1,050,000/yr) University National/Public (¥540,000/yr · 4 yrs) Private Liberal Arts (¥950,000/yr · 4 yrs) Private Science (¥1,300,000/yr · 4 yrs) Private Medical (¥3,500,000/yr · 6 yrs) None Quick Presets All Public Public K–12 + Private University All Private Monthly Savings Amount (¥10K units) ¥10,000¥30,000¥100,000 Total Education Cost (estimate) ¥15,770,000 ~19 years until university graduation Cost by Stage Stage Years Annual Cost Subtotal Savings Simulation Total Saved ¥6,840,000 ¥30,000 × 228 months Total Education Cost ¥15,770,000 If child benefit is saved in full Ages 0–3: ¥15,000/mo / Ages 3–15: ¥10,000/mo Total child benefit received (est.) ~¥2,000,000 Remaining shortfall after benefit — *Estimated based on post-2024 reform amounts. Actual benefit depends on household income and policy changes. Schooling Path Comparison Path Total Cost Monthly Equiv. Education Cost Benchmarks According to the Ministry of Education survey, education costs per child vary widely by schooling path.\nSchooling Path Total Cost Estimate All Public (National/Public University) ~¥8M–¥9M Public K–12 + Private Liberal Arts University ~¥11M–¥12M All Private (Private Science University) ~¥17M–¥20M All Private (Private Medical School) ~¥30M–¥35M Education is one of the \u0026ldquo;three major life expenses\u0026rdquo; alongside housing and retirement. Planning starts from birth.\n5 Smart Ways to Fund Education 1. Education savings insurance (Gakushi Hoken) A savings-type insurance product you can join shortly after birth. Payouts are timed to key enrollment milestones, and premiums are waived if the policyholder dies. Returns run around 107–112%, which suits families prioritizing certainty.\n2. NISA (tax-free investing) Index fund investing through NISA is ideal for long-term goals. ¥30,000/month at 5% per year for 15 years grows from ¥5.4M in contributions to roughly ¥8M — education costs don\u0026rsquo;t wait.\n3. Save all child benefit payments Child benefit from birth through middle school (¥15,000/mo age 0–3; ¥10,000/mo age 3–15) totals about ¥2,000,000 if saved in full. Setting it aside from day one creates a meaningful head start.\n4. Scholarships Japan Student Services Organization offers both grant-type (no repayment) and loan-type scholarships. Grant types have income requirements; loan types are broadly available. Consider as a supplement when projected costs are high.\n5. Education loans The Japan Finance Corporation offers fixed-rate education loans at approximately 2.25% p.a., up to ¥4,500,000. Interest-only repayment during enrollment is an option. Useful as a last resort if savings fall short.\nRelated Tools Savings Goal Calculator — Build a savings plan to cover education costs iDeCo Simulator — Secure retirement funds alongside education savings Asset Allocation Simulator — See how education costs fit your overall financial picture ","permalink":"https://productivity-works.com/tools/education-cost-calculator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"education-cost-calculator-2026\"\u003eEducation Cost Calculator [2026]\u003c/h1\u003e\n\u003cp\u003e\u003cstrong\u003eJust select a schooling path\u003c/strong\u003e from kindergarten through university to automatically calculate the total education cost for one child. Compare savings requirements and see the impact of child benefit funds.\u003c/p\u003e\n\u003cdiv id=\"edu-calc\" style=\"max-width:720px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;color:#1e293b;\"\u003e\n\u003c!-- Input panel --\u003e\n\u003cdiv style=\"padding:24px;border:2px solid #d97706;border-radius:12px;background:#fffbeb;\"\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eChild's Current Age\u003c/label\u003e\n\u003cinput type=\"range\" id=\"childAge\" min=\"0\" max=\"18\" step=\"1\" value=\"3\" oninput=\"calcEdu()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e0\u003c/span\u003e\u003cspan id=\"childAgeVal\" style=\"font-weight:bold;font-size:18px;color:#d97706;\"\u003eAge 3\u003c/span\u003e\u003cspan\u003e18\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003cdiv style=\"font-weight:bold;margin-bottom:10px;\"\u003eSchooling Path\u003c/div\u003e\n\u003cdiv style=\"display:grid;gap:10px;\"\u003e\n\u003cdiv style=\"padding:12px;background:white;border-radius:8px;border:1px solid #fde68a;\"\u003e\n\u003cdiv style=\"font-size:13px;font-weight:bold;color:#92400e;margin-bottom:6px;\"\u003eKindergarten (3 years)\u003c/div\u003e\n\u003cdiv style=\"display:flex;gap:16px;flex-wrap:wrap;\"\u003e\n\u003clabel style=\"cursor:pointer;display:flex;align-items:center;gap:4px;\"\u003e\u003cinput type=\"radio\" name=\"yochien\" value=\"public\" checked onchange=\"calcEdu()\"\u003e Public (¥170,000/yr)\u003c/label\u003e\n\u003clabel style=\"cursor:pointer;display:flex;align-items:center;gap:4px;\"\u003e\u003cinput type=\"radio\" name=\"yochien\" value=\"private\" onchange=\"calcEdu()\"\u003e Private (¥310,000/yr)\u003c/label\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:12px;background:white;border-radius:8px;border:1px solid #fde68a;\"\u003e\n\u003cdiv style=\"font-size:13px;font-weight:bold;color:#92400e;margin-bottom:6px;\"\u003eElementary School (6 years)\u003c/div\u003e\n\u003cdiv style=\"display:flex;gap:16px;flex-wrap:wrap;\"\u003e\n\u003clabel style=\"cursor:pointer;display:flex;align-items:center;gap:4px;\"\u003e\u003cinput type=\"radio\" name=\"shougakkou\" value=\"public\" checked onchange=\"calcEdu()\"\u003e Public (¥350,000/yr)\u003c/label\u003e\n\u003clabel style=\"cursor:pointer;display:flex;align-items:center;gap:4px;\"\u003e\u003cinput type=\"radio\" name=\"shougakkou\" value=\"private\" onchange=\"calcEdu()\"\u003e Private (¥1,670,000/yr)\u003c/label\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:12px;background:white;border-radius:8px;border:1px solid #fde68a;\"\u003e\n\u003cdiv style=\"font-size:13px;font-weight:bold;color:#92400e;margin-bottom:6px;\"\u003eMiddle School (3 years)\u003c/div\u003e\n\u003cdiv style=\"display:flex;gap:16px;flex-wrap:wrap;\"\u003e\n\u003clabel style=\"cursor:pointer;display:flex;align-items:center;gap:4px;\"\u003e\u003cinput type=\"radio\" name=\"chugakkou\" value=\"public\" checked onchange=\"calcEdu()\"\u003e Public (¥540,000/yr)\u003c/label\u003e\n\u003clabel style=\"cursor:pointer;display:flex;align-items:center;gap:4px;\"\u003e\u003cinput type=\"radio\" name=\"chugakkou\" value=\"private\" onchange=\"calcEdu()\"\u003e Private (¥1,440,000/yr)\u003c/label\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:12px;background:white;border-radius:8px;border:1px solid #fde68a;\"\u003e\n\u003cdiv style=\"font-size:13px;font-weight:bold;color:#92400e;margin-bottom:6px;\"\u003eHigh School (3 years)\u003c/div\u003e\n\u003cdiv style=\"display:flex;gap:16px;flex-wrap:wrap;\"\u003e\n\u003clabel style=\"cursor:pointer;display:flex;align-items:center;gap:4px;\"\u003e\u003cinput type=\"radio\" name=\"koukou\" value=\"public\" checked onchange=\"calcEdu()\"\u003e Public (¥510,000/yr)\u003c/label\u003e\n\u003clabel style=\"cursor:pointer;display:flex;align-items:center;gap:4px;\"\u003e\u003cinput type=\"radio\" name=\"koukou\" value=\"private\" onchange=\"calcEdu()\"\u003e Private (¥1,050,000/yr)\u003c/label\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:12px;background:white;border-radius:8px;border:1px solid #fde68a;\"\u003e\n\u003cdiv style=\"font-size:13px;font-weight:bold;color:#92400e;margin-bottom:6px;\"\u003eUniversity\u003c/div\u003e\n\u003cdiv style=\"display:flex;gap:12px;flex-wrap:wrap;\"\u003e\n\u003clabel style=\"cursor:pointer;display:flex;align-items:center;gap:4px;\"\u003e\u003cinput type=\"radio\" name=\"daigaku\" value=\"national\" checked onchange=\"calcEdu()\"\u003e National/Public (¥540,000/yr · 4 yrs)\u003c/label\u003e\n\u003clabel style=\"cursor:pointer;display:flex;align-items:center;gap:4px;\"\u003e\u003cinput type=\"radio\" name=\"daigaku\" value=\"private_bun\" onchange=\"calcEdu()\"\u003e Private Liberal Arts (¥950,000/yr · 4 yrs)\u003c/label\u003e\n\u003clabel style=\"cursor:pointer;display:flex;align-items:center;gap:4px;\"\u003e\u003cinput type=\"radio\" name=\"daigaku\" value=\"private_ri\" onchange=\"calcEdu()\"\u003e Private Science (¥1,300,000/yr · 4 yrs)\u003c/label\u003e\n\u003clabel style=\"cursor:pointer;display:flex;align-items:center;gap:4px;\"\u003e\u003cinput type=\"radio\" name=\"daigaku\" value=\"medical\" onchange=\"calcEdu()\"\u003e Private Medical (¥3,500,000/yr · 6 yrs)\u003c/label\u003e\n\u003clabel style=\"cursor:pointer;display:flex;align-items:center;gap:4px;\"\u003e\u003cinput type=\"radio\" name=\"daigaku\" value=\"none\" onchange=\"calcEdu()\"\u003e None\u003c/label\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Quick presets --\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003cdiv style=\"font-size:13px;font-weight:bold;color:#92400e;margin-bottom:8px;\"\u003eQuick Presets\u003c/div\u003e\n\u003cdiv style=\"display:flex;gap:8px;flex-wrap:wrap;\"\u003e\n\u003cbutton onclick=\"setPreset('all_public')\" style=\"padding:8px 14px;background:#d97706;color:white;border:none;border-radius:6px;cursor:pointer;font-size:13px;font-weight:bold;\"\u003eAll Public\u003c/button\u003e\n\u003cbutton onclick=\"setPreset('high_public_univ_private')\" style=\"padding:8px 14px;background:#f59e0b;color:white;border:none;border-radius:6px;cursor:pointer;font-size:13px;font-weight:bold;\"\u003ePublic K–12 + Private University\u003c/button\u003e\n\u003cbutton onclick=\"setPreset('all_private')\" style=\"padding:8px 14px;background:#b45309;color:white;border:none;border-radius:6px;cursor:pointer;font-size:13px;font-weight:bold;\"\u003eAll Private\u003c/button\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Monthly savings --\u003e\n\u003cdiv style=\"margin-bottom:8px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eMonthly Savings Amount (¥10K units)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"monthlyAmt\" min=\"1\" max=\"10\" step=\"0.5\" value=\"3\" oninput=\"calcEdu()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e¥10,000\u003c/span\u003e\u003cspan id=\"monthlyAmtVal\" style=\"font-weight:bold;font-size:18px;color:#d97706;\"\u003e¥30,000\u003c/span\u003e\u003cspan\u003e¥100,000\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Results panel --\u003e\n\u003cdiv id=\"eduResults\" style=\"margin-top:24px;padding:24px;border-radius:12px;background:#fef3c7;border:2px solid #d97706;\"\u003e\n\u003c!-- Total cost hero --\u003e\n\u003cdiv style=\"text-align:center;margin-bottom:24px;padding:20px;background:white;border-radius:10px;\"\u003e\n\u003cdiv style=\"font-size:14px;color:#64748b;margin-bottom:4px;\"\u003eTotal Education Cost (estimate)\u003c/div\u003e\n\u003cdiv id=\"totalCost\" style=\"font-size:52px;font-weight:bold;color:#d97706;line-height:1.1;\"\u003e¥15,770,000\u003c/div\u003e\n\u003cdiv id=\"remainingYears\" style=\"font-size:13px;color:#64748b;margin-top:6px;\"\u003e~19 years until university graduation\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Stacked bar --\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003cdiv style=\"font-size:14px;font-weight:bold;margin-bottom:8px;\"\u003eCost by Stage\u003c/div\u003e\n\u003cdiv id=\"stackedBar\" style=\"height:28px;border-radius:8px;overflow:hidden;display:flex;\"\u003e\u003c/div\u003e\n\u003cdiv id=\"barLegend\" style=\"display:flex;flex-wrap:wrap;gap:10px;margin-top:8px;font-size:12px;color:#64748b;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Breakdown table --\u003e\n\u003cdiv style=\"overflow-x:auto;margin-bottom:20px;\"\u003e\n\u003ctable style=\"width:100%;border-collapse:collapse;font-size:14px;\"\u003e\n\u003cthead\u003e\n\u003ctr style=\"background:#d97706;color:white;\"\u003e\n\u003cth style=\"padding:10px 8px;text-align:left;\"\u003eStage\u003c/th\u003e\n\u003cth style=\"padding:10px 8px;text-align:center;\"\u003eYears\u003c/th\u003e\n\u003cth style=\"padding:10px 8px;text-align:right;\"\u003eAnnual Cost\u003c/th\u003e\n\u003cth style=\"padding:10px 8px;text-align:right;\"\u003eSubtotal\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody id=\"breakdownTable\"\u003e\u003c/tbody\u003e\n\u003ctfoot id=\"breakdownFoot\"\u003e\u003c/tfoot\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\u003c!-- Savings simulation --\u003e\n\u003cdiv style=\"padding:16px;background:white;border-radius:10px;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"font-size:15px;font-weight:bold;margin-bottom:12px;\"\u003eSavings Simulation\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:12px;text-align:center;margin-bottom:12px;\"\u003e\n\u003cdiv style=\"padding:12px;background:#fef3c7;border-radius:8px;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eTotal Saved\u003c/div\u003e\n\u003cdiv id=\"totalSavings\" style=\"font-size:22px;font-weight:bold;color:#d97706;\"\u003e¥6,840,000\u003c/div\u003e\n\u003cdiv id=\"savingsDetail\" style=\"font-size:11px;color:#64748b;margin-top:2px;\"\u003e¥30,000 × 228 months\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:12px;background:#fef3c7;border-radius:8px;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eTotal Education Cost\u003c/div\u003e\n\u003cdiv id=\"costDisplay\" style=\"font-size:22px;font-weight:bold;color:#1e293b;\"\u003e¥15,770,000\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"gapDisplay\" style=\"padding:12px;border-radius:8px;text-align:center;font-size:15px;font-weight:bold;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Child benefit panel --\u003e\n\u003cdiv style=\"padding:16px;background:white;border-radius:10px;margin-bottom:0;\"\u003e\n\u003cdiv style=\"font-size:15px;font-weight:bold;margin-bottom:8px;\"\u003eIf child benefit is saved in full\u003c/div\u003e\n\u003cdiv style=\"font-size:13px;color:#64748b;margin-bottom:10px;\"\u003eAges 0–3: ¥15,000/mo / Ages 3–15: ¥10,000/mo\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:10px;text-align:center;\"\u003e\n\u003cdiv style=\"padding:10px;background:#fef3c7;border-radius:8px;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eTotal child benefit received (est.)\u003c/div\u003e\n\u003cdiv style=\"font-size:20px;font-weight:bold;color:#d97706;\"\u003e~¥2,000,000\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:10px;background:#fef3c7;border-radius:8px;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eRemaining shortfall after benefit\u003c/div\u003e\n\u003cdiv id=\"gapAfterJidou\" style=\"font-size:20px;font-weight:bold;color:#dc2626;\"\u003e—\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-top:10px;font-size:12px;color:#64748b;\"\u003e*Estimated based on post-2024 reform amounts. Actual benefit depends on household income and policy changes.\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Path comparison --\u003e\n\u003cdiv style=\"margin-top:24px;padding:20px;background:#f8fafc;border-radius:12px;border:1px solid #e2e8f0;\"\u003e\n\u003cdiv style=\"font-size:16px;font-weight:bold;margin-bottom:14px;\"\u003eSchooling Path Comparison\u003c/div\u003e\n\u003cdiv style=\"overflow-x:auto;\"\u003e\n\u003ctable style=\"width:100%;border-collapse:collapse;font-size:14px;\"\u003e\n\u003cthead\u003e\n\u003ctr style=\"background:#f1f5f9;\"\u003e\n\u003cth style=\"padding:10px 8px;text-align:left;border-bottom:2px solid #e2e8f0;\"\u003ePath\u003c/th\u003e\n\u003cth style=\"padding:10px 8px;text-align:right;border-bottom:2px solid #e2e8f0;\"\u003eTotal Cost\u003c/th\u003e\n\u003cth style=\"padding:10px 8px;text-align:right;border-bottom:2px solid #e2e8f0;\"\u003eMonthly Equiv.\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody id=\"comparisonTable\"\u003e\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nvar EDU_COSTS = {\n  yochien:    { public: 17, private: 31,  years: 3 },\n  shougakkou: { public: 35, private: 167, years: 6 },\n  chugakkou:  { public: 54, private: 144, years: 3 },\n  koukou:     { public: 51, private: 105, years: 3 },\n  daigaku: {\n    national:    { cost: 54,  years: 4 },\n    private_bun: { cost: 95,  years: 4 },\n    private_ri:  { cost: 130, years: 4 },\n    medical:     { cost: 350, years: 6 },\n    none:        { cost: 0,   years: 0 }\n  }\n};\n\nvar STAGE_COLORS = {\n  yochien:    '#fbbf24',\n  shougakkou: '#f59e0b',\n  chugakkou:  '#d97706',\n  koukou:     '#b45309',\n  daigaku:    '#92400e'\n};\n\nvar STAGE_LABELS = {\n  yochien:    'Kindergarten',\n  shougakkou: 'Elementary',\n  chugakkou:  'Middle School',\n  koukou:     'High School',\n  daigaku:    'University'\n};\n\nfunction getRadioVal(name) {\n  var radios = document.getElementsByName(name);\n  for (var i = 0; i \u003c radios.length; i++) {\n    if (radios[i].checked) return radios[i].value;\n  }\n  return '';\n}\n\nfunction setRadio(name, value) {\n  var radios = document.getElementsByName(name);\n  for (var i = 0; i \u003c radios.length; i++) {\n    radios[i].checked = (radios[i].value === value);\n  }\n}\n\nfunction setPreset(type) {\n  if (type === 'all_public') {\n    setRadio('yochien', 'public');\n    setRadio('shougakkou', 'public');\n    setRadio('chugakkou', 'public');\n    setRadio('koukou', 'public');\n    setRadio('daigaku', 'national');\n  } else if (type === 'high_public_univ_private') {\n    setRadio('yochien', 'public');\n    setRadio('shougakkou', 'public');\n    setRadio('chugakkou', 'public');\n    setRadio('koukou', 'public');\n    setRadio('daigaku', 'private_bun');\n  } else if (type === 'all_private') {\n    setRadio('yochien', 'private');\n    setRadio('shougakkou', 'private');\n    setRadio('chugakkou', 'private');\n    setRadio('koukou', 'private');\n    setRadio('daigaku', 'private_ri');\n  }\n  calcEdu();\n}\n\nfunction calcTotal(yochien, shougakkou, chugakkou, koukou, daigaku) {\n  var t = 0;\n  t += EDU_COSTS.yochien[yochien] * EDU_COSTS.yochien.years;\n  t += EDU_COSTS.shougakkou[shougakkou] * EDU_COSTS.shougakkou.years;\n  t += EDU_COSTS.chugakkou[chugakkou] * EDU_COSTS.chugakkou.years;\n  t += EDU_COSTS.koukou[koukou] * EDU_COSTS.koukou.years;\n  t += EDU_COSTS.daigaku[daigaku].cost * EDU_COSTS.daigaku[daigaku].years;\n  return t;\n}\n\nfunction fmtMan(val) {\n  return '¥' + (val * 10000).toLocaleString();\n}\n\nfunction calcEdu() {\n  var age = parseInt(document.getElementById('childAge').value);\n  var monthly = parseFloat(document.getElementById('monthlyAmt').value);\n\n  document.getElementById('childAgeVal').textContent = 'Age ' + age;\n  document.getElementById('monthlyAmtVal').textContent = '¥' + (monthly * 10000).toLocaleString();\n\n  var y = getRadioVal('yochien');\n  var s = getRadioVal('shougakkou');\n  var c = getRadioVal('chugakkou');\n  var k = getRadioVal('koukou');\n  var d = getRadioVal('daigaku');\n\n  var stages = [\n    { key: 'yochien',    label: 'Kindergarten', annual: EDU_COSTS.yochien[y],          years: EDU_COSTS.yochien.years,    subtotal: EDU_COSTS.yochien[y] * EDU_COSTS.yochien.years },\n    { key: 'shougakkou', label: 'Elementary',   annual: EDU_COSTS.shougakkou[s],        years: EDU_COSTS.shougakkou.years, subtotal: EDU_COSTS.shougakkou[s] * EDU_COSTS.shougakkou.years },\n    { key: 'chugakkou',  label: 'Middle School',annual: EDU_COSTS.chugakkou[c],         years: EDU_COSTS.chugakkou.years,  subtotal: EDU_COSTS.chugakkou[c] * EDU_COSTS.chugakkou.years },\n    { key: 'koukou',     label: 'High School',  annual: EDU_COSTS.koukou[k],            years: EDU_COSTS.koukou.years,     subtotal: EDU_COSTS.koukou[k] * EDU_COSTS.koukou.years },\n    { key: 'daigaku',    label: 'University',   annual: EDU_COSTS.daigaku[d].cost,      years: EDU_COSTS.daigaku[d].years, subtotal: EDU_COSTS.daigaku[d].cost * EDU_COSTS.daigaku[d].years }\n  ];\n\n  var total = 0;\n  for (var i = 0; i \u003c stages.length; i++) total += stages[i].subtotal;\n\n  var univEndAge = 3 + 3 + 6 + 3 + 3 + EDU_COSTS.daigaku[d].years;\n  var yearsLeft = Math.max(0, univEndAge - age);\n  var monthsLeft = yearsLeft * 12;\n\n  document.getElementById('totalCost').textContent = fmtMan(total);\n  document.getElementById('remainingYears').textContent = '~' + yearsLeft + ' years until university graduation (from age ' + age + ')';\n\n  var barHTML = '';\n  var legendHTML = '';\n  for (var i = 0; i \u003c stages.length; i++) {\n    if (stages[i].subtotal === 0) continue;\n    var pct = total \u003e 0 ? (stages[i].subtotal / total * 100).toFixed(1) : 0;\n    barHTML += '\u003cdiv style=\"width:' + pct + '%;background:' + STAGE_COLORS[stages[i].key] + ';height:100%;transition:width 0.3s;\" title=\"' + stages[i].label + ': ' + fmtMan(stages[i].subtotal) + '\"\u003e\u003c/div\u003e';\n    legendHTML += '\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:' + STAGE_COLORS[stages[i].key] + ';border-radius:2px;margin-right:3px;\"\u003e\u003c/span\u003e' + stages[i].label + ' ' + fmtMan(stages[i].subtotal) + '\u003c/span\u003e';\n  }\n  document.getElementById('stackedBar').innerHTML = barHTML;\n  document.getElementById('barLegend').innerHTML = legendHTML;\n\n  var tbodyHTML = '';\n  for (var i = 0; i \u003c stages.length; i++) {\n    var st = stages[i];\n    var bg = i % 2 === 0 ? '#fff' : '#fffbeb';\n    var typeLabel = '';\n    if (st.key === 'yochien') typeLabel = y === 'public' ? 'Public' : 'Private';\n    else if (st.key === 'shougakkou') typeLabel = s === 'public' ? 'Public' : 'Private';\n    else if (st.key === 'chugakkou') typeLabel = c === 'public' ? 'Public' : 'Private';\n    else if (st.key === 'koukou') typeLabel = k === 'public' ? 'Public' : 'Private';\n    else {\n      var labels = { national: 'National/Public', private_bun: 'Private Liberal Arts', private_ri: 'Private Science', medical: 'Private Medical', none: 'None' };\n      typeLabel = labels[d] || '';\n    }\n    tbodyHTML += '\u003ctr style=\"background:' + bg + ';\"\u003e';\n    tbodyHTML += '\u003ctd style=\"padding:9px 8px;\"\u003e' + st.label + '\u003cspan style=\"font-size:11px;color:#64748b;margin-left:6px;\"\u003e(' + typeLabel + ')\u003c/span\u003e\u003c/td\u003e';\n    tbodyHTML += '\u003ctd style=\"padding:9px 8px;text-align:center;\"\u003e' + st.years + ' yrs\u003c/td\u003e';\n    tbodyHTML += '\u003ctd style=\"padding:9px 8px;text-align:right;\"\u003e' + (st.annual \u003e 0 ? fmtMan(st.annual) : '—') + '\u003c/td\u003e';\n    tbodyHTML += '\u003ctd style=\"padding:9px 8px;text-align:right;font-weight:bold;\"\u003e' + (st.subtotal \u003e 0 ? fmtMan(st.subtotal) : '—') + '\u003c/td\u003e';\n    tbodyHTML += '\u003c/tr\u003e';\n  }\n  document.getElementById('breakdownTable').innerHTML = tbodyHTML;\n  document.getElementById('breakdownFoot').innerHTML = '\u003ctr style=\"background:#d97706;color:white;\"\u003e\u003ctd colspan=\"3\" style=\"padding:10px 8px;font-weight:bold;\"\u003eTotal\u003c/td\u003e\u003ctd style=\"padding:10px 8px;text-align:right;font-weight:bold;font-size:16px;\"\u003e' + fmtMan(total) + '\u003c/td\u003e\u003c/tr\u003e';\n\n  var totalSavings = Math.round(monthly * monthsLeft);\n  document.getElementById('totalSavings').textContent = fmtMan(totalSavings);\n  document.getElementById('savingsDetail').textContent = '¥' + (monthly * 10000).toLocaleString() + ' x ' + monthsLeft + ' months';\n  document.getElementById('costDisplay').textContent = fmtMan(total);\n\n  var gap = totalSavings - total;\n  var gapEl = document.getElementById('gapDisplay');\n  if (gap \u003e= 0) {\n    gapEl.style.background = '#d1fae5';\n    gapEl.style.color = '#065f46';\n    gapEl.textContent = 'Surplus: ' + fmtMan(gap) + ' (your savings plan covers education costs)';\n  } else {\n    gapEl.style.background = '#fee2e2';\n    gapEl.style.color = '#991b1b';\n    gapEl.textContent = 'Shortfall: ' + fmtMan(Math.abs(gap)) + ' (consider increasing savings or adding investment returns)';\n  }\n\n  var jidou = 200;\n  var gapAfterJidou = gap + jidou;\n  var gapJEl = document.getElementById('gapAfterJidou');\n  if (gapAfterJidou \u003e= 0) {\n    gapJEl.style.color = '#065f46';\n    gapJEl.textContent = 'Surplus ' + fmtMan(gapAfterJidou);\n  } else {\n    gapJEl.style.color = '#dc2626';\n    gapJEl.textContent = 'Shortfall ' + fmtMan(Math.abs(gapAfterJidou));\n  }\n\n  var allPublic    = calcTotal('public', 'public', 'public', 'public', 'national');\n  var mixPattern   = calcTotal('public', 'public', 'public', 'public', 'private_bun');\n  var allPrivate   = calcTotal('private', 'private', 'private', 'private', 'private_ri');\n  var userPattern  = total;\n\n  var univTotalYears = 3 + 6 + 3 + 3 + EDU_COSTS.daigaku[d].years;\n  var allPubMonths = univTotalYears * 12;\n\n  var patterns = [\n    { label: 'All Public (National/Public Univ.)', total: allPublic,   months: allPubMonths },\n    { label: 'Public K-12 + Private Liberal Arts', total: mixPattern,  months: allPubMonths },\n    { label: 'All Private (Private Science Univ.)', total: allPrivate,  months: allPubMonths },\n    { label: 'Your Selection', total: userPattern, months: monthsLeft, highlight: true }\n  ];\n\n  var compHTML = '';\n  for (var i = 0; i \u003c patterns.length; i++) {\n    var p = patterns[i];\n    var monthlyEq = p.months \u003e 0 ? Math.ceil(p.total / p.months * 10) / 10 : 0;\n    var bg = p.highlight ? '#fef3c7' : (i % 2 === 0 ? '#fff' : '#f8fafc');\n    var fw = p.highlight ? 'bold' : 'normal';\n    compHTML += '\u003ctr style=\"background:' + bg + ';font-weight:' + fw + ';\"\u003e';\n    compHTML += '\u003ctd style=\"padding:9px 8px;border-bottom:1px solid #e2e8f0;\"\u003e' + p.label + (p.highlight ? ' ★' : '') + '\u003c/td\u003e';\n    compHTML += '\u003ctd style=\"padding:9px 8px;text-align:right;border-bottom:1px solid #e2e8f0;color:#d97706;font-weight:bold;\"\u003e' + fmtMan(p.total) + '\u003c/td\u003e';\n    compHTML += '\u003ctd style=\"padding:9px 8px;text-align:right;border-bottom:1px solid #e2e8f0;\"\u003e' + fmtMan(monthlyEq) + '\u003c/td\u003e';\n    compHTML += '\u003c/tr\u003e';\n  }\n  document.getElementById('comparisonTable').innerHTML = compHTML;\n}\n\ncalcEdu();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"education-cost-benchmarks\"\u003eEducation Cost Benchmarks\u003c/h2\u003e\n\u003cp\u003eAccording to the Ministry of Education survey, education costs per child vary widely by schooling path.\u003c/p\u003e","title":"Education Cost Calculator | Total Schooling Costs for One Child [2026]"},{"content":"This article contains affiliate links. We may earn a commission at no extra cost to you.\nEmergency Fund Calculator Enter your monthly expenses and situation to calculate your recommended emergency fund and see how long it takes to build it.\nMonthly Essential Expenses ($) $1,000$4,000$15,000 Employment Type Stable salaried (government, large company) Regular salaried employee Contract / temporary worker Freelancer / self-employed Gig worker / variable income Household Single, no dependents Couple, dual income Couple, single income Family with 1-2 children Family with 3+ children Monthly Savings Toward Emergency Fund ($) $50$500$3,000 Savings Timeline Why You Need an Emergency Fund An emergency fund is money set aside for unexpected expenses: job loss, medical bills, car repairs, or home maintenance. Without one, a single financial shock can lead to high-interest debt.\nHow Much Is Enough? The standard advice of \u0026ldquo;3-6 months of expenses\u0026rdquo; is a starting point, but the right number depends on your situation:\nSituation Recommended Months Stable government/corporate job, dual income 3 months Regular salaried employee 4 months Contract or temporary worker 6 months Freelancer or self-employed 8 months Gig worker or variable income 9+ months Add 1-2 months if you have dependents or a single-income household.\nWhere to Keep Your Emergency Fund Your emergency fund should be liquid and accessible — not invested in stocks or locked in a certificate of deposit. A high-yield savings account is the standard choice: your money earns some interest while remaining available within 1-2 business days.\nBuilding Your Fund: Practical Tips Automate transfers on payday so saving happens before spending Start small — even $100/month builds momentum Use windfalls — tax refunds, bonuses, and side income can accelerate your timeline Keep it separate from your checking account to reduce temptation Looking to create a monthly budget? Try our Budget Planner to see where your money goes.\nTracking your investments alongside your emergency fund? Our Compound Interest Calculator shows how your long-term savings grow.\nRelated Tools\nBudget Planner US Salary Calculator Compound Interest Calculator Retirement Savings Calculator Related Articles How to Build an Emergency Fund Fast (2026) ","permalink":"https://productivity-works.com/tools/emergency-fund-calculator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links. We may earn a commission at no extra cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"emergency-fund-calculator\"\u003eEmergency Fund Calculator\u003c/h1\u003e\n\u003cp\u003eEnter your monthly expenses and situation to calculate your \u003cstrong\u003erecommended emergency fund\u003c/strong\u003e and see how long it takes to build it.\u003c/p\u003e\n\u003cdiv id=\"ef-calc\" style=\"max-width:680px;margin:0 auto;font-family:Inter,sans-serif;\"\u003e\n\u003cdiv style=\"padding:24px;border:2px solid #2563eb;border-radius:12px;background:#f8fafc;\"\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eMonthly Essential Expenses ($)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"expenses\" min=\"1000\" max=\"15000\" step=\"100\" value=\"4000\" oninput=\"calcEF()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e$1,000\u003c/span\u003e\u003cspan id=\"expVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e$4,000\u003c/span\u003e\u003cspan\u003e$15,000\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eEmployment Type\u003c/label\u003e\n\u003cselect id=\"employment\" onchange=\"calcEF()\" style=\"width:100%;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:15px;\"\u003e\n\u003coption value=\"stable\"\u003eStable salaried (government, large company)\u003c/option\u003e\n\u003coption value=\"regular\" selected\u003eRegular salaried employee\u003c/option\u003e\n\u003coption value=\"contract\"\u003eContract / temporary worker\u003c/option\u003e\n\u003coption value=\"freelance\"\u003eFreelancer / self-employed\u003c/option\u003e\n\u003coption value=\"gig\"\u003eGig worker / variable income\u003c/option\u003e\n\u003c/select\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eHousehold\u003c/label\u003e\n\u003cselect id=\"household\" onchange=\"calcEF()\" style=\"width:100%;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:15px;\"\u003e\n\u003coption value=\"single\"\u003eSingle, no dependents\u003c/option\u003e\n\u003coption value=\"couple\" selected\u003eCouple, dual income\u003c/option\u003e\n\u003coption value=\"couple_single\"\u003eCouple, single income\u003c/option\u003e\n\u003coption value=\"family_small\"\u003eFamily with 1-2 children\u003c/option\u003e\n\u003coption value=\"family_large\"\u003eFamily with 3+ children\u003c/option\u003e\n\u003c/select\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eMonthly Savings Toward Emergency Fund ($)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"savings\" min=\"50\" max=\"3000\" step=\"50\" value=\"500\" oninput=\"calcEF()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e$50\u003c/span\u003e\u003cspan id=\"savVal\" style=\"font-weight:bold;font-size:18px;color:#10b981;\"\u003e$500\u003c/span\u003e\u003cspan\u003e$3,000\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"efResult\" style=\"margin-top:24px;padding:20px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-top:24px;padding:24px;border:2px solid #10b981;border-radius:12px;background:#f0fdf4;\"\u003e\n\u003ch3 style=\"margin:0 0 16px;color:#166534;font-size:16px;\"\u003eSavings Timeline\u003c/h3\u003e\n\u003ccanvas id=\"efChart\" width=\"640\" height=\"260\" style=\"width:100%;height:auto;\"\u003e\u003c/canvas\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nfunction fmt(n){return n.toLocaleString('en-US');}\n\nfunction calcEF(){\n  var exp=+document.getElementById('expenses').value;\n  var emp=document.getElementById('employment').value;\n  var hh=document.getElementById('household').value;\n  var sav=+document.getElementById('savings').value;\n\n  document.getElementById('expVal').textContent='$'+fmt(exp);\n  document.getElementById('savVal').textContent='$'+fmt(sav);\n\n  var baseMonths={stable:3,regular:4,contract:6,freelance:8,gig:9};\n  var hhAdj={single:0,couple:-0.5,couple_single:1,family_small:1.5,family_large:2};\n  var months=baseMonths[emp]+(hhAdj[hh]||0);\n  months=Math.max(3,Math.round(months*2)/2);\n\n  var minM=months,maxM=months+2;\n  var target=exp*months;\n  var targetMax=exp*maxM;\n  var monthsToGoal=Math.ceil(target/sav);\n\n  var html='\u003cdiv style=\"text-align:center;margin-bottom:16px;\"\u003e';\n  html+='\u003cdiv style=\"font-size:14px;color:#64748b;\"\u003eRecommended Emergency Fund\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:36px;font-weight:bold;color:#2563eb;\"\u003e$'+fmt(target)+'\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:14px;color:#64748b;\"\u003e'+minM+' months of expenses\u003c/div\u003e';\n  html+='\u003c/div\u003e';\n\n  html+='\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;text-align:center;\"\u003e';\n  html+='\u003cdiv style=\"padding:12px;background:#f0fdf4;border-radius:8px;\"\u003e\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eMonthly Expenses\u003c/div\u003e\u003cdiv style=\"font-size:18px;font-weight:bold;\"\u003e$'+fmt(exp)+'\u003c/div\u003e\u003c/div\u003e';\n  html+='\u003cdiv style=\"padding:12px;background:#eff6ff;border-radius:8px;\"\u003e\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eStretch Goal\u003c/div\u003e\u003cdiv style=\"font-size:18px;font-weight:bold;color:#1e40af;\"\u003e$'+fmt(targetMax)+'\u003c/div\u003e\u003c/div\u003e';\n  html+='\u003cdiv style=\"padding:12px;background:#fefce8;border-radius:8px;\"\u003e\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eTime to Goal\u003c/div\u003e\u003cdiv style=\"font-size:18px;font-weight:bold;color:#a16207;\"\u003e'+monthsToGoal+' mo\u003c/div\u003e\u003c/div\u003e';\n  html+='\u003c/div\u003e';\n\n  var yrs=Math.floor(monthsToGoal/12);var mo=monthsToGoal%12;\n  var timeStr=yrs\u003e0?yrs+' year'+(yrs\u003e1?'s':'')+' '+mo+' month'+(mo!==1?'s':''):mo+' month'+(mo!==1?'s':'');\n  html+='\u003cdiv style=\"margin-top:12px;text-align:center;font-size:13px;color:#64748b;\"\u003eSaving $'+fmt(sav)+'/month, you\\'ll reach your goal in \u003cstrong\u003e'+timeStr+'\u003c/strong\u003e\u003c/div\u003e';\n\n  document.getElementById('efResult').innerHTML=html;\n  drawEFChart(target,targetMax,sav,monthsToGoal);\n}\n\nfunction drawEFChart(target,targetMax,sav,totalM){\n  var c=document.getElementById('efChart');\n  var ctx=c.getContext('2d');\n  var W=c.width,H=c.height;\n  ctx.clearRect(0,0,W,H);\n\n  var maxVal=Math.max(targetMax,sav*totalM*1.1);\n  var maxM=Math.ceil(totalM*1.3);\n  if(maxM\u003c12)maxM=12;\n  var pad={t:20,r:20,b:40,l:70};\n  var cw=W-pad.l-pad.r,ch=H-pad.t-pad.b;\n\n  ctx.strokeStyle='#e2e8f0';ctx.lineWidth=1;\n  for(var i=0;i\u003c=4;i++){\n    var y=pad.t+ch*(1-i/4);\n    ctx.beginPath();ctx.moveTo(pad.l,y);ctx.lineTo(W-pad.r,y);ctx.stroke();\n    ctx.fillStyle='#94a3b8';ctx.font='11px sans-serif';ctx.textAlign='right';\n    ctx.fillText('$'+fmt(Math.round(maxVal*i/4)),pad.l-6,y+4);\n  }\n\n  var targetY=pad.t+ch*(1-target/maxVal);\n  ctx.setLineDash([6,4]);ctx.strokeStyle='#10b981';ctx.lineWidth=1.5;\n  ctx.beginPath();ctx.moveTo(pad.l,targetY);ctx.lineTo(W-pad.r,targetY);ctx.stroke();\n  ctx.setLineDash([]);\n  ctx.fillStyle='#10b981';ctx.font='bold 11px sans-serif';ctx.textAlign='left';\n  ctx.fillText('Goal: $'+fmt(target),pad.l+4,targetY-6);\n\n  ctx.beginPath();ctx.strokeStyle='#2563eb';ctx.lineWidth=2.5;\n  for(var m=0;m\u003c=maxM;m++){\n    var bal=Math.min(sav*m,sav*totalM);\n    var x=pad.l+cw*m/maxM,y=pad.t+ch*(1-bal/maxVal);\n    if(m===0)ctx.moveTo(x,y);else ctx.lineTo(x,y);\n  }\n  ctx.stroke();\n\n  ctx.fillStyle='rgba(37,99,235,0.08)';\n  ctx.beginPath();ctx.moveTo(pad.l,pad.t+ch);\n  for(var m=0;m\u003c=maxM;m++){\n    var bal=Math.min(sav*m,sav*totalM);\n    var x=pad.l+cw*m/maxM,y=pad.t+ch*(1-bal/maxVal);\n    ctx.lineTo(x,y);\n  }\n  ctx.lineTo(pad.l+cw,pad.t+ch);ctx.closePath();ctx.fill();\n\n  ctx.fillStyle='#64748b';ctx.font='11px sans-serif';ctx.textAlign='center';\n  var step=maxM\u003c=12?1:maxM\u003c=24?3:6;\n  for(var m=0;m\u003c=maxM;m+=step){\n    ctx.fillText(m+'mo',pad.l+cw*m/maxM,H-pad.b+20);\n  }\n}\n\ndocument.addEventListener('DOMContentLoaded',function(){calcEF();});\ncalcEF();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"why-you-need-an-emergency-fund\"\u003eWhy You Need an Emergency Fund\u003c/h2\u003e\n\u003cp\u003eAn emergency fund is money set aside for unexpected expenses: job loss, medical bills, car repairs, or home maintenance. Without one, a single financial shock can lead to high-interest debt.\u003c/p\u003e","title":"Emergency Fund Calculator | How Much Do You Really Need?"},{"content":"This article contains affiliate links. We may earn a commission at no extra cost to you.\nFIRE Calculator Enter your numbers below to find your Financial Independence number, years to early retirement, and see exactly how your savings rate affects your timeline.\nYour Financial Details Current Age Annual Income ($) Annual Expenses ($) Current Savings / Investments ($) Expected Annual Return: 7.0% 3%12% Safe Withdrawal Rate (SWR): 4.00% 2% (Conservative)5% (Aggressive) The 4% rule is the most widely used guideline. Annual Savings Increase: 2.0% 0%5% (raises + optimization) Your FIRE Number $1,250,000 Annual expenses \u0026divide; Safe withdrawal rate Years to FIRE 14y 3m FIRE Age Age 44 Savings Rate 37.5% Monthly Savings $2,500 Progress to FIRE 4% $50,000 now $1,250,000 goal FIRE Variants Lean FIRE Expenses \u0026times; 0.8 $1,000,000 12 years 1 month Regular FIRE YOUR TARGET Your current expenses $1,250,000 14 years 3 months Fat FIRE Expenses \u0026times; 1.5 $1,875,000 20 years 8 months Coast FIRE Stop saving, coast to 65 $182,000 Need $132,000 more Savings Rate Impact Years to FIRE at different savings rates (assuming same return rate)\nSavings Rate Annual Savings Years to FIRE FIRE Age Year-by-Year Projection + Year Age Annual Savings Investment Returns Total Portfolio What Is FIRE? FIRE stands for Financial Independence, Retire Early — a movement built around the idea that aggressive saving and investing can allow you to leave traditional employment far ahead of the standard retirement age. Instead of working into your 60s, FIRE practitioners aim to build a portfolio large enough that investment returns alone cover all their living expenses, permanently. The core insight is simple: the less you spend relative to what you earn, the faster your savings compound, and the sooner you reach the crossover point where your money works harder than you do.\nThe movement gained mainstream traction in the 1990s through Vicki Robin and Joe Dominguez\u0026rsquo;s book Your Money or Your Life, and has since grown into a global community of bloggers, podcasters, and practitioners sharing strategies across forums like r/financialindependence. What unites them is a focus on savings rate over raw income — a household earning $60,000 and saving 50% of it will reach FIRE faster than one earning $150,000 and saving just 10%.\nThe 4% Rule Explained The 4% rule is the cornerstone of FIRE withdrawal planning. It originates from the Trinity Study (1998, updated 2011), a landmark paper by three Trinity University professors who analyzed historical stock and bond market data going back to 1926. Their finding: a portfolio invested in a diversified mix of stocks and bonds could sustain annual withdrawals of 4% of its initial value — adjusted for inflation each year — for at least 30 years with a very high probability of success (around 95% for a 75% stock / 25% bond allocation).\nIn practical terms, the 4% rule means your FIRE number is 25 times your annual expenses (since 1 / 4% = 25). If you spend $50,000 per year, you need $1,250,000. If you plan to retire for 40 or 50 years rather than 30, many FIRE practitioners use a more conservative 3% to 3.5% SWR, which implies a FIRE number of 29–33 times annual spending. The rule is a guideline, not a guarantee — sequence-of-returns risk (a market crash early in retirement) remains the primary danger, which is why many FIRE retirees keep 1–2 years of expenses in cash and remain flexible about part-time income.\nTypes of FIRE Lean FIRE targets a minimalist lifestyle with reduced expenses — typically 20–30% below your current spending. The portfolio required is smaller, making FIRE achievable sooner, but it leaves less cushion for unexpected costs or lifestyle inflation over decades.\nRegular FIRE is the baseline: retire on your current expenses, funded by a portfolio sized at 25× annual spending using the 4% rule. This is the most common goal and the default in the calculator above.\nFat FIRE targets a comfortable, affluent retirement at 1.5× or more of current expenses. You maintain full flexibility — travel, dining out, private schooling — without any lifestyle compromise. The tradeoff is a larger target and a longer accumulation phase.\nCoast FIRE is a milestone rather than a finish line. Once your invested portfolio is large enough to grow to your full FIRE number by traditional retirement age (65) with no additional contributions, you\u0026rsquo;ve \u0026ldquo;coasted.\u0026rdquo; Many people use Coast FIRE as permission to shift to lower-stress, lower-paying work they enjoy, since they no longer need to save aggressively.\nBarista FIRE is a hybrid: you retire from your primary career before hitting your full number, then cover a portion of expenses through part-time or flexible work (the name comes from working at a coffee shop for health benefits). Your portfolio covers the rest. This approach dramatically shortens the accumulation phase and is popular among people who want to leave corporate life early without needing a fully funded portfolio.\nTips to Reach FIRE Faster Increase your savings rate. The savings rate impact table in the calculator above illustrates this starkly: going from a 20% to a 50% savings rate can cut your timeline roughly in half. Even a 5-percentage-point improvement makes a meaningful difference over a decade.\nReduce expenses systematically. Housing and transportation are the two largest budget categories for most households. Downsizing, house hacking (renting part of your home), or eliminating a car payment often moves the needle more than years of cutting discretionary spending. The key is finding cuts that don\u0026rsquo;t reduce your quality of life, which compounds your results indefinitely.\nIncrease your income. Raises, promotions, job-hopping, and side hustles increase the numerator of your savings rate without requiring any lifestyle change. The FIRE community often emphasizes that income growth is underrated compared to frugality — especially early in a career when skills and earning potential are compounding rapidly alongside your portfolio.\nOptimize your investments. Low-cost index funds (total market or S\u0026amp;P 500) consistently outperform the average actively managed fund after fees. Maximizing tax-advantaged accounts — 401(k), IRA, HSA — reduces your tax drag and keeps more capital compounding. Every basis point of fees you eliminate is permanent, risk-free return improvement.\nRelated Tools See how your investments grow → Compound Interest Calculator Track your total net worth → Net Worth Calculator Plan your retirement savings → Retirement Calculator Calculate your take-home pay → Salary Calculator Create a monthly budget → Budget Planner Set savings milestones → Savings Goal Calculator Related Articles What Is the FIRE Movement? A Beginner\u0026rsquo;s Guide to Financial Independence How to Calculate Your FIRE Number and Hit It Faster The 4% Rule: Is It Still Safe for Early Retirees? Lean FIRE vs. Fat FIRE: Which Path Is Right for You? How to Increase Your Savings Rate Without Feeling Deprived This calculator is provided for educational and informational purposes only. It does not constitute financial advice. Investment returns are not guaranteed, and past market performance does not predict future results. Consult a qualified financial professional before making major financial decisions.\nRelated Tools Compound Interest Calculator Ideco Simulator Investment Return Calculator ","permalink":"https://productivity-works.com/tools/fire-calculator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links. We may earn a commission at no extra cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"fire-calculator\"\u003eFIRE Calculator\u003c/h1\u003e\n\u003cp\u003eEnter your numbers below to find your \u003cstrong\u003eFinancial Independence number\u003c/strong\u003e, years to early retirement, and see exactly how your savings rate affects your timeline.\u003c/p\u003e\n\u003cdiv id=\"fire-calc\" style=\"max-width:760px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:#1e293b;\"\u003e\n\u003c!-- INPUTS --\u003e\n\u003cdiv style=\"padding:28px;border:2px solid #7c3aed;border-radius:12px;background:#faf5ff;margin-bottom:20px;\"\u003e\n\u003ch2 style=\"margin:0 0 24px 0;font-size:20px;color:#7c3aed;\"\u003eYour Financial Details\u003c/h2\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:20px;\"\u003e\n\u003cdiv\u003e\n\u003clabel style=\"display:block;font-size:13px;font-weight:600;color:#64748b;margin-bottom:6px;text-transform:uppercase;letter-spacing:0.05em;\"\u003eCurrent Age\u003c/label\u003e\n\u003cinput type=\"number\" id=\"fireAge\" min=\"18\" max=\"70\" step=\"1\" value=\"30\" oninput=\"calcFIRE()\" style=\"width:100%;padding:10px 12px;border:1.5px solid #c4b5fd;border-radius:8px;font-size:16px;box-sizing:border-box;background:#fff;color:#1e293b;\"\u003e\n\u003c/div\u003e\n\u003cdiv\u003e\n\u003clabel style=\"display:block;font-size:13px;font-weight:600;color:#64748b;margin-bottom:6px;text-transform:uppercase;letter-spacing:0.05em;\"\u003eAnnual Income ($)\u003c/label\u003e\n\u003cinput type=\"number\" id=\"fireIncome\" min=\"0\" step=\"1000\" value=\"80000\" oninput=\"calcFIRE()\" style=\"width:100%;padding:10px 12px;border:1.5px solid #c4b5fd;border-radius:8px;font-size:16px;box-sizing:border-box;background:#fff;color:#1e293b;\"\u003e\n\u003c/div\u003e\n\u003cdiv\u003e\n\u003clabel style=\"display:block;font-size:13px;font-weight:600;color:#64748b;margin-bottom:6px;text-transform:uppercase;letter-spacing:0.05em;\"\u003eAnnual Expenses ($)\u003c/label\u003e\n\u003cinput type=\"number\" id=\"fireExpenses\" min=\"0\" step=\"1000\" value=\"50000\" oninput=\"calcFIRE()\" style=\"width:100%;padding:10px 12px;border:1.5px solid #c4b5fd;border-radius:8px;font-size:16px;box-sizing:border-box;background:#fff;color:#1e293b;\"\u003e\n\u003c/div\u003e\n\u003cdiv\u003e\n\u003clabel style=\"display:block;font-size:13px;font-weight:600;color:#64748b;margin-bottom:6px;text-transform:uppercase;letter-spacing:0.05em;\"\u003eCurrent Savings / Investments ($)\u003c/label\u003e\n\u003cinput type=\"number\" id=\"fireSavings\" min=\"0\" step=\"1000\" value=\"50000\" oninput=\"calcFIRE()\" style=\"width:100%;padding:10px 12px;border:1.5px solid #c4b5fd;border-radius:8px;font-size:16px;box-sizing:border-box;background:#fff;color:#1e293b;\"\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-size:13px;font-weight:600;color:#64748b;margin-bottom:8px;text-transform:uppercase;letter-spacing:0.05em;\"\u003eExpected Annual Return: \u003cspan id=\"fireReturnVal\" style=\"color:#7c3aed;font-size:15px;\"\u003e7.0%\u003c/span\u003e\u003c/label\u003e\n\u003cinput type=\"range\" id=\"fireReturn\" min=\"3\" max=\"12\" step=\"0.5\" value=\"7\" oninput=\"calcFIRE()\" style=\"width:100%;accent-color:#7c3aed;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:12px;color:#94a3b8;margin-top:2px;\"\u003e\u003cspan\u003e3%\u003c/span\u003e\u003cspan\u003e12%\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-size:13px;font-weight:600;color:#64748b;margin-bottom:8px;text-transform:uppercase;letter-spacing:0.05em;\"\u003eSafe Withdrawal Rate (SWR): \u003cspan id=\"fireSWRVal\" style=\"color:#7c3aed;font-size:15px;\"\u003e4.00%\u003c/span\u003e\u003c/label\u003e\n\u003cinput type=\"range\" id=\"fireSWR\" min=\"2\" max=\"5\" step=\"0.25\" value=\"4\" oninput=\"calcFIRE()\" style=\"width:100%;accent-color:#7c3aed;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:12px;color:#94a3b8;margin-top:2px;\"\u003e\u003cspan\u003e2% (Conservative)\u003c/span\u003e\u003cspan\u003e5% (Aggressive)\u003c/span\u003e\u003c/div\u003e\n\u003cdiv style=\"font-size:12px;color:#94a3b8;margin-top:2px;\"\u003eThe 4% rule is the most widely used guideline.\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:4px;\"\u003e\n\u003clabel style=\"display:block;font-size:13px;font-weight:600;color:#64748b;margin-bottom:8px;text-transform:uppercase;letter-spacing:0.05em;\"\u003eAnnual Savings Increase: \u003cspan id=\"fireSavIncVal\" style=\"color:#7c3aed;font-size:15px;\"\u003e2.0%\u003c/span\u003e\u003c/label\u003e\n\u003cinput type=\"range\" id=\"fireSavInc\" min=\"0\" max=\"5\" step=\"0.5\" value=\"2\" oninput=\"calcFIRE()\" style=\"width:100%;accent-color:#7c3aed;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:12px;color:#94a3b8;margin-top:2px;\"\u003e\u003cspan\u003e0%\u003c/span\u003e\u003cspan\u003e5% (raises + optimization)\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- RESULTS PANEL --\u003e\n\u003cdiv id=\"fireResults\" style=\"padding:28px;border:2px solid #7c3aed;border-radius:12px;background:linear-gradient(135deg,#7c3aed 0%,#6d28d9 100%);color:white;margin-bottom:20px;\"\u003e\n\u003cdiv style=\"text-align:center;margin-bottom:24px;\"\u003e\n\u003cdiv style=\"font-size:13px;letter-spacing:0.1em;opacity:0.85;margin-bottom:6px;text-transform:uppercase;\"\u003eYour FIRE Number\u003c/div\u003e\n\u003cdiv id=\"fireNumber\" style=\"font-size:52px;font-weight:800;letter-spacing:-1px;\"\u003e$1,250,000\u003c/div\u003e\n\u003cdiv style=\"font-size:13px;opacity:0.75;margin-top:4px;\"\u003eAnnual expenses \u0026divide; Safe withdrawal rate\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:12px;margin-bottom:20px;\"\u003e\n\u003cdiv style=\"background:rgba(255,255,255,0.15);border-radius:10px;padding:14px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:11px;opacity:0.8;margin-bottom:4px;text-transform:uppercase;letter-spacing:0.05em;\"\u003eYears to FIRE\u003c/div\u003e\n\u003cdiv id=\"fireYears\" style=\"font-size:20px;font-weight:700;\"\u003e14y 3m\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:rgba(255,255,255,0.15);border-radius:10px;padding:14px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:11px;opacity:0.8;margin-bottom:4px;text-transform:uppercase;letter-spacing:0.05em;\"\u003eFIRE Age\u003c/div\u003e\n\u003cdiv id=\"fireAgeResult\" style=\"font-size:20px;font-weight:700;\"\u003eAge 44\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:rgba(255,255,255,0.15);border-radius:10px;padding:14px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:11px;opacity:0.8;margin-bottom:4px;text-transform:uppercase;letter-spacing:0.05em;\"\u003eSavings Rate\u003c/div\u003e\n\u003cdiv id=\"fireSavRate\" style=\"font-size:20px;font-weight:700;\"\u003e37.5%\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:rgba(255,255,255,0.15);border-radius:10px;padding:14px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:11px;opacity:0.8;margin-bottom:4px;text-transform:uppercase;letter-spacing:0.05em;\"\u003eMonthly Savings\u003c/div\u003e\n\u003cdiv id=\"fireMonthly\" style=\"font-size:20px;font-weight:700;\"\u003e$2,500\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- PROGRESS BAR --\u003e\n\u003cdiv\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;opacity:0.85;margin-bottom:6px;\"\u003e\n\u003cspan\u003eProgress to FIRE\u003c/span\u003e\n\u003cspan id=\"fireProgressPct\"\u003e4%\u003c/span\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:rgba(255,255,255,0.2);border-radius:999px;height:12px;overflow:hidden;\"\u003e\n\u003cdiv id=\"fireProgressBar\" style=\"background:#a78bfa;height:100%;border-radius:999px;transition:width 0.4s ease;width:4%;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:12px;opacity:0.7;margin-top:5px;\"\u003e\n\u003cspan id=\"fireCurrSavDisplay\"\u003e$50,000 now\u003c/span\u003e\n\u003cspan id=\"fireNumDisplay\"\u003e$1,250,000 goal\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- FIRE VARIANTS --\u003e\n\u003cdiv style=\"padding:24px;border:2px solid #c4b5fd;border-radius:12px;background:#fff;margin-bottom:20px;\"\u003e\n\u003ch3 style=\"margin:0 0 16px 0;font-size:16px;color:#7c3aed;font-weight:700;\"\u003eFIRE Variants\u003c/h3\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:12px;\" id=\"fireVariants\"\u003e\n\u003cdiv id=\"varLean\" style=\"padding:16px;border-radius:10px;border:2px solid #e9d5ff;background:#f5f3ff;\"\u003e\n\u003cdiv style=\"font-weight:700;font-size:14px;color:#6d28d9;margin-bottom:4px;\"\u003eLean FIRE\u003c/div\u003e\n\u003cdiv style=\"font-size:11px;color:#94a3b8;margin-bottom:8px;\"\u003eExpenses \u0026times; 0.8\u003c/div\u003e\n\u003cdiv id=\"leanNum\" style=\"font-size:22px;font-weight:800;color:#1e293b;\"\u003e$1,000,000\u003c/div\u003e\n\u003cdiv id=\"leanYears\" style=\"font-size:13px;color:#64748b;margin-top:4px;\"\u003e12 years 1 month\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"varRegular\" style=\"padding:16px;border-radius:10px;border:2px solid #7c3aed;background:#ede9fe;\"\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:6px;margin-bottom:4px;\"\u003e\n\u003cdiv style=\"font-weight:700;font-size:14px;color:#6d28d9;\"\u003eRegular FIRE\u003c/div\u003e\n\u003cspan style=\"background:#7c3aed;color:white;font-size:10px;padding:1px 6px;border-radius:999px;font-weight:600;\"\u003eYOUR TARGET\u003c/span\u003e\n\u003c/div\u003e\n\u003cdiv style=\"font-size:11px;color:#94a3b8;margin-bottom:8px;\"\u003eYour current expenses\u003c/div\u003e\n\u003cdiv id=\"regNum\" style=\"font-size:22px;font-weight:800;color:#1e293b;\"\u003e$1,250,000\u003c/div\u003e\n\u003cdiv id=\"regYears\" style=\"font-size:13px;color:#64748b;margin-top:4px;\"\u003e14 years 3 months\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"varFat\" style=\"padding:16px;border-radius:10px;border:2px solid #e9d5ff;background:#f5f3ff;\"\u003e\n\u003cdiv style=\"font-weight:700;font-size:14px;color:#6d28d9;margin-bottom:4px;\"\u003eFat FIRE\u003c/div\u003e\n\u003cdiv style=\"font-size:11px;color:#94a3b8;margin-bottom:8px;\"\u003eExpenses \u0026times; 1.5\u003c/div\u003e\n\u003cdiv id=\"fatNum\" style=\"font-size:22px;font-weight:800;color:#1e293b;\"\u003e$1,875,000\u003c/div\u003e\n\u003cdiv id=\"fatYears\" style=\"font-size:13px;color:#64748b;margin-top:4px;\"\u003e20 years 8 months\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"varCoast\" style=\"padding:16px;border-radius:10px;border:2px solid #e9d5ff;background:#f5f3ff;\"\u003e\n\u003cdiv style=\"font-weight:700;font-size:14px;color:#6d28d9;margin-bottom:4px;\"\u003eCoast FIRE\u003c/div\u003e\n\u003cdiv style=\"font-size:11px;color:#94a3b8;margin-bottom:8px;\"\u003eStop saving, coast to 65\u003c/div\u003e\n\u003cdiv id=\"coastNum\" style=\"font-size:22px;font-weight:800;color:#1e293b;\"\u003e$182,000\u003c/div\u003e\n\u003cdiv id=\"coastStatus\" style=\"font-size:13px;color:#64748b;margin-top:4px;\"\u003eNeed $132,000 more\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- SAVINGS RATE IMPACT TABLE --\u003e\n\u003cdiv style=\"padding:24px;border:2px solid #c4b5fd;border-radius:12px;background:#fff;margin-bottom:20px;\"\u003e\n\u003ch3 style=\"margin:0 0 4px 0;font-size:16px;color:#7c3aed;font-weight:700;\"\u003eSavings Rate Impact\u003c/h3\u003e\n\u003cp style=\"margin:0 0 16px 0;font-size:13px;color:#64748b;\"\u003eYears to FIRE at different savings rates (assuming same return rate)\u003c/p\u003e","title":"FIRE Calculator | When Can You Retire Early?"},{"content":"This article contains affiliate links.\nFIRE Retirement Simulator [2026] Enter your income, annual living expenses, and current assets to automatically calculate years to FIRE and your target nest egg. Lean FIRE, Fat FIRE, and Coast FIRE are all compared in one view.\nEnter Your Information Current Age yrs Annual Income ($) $ Annual Living Expenses ($) $ Current Net Worth ($) $ Expected Annual Return (%): 5.0% 3%12% Withdrawal Rate (%): 4.00% 2% (conservative)5% (aggressive) FIRE Target (Required Nest Egg) $1,250,000 Annual Expenses \u0026divide; Withdrawal Rate Years to FIRE — FIRE Age — Monthly Savings — Savings Rate — Progress to FIRE 0% Current: $0 Target: $0 FIRE Type Comparison Lean FIRE (expenses \u0026times; 0.8) — Calculating... Current Setting Standard FIRE — Calculating... Fat FIRE (expenses \u0026times; 1.5) — Calculating... Coast FIRE (amount needed now to reach target by 65) — Calculating... \u0026#9658; Show Year-by-Year Projection Year Age Annual Savings Investment Gains Total Assets Years to FIRE by Savings Rate Savings Rate 20% 30% 40% 50% 60% 70% Years to FIRE * Based on current annual expenses, withdrawal rate, and expected return. Calculated from $0 starting assets. Calculation assumptions: Annual compound interest. Taxes and inflation are not factored in. Actual investment returns vary with market conditions. This simulator is for reference only and does not constitute investment advice. What Is FIRE? FIRE (Financial Independence, Retire Early) is a lifestyle movement centered on achieving the freedom to stop working for money by accumulating enough investment assets to live off their returns indefinitely. It gained popularity in the US in the 2010s and has since spread worldwide.\nThe core FIRE formula is simple: earn more, spend less, invest the difference. A higher savings rate accelerates FIRE in two ways — you accumulate faster, and your required nest egg shrinks because your lifestyle costs less.\nHow the 4% Rule Works The 4% rule originates from the Trinity Study, a landmark research paper showing that a portfolio invested primarily in stocks can sustain annual withdrawals of 4% for 30+ years without being depleted. This means 25x your annual expenses is the classic FIRE target.\nConservative planners use 3–3.5% withdrawal rates for longer retirements. This simulator lets you adjust the withdrawal rate from 2–5% so you can model any scenario.\nFIRE Types Explained Lean FIRE — Achieve financial independence on a minimal budget (expenses × 0.8). Lower required assets, but demands frugal living.\nStandard FIRE — Maintain your current lifestyle in retirement. The classic 25x annual expenses target.\nFat FIRE — Retire with a more generous lifestyle (expenses × 1.5). Requires a larger portfolio but provides maximum comfort.\nCoast FIRE — The point at which your current assets will grow on their own to your full FIRE number by age 65, with no further contributions. Once you reach Coast FIRE, you only need to cover living expenses.\nBarista FIRE — Semi-retire before reaching full FIRE by covering part of living expenses with part-time or passion work.\nHow to Reach FIRE Faster Maximize your savings rate — Going from 20% to 50% savings can cut your timeline by 20+ years Invest in low-cost index funds — Broad market ETFs with expense ratios under 0.2% compound powerfully over time Add income streams — Side income raises your savings rate without cutting lifestyle Cut fixed costs first — Every $100/month in permanent savings = $30,000 less needed (at 4% rule) Related Tools Take-Home Pay Calculator — Calculate your net pay after tax Income Tax Simulator — See how taxes are calculated at any income level Budget Journal Generator — Optimize your monthly spending allocation ","permalink":"https://productivity-works.com/tools/fire-retirement-simulator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"fire-retirement-simulator-2026\"\u003eFIRE Retirement Simulator [2026]\u003c/h1\u003e\n\u003cp\u003eEnter your income, annual living expenses, and current assets to automatically calculate \u003cstrong\u003eyears to FIRE\u003c/strong\u003e and your \u003cstrong\u003etarget nest egg\u003c/strong\u003e. Lean FIRE, Fat FIRE, and Coast FIRE are all compared in one view.\u003c/p\u003e\n\u003cdiv id=\"fire-calc\" style=\"max-width:680px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;color:#1e293b;\"\u003e\n\u003c!-- Input Panel --\u003e\n\u003cdiv style=\"padding:24px;border:2px solid #7c3aed;border-radius:12px;background:#faf5ff;margin-bottom:20px;\"\u003e\n\u003ch2 style=\"margin:0 0 20px 0;font-size:18px;color:#7c3aed;\"\u003eEnter Your Information\u003c/h2\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px;\"\u003e\n\u003cdiv\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:14px;\"\u003eCurrent Age\u003c/label\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cinput type=\"number\" id=\"fireAge\" min=\"20\" max=\"70\" step=\"1\" value=\"30\" oninput=\"calcFire()\" style=\"flex:1;padding:10px;border:1px solid #c4b5fd;border-radius:8px;font-size:16px;background:white;\"\u003e\n\u003cspan style=\"color:#64748b;white-space:nowrap;\"\u003eyrs\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:14px;\"\u003eAnnual Income ($)\u003c/label\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cinput type=\"number\" id=\"fireIncome\" min=\"0\" max=\"1000000\" step=\"1000\" value=\"80000\" oninput=\"calcFire()\" style=\"flex:1;padding:10px;border:1px solid #c4b5fd;border-radius:8px;font-size:16px;background:white;\"\u003e\n\u003cspan style=\"color:#64748b;white-space:nowrap;\"\u003e$\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:14px;\"\u003eAnnual Living Expenses ($)\u003c/label\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cinput type=\"number\" id=\"fireExpense\" min=\"0\" max=\"500000\" step=\"1000\" value=\"50000\" oninput=\"calcFire()\" style=\"flex:1;padding:10px;border:1px solid #c4b5fd;border-radius:8px;font-size:16px;background:white;\"\u003e\n\u003cspan style=\"color:#64748b;white-space:nowrap;\"\u003e$\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:14px;\"\u003eCurrent Net Worth ($)\u003c/label\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cinput type=\"number\" id=\"fireAsset\" min=\"0\" max=\"10000000\" step=\"1000\" value=\"50000\" oninput=\"calcFire()\" style=\"flex:1;padding:10px;border:1px solid #c4b5fd;border-radius:8px;font-size:16px;background:white;\"\u003e\n\u003cspan style=\"color:#64748b;white-space:nowrap;\"\u003e$\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;font-size:14px;\"\u003eExpected Annual Return (%): \u003cspan id=\"fireReturnVal\" style=\"color:#7c3aed;\"\u003e5.0%\u003c/span\u003e\u003c/label\u003e\n\u003cinput type=\"range\" id=\"fireReturn\" min=\"3\" max=\"12\" step=\"0.5\" value=\"5\" oninput=\"calcFire()\" style=\"width:100%;accent-color:#7c3aed;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:12px;color:#94a3b8;\"\u003e\u003cspan\u003e3%\u003c/span\u003e\u003cspan\u003e12%\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:4px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;font-size:14px;\"\u003eWithdrawal Rate (%): \u003cspan id=\"fireWithdrawVal\" style=\"color:#7c3aed;\"\u003e4.00%\u003c/span\u003e\u003c/label\u003e\n\u003cinput type=\"range\" id=\"fireWithdraw\" min=\"2\" max=\"5\" step=\"0.25\" value=\"4\" oninput=\"calcFire()\" style=\"width:100%;accent-color:#7c3aed;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:12px;color:#94a3b8;\"\u003e\u003cspan\u003e2% (conservative)\u003c/span\u003e\u003cspan\u003e5% (aggressive)\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Main Results Panel --\u003e\n\u003cdiv style=\"background:linear-gradient(135deg,#7c3aed,#5b21b6);color:white;border-radius:12px;padding:24px;margin-bottom:16px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:14px;opacity:0.85;margin-bottom:4px;\"\u003eFIRE Target (Required Nest Egg)\u003c/div\u003e\n\u003cdiv id=\"fireTarget\" style=\"font-size:42px;font-weight:bold;letter-spacing:-1px;\"\u003e$1,250,000\u003c/div\u003e\n\u003cdiv style=\"font-size:13px;opacity:0.75;margin-top:4px;\"\u003eAnnual Expenses \u0026divide; Withdrawal Rate\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"background:#ede9fe;border-radius:10px;padding:16px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#6b7280;margin-bottom:4px;\"\u003eYears to FIRE\u003c/div\u003e\n\u003cdiv id=\"fireYears\" style=\"font-size:22px;font-weight:bold;color:#7c3aed;\"\u003e—\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#ede9fe;border-radius:10px;padding:16px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#6b7280;margin-bottom:4px;\"\u003eFIRE Age\u003c/div\u003e\n\u003cdiv id=\"fireAgeResult\" style=\"font-size:22px;font-weight:bold;color:#7c3aed;\"\u003e—\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#ede9fe;border-radius:10px;padding:16px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#6b7280;margin-bottom:4px;\"\u003eMonthly Savings\u003c/div\u003e\n\u003cdiv id=\"fireMonthlySave\" style=\"font-size:22px;font-weight:bold;color:#7c3aed;\"\u003e—\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#f5f3ff;border-radius:10px;padding:16px;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;\"\u003e\n\u003cspan style=\"font-size:14px;font-weight:bold;\"\u003eSavings Rate\u003c/span\u003e\n\u003cspan id=\"fireSavingsRate\" style=\"font-size:20px;font-weight:bold;color:#7c3aed;\"\u003e—\u003c/span\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;\"\u003e\n\u003cspan style=\"font-size:13px;color:#6b7280;\"\u003eProgress to FIRE\u003c/span\u003e\n\u003cspan id=\"fireProgressPct\" style=\"font-size:14px;font-weight:bold;color:#7c3aed;\"\u003e0%\u003c/span\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#ddd6fe;border-radius:999px;height:12px;overflow:hidden;\"\u003e\n\u003cdiv id=\"fireProgressBar\" style=\"height:100%;background:linear-gradient(90deg,#7c3aed,#a78bfa);border-radius:999px;width:0%;transition:width 0.4s;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:11px;color:#94a3b8;margin-top:4px;\"\u003e\n\u003cspan\u003eCurrent: \u003cspan id=\"fireCurrentAssetLabel\"\u003e$0\u003c/span\u003e\u003c/span\u003e\n\u003cspan\u003eTarget: \u003cspan id=\"fireTargetLabel\"\u003e$0\u003c/span\u003e\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- FIRE Type Comparison Panel --\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003ch3 style=\"font-size:16px;font-weight:bold;margin:0 0 12px 0;color:#4c1d95;\"\u003eFIRE Type Comparison\u003c/h3\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:10px;\" id=\"fireTypeGrid\"\u003e\n\u003cdiv style=\"border:1px solid #c4b5fd;border-radius:10px;padding:14px;background:#faf5ff;\"\u003e\n\u003cdiv style=\"font-size:13px;font-weight:bold;color:#6b7280;margin-bottom:6px;\"\u003eLean FIRE \u003cspan style=\"font-size:11px;font-weight:normal;\"\u003e(expenses \u0026times; 0.8)\u003c/span\u003e\u003c/div\u003e\n\u003cdiv id=\"leanTarget\" style=\"font-size:18px;font-weight:bold;color:#7c3aed;margin-bottom:2px;\"\u003e—\u003c/div\u003e\n\u003cdiv id=\"leanYears\" style=\"font-size:13px;color:#6b7280;\"\u003eCalculating...\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"border:2px solid #7c3aed;border-radius:10px;padding:14px;background:#ede9fe;position:relative;\"\u003e\n\u003cdiv style=\"position:absolute;top:-10px;left:12px;background:#7c3aed;color:white;font-size:11px;padding:2px 8px;border-radius:999px;\"\u003eCurrent Setting\u003c/div\u003e\n\u003cdiv style=\"font-size:13px;font-weight:bold;color:#5b21b6;margin-bottom:6px;\"\u003eStandard FIRE\u003c/div\u003e\n\u003cdiv id=\"normalTarget\" style=\"font-size:18px;font-weight:bold;color:#7c3aed;margin-bottom:2px;\"\u003e—\u003c/div\u003e\n\u003cdiv id=\"normalYears\" style=\"font-size:13px;color:#5b21b6;font-weight:bold;\"\u003eCalculating...\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"border:1px solid #c4b5fd;border-radius:10px;padding:14px;background:#faf5ff;\"\u003e\n\u003cdiv style=\"font-size:13px;font-weight:bold;color:#6b7280;margin-bottom:6px;\"\u003eFat FIRE \u003cspan style=\"font-size:11px;font-weight:normal;\"\u003e(expenses \u0026times; 1.5)\u003c/span\u003e\u003c/div\u003e\n\u003cdiv id=\"fatTarget\" style=\"font-size:18px;font-weight:bold;color:#7c3aed;margin-bottom:2px;\"\u003e—\u003c/div\u003e\n\u003cdiv id=\"fatYears\" style=\"font-size:13px;color:#6b7280;\"\u003eCalculating...\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"border:1px solid #c4b5fd;border-radius:10px;padding:14px;background:#faf5ff;\"\u003e\n\u003cdiv style=\"font-size:13px;font-weight:bold;color:#6b7280;margin-bottom:6px;\"\u003eCoast FIRE \u003cspan style=\"font-size:11px;font-weight:normal;\"\u003e(amount needed now to reach target by 65)\u003c/span\u003e\u003c/div\u003e\n\u003cdiv id=\"coastTarget\" style=\"font-size:18px;font-weight:bold;color:#7c3aed;margin-bottom:2px;\"\u003e—\u003c/div\u003e\n\u003cdiv id=\"coastStatus\" style=\"font-size:13px;color:#6b7280;\"\u003eCalculating...\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Year-by-Year Table (collapsible) --\u003e\n\u003cdetails style=\"margin-bottom:20px;border:1px solid #c4b5fd;border-radius:10px;overflow:hidden;\"\u003e\n\u003csummary style=\"padding:14px 16px;font-weight:bold;font-size:14px;cursor:pointer;background:#faf5ff;color:#4c1d95;list-style:none;display:flex;align-items:center;gap:8px;\"\u003e\n\u003cspan\u003e\u0026#9658; Show Year-by-Year Projection\u003c/span\u003e\n\u003c/summary\u003e\n\u003cdiv style=\"overflow-x:auto;\"\u003e\n\u003ctable id=\"fireTable\" style=\"width:100%;border-collapse:collapse;font-size:13px;\"\u003e\n\u003cthead\u003e\n\u003ctr style=\"background:#7c3aed;color:white;\"\u003e\n\u003cth style=\"padding:8px 12px;text-align:right;\"\u003eYear\u003c/th\u003e\n\u003cth style=\"padding:8px 12px;text-align:right;\"\u003eAge\u003c/th\u003e\n\u003cth style=\"padding:8px 12px;text-align:right;\"\u003eAnnual Savings\u003c/th\u003e\n\u003cth style=\"padding:8px 12px;text-align:right;\"\u003eInvestment Gains\u003c/th\u003e\n\u003cth style=\"padding:8px 12px;text-align:right;\"\u003eTotal Assets\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody id=\"fireTableBody\"\u003e\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\u003c/details\u003e\n\u003c!-- Savings Rate Table --\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003ch3 style=\"font-size:16px;font-weight:bold;margin:0 0 12px 0;color:#4c1d95;\"\u003eYears to FIRE by Savings Rate\u003c/h3\u003e\n\u003cdiv style=\"overflow-x:auto;\"\u003e\n\u003ctable style=\"width:100%;border-collapse:collapse;font-size:13px;text-align:center;\"\u003e\n\u003cthead\u003e\n\u003ctr style=\"background:#7c3aed;color:white;\"\u003e\n\u003cth style=\"padding:8px 10px;\"\u003eSavings Rate\u003c/th\u003e\n\u003cth style=\"padding:8px 10px;\"\u003e20%\u003c/th\u003e\n\u003cth style=\"padding:8px 10px;\"\u003e30%\u003c/th\u003e\n\u003cth style=\"padding:8px 10px;\"\u003e40%\u003c/th\u003e\n\u003cth style=\"padding:8px 10px;\"\u003e50%\u003c/th\u003e\n\u003cth style=\"padding:8px 10px;\"\u003e60%\u003c/th\u003e\n\u003cth style=\"padding:8px 10px;\"\u003e70%\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr id=\"savingsRateRow\" style=\"background:#f5f3ff;\"\u003e\n\u003ctd style=\"padding:8px 10px;font-weight:bold;color:#4c1d95;\"\u003eYears to FIRE\u003c/td\u003e\n\u003ctd id=\"sr20\" style=\"padding:8px 10px;font-weight:bold;\"\u003e\u003c/td\u003e\n\u003ctd id=\"sr30\" style=\"padding:8px 10px;font-weight:bold;\"\u003e\u003c/td\u003e\n\u003ctd id=\"sr40\" style=\"padding:8px 10px;font-weight:bold;\"\u003e\u003c/td\u003e\n\u003ctd id=\"sr50\" style=\"padding:8px 10px;font-weight:bold;\"\u003e\u003c/td\u003e\n\u003ctd id=\"sr60\" style=\"padding:8px 10px;font-weight:bold;\"\u003e\u003c/td\u003e\n\u003ctd id=\"sr70\" style=\"padding:8px 10px;font-weight:bold;\"\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\u003cdiv style=\"font-size:11px;color:#94a3b8;margin-top:6px;\"\u003e* Based on current annual expenses, withdrawal rate, and expected return. Calculated from $0 starting assets.\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#f5f3ff;border-radius:8px;padding:12px;font-size:12px;color:#64748b;\"\u003e\n\u003cstrong\u003eCalculation assumptions:\u003c/strong\u003e Annual compound interest. Taxes and inflation are not factored in. Actual investment returns vary with market conditions. This simulator is for reference only and does not constitute investment advice.\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n\nfunction fmt(v){\n  return '$'+Math.round(v).toLocaleString();\n}\n\nfunction calcYearsToFireFractional(currentAsset, annualSave, targetAmount, returnRate){\n  var asset = currentAsset;\n  var r = returnRate / 100;\n  for(var y = 0; y \u003c= 200; y++){\n    if(asset \u003e= targetAmount) return y;\n    var nextAsset = asset * (1 + r) + annualSave;\n    if(nextAsset \u003e= targetAmount){\n      var frac = (targetAmount - asset) / (nextAsset - asset);\n      return y + frac;\n    }\n    asset = nextAsset;\n  }\n  return Infinity;\n}\n\nfunction fmtYears(y){\n  if(y === Infinity || isNaN(y)) return 'Not achievable';\n  if(y \u003c 0) return 'Already achieved!';\n  var totalMonths = Math.round(y * 12);\n  var yy = Math.floor(totalMonths / 12);\n  var mm = totalMonths % 12;\n  if(yy === 0) return mm + ' mo';\n  if(mm === 0) return yy + ' yr';\n  return yy + ' yr ' + mm + ' mo';\n}\n\nwindow.calcFire = function(){\n  var age      = parseInt(document.getElementById('fireAge').value)    || 30;\n  var income   = parseFloat(document.getElementById('fireIncome').value)  || 0;\n  var expense  = parseFloat(document.getElementById('fireExpense').value) || 0;\n  var asset    = parseFloat(document.getElementById('fireAsset').value)   || 0;\n  var retRate  = parseFloat(document.getElementById('fireReturn').value)  || 5;\n  var wdRate   = parseFloat(document.getElementById('fireWithdraw').value) || 4;\n\n  document.getElementById('fireReturnVal').textContent  = retRate.toFixed(1) + '%';\n  document.getElementById('fireWithdrawVal').textContent = wdRate.toFixed(2) + '%';\n\n  var annualSave = income - expense;\n  var savingsRate = income \u003e 0 ? (annualSave / income * 100) : 0;\n  var monthlySave = annualSave / 12;\n\n  // FIRE targets\n  var targetNormal = expense / (wdRate / 100);\n  var targetLean   = (expense * 0.8) / (wdRate / 100);\n  var targetFat    = (expense * 1.5) / (wdRate / 100);\n\n  // Progress\n  var progressPct = targetNormal \u003e 0 ? Math.min((asset / targetNormal) * 100, 100) : 0;\n  var progressDisplay = asset \u003c 0 ? 0 : progressPct;\n\n  // FIRE years\n  var yearsNormal = calcYearsToFireFractional(asset, annualSave, targetNormal, retRate);\n  var yearsLean   = calcYearsToFireFractional(asset, annualSave, targetLean,   retRate);\n  var yearsFat    = calcYearsToFireFractional(asset, annualSave, targetFat,    retRate);\n\n  // Coast FIRE\n  var yearsToRetire = Math.max(65 - age, 0);\n  var coastAmount = yearsToRetire \u003e 0\n    ? targetNormal / Math.pow(1 + retRate / 100, yearsToRetire)\n    : targetNormal;\n  var coastAchieved = asset \u003e= coastAmount;\n\n  // Update main results\n  document.getElementById('fireTarget').textContent    = fmt(targetNormal);\n  document.getElementById('fireTargetLabel').textContent = fmt(targetNormal);\n  document.getElementById('fireCurrentAssetLabel').textContent = fmt(asset);\n  document.getElementById('fireProgressPct').textContent = progressDisplay.toFixed(1) + '%';\n  document.getElementById('fireProgressBar').style.width = progressDisplay + '%';\n  document.getElementById('fireSavingsRate').textContent = savingsRate.toFixed(1) + '%';\n  document.getElementById('fireMonthlySave').textContent = (monthlySave \u003e= 0 ? '' : '-') + '$' + Math.round(Math.abs(monthlySave)).toLocaleString();\n\n  if(yearsNormal === Infinity){\n    document.getElementById('fireYears').textContent    = 'Not achievable';\n    document.getElementById('fireAgeResult').textContent = '—';\n  } else if(asset \u003e= targetNormal){\n    document.getElementById('fireYears').textContent    = 'Already achieved!';\n    document.getElementById('fireAgeResult').textContent = age + ' yrs';\n  } else {\n    document.getElementById('fireYears').textContent    = fmtYears(yearsNormal);\n    document.getElementById('fireAgeResult').textContent = Math.floor(age + yearsNormal) + ' yrs';\n  }\n\n  // FIRE type cards\n  document.getElementById('leanTarget').textContent  = fmt(targetLean);\n  document.getElementById('normalTarget').textContent = fmt(targetNormal);\n  document.getElementById('fatTarget').textContent   = fmt(targetFat);\n\n  document.getElementById('leanYears').textContent  = asset \u003e= targetLean   ? 'Already achieved!' : (yearsLean   === Infinity ? 'Not achievable' : fmtYears(yearsLean)   + ' to FIRE');\n  document.getElementById('normalYears').textContent = asset \u003e= targetNormal ? 'Already achieved!' : (yearsNormal === Infinity ? 'Not achievable' : fmtYears(yearsNormal) + ' to FIRE');\n  document.getElementById('fatYears').textContent   = asset \u003e= targetFat    ? 'Already achieved!' : (yearsFat    === Infinity ? 'Not achievable' : fmtYears(yearsFat)    + ' to FIRE');\n\n  document.getElementById('coastTarget').textContent = fmt(coastAmount);\n  if(coastAchieved){\n    document.getElementById('coastStatus').textContent = 'Achieved! No more contributions needed to FIRE at 65';\n    document.getElementById('coastStatus').style.color = '#059669';\n  } else {\n    var coastShortfall = coastAmount - asset;\n    document.getElementById('coastStatus').textContent = '$' + Math.round(coastShortfall).toLocaleString() + ' more to achieve Coast FIRE';\n    document.getElementById('coastStatus').style.color = '#6b7280';\n  }\n\n  // Year-by-year table\n  var tbody = document.getElementById('fireTableBody');\n  tbody.innerHTML = '';\n  var a = asset;\n  var r = retRate / 100;\n  var maxYears = Math.min(\n    yearsNormal === Infinity ? 50 : Math.ceil(yearsNormal) + 2,\n    60\n  );\n  var fireReached = false;\n  for(var y = 0; y \u003c= maxYears; y++){\n    var investment = a * r;\n    var tr = document.createElement('tr');\n    var isFireYear = !fireReached \u0026\u0026 a \u003e= targetNormal;\n    if(isFireYear){ fireReached = true; }\n    tr.style.background = isFireYear ? '#ede9fe' : (y % 2 === 0 ? 'white' : '#faf5ff');\n    tr.style.fontWeight = isFireYear ? 'bold' : 'normal';\n    var saveCurrent = y === 0 ? 0 : annualSave;\n    tr.innerHTML =\n      '\u003ctd style=\"padding:7px 12px;text-align:right;\"\u003e' + (isFireYear ? '\u003cspan style=\"color:#7c3aed;\"\u003e\u0026#9733; ' : '') + 'Year ' + y + (isFireYear ? '\u003c/span\u003e' : '') + '\u003c/td\u003e' +\n      '\u003ctd style=\"padding:7px 12px;text-align:right;\"\u003e' + (age + y) + '\u003c/td\u003e' +\n      '\u003ctd style=\"padding:7px 12px;text-align:right;\"\u003e' + (y === 0 ? '—' : '$' + Math.round(saveCurrent).toLocaleString()) + '\u003c/td\u003e' +\n      '\u003ctd style=\"padding:7px 12px;text-align:right;\"\u003e$' + Math.round(y === 0 ? 0 : a * r / (1 + r)).toLocaleString() + '\u003c/td\u003e' +\n      '\u003ctd style=\"padding:7px 12px;text-align:right;color:' + (isFireYear ? '#7c3aed' : '#1e293b') + ';\"\u003e$' + Math.round(a).toLocaleString() + '\u003c/td\u003e';\n    tbody.appendChild(tr);\n    if(y \u003e 0) a = a * (1 + r) + annualSave;\n    else a = a * (1 + r) + annualSave;\n  }\n\n  // Savings rate table\n  var rates = [20, 30, 40, 50, 60, 70];\n  rates.forEach(function(sr){\n    var annSave2 = income * (sr / 100);\n    var exp2 = income - annSave2;\n    var target2 = exp2 \u003e 0 ? exp2 / (wdRate / 100) : 0;\n    var y2 = target2 \u003e 0 ? calcYearsToFireFractional(0, annSave2, target2, retRate) : 0;\n    var el = document.getElementById('sr' + sr);\n    if(el){\n      el.textContent = y2 === Infinity ? 'N/A' : (y2 \u003c= 0 ? 'Now' : fmtYears(y2));\n      el.style.color = '#7c3aed';\n      var curSR = Math.round(savingsRate / 10) * 10;\n      el.style.background = (sr === curSR) ? '#ddd6fe' : '';\n    }\n  });\n};\n\ncalcFire();\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"what-is-fire\"\u003eWhat Is FIRE?\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eFIRE (Financial Independence, Retire Early)\u003c/strong\u003e is a lifestyle movement centered on achieving the freedom to stop working for money by accumulating enough investment assets to live off their returns indefinitely. It gained popularity in the US in the 2010s and has since spread worldwide.\u003c/p\u003e","title":"FIRE Retirement Simulator | How Many Years to Financial Independence? [2026]"},{"content":"This article contains affiliate links.\nManaging finances as a freelancer in Japan is one of those tasks that sounds intimidating — and often is. Between navigating an accounting system built around Japanese tax law, understanding the difference between blue and white tax declarations, and figuring out which expenses you can actually deduct, many foreign freelancers end up either overpaying their taxes or making costly mistakes.\nfreee (フリー) is the cloud accounting software that has quietly become the go-to solution for self-employed individuals and small businesses across Japan. It\u0026rsquo;s available in a partially bilingual interface, connects directly to your Japanese bank accounts and credit cards, and — critically — it can generate the documents you need to file your kakuteishinkoku (確定申告, annual tax return) without needing to understand every detail of Japan\u0026rsquo;s tax code.\nThis guide is a complete, hands-on tutorial for English-speaking freelancers who want to use freee effectively in 2026. We will walk through account setup, bank linking, transaction categorization, invoice creation, and the full kakuteishinkoku filing process.\nFor a broader comparison of accounting tools available to freelancers in Japan, see our guide to accounting software for freelancers in Japan .\n1. Why freee Is the Top Choice for Freelancers in Japan Japan has several accounting software products aimed at self-employed individuals, but freee stands apart for a few reasons that matter specifically to foreign residents.\nCloud-first architecture. freee is entirely browser- and app-based. There is no software to install, no annual disc to buy, and your data is accessible from any device. This is especially useful for freelancers who work from multiple locations or travel frequently.\nBank and card synchronization. freee connects directly with hundreds of Japanese financial institutions — including major city banks, regional banks, Japan Post Bank (ゆうちょ銀行), and credit card companies. Once connected, transactions are imported automatically every day, which eliminates manual data entry for most routine expenses.\nPartially bilingual interface. The main navigation and many labels in freee have English translations available or are intuitive enough for users familiar with standard accounting concepts. The tax filing wizard also uses plain language rather than dense accounting jargon.\nBuilt for Japanese tax law. freee is designed from the ground up to handle Japan-specific requirements: the consumption tax (消費税, shohizei) rate changes, the My Number (マイナンバー) system, blue-form (青色申告) deductions, and the new invoice (インボイス) registration system that came into effect in 2023.\nStrong mobile app. The freee mobile app (iOS and Android) includes a receipt scanning feature that uses OCR to extract amounts and dates from paper receipts. This is genuinely useful for freelancers who deal with cash transactions or small vendor receipts.\n2. Getting Started: Sign-Up, Plans, and Initial Setup 2.1 Creating Your freee Account Go to freee.co.jp and click the sign-up button (新規登録 or 無料で始める). You will need:\nA valid email address A Japanese phone number (for SMS verification) Your business type — select 個人事業主 (kojin jigyonushi) for freelancer / sole proprietor During setup, freee will ask you a few questions about your business to configure the initial settings. Answer honestly; these settings affect how expense categories and tax rates are pre-configured, but all of them can be changed later.\n2.2 Plans and Pricing (2026) freee for sole proprietors (個人事業主向けプラン) offers three tiers:\nPlan Monthly Fee (approx.) Key Features Starter (スターター) ~¥1,980/mo Basic income/expense tracking, bank sync (1 account), kakuteishinkoku wizard Standard (スタンダード) ~¥3,316/mo Unlimited bank sync, invoicing, consumption tax tracking, receipt scanning Premium (プレミアム) ~¥43,780/mo All Standard features + dedicated tax accountant support For most freelancers, Standard is the practical choice. It covers all the features discussed in this guide, including unlimited bank account connections and the receipt scanning OCR. A free trial is available, typically 30 days, which gives you enough time to go through a full month of transactions before committing.\nNote: Prices are listed before consumption tax (10%) and may change. Always verify current pricing on the freee website.\n2.3 Connecting Your Bank Accounts and Credit Cards After logging in, navigate to 口座 (Koza, Accounts) in the left sidebar. Click 口座を連携する (Connect an account).\nfreee supports automatic synchronization (自動連携) with most major Japanese banks:\nMega banks: Mitsubishi UFJ (三菱UFJ), Sumitomo Mitsui (三井住友), Mizuho (みずほ) Online banks: Rakuten Bank (楽天銀行), PayPay Bank, SBI Shinsei Bank Japan Post Bank: ゆうちょ銀行 Regional banks: Most regional banks (地方銀行) are supported Credit cards: Major issuers including Rakuten Card, Amazon Card, and most Visa/Mastercard issuers For freelancers who do not yet have a dedicated business bank account, opening a Rakuten Bank (楽天銀行) business account is strongly recommended. Rakuten Bank has excellent freee integration, real-time sync, and a straightforward online application process. Having a separate account for your freelance income makes expense tracking dramatically cleaner. Open a Rakuten Bank business account here. Once connected, freee begins pulling in historical transactions (typically the last 1-3 months, depending on the institution). From this point forward, new transactions appear in freee automatically — usually within 24 hours.\n3. Key Features Walkthrough 3.1 Auto-Import and Transaction Review Every imported transaction lands in the 取引 (Torihiki, Transactions) section. By default, new imports appear in a queue called 未処理 (mishori, unprocessed). Your job is to review each one, assign it to the correct account category (勘定科目, kanjo kamoku), and confirm it.\nfreee uses machine learning to suggest categories based on the transaction description and your past behavior. After a few weeks of use, the suggestion accuracy is quite high for recurring vendors — your phone bill, internet, regular software subscriptions, and co-working space memberships will typically be auto-categorized correctly.\nBulk actions are available for repetitive transactions. If you pay the same rent every month, you can set up a rule (ルール) that automatically assigns that transaction to the correct category whenever it appears.\n3.2 Expense Categorization: Key 勘定科目 (Kanjo Kamoku) The Japanese accounting system uses standardized account categories. Here are the ones freelancers use most often, with English equivalents:\nJapanese Romaji English 売上高 uriage-daka Revenue / Sales 消耗品費 shomohimpi Supplies / Consumables 通信費 tsushinhi Communications (phone, internet) 交通費 kotsunhi Transportation 外注費 gaichuhi Subcontracting fees 広告宣伝費 kokoku senden-hi Advertising / Marketing 地代家賃 chidai yachin Rent 水道光熱費 suidokonetsuhi Utilities 研修費 kenshinhi Training / Education 会議費 kaigihi Meeting expenses 接待交際費 settai kosaishi Entertainment / Client meals 新聞図書費 shinbun toshohi Books / Subscriptions When you assign a transaction to a category in freee, simply click the 勘定科目 field and type the Japanese term or scroll through the list. freee\u0026rsquo;s suggestions will appear as you type. For expenses that partially overlap personal and business use (more on this in Section 6), freee allows you to split a transaction and apply a percentage.\n3.3 Receipt Scanning with the freee Mobile App Paper receipts from cafes, convenience stores, taxis, or stationery shops need to be recorded before they are lost. The freee mobile app\u0026rsquo;s receipt scanning feature (スキャン入力) handles this.\nSteps:\nOpen the freee app and tap the camera icon Point at the receipt and tap to capture OCR extracts the date, vendor name, and amount automatically Review the extracted data, assign a 勘定科目, and save The transaction is then synced to your freee account and appears alongside your bank-imported transactions. This keeps your records complete without manual entry.\n3.4 Creating and Sending Invoices Freelancers who issue invoices to clients should manage them inside freee to keep revenue records clean. Navigate to 請求書 (Seikyu-sho, Invoices) in the sidebar.\nClick 新規作成 (New) to create an invoice. Key fields:\n取引先 (Torihikisaki): Client name — add new clients to your contacts (取引先マスタ) on first use 品目 (Hinmoku): Line item description 金額 (Kingaku): Amount 消費税 (Shohizei): Consumption tax rate — 10% standard, 8% reduced rate for food/beverages 支払期日 (Shiharai kijitsu): Payment due date freee generates a properly formatted invoice PDF. If you are registered under the qualified invoice system (適格請求書発行事業者, tekikaku seikyu-sho hakko jigyonushi — the \u0026ldquo;Invoice System\u0026rdquo; that has been mandatory since October 2023 for VAT-registered businesses), your registration number (登録番号, toroku bango) can be added to the invoice template automatically.\nOnce issued, freee tracks the invoice status — sent, viewed, paid. When payment arrives in your linked bank account, freee will suggest matching the incoming deposit to the outstanding invoice, completing the revenue record in one click.\n4. Navigating freee in Japanese: Key Menu Items Translated The freee interface defaults to Japanese, but once you know what each section does, navigation becomes straightforward. Here is a reference for the main menu items:\nJapanese Menu Item English Translation What You Do Here ホーム Home Dashboard overview, quick actions 取引 Transactions Review and categorize all income/expenses 口座 Accounts Bank accounts, credit cards, cash 請求書 Invoices Create, send, track client invoices レポート Reports P\u0026amp;L, balance sheet, expense summaries 確定申告 Tax Filing (Kakuteishinkoku) Annual tax return preparation 設定 Settings Business info, tax settings, user accounts 取引先 Contacts Client and vendor directory 帳票 Ledgers / Books Formal accounting ledgers freee会計 freee Accounting Main product module The レポート (Reports) section is particularly useful for freelancers who want to track earnings by month, see which expense categories are growing, or produce a simple profit and loss statement for a visa renewal or bank loan application.\n5. Filing Kakuteishinkoku with freee This is where freee pays for itself. The kakuteishinkoku (確定申告) is Japan\u0026rsquo;s annual income tax self-assessment, due every year between February 16 and March 15 for the prior tax year (January–December).\n5.1 Blue Form vs. White Form Japan\u0026rsquo;s self-employment tax system has two declaration types:\nWhite form (白色申告, shiro-shoku shinkoku)\nNo pre-registration required Simpler record-keeping obligations No special deductions beyond standard ones Blue form (青色申告, ao-shoku shinkoku)\nMust register in advance at your local tax office (税務署) — submit Form 青色申告承認申請書 by March 15 of the year you want it to apply, or within 2 months of starting your business Requires double-entry bookkeeping Entitles you to a ¥650,000 deduction from your declared income (when filing electronically via e-Tax with proper double-entry records) Additional deductions: up to ¥480,000 in family employee salaries (青色事業専従者給与) The ¥650,000 deduction is the single most important reason to file blue form. At a 20% marginal tax rate, that deduction saves you ¥130,000 in income tax per year. freee\u0026rsquo;s Standard plan supports double-entry bookkeeping (複式簿記) automatically — every transaction you categorize generates the correct debit and credit entries behind the scenes.\nIf you are not yet on blue form, registering for it should be a priority. The registration form is straightforward, and the tax office staff can assist even if your Japanese is limited.\n5.2 Step-by-Step: Filing with freee Once all your transactions for the year are categorized and reconciled, freee\u0026rsquo;s kakuteishinkoku wizard guides you through the filing.\nStep 1: Navigate to 確定申告\nFrom the left sidebar, click 確定申告 (Tax Filing). freee will display a checklist of pre-filing tasks — completing these in order ensures your declaration is accurate.\nStep 2: Review the annual summary\nfreee shows your total income (収入), total expenses (経費), and calculated profit (所得). Review these numbers against your own expectations. If something looks wrong, click back into 取引 and check for miscategorized transactions.\nStep 3: Enter personal deductions (所得控除)\nThese are deductions applied to your taxable income based on personal circumstances. Common ones for freelancers:\n基礎控除 (kiso kojo): Basic deduction — ¥480,000 for most taxpayers 社会保険料控除 (shakai hoken-ryo kojo): National health insurance and pension premiums you paid — enter the amounts from your payment receipts 生命保険料控除 (seimei hoken-ryo kojo): Life insurance premium deductions 医療費控除 (iryo-hi kojo): Medical expense deductions (for expenses exceeding ¥100,000 or 5% of income) 小規模企業共済等掛金控除 (shokibo kigyo kyosai): iDeCo contributions, if applicable freee has dedicated input screens for each of these deduction types. The wizard walks through them sequentially.\nStep 4: Calculate tax\nfreee automatically calculates your income tax (所得税), consumption tax (if you are a consumption tax registrant), and any reconstruction special income tax (復興特別所得税). The calculated amounts are shown on screen before submission.\nStep 5: Submit via e-Tax or print\nfreee supports direct submission via e-Tax (電子申告), Japan\u0026rsquo;s online tax filing system. To use e-Tax, you need either:\nA My Number card (マイナンバーカード) with an IC card reader, or An ID and password (ID・パスワード方式) issued by your local tax office Submitting via e-Tax is required to claim the full ¥650,000 blue-form deduction. If you submit a paper print-out instead, the deduction is reduced to ¥550,000.\nFor a more detailed walkthrough of the e-Tax submission process, see our guide on freelance Japan freee tax filing .\nStep 6: Save your records\nAfter filing, download and store your final declaration documents. freee keeps a copy in your account, but maintaining your own offline archive is good practice. Tax records in Japan must be retained for 7 years (青色申告者) or 5 years (白色申告者).\n6. Tips for Expat Freelancers in Japan 6.1 My Number Requirements Your My Number (マイナンバー, individual number) is required for tax filing. It appears on your Residence Card documents and the official My Number notification letter you received when it was issued.\nIf you have lost your notification letter, you can verify your number at your local municipal office (市区町村役場) by presenting your residence card (在留カード) and identification.\nEnsure your My Number is entered correctly in freee\u0026rsquo;s settings (設定 \u0026gt; 事業者情報 \u0026gt; マイナンバー). An incorrect number will cause problems during e-Tax submission.\n6.2 Business Expense Deductions Japan\u0026rsquo;s tax authorities allow freelancers to deduct expenses that are \u0026ldquo;directly necessary for generating income\u0026rdquo; (事業に直接必要な経費). Key categories for freelancers in knowledge industries:\nSoftware and subscriptions. Any software or SaaS tool you use for work is deductible — this includes freee itself, design tools, project management platforms, and cloud storage. Categorize as 消耗品費 or 通信費.\nInternet and phone. If you use your home internet and phone for work, you can deduct the business-use portion. A 50% business ratio is commonly accepted for a sole freelancer working from home; higher ratios may require documentation.\nCo-working spaces and cafe work. Day passes and memberships to co-working spaces are fully deductible as 地代家賃 or 会議費. Cafe purchases made during work sessions can be deducted as 会議費, though individual receipts should be ≤¥5,000 per person to avoid scrutiny.\nProfessional development. Books, online courses, conference fees, and workshops related to your professional field are deductible as 研修費 or 新聞図書費.\nEquipment. Computers, monitors, cameras, and other equipment used for work can be deducted. Items under ¥100,000 can typically be expensed in the current year as 消耗品費. Items ¥100,000 or over must be depreciated (減価償却, genka shokyaku) over multiple years. freee handles depreciation calculations automatically when you enter the asset\u0026rsquo;s purchase price and expected useful life.\n6.3 Home Office Ratio (家事按分, Kaji Anbun) If you work from home, you can deduct a portion of your rent, utilities, and internet as business expenses. This is called 家事按分 (kaji anbun, household expense allocation).\nThe acceptable ratio is calculated based on the floor area used for work as a proportion of total floor space, multiplied by the hours used for work per week relative to total hours. A simple and defensible calculation:\nBusiness use ratio = (Work area in m2 / Total apartment area in m2) x (Work hours per week / 168 hours per week) For example, a 40m2 apartment where 8m2 is a dedicated work desk area, used 40 hours per week:\nBusiness ratio = (8/40) x (40/168) = 0.20 x 0.24 = ~4.8% If your monthly rent is ¥120,000, the deductible portion would be approximately ¥5,760/month, or ¥69,120/year.\nIn freee, set up a recurring transaction split: when your rent payment imports, split it so that the calculated percentage is assigned to 地代家賃 (business rent) and the remainder to a personal (non-deductible) category.\n6.4 Consumption Tax: Are You Liable? As of 2026, freelancers whose taxable sales exceeded ¥10 million in the base period (two years prior) are required to register as consumption tax businesses (課税事業者) and collect and remit 10% consumption tax on their services.\nAdditionally, if you are registered under the qualified invoice system (インボイス制度) — registration number beginning with T — you must issue qualified invoices and file consumption tax returns separately from your income tax return.\nfreee handles consumption tax tracking automatically once your tax status is configured in settings. Navigate to 設定 \u0026gt; 消費税設定 to confirm your status. For most new freelancers earning under ¥10 million, consumption tax registration is optional and many choose to remain exempt (免税事業者) for their first two years.\n6.5 Working with a Tax Accountant (税理士, Zeirishi) freee\u0026rsquo;s data can be shared directly with a registered tax accountant (税理士) via freee\u0026rsquo;s accountant portal. If your tax situation is complex — multiple income streams, significant capital gains, or you are unsure about your residency status for tax purposes — consider engaging a zeirishi who works with foreign residents. Having clean, categorized freee data dramatically reduces the time (and cost) a zeirishi needs to prepare your filing.\n7. Setting Up Your Workflow: A Practical Monthly Routine Staying on top of accounting throughout the year is far easier than catching up at tax time. Here is a lightweight monthly routine:\nWeek 1 of each month (30 minutes)\nLog into freee and open 取引 \u0026gt; 未処理 (unprocessed transactions) Review all transactions imported from your linked accounts for the prior month Accept freee\u0026rsquo;s suggested categories for obvious transactions; manually assign ambiguous ones Add any cash transactions via the mobile app\u0026rsquo;s manual entry or receipt scan feature Mark all reviewed transactions as 確認済 (confirmed) Ongoing (as needed)\nIssue invoices via freee immediately after completing client work Photograph and upload receipts via mobile app before they are lost Reconcile any discrepancies between freee\u0026rsquo;s balance and your actual bank balance January–February (tax prep season)\nReview the full year\u0026rsquo;s 取引 for any miscategorizations Gather pension and health insurance payment records (年金控除証明書 / 社会保険料控除証明書) from physical mail Complete the kakuteishinkoku wizard in freee and submit by March 15 This routine requires roughly 30–60 minutes per month and eliminates the annual scramble that causes most freelancers\u0026rsquo; tax season stress.\n8. Conclusion: freee Is Worth It for Freelancers in Japan Managing taxes as a self-employed foreigner in Japan does not have to be a mystery. freee removes most of the friction: bank connections eliminate manual data entry, the receipt scanning app captures cash expenses, and the kakuteishinkoku wizard guides you through the annual filing step by step.\nThe ¥650,000 blue-form deduction alone — which freee\u0026rsquo;s double-entry bookkeeping system automatically enables — saves most freelancers more money per year than several years of freee subscription costs. Add the time savings and reduced accountant fees, and freee pays for itself quickly.\nReady to get started?\nSign up for freee and start your free trial here. The setup takes about 20 minutes, and your first bank sync will happen within 24 hours. Use the Standard plan to get access to all the features described in this guide.\nIf you do not yet have a dedicated business bank account to link with freee, Rakuten Bank\u0026rsquo;s business account is an excellent choice — the online application is straightforward, the freee integration is real-time, and there are no minimum balance requirements.\nFurther Reading Best Accounting Software for Freelancers in Japan: Full Comparison How to File Your Kakuteishinkoku Using freee: Full Tax Season Walkthrough Related Tools Estimate your freelance tax burden → Side Hustle Tax Calculator Create a monthly budget → Budget Planner Calculate your take-home pay → Salary Calculator This article contains affiliate links. If you sign up for freee or Rakuten Bank through our links, we may earn a commission at no additional cost to you. All opinions and feature descriptions are based on independent research and publicly available product information.\nYou May Also Like Freelance in Japan: How to Register as Kojin Jigyonushi and File Taxes With freee Best Accounting Software for Freelancers in Japan 2026: freee vs Money Forward vs Yayoi Best Accounting Software for Freelancers 2026: Full Compare ","permalink":"https://productivity-works.com/posts/freee-cloud-accounting-tutorial-japan-english/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eManaging finances as a freelancer in Japan is one of those tasks that sounds intimidating — and often is. Between navigating an accounting system built around Japanese tax law, understanding the difference between blue and white tax declarations, and figuring out which expenses you can actually deduct, many foreign freelancers end up either overpaying their taxes or making costly mistakes.\u003c/p\u003e\n\u003cp\u003efreee (フリー) is the cloud accounting software that has quietly become the go-to solution for self-employed individuals and small businesses across Japan. It\u0026rsquo;s available in a partially bilingual interface, connects directly to your Japanese bank accounts and credit cards, and — critically — it can generate the documents you need to file your kakuteishinkoku (確定申告, annual tax return) without needing to understand every detail of Japan\u0026rsquo;s tax code.\u003c/p\u003e","title":"freee Cloud Accounting Tutorial: The Complete English Guide for Freelancers in Japan (2026)"},{"content":"This article contains affiliate links. We may earn a commission at no extra cost to you.\nFreelance Rate Calculator Enter your income goals and working hours to find the minimum hourly rate you need to charge — and the recommended rate that builds in profit and sustainability.\nYour Income Goals Target Annual Income ($) Billable Hours per Week: 30 hrs 20 40 Only count hours you can actually bill to clients Weeks Worked per Year: 48 wks 40 52 Subtract vacation, sick days, and holidays Annual Business Expenses ($) Software, equipment, insurance, marketing, accounting, etc. Self-Employment Tax Rate: 25% 10% 35% Includes SE tax (~15%) + federal/state income tax Profit Margin: 15% 0% 30% Buffer for business growth, slow months, reinvestment Your Freelance Rate Rate Comparison by Income Target Based on your current hours/weeks/tax settings. Your closest bracket is highlighted. Income Target Min. Rate Recommended Rate Annual Revenue Needed What You Actually Keep Pricing Strategy Never Quote Hourly — Show Project-Based Pricing Project prices based on your recommended rate. Always quote scope, not time. How to Use This Calculator Enter your target annual income — the amount you want to take home after taxes. Set your billable hours — be realistic. Most freelancers bill 25–35 hours per week; the rest goes to admin, marketing, and client management. Adjust weeks worked — subtract vacation, holidays, and sick days. Working 48 weeks is typical. Add your business expenses — software subscriptions, equipment, insurance, professional development, and accounting fees all count. Set your tax rate — self-employment tax is ~15.3% plus federal/state income tax. 25–30% total is a safe estimate for most freelancers. Choose a profit margin — this buffer protects you during slow months and funds business growth. The minimum rate keeps you afloat. The recommended rate is what you should actually charge.\nHow to Set Your Freelance Rate 1. Research Market Rates Before setting your rate, benchmark against your market. Check platforms like Upwork, Toptal, and LinkedIn. Industry salary surveys, professional associations, and job boards can also reveal what clients expect to pay. Your rate should be competitive — not the cheapest.\n2. Account for Non-Billable Time Every hour you spend on email, proposals, invoicing, networking, and professional development is an hour you can\u0026rsquo;t bill. If you work 40 hours per week but only bill 25, your effective hourly cost to run your business is based on those 40 hours — not 25. The calculator\u0026rsquo;s billable hours slider reflects this reality.\n3. Move Toward Value-Based Pricing Hourly rates create a ceiling on your income. As you gain expertise, consider pricing by project or outcome instead of time. A logo that takes you 3 hours but saves a client $50,000 in brand confusion is worth far more than 3× your hourly rate. Use the \u0026ldquo;Never Quote Hourly\u0026rdquo; toggle to see project-based equivalents of your recommended rate.\nCommon Freelance Rate Benchmarks Role Entry Level Mid-Level Experienced Freelance Writer $30–$50/hr $60–$100/hr $100–$200/hr Graphic Designer $35–$55/hr $65–$110/hr $110–$200/hr Web Developer $50–$75/hr $80–$150/hr $150–$300/hr Marketing Consultant $50–$80/hr $90–$150/hr $150–$350/hr Business Consultant $75–$125/hr $125–$250/hr $250–$500/hr UX/UI Designer $50–$80/hr $85–$150/hr $150–$275/hr Copywriter $40–$70/hr $75–$130/hr $130–$250/hr Data Analyst $45–$75/hr $80–$140/hr $140–$275/hr Rates vary significantly by geography, niche, and client type. These are US market estimates.\nRelated Tools Estimate your freelance taxes → Side Hustle Tax Calculator Calculate your take-home pay → Salary Calculator Check your tax bracket → Tax Bracket Calculator Plan your monthly budget → Budget Planner Track your financial independence → FIRE Calculator Track your net worth → Net Worth Calculator Related Articles How to Price Your Freelance Services: A Complete Guide The True Cost of Being Self-Employed (What No One Tells You) How to Find Your First Freelance Client Freelance vs. Full-Time: Which Earns More? How to Save for Taxes as a Freelancer Disclaimer: This calculator provides estimates for educational purposes only and does not constitute financial, tax, or legal advice. Tax rates vary based on your total income, filing status, state of residence, and deductions. Consult a qualified tax professional or financial advisor for guidance specific to your situation.\nRelated Tools Hourly To Salary Calculator Pension Simulator Salary Calculator ","permalink":"https://productivity-works.com/tools/freelance-rate-calculator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links. We may earn a commission at no extra cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"freelance-rate-calculator\"\u003eFreelance Rate Calculator\u003c/h1\u003e\n\u003cp\u003eEnter your income goals and working hours to find the \u003cstrong\u003eminimum hourly rate\u003c/strong\u003e you need to charge — and the \u003cstrong\u003erecommended rate\u003c/strong\u003e that builds in profit and sustainability.\u003c/p\u003e\n\u003cstyle\u003e\n.fr-wrap {\n  max-width: 720px;\n  margin: 0 auto;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  color: #1e293b;\n}\n.fr-card {\n  background: #ffffff;\n  border: 1px solid #e2e8f0;\n  border-radius: 14px;\n  padding: 28px;\n  margin-bottom: 20px;\n  box-shadow: 0 2px 8px rgba(13,148,136,0.07);\n}\n.fr-section-title {\n  font-size: 15px;\n  font-weight: 700;\n  color: #0d9488;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  margin: 0 0 18px 0;\n}\n.fr-field {\n  margin-bottom: 18px;\n}\n.fr-label {\n  display: block;\n  font-size: 14px;\n  font-weight: 600;\n  color: #475569;\n  margin-bottom: 8px;\n}\n.fr-range-row {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n}\n.fr-range-row input[type=\"range\"] {\n  flex: 1;\n  accent-color: #0d9488;\n  height: 6px;\n  cursor: pointer;\n}\n.fr-range-val {\n  min-width: 80px;\n  text-align: right;\n  font-weight: 700;\n  font-size: 15px;\n  color: #0d9488;\n}\n.fr-input {\n  width: 100%;\n  padding: 10px 14px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 15px;\n  color: #1e293b;\n  box-sizing: border-box;\n  transition: border-color 0.2s;\n}\n.fr-input:focus {\n  outline: none;\n  border-color: #0d9488;\n}\n.fr-results-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 14px;\n  margin-bottom: 20px;\n}\n.fr-result-box {\n  background: #f0fdfa;\n  border: 2px solid #0d9488;\n  border-radius: 12px;\n  padding: 18px;\n  text-align: center;\n}\n.fr-result-box.primary {\n  background: #0d9488;\n  color: #fff;\n}\n.fr-result-label {\n  font-size: 12px;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  color: #134e4a;\n  margin-bottom: 6px;\n}\n.fr-result-box.primary .fr-result-label {\n  color: #ccfbf1;\n}\n.fr-result-value {\n  font-size: 32px;\n  font-weight: 800;\n  color: #0d9488;\n  line-height: 1;\n}\n.fr-result-box.primary .fr-result-value {\n  color: #fff;\n  font-size: 38px;\n}\n.fr-result-sub {\n  font-size: 12px;\n  color: #5eead4;\n  margin-top: 4px;\n}\n.fr-stat-row {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  padding: 10px 0;\n  border-bottom: 1px solid #e2e8f0;\n  font-size: 14px;\n}\n.fr-stat-row:last-child { border-bottom: none; }\n.fr-stat-label { color: #64748b; }\n.fr-stat-val { font-weight: 700; color: #0d9488; }\n.fr-bar-wrap {\n  margin-top: 10px;\n}\n.fr-bar-track {\n  display: flex;\n  height: 28px;\n  border-radius: 8px;\n  overflow: hidden;\n  margin-bottom: 12px;\n}\n.fr-bar-seg {\n  transition: width 0.3s;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 11px;\n  font-weight: 700;\n  color: #fff;\n  overflow: hidden;\n  white-space: nowrap;\n}\n.fr-legend {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 8px;\n}\n.fr-legend-item {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  font-size: 13px;\n}\n.fr-legend-dot {\n  width: 12px;\n  height: 12px;\n  border-radius: 3px;\n  flex-shrink: 0;\n}\n.fr-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 14px;\n}\n.fr-table th {\n  background: #0d9488;\n  color: #fff;\n  padding: 10px 12px;\n  text-align: left;\n  font-weight: 600;\n}\n.fr-table td {\n  padding: 10px 12px;\n  border-bottom: 1px solid #e2e8f0;\n}\n.fr-table tr.highlighted td {\n  background: #f0fdfa;\n  font-weight: 700;\n  color: #0d9488;\n}\n.fr-table tr:hover td { background: #f8fafc; }\n.fr-toggle-btn {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  cursor: pointer;\n  padding: 12px 16px;\n  background: #f0fdfa;\n  border: 2px solid #0d9488;\n  border-radius: 10px;\n  font-weight: 700;\n  font-size: 14px;\n  color: #134e4a;\n  margin-bottom: 16px;\n  user-select: none;\n  transition: background 0.2s;\n}\n.fr-toggle-btn:hover { background: #ccfbf1; }\n.fr-toggle-switch {\n  width: 40px;\n  height: 22px;\n  background: #cbd5e1;\n  border-radius: 11px;\n  position: relative;\n  transition: background 0.2s;\n  flex-shrink: 0;\n}\n.fr-toggle-switch.on { background: #0d9488; }\n.fr-toggle-switch::after {\n  content: '';\n  position: absolute;\n  width: 16px;\n  height: 16px;\n  background: #fff;\n  border-radius: 50%;\n  top: 3px;\n  left: 3px;\n  transition: left 0.2s;\n}\n.fr-toggle-switch.on::after { left: 21px; }\n.fr-project-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 10px;\n}\n.fr-project-box {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 14px;\n  text-align: center;\n}\n.fr-project-hours {\n  font-size: 12px;\n  color: #64748b;\n  margin-bottom: 4px;\n}\n.fr-project-price {\n  font-size: 22px;\n  font-weight: 800;\n  color: #0d9488;\n}\n.fr-project-label {\n  font-size: 11px;\n  color: #94a3b8;\n  margin-top: 2px;\n}\n@media (max-width: 520px) {\n  .fr-results-grid { grid-template-columns: 1fr; }\n  .fr-project-grid { grid-template-columns: 1fr; }\n  .fr-legend { grid-template-columns: 1fr; }\n}\n\u003c/style\u003e\n\u003cdiv class=\"fr-wrap\"\u003e\n\u003c!-- Inputs --\u003e\n\u003cdiv class=\"fr-card\"\u003e\n\u003cdiv class=\"fr-section-title\"\u003eYour Income Goals\u003c/div\u003e\n\u003cdiv class=\"fr-field\"\u003e\n  \u003clabel class=\"fr-label\" for=\"frAnnualIncome\"\u003eTarget Annual Income ($)\u003c/label\u003e\n  \u003cinput type=\"number\" id=\"frAnnualIncome\" class=\"fr-input\" value=\"80000\" min=\"10000\" max=\"500000\" step=\"1000\" oninput=\"calcFR()\"\u003e\n\u003c/div\u003e\n\u003cdiv class=\"fr-field\"\u003e\n  \u003clabel class=\"fr-label\"\u003eBillable Hours per Week: \u003cspan id=\"frHoursVal\" style=\"color:#0d9488;\"\u003e30\u003c/span\u003e hrs\u003c/label\u003e\n  \u003cdiv class=\"fr-range-row\"\u003e\n    \u003cspan style=\"font-size:12px;color:#94a3b8;\"\u003e20\u003c/span\u003e\n    \u003cinput type=\"range\" id=\"frHours\" min=\"20\" max=\"40\" step=\"1\" value=\"30\" oninput=\"calcFR()\"\u003e\n    \u003cspan style=\"font-size:12px;color:#94a3b8;\"\u003e40\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv style=\"font-size:12px;color:#94a3b8;margin-top:4px;\"\u003eOnly count hours you can actually bill to clients\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"fr-field\"\u003e\n  \u003clabel class=\"fr-label\"\u003eWeeks Worked per Year: \u003cspan id=\"frWeeksVal\" style=\"color:#0d9488;\"\u003e48\u003c/span\u003e wks\u003c/label\u003e\n  \u003cdiv class=\"fr-range-row\"\u003e\n    \u003cspan style=\"font-size:12px;color:#94a3b8;\"\u003e40\u003c/span\u003e\n    \u003cinput type=\"range\" id=\"frWeeks\" min=\"40\" max=\"52\" step=\"1\" value=\"48\" oninput=\"calcFR()\"\u003e\n    \u003cspan style=\"font-size:12px;color:#94a3b8;\"\u003e52\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv style=\"font-size:12px;color:#94a3b8;margin-top:4px;\"\u003eSubtract vacation, sick days, and holidays\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"fr-field\"\u003e\n  \u003clabel class=\"fr-label\" for=\"frExpenses\"\u003eAnnual Business Expenses ($)\u003c/label\u003e\n  \u003cinput type=\"number\" id=\"frExpenses\" class=\"fr-input\" value=\"5000\" min=\"0\" max=\"200000\" step=\"500\" oninput=\"calcFR()\"\u003e\n  \u003cdiv style=\"font-size:12px;color:#94a3b8;margin-top:4px;\"\u003eSoftware, equipment, insurance, marketing, accounting, etc.\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"fr-field\"\u003e\n  \u003clabel class=\"fr-label\"\u003eSelf-Employment Tax Rate: \u003cspan id=\"frTaxVal\" style=\"color:#0d9488;\"\u003e25\u003c/span\u003e%\u003c/label\u003e\n  \u003cdiv class=\"fr-range-row\"\u003e\n    \u003cspan style=\"font-size:12px;color:#94a3b8;\"\u003e10%\u003c/span\u003e\n    \u003cinput type=\"range\" id=\"frTax\" min=\"10\" max=\"35\" step=\"1\" value=\"25\" oninput=\"calcFR()\"\u003e\n    \u003cspan style=\"font-size:12px;color:#94a3b8;\"\u003e35%\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv style=\"font-size:12px;color:#94a3b8;margin-top:4px;\"\u003eIncludes SE tax (~15%) + federal/state income tax\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"fr-field\"\u003e\n  \u003clabel class=\"fr-label\"\u003eProfit Margin: \u003cspan id=\"frMarginVal\" style=\"color:#0d9488;\"\u003e15\u003c/span\u003e%\u003c/label\u003e\n  \u003cdiv class=\"fr-range-row\"\u003e\n    \u003cspan style=\"font-size:12px;color:#94a3b8;\"\u003e0%\u003c/span\u003e\n    \u003cinput type=\"range\" id=\"frMargin\" min=\"0\" max=\"30\" step=\"1\" value=\"15\" oninput=\"calcFR()\"\u003e\n    \u003cspan style=\"font-size:12px;color:#94a3b8;\"\u003e30%\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv style=\"font-size:12px;color:#94a3b8;margin-top:4px;\"\u003eBuffer for business growth, slow months, reinvestment\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Primary Results --\u003e\n\u003cdiv id=\"frResultsCard\" class=\"fr-card\"\u003e\n\u003cdiv class=\"fr-section-title\"\u003eYour Freelance Rate\u003c/div\u003e\n\u003cdiv class=\"fr-results-grid\" id=\"frRatesGrid\"\u003e\u003c/div\u003e\n\u003cdiv id=\"frStatsGrid\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Rate Comparison Table --\u003e\n\u003cdiv class=\"fr-card\"\u003e\n\u003cdiv class=\"fr-section-title\"\u003eRate Comparison by Income Target\u003c/div\u003e\n\u003cdiv style=\"font-size:13px;color:#64748b;margin-bottom:14px;\"\u003eBased on your current hours/weeks/tax settings. Your closest bracket is highlighted.\u003c/div\u003e\n\u003cdiv style=\"overflow-x:auto;\"\u003e\n\u003ctable class=\"fr-table\" id=\"frCompTable\"\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n  \u003cth\u003eIncome Target\u003c/th\u003e\n  \u003cth\u003eMin. Rate\u003c/th\u003e\n  \u003cth\u003eRecommended Rate\u003c/th\u003e\n  \u003cth\u003eAnnual Revenue Needed\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody id=\"frCompBody\"\u003e\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- What You Keep --\u003e\n\u003cdiv class=\"fr-card\"\u003e\n\u003cdiv class=\"fr-section-title\"\u003eWhat You Actually Keep\u003c/div\u003e\n\u003cdiv class=\"fr-bar-wrap\"\u003e\n  \u003cdiv class=\"fr-bar-track\" id=\"frBarTrack\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"fr-legend\" id=\"frLegend\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Project Pricing Toggle --\u003e\n\u003cdiv class=\"fr-card\"\u003e\n\u003cdiv class=\"fr-section-title\"\u003ePricing Strategy\u003c/div\u003e\n\u003cdiv class=\"fr-toggle-btn\" onclick=\"toggleProjectView()\" id=\"frToggleBtn\"\u003e\n  \u003cdiv class=\"fr-toggle-switch\" id=\"frToggleSwitch\"\u003e\u003c/div\u003e\n  Never Quote Hourly — Show Project-Based Pricing\n\u003c/div\u003e\n\u003cdiv id=\"frProjectPanel\" style=\"display:none;\"\u003e\n  \u003cdiv style=\"font-size:13px;color:#64748b;margin-bottom:14px;\"\u003eProject prices based on your recommended rate. Always quote scope, not time.\u003c/div\u003e\n  \u003cdiv class=\"fr-project-grid\" id=\"frProjectGrid\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003c!-- end fr-wrap --\u003e\n\u003cscript\u003e\nvar frProjectsOn = false;\n\nfunction fmtD(n) {\n  return '$' + Math.round(n).toLocaleString('en-US');\n}\nfunction fmtR(n) {\n  return '$' + n.toFixed(2);\n}\n\nfunction calcFRRate(income, hours, weeks, expenses, taxRate, margin) {\n  var totalHours = hours * weeks;\n  var revenueNeeded = income + expenses;\n  var revenueWithTax = revenueNeeded / (1 - taxRate / 100);\n  var minRate = revenueWithTax / totalHours;\n  var recRate = minRate / (1 - margin / 100);\n  return { minRate: minRate, recRate: recRate, revenueWithTax: revenueWithTax, totalHours: totalHours };\n}\n\nfunction calcFR() {\n  var income = parseFloat(document.getElementById('frAnnualIncome').value) || 80000;\n  var hours = parseInt(document.getElementById('frHours').value);\n  var weeks = parseInt(document.getElementById('frWeeks').value);\n  var expenses = parseFloat(document.getElementById('frExpenses').value) || 0;\n  var taxRate = parseInt(document.getElementById('frTax').value);\n  var margin = parseInt(document.getElementById('frMargin').value);\n\n  // Update slider displays\n  document.getElementById('frHoursVal').textContent = hours;\n  document.getElementById('frWeeksVal').textContent = weeks;\n  document.getElementById('frTaxVal').textContent = taxRate;\n  document.getElementById('frMarginVal').textContent = margin;\n\n  var r = calcFRRate(income, hours, weeks, expenses, taxRate, margin);\n  var totalHours = r.totalHours;\n  var minRate = r.minRate;\n  var recRate = r.recRate;\n  var annualRevenue = recRate * totalHours;\n  var monthlyRevenue = annualRevenue / 12;\n  var dailyRate = recRate * 8;\n  var halfDayRate = recRate * 4;\n\n  // Rates grid\n  var rg = document.getElementById('frRatesGrid');\n  rg.innerHTML =\n    '\u003cdiv class=\"fr-result-box\"\u003e' +\n      '\u003cdiv class=\"fr-result-label\"\u003eMinimum Hourly Rate\u003c/div\u003e' +\n      '\u003cdiv class=\"fr-result-value\"\u003e' + fmtR(minRate) + '\u003c/div\u003e' +\n      '\u003cdiv style=\"font-size:12px;color:#134e4a;margin-top:6px;\"\u003eCovers income + expenses + taxes\u003c/div\u003e' +\n    '\u003c/div\u003e' +\n    '\u003cdiv class=\"fr-result-box primary\"\u003e' +\n      '\u003cdiv class=\"fr-result-label\"\u003eRecommended Rate\u003c/div\u003e' +\n      '\u003cdiv class=\"fr-result-value\"\u003e' + fmtR(recRate) + '\u003c/div\u003e' +\n      '\u003cdiv class=\"fr-result-sub\"\u003eIncludes ' + margin + '% profit margin\u003c/div\u003e' +\n    '\u003c/div\u003e';\n\n  // Stats\n  var sg = document.getElementById('frStatsGrid');\n  var stats = [\n    ['Annual Revenue Needed', fmtD(annualRevenue)],\n    ['Monthly Revenue Needed', fmtD(monthlyRevenue)],\n    ['Effective Daily Rate (8 hrs)', fmtD(dailyRate)],\n    ['Half-Day Rate (4 hrs)', fmtD(halfDayRate)],\n    ['Total Billable Hours/Year', totalHours.toLocaleString('en-US') + ' hrs']\n  ];\n  var sh = '';\n  for (var i = 0; i \u003c stats.length; i++) {\n    sh += '\u003cdiv class=\"fr-stat-row\"\u003e\u003cspan class=\"fr-stat-label\"\u003e' + stats[i][0] + '\u003c/span\u003e\u003cspan class=\"fr-stat-val\"\u003e' + stats[i][1] + '\u003c/span\u003e\u003c/div\u003e';\n  }\n  sg.innerHTML = sh;\n\n  // Comparison table\n  var targets = [50000, 60000, 80000, 100000, 120000, 150000];\n  var closestIdx = 0;\n  var closestDiff = Math.abs(income - targets[0]);\n  for (var j = 1; j \u003c targets.length; j++) {\n    var diff = Math.abs(income - targets[j]);\n    if (diff \u003c closestDiff) { closestDiff = diff; closestIdx = j; }\n  }\n  var cb = document.getElementById('frCompBody');\n  var ch = '';\n  for (var k = 0; k \u003c targets.length; k++) {\n    var tr = calcFRRate(targets[k], hours, weeks, expenses, taxRate, margin);\n    var hl = k === closestIdx ? ' class=\"highlighted\"' : '';\n    var star = k === closestIdx ? ' ★' : '';\n    ch += '\u003ctr' + hl + '\u003e' +\n      '\u003ctd\u003e' + fmtD(targets[k]) + star + '\u003c/td\u003e' +\n      '\u003ctd\u003e' + fmtR(tr.minRate) + '/hr\u003c/td\u003e' +\n      '\u003ctd\u003e' + fmtR(tr.recRate) + '/hr\u003c/td\u003e' +\n      '\u003ctd\u003e' + fmtD(tr.recRate * tr.totalHours) + '\u003c/td\u003e' +\n    '\u003c/tr\u003e';\n  }\n  cb.innerHTML = ch;\n\n  // What you keep bar\n  var taxAmt = annualRevenue * (taxRate / 100);\n  var expAmt = expenses;\n  var profitAmt = annualRevenue * (margin / 100);\n  var takeHome = annualRevenue - taxAmt - expAmt - profitAmt;\n  if (takeHome \u003c 0) takeHome = 0;\n  var total = taxAmt + expAmt + profitAmt + takeHome;\n\n  function pct(v) { return total \u003e 0 ? (v / total * 100).toFixed(1) : '0'; }\n\n  var segs = [\n    { label: 'Take-Home', val: takeHome, color: '#0d9488' },\n    { label: 'Taxes', val: taxAmt, color: '#ef4444' },\n    { label: 'Expenses', val: expAmt, color: '#f59e0b' },\n    { label: 'Profit Buffer', val: profitAmt, color: '#8b5cf6' }\n  ];\n\n  var barH = '';\n  var legH = '';\n  for (var s = 0; s \u003c segs.length; s++) {\n    var p = pct(segs[s].val);\n    barH += '\u003cdiv class=\"fr-bar-seg\" style=\"width:' + p + '%;background:' + segs[s].color + ';\"\u003e' +\n      (parseFloat(p) \u003e 8 ? p + '%' : '') + '\u003c/div\u003e';\n    legH += '\u003cdiv class=\"fr-legend-item\"\u003e' +\n      '\u003cdiv class=\"fr-legend-dot\" style=\"background:' + segs[s].color + ';\"\u003e\u003c/div\u003e' +\n      '\u003cspan\u003e\u003cstrong\u003e' + segs[s].label + '\u003c/strong\u003e: ' + fmtD(segs[s].val) + ' (' + p + '%)\u003c/span\u003e' +\n    '\u003c/div\u003e';\n  }\n  document.getElementById('frBarTrack').innerHTML = barH;\n  document.getElementById('frLegend').innerHTML = legH;\n\n  // Project grid\n  var projectHours = [2, 5, 10, 20, 40];\n  var projectLabels = ['Quick Task (2 hrs)', 'Half-Day Project (5 hrs)', 'Full Day Project (10 hrs)', 'Two-Day Project (20 hrs)', 'Weekly Project (40 hrs)'];\n  var pg = document.getElementById('frProjectGrid');\n  var ph = '';\n  for (var p2 = 0; p2 \u003c projectHours.length; p2++) {\n    var price = recRate * projectHours[p2];\n    ph += '\u003cdiv class=\"fr-project-box\"\u003e' +\n      '\u003cdiv class=\"fr-project-hours\"\u003e' + projectLabels[p2] + '\u003c/div\u003e' +\n      '\u003cdiv class=\"fr-project-price\"\u003e' + fmtD(price) + '\u003c/div\u003e' +\n      '\u003cdiv class=\"fr-project-label\"\u003e@ ' + fmtR(recRate) + '/hr recommended rate\u003c/div\u003e' +\n    '\u003c/div\u003e';\n  }\n  pg.innerHTML = ph;\n}\n\nfunction toggleProjectView() {\n  frProjectsOn = !frProjectsOn;\n  var sw = document.getElementById('frToggleSwitch');\n  var panel = document.getElementById('frProjectPanel');\n  if (frProjectsOn) {\n    sw.classList.add('on');\n    panel.style.display = 'block';\n  } else {\n    sw.classList.remove('on');\n    panel.style.display = 'none';\n  }\n}\n\ndocument.addEventListener('DOMContentLoaded', function() { calcFR(); });\ncalcFR();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-to-use-this-calculator\"\u003eHow to Use This Calculator\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eEnter your target annual income\u003c/strong\u003e — the amount you want to take home after taxes.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSet your billable hours\u003c/strong\u003e — be realistic. Most freelancers bill 25–35 hours per week; the rest goes to admin, marketing, and client management.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAdjust weeks worked\u003c/strong\u003e — subtract vacation, holidays, and sick days. Working 48 weeks is typical.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAdd your business expenses\u003c/strong\u003e — software subscriptions, equipment, insurance, professional development, and accounting fees all count.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSet your tax rate\u003c/strong\u003e — self-employment tax is ~15.3% plus federal/state income tax. 25–30% total is a safe estimate for most freelancers.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eChoose a profit margin\u003c/strong\u003e — this buffer protects you during slow months and funds business growth.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eThe \u003cstrong\u003eminimum rate\u003c/strong\u003e keeps you afloat. The \u003cstrong\u003erecommended rate\u003c/strong\u003e is what you should actually charge.\u003c/p\u003e","title":"Freelance Rate Calculator | Find Your Ideal Hourly Rate"},{"content":"This article contains affiliate links.\nFurusato Nozei (Hometown Tax) Simulator [2026] Enter your annual income and household situation to automatically calculate your Furusato Nozei deduction ceiling and net tax savings.\nAnnual Income (gross, in 10,000 JPY) 2M JPY5M JPY25M JPY Household Situation Single or dual-income couple (no spousal deduction) Married couple (spousal deduction applies) Married + 1 child (high school age) Married + 2 children (university + high school) Housing Loan Tax Credit (Jutaku Loan Kojo) None Yes (100,000 JPY/yr) Yes (200,000 JPY/yr) Yes (300,000 JPY/yr) Deduction Ceiling by Income Bracket Gift Return Value Estimates How Furusato Nozei Works Furusato Nozei (literally \u0026ldquo;hometown tax donation\u0026rdquo;) lets you donate to any Japanese municipality of your choice. The donated amount minus 2,000 JPY is deducted from your income tax and resident tax. On top of that, you receive a local gift (return item) worth roughly 30% of your donation — making it a highly efficient way to reduce taxes while supporting regional communities.\nStaying Within Your Deduction Ceiling Any donation above your ceiling is not deductible and becomes a pure out-of-pocket expense. Use this simulator to confirm your ceiling before making any donations.\nThe Process Step by Step Choose a municipality — typically done by browsing gift catalogues on Furusato Nozei portals Complete the donation — apply through a dedicated platform for convenience Receive your gift and receipt — the municipality sends both Claim the deduction — via your annual tax return, or via the One-Stop Special Exception (for up to 5 municipalities, no tax return needed) Related Tools NISA Investment Simulator — Simulate tax-free NISA growth Compound Interest Calculator — See the long-term power of compounding Percentage Calculator — Calculate tax rates and discount amounts ","permalink":"https://productivity-works.com/tools/furusato-tax-simulator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"furusato-nozei-hometown-tax-simulator-2026\"\u003eFurusato Nozei (Hometown Tax) Simulator [2026]\u003c/h1\u003e\n\u003cp\u003eEnter your annual income and household situation to automatically calculate your \u003cstrong\u003eFurusato Nozei deduction ceiling\u003c/strong\u003e and \u003cstrong\u003enet tax savings\u003c/strong\u003e.\u003c/p\u003e\n\u003cdiv id=\"fn-calc\" style=\"max-width:680px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;\"\u003e\n\u003cdiv style=\"padding:24px;border:2px solid #2563eb;border-radius:12px;background:#f8fafc;\"\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;font-size:14px;\"\u003eAnnual Income (gross, in 10,000 JPY)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"fnIncome\" min=\"200\" max=\"2500\" step=\"10\" value=\"500\" oninput=\"calcFN()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e2M JPY\u003c/span\u003e\u003cspan id=\"fnIncVal\" style=\"font-weight:bold;font-size:20px;color:#2563eb;\"\u003e5M JPY\u003c/span\u003e\u003cspan\u003e25M JPY\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;font-size:14px;\"\u003eHousehold Situation\u003c/label\u003e\n\u003cselect id=\"fnFamily\" onchange=\"calcFN()\" style=\"width:100%;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:15px;\"\u003e\n\u003coption value=\"single\"\u003eSingle or dual-income couple (no spousal deduction)\u003c/option\u003e\n\u003coption value=\"couple\"\u003eMarried couple (spousal deduction applies)\u003c/option\u003e\n\u003coption value=\"couple1\"\u003eMarried + 1 child (high school age)\u003c/option\u003e\n\u003coption value=\"couple2\"\u003eMarried + 2 children (university + high school)\u003c/option\u003e\n\u003c/select\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;font-size:14px;\"\u003eHousing Loan Tax Credit (Jutaku Loan Kojo)\u003c/label\u003e\n\u003cselect id=\"fnLoan\" onchange=\"calcFN()\" style=\"width:100%;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:15px;\"\u003e\n\u003coption value=\"0\"\u003eNone\u003c/option\u003e\n\u003coption value=\"100000\"\u003eYes (100,000 JPY/yr)\u003c/option\u003e\n\u003coption value=\"200000\"\u003eYes (200,000 JPY/yr)\u003c/option\u003e\n\u003coption value=\"300000\"\u003eYes (300,000 JPY/yr)\u003c/option\u003e\n\u003c/select\u003e\n\u003c/div\u003e\n\u003cdiv id=\"fnResult\" style=\"margin-top:24px;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-top:24px;padding:24px;border:2px solid #10b981;border-radius:12px;background:#f0fdf4;\"\u003e\n\u003ch3 style=\"margin:0 0 16px;color:#166534;font-size:16px;\"\u003eDeduction Ceiling by Income Bracket\u003c/h3\u003e\n\u003cdiv id=\"fnTable\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-top:24px;padding:24px;border:2px solid #f59e0b;border-radius:12px;background:#fffbeb;\"\u003e\n\u003ch3 style=\"margin:0 0 12px;color:#92400e;font-size:16px;\"\u003eGift Return Value Estimates\u003c/h3\u003e\n\u003cdiv id=\"fnReturn\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nfunction fmt(n){return Math.round(n).toLocaleString();}\n\nfunction calcFurusatoLimit(income,familyType,loanDeduction){\n  var annual=income*10000;\n  // Salary income deduction\n  var deduction;\n  if(annual\u003c=1625000) deduction=550000;\n  else if(annual\u003c=1800000) deduction=annual*0.4-100000;\n  else if(annual\u003c=3600000) deduction=annual*0.3+80000;\n  else if(annual\u003c=6600000) deduction=annual*0.2+440000;\n  else if(annual\u003c=8500000) deduction=annual*0.1+1100000;\n  else deduction=1950000;\n\n  var netIncome=annual-deduction;\n\n  // Personal deductions\n  var kiso=480000;\n  var shakai=annual*0.15; // approx. 15% for social insurance\n  var haigusha=0,fuyou=0;\n  if(familyType==='couple') haigusha=380000;\n  else if(familyType==='couple1'){haigusha=380000;fuyou=380000;}\n  else if(familyType==='couple2'){haigusha=380000;fuyou=380000+630000;}\n\n  var totalDeduction=kiso+shakai+haigusha+fuyou;\n  var taxableIncome=Math.max(0,netIncome-totalDeduction);\n\n  // Income tax rate\n  var taxRate;\n  if(taxableIncome\u003c=1950000) taxRate=0.05;\n  else if(taxableIncome\u003c=3300000) taxRate=0.10;\n  else if(taxableIncome\u003c=6950000) taxRate=0.20;\n  else if(taxableIncome\u003c=9000000) taxRate=0.23;\n  else if(taxableIncome\u003c=18000000) taxRate=0.33;\n  else if(taxableIncome\u003c=40000000) taxRate=0.40;\n  else taxRate=0.45;\n\n  // Resident tax (approx.)\n  var juminTax=taxableIncome*0.10;\n\n  // Furusato Nozei ceiling formula\n  var denominator=1-taxRate*1.021-0.10;\n  if(denominator\u003c=0) denominator=0.01;\n  var limit=juminTax*0.20/denominator+2000;\n\n  // Housing loan credit impact\n  if(loanDeduction\u003e0){\n    var reduction=Math.min(loanDeduction*0.15,limit*0.15);\n    limit=Math.max(limit-reduction,2000);\n  }\n\n  return{\n    limit:Math.floor(limit),\n    taxRate:taxRate,\n    taxableIncome:taxableIncome,\n    juminTax:juminTax\n  };\n}\n\nfunction calcFN(){\n  var income=+document.getElementById('fnIncome').value;\n  var family=document.getElementById('fnFamily').value;\n  var loan=+document.getElementById('fnLoan').value;\n\n  document.getElementById('fnIncVal').textContent=fmt(income*10000)+' JPY';\n\n  var r=calcFurusatoLimit(income,family,loan);\n  var returnValue=Math.floor(r.limit*0.3);\n\n  var html='\u003cdiv style=\"text-align:center;padding:20px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e';\n  html+='\u003cdiv style=\"font-size:13px;color:#64748b;\"\u003eFurusato Nozei Deduction Ceiling (estimate)\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:40px;font-weight:bold;color:#2563eb;\"\u003e'+fmt(r.limit)+'\u003cspan style=\"font-size:16px;\"\u003e JPY\u003c/span\u003e\u003c/div\u003e';\n  html+='\u003c/div\u003e';\n\n  html+='\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;margin-top:16px;text-align:center;\"\u003e';\n  html+='\u003cdiv style=\"padding:12px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e\u003cdiv style=\"font-size:11px;color:#64748b;\"\u003eYour out-of-pocket cost\u003c/div\u003e\u003cdiv style=\"font-size:20px;font-weight:bold;color:#ef4444;\"\u003e2,000\u003cspan style=\"font-size:12px;\"\u003e JPY\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e';\n  html+='\u003cdiv style=\"padding:12px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e\u003cdiv style=\"font-size:11px;color:#64748b;\"\u003eGift return value (~30%)\u003c/div\u003e\u003cdiv style=\"font-size:20px;font-weight:bold;color:#10b981;\"\u003e'+fmt(returnValue)+'\u003cspan style=\"font-size:12px;\"\u003e JPY\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e';\n  html+='\u003cdiv style=\"padding:12px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e\u003cdiv style=\"font-size:11px;color:#64748b;\"\u003eNet benefit\u003c/div\u003e\u003cdiv style=\"font-size:20px;font-weight:bold;color:#f59e0b;\"\u003e'+fmt(returnValue-2000)+'\u003cspan style=\"font-size:12px;\"\u003e JPY\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e';\n  html+='\u003c/div\u003e';\n\n  html+='\u003cdiv style=\"margin-top:12px;font-size:12px;color:#64748b;text-align:center;\"\u003e* Calculated at income tax rate '+Math.round(r.taxRate*100)+'% (including surtax). Actual ceiling varies depending on your individual deductions.\u003c/div\u003e';\n\n  document.getElementById('fnResult').innerHTML=html;\n\n  // Reference table\n  var incomes=[300,400,500,600,700,800,900,1000,1200,1500,2000];\n  var thtml='\u003ctable style=\"width:100%;border-collapse:collapse;font-size:13px;\"\u003e';\n  thtml+='\u003ctr style=\"border-bottom:2px solid #10b981;\"\u003e\u003cth style=\"text-align:left;padding:6px;\"\u003eIncome\u003c/th\u003e\u003cth style=\"text-align:right;padding:6px;\"\u003eSingle\u003c/th\u003e\u003cth style=\"text-align:right;padding:6px;\"\u003eMarried\u003c/th\u003e\u003cth style=\"text-align:right;padding:6px;\"\u003eMarried+1\u003c/th\u003e\u003c/tr\u003e';\n  for(var i=0;i\u003cincomes.length;i++){\n    var s=calcFurusatoLimit(incomes[i],'single',0);\n    var c=calcFurusatoLimit(incomes[i],'couple',0);\n    var c1=calcFurusatoLimit(incomes[i],'couple1',0);\n    var bg=incomes[i]===income?'#dcfce7':i%2===0?'#fff':'#f0fdf4';\n    var bold=incomes[i]===income?'font-weight:bold;':'';\n    thtml+='\u003ctr style=\"background:'+bg+';'+bold+'\"\u003e';\n    thtml+='\u003ctd style=\"padding:6px;\"\u003e'+fmt(incomes[i]*10000)+' JPY\u003c/td\u003e';\n    thtml+='\u003ctd style=\"padding:6px;text-align:right;\"\u003e'+fmt(s.limit)+' JPY\u003c/td\u003e';\n    thtml+='\u003ctd style=\"padding:6px;text-align:right;\"\u003e'+fmt(c.limit)+' JPY\u003c/td\u003e';\n    thtml+='\u003ctd style=\"padding:6px;text-align:right;\"\u003e'+fmt(c1.limit)+' JPY\u003c/td\u003e';\n    thtml+='\u003c/tr\u003e';\n  }\n  thtml+='\u003c/table\u003e';\n  thtml+='\u003cdiv style=\"margin-top:8px;font-size:11px;color:#64748b;\"\u003e* Estimates only. Medical expense deductions, life insurance deductions, etc. may alter your actual ceiling.\u003c/div\u003e';\n  document.getElementById('fnTable').innerHTML=thtml;\n\n  // Gift return goods\n  var rhtml='\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:12px;\"\u003e';\n  var goods=[\n    {name:'Rice 20kg (domestic)',price:20000,value:8000},\n    {name:'Japanese Wagyu Beef 1kg',price:15000,value:5000},\n    {name:'Scallops 1kg (Hokkaido)',price:12000,value:4000},\n    {name:'Shine Muscat Grapes',price:10000,value:3500}\n  ];\n  for(var i=0;i\u003cgoods.length;i++){\n    var canAfford=r.limit\u003e=goods[i].price?'#10b981':'#94a3b8';\n    var badge=r.limit\u003e=goods[i].price?'Within limit':'Exceeds limit';\n    var badgeBg=r.limit\u003e=goods[i].price?'#dcfce7':'#f1f5f9';\n    rhtml+='\u003cdiv style=\"padding:12px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e';\n    rhtml+='\u003cdiv style=\"font-size:13px;font-weight:bold;color:#1e293b;\"\u003e'+goods[i].name+'\u003c/div\u003e';\n    rhtml+='\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eDonation: '+fmt(goods[i].price)+' JPY\u003c/div\u003e';\n    rhtml+='\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eMarket value: ~'+fmt(goods[i].value)+' JPY\u003c/div\u003e';\n    rhtml+='\u003cspan style=\"display:inline-block;margin-top:4px;padding:2px 8px;font-size:11px;font-weight:bold;background:'+badgeBg+';color:'+canAfford+';border-radius:4px;\"\u003e'+badge+'\u003c/span\u003e';\n    rhtml+='\u003c/div\u003e';\n  }\n  rhtml+='\u003c/div\u003e';\n  rhtml+='\u003cdiv style=\"margin-top:12px;font-size:12px;color:#92400e;text-align:center;\"\u003e* Gift return rates are typical estimates as of 2026. Actual values vary by municipality and time of year.\u003c/div\u003e';\n  document.getElementById('fnReturn').innerHTML=rhtml;\n}\n\ndocument.addEventListener('DOMContentLoaded',function(){calcFN();});\ncalcFN();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-furusato-nozei-works\"\u003eHow Furusato Nozei Works\u003c/h2\u003e\n\u003cp\u003eFurusato Nozei (literally \u0026ldquo;hometown tax donation\u0026rdquo;) lets you donate to any Japanese municipality of your choice. The donated amount minus 2,000 JPY is deducted from your income tax and resident tax. On top of that, you receive a local gift (return item) worth roughly 30% of your donation — making it a highly efficient way to reduce taxes while supporting regional communities.\u003c/p\u003e","title":"Furusato Nozei (Hometown Tax) Simulator | Calculate Your Deduction Limit \u0026 Savings [2026]"},{"content":"This article contains affiliate links. We may earn a commission at no extra cost to you.\nHourly to Salary Calculator Enter your hourly wage or annual salary below to instantly see your weekly, biweekly, monthly, and annual pay equivalents — plus overtime estimates and a comparison of what small raises can do to your income over a year.\nHourly \u0026rarr; Salary Salary \u0026rarr; Hourly Your Hourly Rate Hourly Wage $ Annual Salary $ Hours per Week: 40 hrs 1 60 Weeks per Year: 52 wks 48 52 Subtract vacation weeks and unpaid holidays Your Pay Breakdown Overtime Breakdown You entered more than 40 hours/week. Hours over 40 are calculated at 1.5× the regular rate under standard overtime rules. What a Raise Adds Up To See how small increases to your hourly rate translate into annual earnings. Your current rate is highlighted. Hourly Rate Weekly Monthly Annual vs. Now Common Hourly Rates \u0026amp; Annual Equivalents Click any rate to populate the calculator. How to Negotiate a Higher Hourly Rate 1. Anchor With Market Data, Not Feelings Before any negotiation, research what peers in your role, location, and industry actually earn. Use resources like the Bureau of Labor Statistics Occupational Employment Statistics, salary aggregators, and professional association surveys. Walking in with a specific number backed by three sources is far more persuasive than \u0026ldquo;I feel underpaid.\u0026rdquo; Show your employer the range, then place yourself within it based on your experience level.\n2. Time the Conversation Strategically The best moment to negotiate is after a clear win — completing a major project, landing a new client, or receiving positive performance feedback. Avoid asking during budget freezes, layoff cycles, or organizational restructuring. If your employer runs annual reviews, prepare your case two to three months in advance so your manager has time to advocate for you in budget discussions.\n3. Quantify Your Contribution in Dollar Terms Managers approve raises when they can justify the cost internally. Help them by translating your work into revenue generated, costs saved, or time recovered. \u0026ldquo;I automated the weekly reporting process, saving the team six hours per week at an average cost of $35/hr — that\u0026rsquo;s over $10,000 annually\u0026rdquo; is a request that practically writes its own approval memo.\n4. Negotiate the Full Package, Not Just the Rate If a higher hourly rate is off the table, shift to total compensation. Remote work days, additional PTO, a signing bonus, a performance bonus tied to measurable outcomes, professional development budget, or a guaranteed review in 90 days all have real monetary value. A flexible schedule that lets you avoid commuting costs can be worth several dollars per hour on its own.\nRelated Tools Calculate your take-home pay → Salary Calculator Estimate your tax bracket → Tax Bracket Calculator Calculate your ideal freelance rate → Freelance Rate Calculator Create a monthly budget → Budget Planner Disclaimer: This calculator provides estimates for informational purposes only and does not constitute financial, tax, or legal advice. Overtime rules vary by jurisdiction, employer type, and employment classification. Consult a qualified professional for advice specific to your situation.\n","permalink":"https://productivity-works.com/tools/hourly-to-salary-calculator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links. We may earn a commission at no extra cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"hourly-to-salary-calculator\"\u003eHourly to Salary Calculator\u003c/h1\u003e\n\u003cp\u003eEnter your hourly wage or annual salary below to instantly see your \u003cstrong\u003eweekly, biweekly, monthly, and annual\u003c/strong\u003e pay equivalents — plus overtime estimates and a comparison of what small raises can do to your income over a year.\u003c/p\u003e\n\u003cstyle\u003e\n.hs-wrap {\n  max-width: 720px;\n  margin: 0 auto;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  color: #1e293b;\n}\n.hs-mode-toggle {\n  display: flex;\n  background: #f0fdf4;\n  border: 2px solid #059669;\n  border-radius: 12px;\n  overflow: hidden;\n  margin-bottom: 20px;\n}\n.hs-mode-btn {\n  flex: 1;\n  padding: 14px 10px;\n  text-align: center;\n  font-size: 15px;\n  font-weight: 700;\n  cursor: pointer;\n  color: #065f46;\n  transition: background 0.2s, color 0.2s;\n  user-select: none;\n}\n.hs-mode-btn.active {\n  background: #059669;\n  color: #fff;\n}\n.hs-mode-btn:not(.active):hover {\n  background: #d1fae5;\n}\n.hs-card {\n  background: #ffffff;\n  border: 1px solid #e2e8f0;\n  border-radius: 14px;\n  padding: 28px;\n  margin-bottom: 20px;\n  box-shadow: 0 2px 8px rgba(5,150,105,0.07);\n}\n.hs-section-title {\n  font-size: 15px;\n  font-weight: 700;\n  color: #059669;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  margin: 0 0 18px 0;\n}\n.hs-field {\n  margin-bottom: 18px;\n}\n.hs-label {\n  display: block;\n  font-size: 14px;\n  font-weight: 600;\n  color: #475569;\n  margin-bottom: 8px;\n}\n.hs-input-wrap {\n  position: relative;\n  display: flex;\n  align-items: center;\n}\n.hs-input-prefix {\n  position: absolute;\n  left: 12px;\n  font-size: 16px;\n  font-weight: 700;\n  color: #059669;\n  pointer-events: none;\n}\n.hs-input {\n  width: 100%;\n  padding: 11px 14px 11px 28px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 16px;\n  font-weight: 700;\n  color: #1e293b;\n  box-sizing: border-box;\n  transition: border-color 0.2s;\n}\n.hs-input:focus {\n  outline: none;\n  border-color: #059669;\n}\n.hs-range-row {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n}\n.hs-range-row input[type=\"range\"] {\n  flex: 1;\n  accent-color: #059669;\n  height: 6px;\n  cursor: pointer;\n}\n.hs-range-val {\n  min-width: 54px;\n  text-align: right;\n  font-weight: 700;\n  font-size: 15px;\n  color: #059669;\n}\n.hs-results-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 14px;\n  margin-bottom: 6px;\n}\n.hs-results-grid-3 {\n  display: grid;\n  grid-template-columns: 1fr 1fr 1fr;\n  gap: 14px;\n  margin-bottom: 14px;\n}\n.hs-result-box {\n  background: #f0fdf4;\n  border: 2px solid #059669;\n  border-radius: 12px;\n  padding: 18px 14px;\n  text-align: center;\n}\n.hs-result-box.primary {\n  background: #059669;\n  color: #fff;\n}\n.hs-result-label {\n  font-size: 11px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  color: #065f46;\n  margin-bottom: 6px;\n}\n.hs-result-box.primary .hs-result-label {\n  color: #a7f3d0;\n}\n.hs-result-value {\n  font-size: 26px;\n  font-weight: 800;\n  color: #059669;\n  line-height: 1.1;\n}\n.hs-result-box.primary .hs-result-value {\n  color: #fff;\n  font-size: 32px;\n}\n.hs-result-sub {\n  font-size: 12px;\n  color: #6ee7b7;\n  margin-top: 4px;\n}\n.hs-result-sub-dark {\n  font-size: 12px;\n  color: #6b7280;\n  margin-top: 4px;\n}\n.hs-stat-row {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  padding: 10px 0;\n  border-bottom: 1px solid #e2e8f0;\n  font-size: 14px;\n}\n.hs-stat-row:last-child { border-bottom: none; }\n.hs-stat-label { color: #64748b; }\n.hs-stat-val { font-weight: 700; color: #059669; }\n.hs-ot-box {\n  background: #fff7ed;\n  border: 2px solid #f59e0b;\n  border-radius: 12px;\n  padding: 18px;\n}\n.hs-ot-title {\n  font-size: 14px;\n  font-weight: 700;\n  color: #92400e;\n  margin-bottom: 12px;\n}\n.hs-ot-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 12px;\n}\n.hs-ot-item {\n  background: #fef3c7;\n  border-radius: 8px;\n  padding: 12px;\n  text-align: center;\n}\n.hs-ot-item-label {\n  font-size: 11px;\n  font-weight: 600;\n  color: #78350f;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  margin-bottom: 4px;\n}\n.hs-ot-item-val {\n  font-size: 20px;\n  font-weight: 800;\n  color: #d97706;\n}\n.hs-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 14px;\n}\n.hs-table th {\n  background: #059669;\n  color: #fff;\n  padding: 10px 12px;\n  text-align: left;\n  font-weight: 600;\n}\n.hs-table th:not(:first-child) {\n  text-align: right;\n}\n.hs-table td {\n  padding: 10px 12px;\n  border-bottom: 1px solid #e2e8f0;\n}\n.hs-table td:not(:first-child) {\n  text-align: right;\n}\n.hs-table tr.hs-current td {\n  background: #f0fdf4;\n  font-weight: 700;\n  color: #059669;\n}\n.hs-table tr:hover td { background: #f8fafc; }\n.hs-qr-grid {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 10px;\n}\n.hs-qr-box {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 14px 10px;\n  text-align: center;\n  transition: border-color 0.2s, background 0.2s;\n  cursor: pointer;\n}\n.hs-qr-box:hover {\n  background: #f0fdf4;\n  border-color: #059669;\n}\n.hs-qr-rate {\n  font-size: 18px;\n  font-weight: 800;\n  color: #059669;\n  margin-bottom: 4px;\n}\n.hs-qr-annual {\n  font-size: 12px;\n  font-weight: 700;\n  color: #1e293b;\n}\n.hs-qr-label {\n  font-size: 11px;\n  color: #94a3b8;\n  margin-top: 2px;\n}\n.hs-hidden { display: none; }\n@media (max-width: 560px) {\n  .hs-results-grid { grid-template-columns: 1fr; }\n  .hs-results-grid-3 { grid-template-columns: 1fr 1fr; }\n  .hs-ot-grid { grid-template-columns: 1fr; }\n  .hs-qr-grid { grid-template-columns: 1fr 1fr; }\n}\n@media (max-width: 380px) {\n  .hs-results-grid-3 { grid-template-columns: 1fr; }\n  .hs-qr-grid { grid-template-columns: 1fr; }\n}\n\u003c/style\u003e\n\u003cdiv class=\"hs-wrap\"\u003e\n\u003c!-- Mode Toggle --\u003e\n\u003cdiv class=\"hs-mode-toggle\"\u003e\n  \u003cdiv class=\"hs-mode-btn active\" id=\"hsModeHtoS\" onclick=\"hsSetMode('hourly')\"\u003eHourly \u0026rarr; Salary\u003c/div\u003e\n  \u003cdiv class=\"hs-mode-btn\" id=\"hsModeStoH\" onclick=\"hsSetMode('salary')\"\u003eSalary \u0026rarr; Hourly\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Inputs --\u003e\n\u003cdiv class=\"hs-card\"\u003e\n  \u003cdiv class=\"hs-section-title\" id=\"hsInputTitle\"\u003eYour Hourly Rate\u003c/div\u003e\n  \u003c!-- Hourly input --\u003e\n  \u003cdiv class=\"hs-field\" id=\"hsHourlyField\"\u003e\n    \u003clabel class=\"hs-label\" for=\"hsHourlyInput\"\u003eHourly Wage\u003c/label\u003e\n    \u003cdiv class=\"hs-input-wrap\"\u003e\n      \u003cspan class=\"hs-input-prefix\"\u003e$\u003c/span\u003e\n      \u003cinput type=\"number\" id=\"hsHourlyInput\" class=\"hs-input\" value=\"20\" min=\"1\" max=\"10000\" step=\"0.25\" oninput=\"hsCalc()\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Salary input --\u003e\n  \u003cdiv class=\"hs-field hs-hidden\" id=\"hsSalaryField\"\u003e\n    \u003clabel class=\"hs-label\" for=\"hsSalaryInput\"\u003eAnnual Salary\u003c/label\u003e\n    \u003cdiv class=\"hs-input-wrap\"\u003e\n      \u003cspan class=\"hs-input-prefix\"\u003e$\u003c/span\u003e\n      \u003cinput type=\"number\" id=\"hsSalaryInput\" class=\"hs-input\" value=\"52000\" min=\"1000\" max=\"10000000\" step=\"1000\" oninput=\"hsCalc()\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Hours per week --\u003e\n  \u003cdiv class=\"hs-field\"\u003e\n    \u003clabel class=\"hs-label\"\u003eHours per Week: \u003cspan id=\"hsHoursVal\" style=\"color:#059669;\"\u003e40\u003c/span\u003e hrs\u003c/label\u003e\n    \u003cdiv class=\"hs-range-row\"\u003e\n      \u003cspan style=\"font-size:12px;color:#94a3b8;\"\u003e1\u003c/span\u003e\n      \u003cinput type=\"range\" id=\"hsHoursRange\" min=\"1\" max=\"60\" step=\"1\" value=\"40\" oninput=\"hsCalc()\"\u003e\n      \u003cspan style=\"font-size:12px;color:#94a3b8;\"\u003e60\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Weeks per year --\u003e\n  \u003cdiv class=\"hs-field\" style=\"margin-bottom:0;\"\u003e\n    \u003clabel class=\"hs-label\"\u003eWeeks per Year: \u003cspan id=\"hsWeeksVal\" style=\"color:#059669;\"\u003e52\u003c/span\u003e wks\u003c/label\u003e\n    \u003cdiv class=\"hs-range-row\"\u003e\n      \u003cspan style=\"font-size:12px;color:#94a3b8;\"\u003e48\u003c/span\u003e\n      \u003cinput type=\"range\" id=\"hsWeeksRange\" min=\"48\" max=\"52\" step=\"1\" value=\"52\" oninput=\"hsCalc()\"\u003e\n      \u003cspan style=\"font-size:12px;color:#94a3b8;\"\u003e52\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv style=\"font-size:12px;color:#94a3b8;margin-top:6px;\"\u003eSubtract vacation weeks and unpaid holidays\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Results --\u003e\n\u003cdiv class=\"hs-card\"\u003e\n  \u003cdiv class=\"hs-section-title\"\u003eYour Pay Breakdown\u003c/div\u003e\n  \u003cdiv id=\"hsResultsGrid\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Overtime Panel --\u003e\n\u003cdiv class=\"hs-card\" id=\"hsOvertimeCard\" style=\"display:none;\"\u003e\n  \u003cdiv class=\"hs-section-title\"\u003eOvertime Breakdown\u003c/div\u003e\n  \u003cdiv style=\"font-size:13px;color:#64748b;margin-bottom:14px;\"\u003eYou entered more than 40 hours/week. Hours over 40 are calculated at 1.5× the regular rate under standard overtime rules.\u003c/div\u003e\n  \u003cdiv class=\"hs-ot-box\"\u003e\n    \u003cdiv class=\"hs-ot-title\" id=\"hsOtTitle\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"hs-ot-grid\" id=\"hsOtGrid\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Comparison Table: What if I earned $X more per hour? --\u003e\n\u003cdiv class=\"hs-card\"\u003e\n  \u003cdiv class=\"hs-section-title\"\u003eWhat a Raise Adds Up To\u003c/div\u003e\n  \u003cdiv style=\"font-size:13px;color:#64748b;margin-bottom:14px;\"\u003eSee how small increases to your hourly rate translate into annual earnings. Your current rate is highlighted.\u003c/div\u003e\n  \u003cdiv style=\"overflow-x:auto;\"\u003e\n    \u003ctable class=\"hs-table\"\u003e\n      \u003cthead\u003e\n        \u003ctr\u003e\n          \u003cth\u003eHourly Rate\u003c/th\u003e\n          \u003cth\u003eWeekly\u003c/th\u003e\n          \u003cth\u003eMonthly\u003c/th\u003e\n          \u003cth\u003eAnnual\u003c/th\u003e\n          \u003cth\u003evs. Now\u003c/th\u003e\n        \u003c/tr\u003e\n      \u003c/thead\u003e\n      \u003ctbody id=\"hsCompBody\"\u003e\u003c/tbody\u003e\n    \u003c/table\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Quick Reference --\u003e\n\u003cdiv class=\"hs-card\"\u003e\n  \u003cdiv class=\"hs-section-title\"\u003eCommon Hourly Rates \u0026amp; Annual Equivalents\u003c/div\u003e\n  \u003cdiv style=\"font-size:13px;color:#64748b;margin-bottom:14px;\"\u003eClick any rate to populate the calculator.\u003c/div\u003e\n  \u003cdiv class=\"hs-qr-grid\" id=\"hsQrGrid\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003c!-- end hs-wrap --\u003e\n\u003cscript\u003e\nvar hsMode = 'hourly';\n\nfunction hsSetMode(mode) {\n  hsMode = mode;\n  if (mode === 'hourly') {\n    document.getElementById('hsModeHtoS').classList.add('active');\n    document.getElementById('hsModeStoH').classList.remove('active');\n    document.getElementById('hsHourlyField').classList.remove('hs-hidden');\n    document.getElementById('hsSalaryField').classList.add('hs-hidden');\n    document.getElementById('hsInputTitle').textContent = 'Your Hourly Rate';\n  } else {\n    document.getElementById('hsModeStoH').classList.add('active');\n    document.getElementById('hsModeHtoS').classList.remove('active');\n    document.getElementById('hsSalaryField').classList.remove('hs-hidden');\n    document.getElementById('hsHourlyField').classList.add('hs-hidden');\n    document.getElementById('hsInputTitle').textContent = 'Your Annual Salary';\n  }\n  hsCalc();\n}\n\nfunction fmtMoney(n) {\n  if (n \u003e= 1000) {\n    return '$' + Math.round(n).toLocaleString('en-US');\n  }\n  return '$' + n.toFixed(2);\n}\n\nfunction fmtDelta(n) {\n  if (n === 0) return '—';\n  return (n \u003e 0 ? '+' : '') + '$' + Math.round(Math.abs(n)).toLocaleString('en-US');\n}\n\nfunction fmtRate(n) {\n  return '$' + n.toFixed(2) + '/hr';\n}\n\nfunction hsGetValues() {\n  var hours = parseInt(document.getElementById('hsHoursRange').value) || 40;\n  var weeks = parseInt(document.getElementById('hsWeeksRange').value) || 52;\n  var hourly, annual;\n  if (hsMode === 'hourly') {\n    hourly = parseFloat(document.getElementById('hsHourlyInput').value) || 0;\n    annual = hourly * hours * weeks;\n  } else {\n    annual = parseFloat(document.getElementById('hsSalaryInput').value) || 0;\n    hourly = (hours \u003e 0 \u0026\u0026 weeks \u003e 0) ? annual / (hours * weeks) : 0;\n  }\n  return { hourly: hourly, hours: hours, weeks: weeks, annual: annual };\n}\n\nfunction hsCalc() {\n  var v = hsGetValues();\n  var hours = v.hours;\n  var weeks = v.weeks;\n  var hourly = v.hourly;\n  var annual = v.annual;\n\n  // Update slider displays\n  document.getElementById('hsHoursVal').textContent = hours;\n  document.getElementById('hsWeeksVal').textContent = weeks;\n\n  var weekly = hourly * hours;\n  var biweekly = weekly * 2;\n  var monthly = annual / 12;\n  var daily = hourly * 8;\n\n  // Build results grid\n  var rg = document.getElementById('hsResultsGrid');\n  var html = '';\n\n  if (hsMode === 'hourly') {\n    // 4 boxes: weekly, biweekly, monthly, annual\n    html += '\u003cdiv class=\"hs-results-grid\"\u003e';\n    html += hsResultBox('Weekly Pay', fmtMoney(weekly), hours + ' hrs \u0026times; ' + weeks + ' wks/yr', false);\n    html += hsResultBox('Biweekly Pay', fmtMoney(biweekly), 'Every two weeks', false);\n    html += '\u003c/div\u003e';\n    html += '\u003cdiv class=\"hs-results-grid\"\u003e';\n    html += hsResultBox('Monthly Pay', fmtMoney(monthly), 'Annual \u0026divide; 12', false);\n    html += hsResultBox('Annual Salary', fmtMoney(annual), hourly.toFixed(2) + '/hr \u0026times; ' + (hours * weeks) + ' hrs', true);\n    html += '\u003c/div\u003e';\n  } else {\n    // 5 boxes: hourly, daily, weekly, biweekly, monthly\n    html += '\u003cdiv class=\"hs-results-grid\"\u003e';\n    html += hsResultBox('Hourly Rate', fmtRate(hourly), annual \u003e 0 ? fmtMoney(annual) + '/yr \u0026divide; ' + (hours * weeks) + ' hrs' : '—', true);\n    html += hsResultBox('Daily Rate', fmtMoney(daily), '8 hours at ' + fmtRate(hourly), false);\n    html += '\u003c/div\u003e';\n    html += '\u003cdiv class=\"hs-results-grid-3\"\u003e';\n    html += hsResultBox('Weekly', fmtMoney(weekly), hours + ' hrs/wk', false);\n    html += hsResultBox('Biweekly', fmtMoney(biweekly), 'Every 2 weeks', false);\n    html += hsResultBox('Monthly', fmtMoney(monthly), 'Annual \u0026divide; 12', false);\n    html += '\u003c/div\u003e';\n  }\n  rg.innerHTML = html;\n\n  // Overtime panel\n  var otCard = document.getElementById('hsOvertimeCard');\n  if (hours \u003e 40) {\n    otCard.style.display = 'block';\n    var regularHours = 40;\n    var otHours = hours - 40;\n    var otRate = hourly * 1.5;\n    var regularWeeklyPay = regularHours * hourly;\n    var otWeeklyPay = otHours * otRate;\n    var totalWeeklyWithOt = regularWeeklyPay + otWeeklyPay;\n    var totalAnnualWithOt = totalWeeklyWithOt * weeks;\n    var otAnnualBonus = totalAnnualWithOt - (hourly * 40 * weeks);\n\n    document.getElementById('hsOtTitle').innerHTML =\n      'Working ' + hours + ' hrs/wk: ' + regularHours + ' regular + ' + otHours + ' overtime hrs at ' + fmtRate(otRate) + ' (1.5\u0026times;)';\n\n    var og = document.getElementById('hsOtGrid');\n    og.innerHTML =\n      '\u003cdiv class=\"hs-ot-item\"\u003e' +\n        '\u003cdiv class=\"hs-ot-item-label\"\u003eOT Weekly Pay\u003c/div\u003e' +\n        '\u003cdiv class=\"hs-ot-item-val\"\u003e' + fmtMoney(totalWeeklyWithOt) + '\u003c/div\u003e' +\n      '\u003c/div\u003e' +\n      '\u003cdiv class=\"hs-ot-item\"\u003e' +\n        '\u003cdiv class=\"hs-ot-item-label\"\u003eOT Annual Pay\u003c/div\u003e' +\n        '\u003cdiv class=\"hs-ot-item-val\"\u003e' + fmtMoney(totalAnnualWithOt) + '\u003c/div\u003e' +\n      '\u003c/div\u003e' +\n      '\u003cdiv class=\"hs-ot-item\"\u003e' +\n        '\u003cdiv class=\"hs-ot-item-label\"\u003eOT Hours/Week\u003c/div\u003e' +\n        '\u003cdiv class=\"hs-ot-item-val\"\u003e' + otHours + ' hrs\u003c/div\u003e' +\n      '\u003c/div\u003e' +\n      '\u003cdiv class=\"hs-ot-item\"\u003e' +\n        '\u003cdiv class=\"hs-ot-item-label\"\u003eAnnual OT Bonus\u003c/div\u003e' +\n        '\u003cdiv class=\"hs-ot-item-val\"\u003e+' + fmtMoney(otAnnualBonus) + '\u003c/div\u003e' +\n      '\u003c/div\u003e';\n  } else {\n    otCard.style.display = 'none';\n  }\n\n  // Comparison table: current rate +1, +2, +5\n  var baseHourly = hourly;\n  var increments = [-2, -1, 0, 1, 2, 5];\n  var cb = document.getElementById('hsCompBody');\n  var ch = '';\n  for (var i = 0; i \u003c increments.length; i++) {\n    var r = baseHourly + increments[i];\n    if (r \u003c 0) continue;\n    var isCurrent = increments[i] === 0;\n    var rowAnnual = r * hours * weeks;\n    var rowWeekly = r * hours;\n    var rowMonthly = rowAnnual / 12;\n    var delta = rowAnnual - annual;\n    var hlClass = isCurrent ? ' class=\"hs-current\"' : '';\n    var rateLabel = isCurrent ? fmtRate(r) + ' \u003cstrong\u003e(you)\u003c/strong\u003e' : fmtRate(r);\n    var deltaStr = isCurrent ? '—' : fmtDelta(delta) + '/yr';\n    ch += '\u003ctr' + hlClass + '\u003e' +\n      '\u003ctd\u003e' + rateLabel + '\u003c/td\u003e' +\n      '\u003ctd\u003e' + fmtMoney(rowWeekly) + '\u003c/td\u003e' +\n      '\u003ctd\u003e' + fmtMoney(rowMonthly) + '\u003c/td\u003e' +\n      '\u003ctd\u003e' + fmtMoney(rowAnnual) + '\u003c/td\u003e' +\n      '\u003ctd style=\"color:' + (delta \u003e 0 ? '#059669' : delta \u003c 0 ? '#dc2626' : '#64748b') + ';font-weight:700;\"\u003e' + deltaStr + '\u003c/td\u003e' +\n    '\u003c/tr\u003e';\n  }\n  cb.innerHTML = ch;\n\n  // Quick reference grid\n  var qrRates = [15, 20, 25, 30, 40, 50];\n  var qg = document.getElementById('hsQrGrid');\n  var qh = '';\n  for (var j = 0; j \u003c qrRates.length; j++) {\n    var qr = qrRates[j];\n    var qAnnual = qr * hours * weeks;\n    qh += '\u003cdiv class=\"hs-qr-box\" onclick=\"hsSetRate(' + qr + ')\"\u003e' +\n      '\u003cdiv class=\"hs-qr-rate\"\u003e' + fmtRate(qr) + '\u003c/div\u003e' +\n      '\u003cdiv class=\"hs-qr-annual\"\u003e' + fmtMoney(qAnnual) + '/yr\u003c/div\u003e' +\n      '\u003cdiv class=\"hs-qr-label\"\u003e' + hours + ' hrs \u0026times; ' + weeks + ' wks\u003c/div\u003e' +\n    '\u003c/div\u003e';\n  }\n  qg.innerHTML = qh;\n}\n\nfunction hsResultBox(label, value, sub, isPrimary) {\n  var cls = isPrimary ? 'hs-result-box primary' : 'hs-result-box';\n  var subCls = isPrimary ? 'hs-result-sub' : 'hs-result-sub-dark';\n  return '\u003cdiv class=\"' + cls + '\"\u003e' +\n    '\u003cdiv class=\"hs-result-label\"\u003e' + label + '\u003c/div\u003e' +\n    '\u003cdiv class=\"hs-result-value\"\u003e' + value + '\u003c/div\u003e' +\n    '\u003cdiv class=\"' + subCls + '\"\u003e' + sub + '\u003c/div\u003e' +\n  '\u003c/div\u003e';\n}\n\nfunction hsSetRate(rate) {\n  hsMode = 'hourly';\n  document.getElementById('hsModeHtoS').classList.add('active');\n  document.getElementById('hsModeStoH').classList.remove('active');\n  document.getElementById('hsHourlyField').classList.remove('hs-hidden');\n  document.getElementById('hsSalaryField').classList.add('hs-hidden');\n  document.getElementById('hsInputTitle').textContent = 'Your Hourly Rate';\n  document.getElementById('hsHourlyInput').value = rate;\n  hsCalc();\n}\n\ndocument.addEventListener('DOMContentLoaded', function() { hsCalc(); });\nhsCalc();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-to-negotiate-a-higher-hourly-rate\"\u003eHow to Negotiate a Higher Hourly Rate\u003c/h2\u003e\n\u003ch3 id=\"1-anchor-with-market-data-not-feelings\"\u003e1. Anchor With Market Data, Not Feelings\u003c/h3\u003e\n\u003cp\u003eBefore any negotiation, research what peers in your role, location, and industry actually earn. Use resources like the Bureau of Labor Statistics Occupational Employment Statistics, salary aggregators, and professional association surveys. Walking in with a specific number backed by three sources is far more persuasive than \u0026ldquo;I feel underpaid.\u0026rdquo; Show your employer the range, then place yourself within it based on your experience level.\u003c/p\u003e","title":"Hourly to Salary Calculator | Convert Wages Instantly"},{"content":"Switching jobs in Japan comes with a rite of passage that surprises nearly every foreign professional the first time they go through it: you negotiate hard, land a headline number that looks great, and then your first payslip arrives and the figure in your bank account is roughly ¥50,000–¥120,000 less than you expected. This guide is designed to close that gap between expectation and reality—and to give you practical leverage at the negotiating table.\n1. Why Gross Salary Is Misleading — Use the Take-Home Calculator First When a recruiter quotes you ¥6,000,000 a year, that number is nenkyū (年収): the gross annual figure before any deductions. In Japan, those deductions are substantial and structurally different from what most Western employees are used to.\nThe main deductions from your monthly gross:\nDeduction Approximate Rate Health insurance (健康保険) ~5.0% of standard monthly remuneration Pension (厚生年金) ~9.15% of standard monthly remuneration Employment insurance (雇用保険) ~0.6% of gross monthly Income tax (所得税) Progressive, roughly 5–20% on the taxable base Residence tax (住民税) ~10% of prior-year income, paid from June Taken together, a ¥6 million gross annual salary typically nets somewhere between ¥4.2 million and ¥4.5 million after all deductions—roughly 70–75 cents on every yen. That gap is larger than in many European countries and far larger than most newcomers anticipate.\nBefore you set a target number in any negotiation, run the actual numbers. Our Take-Home Pay Calculator lets you enter any gross monthly figure and see your estimated net after income tax, residence tax, and social insurance. Use it to work backward: decide what monthly net you need to cover your life, then calculate what gross you must ask for. This one step alone prevents the most common post-offer disappointment.\nPro tip: Social insurance contributions are calculated based on your hyōjun hōshū getsugaku (標準報酬月額)—a standardized remuneration bracket, not your exact salary. The brackets update every September based on a four-month average (April–July). Ask your HR contact which bracket your new salary falls into, because the difference between adjacent brackets can mean ¥2,000–¥5,000 a month.\n2. How Japanese Companies Structure Offers and Where Negotiation Room Exists Japanese compensation packages have a distinctive architecture. Understanding it tells you exactly where to push.\nBase Salary (基本給) This is the foundation. It drives everything: overtime calculations, bonus multipliers, and retirement allowance. Getting the base salary right is more important than any allowance. Even a ¥20,000/month increase in base salary compounds significantly over a multi-year tenure.\nMany companies are reluctant to offer a base salary far above their own internal grade ranges, but they have more flexibility than HR will initially admit. The key phrase to use is ikkaisei chōsei (一回性調整)—a one-time adjustment—or you can reference your current total compensation in writing and ask them to match it.\nFixed Allowances (固定手当) Role allowances, housing allowances, commuter allowances, and family allowances are common. These are sometimes negotiable as a lump sum when a company cannot move on base. Importantly, some allowances (commuter pass, for example) are tax-advantaged up to statutory limits, so they effectively deliver more net than equivalent base salary.\nBonus (賞与) Most Japanese companies pay bonus twice a year—June and December are the most common cycles, though the exact months vary by company. Bonuses are typically expressed as a number of months\u0026rsquo; base salary (e.g., \u0026ldquo;2.5 months per annum guaranteed\u0026rdquo;). When switching jobs, pay close attention to:\nWhether the bonus in your first year is prorated or waived entirely Whether there is a seika kyūyo (業績給) component that depends on company or individual performance Whether the December bonus requires you to still be employed on the payment date (many do) Negotiate the first-year bonus explicitly. It is common to lose an entire bonus cycle when switching mid-year, which can represent ¥600,000–¥1,000,000 in foregone compensation. If you are leaving a June bonus on the table at your current employer, quantify that number and present it as a sign-on offset request.\nOvertime and Residual Overtime Allowance (みなし残業) A growing number of Japanese companies pay a minashi zangyō teate—a fixed monthly allowance that covers a pre-agreed number of overtime hours (typically 20–45 hours). This allowance is included in gross salary figures but represents payment for hours worked. When comparing offers, identify whether the headline figure contains a ¥30,000–¥80,000 monthly overtime component. If it does, your real base is lower than it looks.\nRetirement Allowance (退職金) This is a feature of Japanese employment that has no real Western equivalent. Traditional companies fund a lump-sum payment at retirement calculated roughly as:\nYears of service × ¥400,000 (standard multiplier) A higher multiplier of ¥700,000 per year applies after 20 years of continuous service at many firms If you are leaving a company after 10 years to switch jobs, you may be walking away from a ¥4,000,000 retirement allowance you would have earned in 10 more years. Factor this into your negotiation. Use our Budget Planner to model the long-term wealth impact of staying versus switching.\n3. Tax and Social Insurance Surprises in the Month You Change Jobs The month you change jobs is the most financially complex month of your working life in Japan. Three specific mechanisms cause nearly every first-time job-switcher to be blindsided.\nResidence Tax: The June Cliff Residence tax (jūminzei, 住民税) is levied on the previous calendar year\u0026rsquo;s income at a rate of approximately 10%. It is billed from June of the current year through May of the following year—always one year in arrears.\nWhen you leave a company, your employer will typically perform ikkatsu kōjo (一括控除): collecting all remaining residence tax installments for the year in your final paycheck if you resign before the December standard deadline. This can mean a ¥100,000–¥200,000 deduction from your final salary in one hit.\nIf you switch jobs, your new employer will pick up the remaining installments in your new payslip from around July. But if you switch in the June–August window, you may face a temporary period of paying double-loaded tax: the tail end of last year\u0026rsquo;s bill and the start of this year\u0026rsquo;s higher bill based on your presumably higher current income.\nAction: Check the exact amounts in writing before your final paycheck. Request a breakdown from your current company\u0026rsquo;s payroll team.\nSocial Insurance: The Month-End Employment Rule Social insurance enrollment (shakai hoken, 社会保険) is determined by whether you are employed at a company as of the last day of the month. If you resign on September 30, your employer pays the contribution for September. If you resign on September 29, you lose that month\u0026rsquo;s employer-matched contribution and may need to enroll in Kokumin Kenkō Hoken (National Health Insurance) to cover the gap.\nThe practical implication: whenever possible, negotiate your start date at the new company to be the first day of the month, and your resignation date at the current company to be the last day of the previous month. This ensures seamless social insurance coverage with no gap and no double-payment.\nIncome Tax: The Year-End Adjustment Reset Japanese companies perform an end-of-year tax adjustment (nenmatsuchōsei, 年末調整) in December to reconcile withholding tax. When you switch mid-year, neither company will perform this adjustment automatically for your combined annual income. You will need to file a final tax return (kakutei shinkoku, 確定申告) by March 15 of the following year.\nIn many cases—especially if your income dipped during any unpaid transition gap—you will receive a refund. But you must file to claim it. Set a calendar reminder.\n4. Using doda\u0026rsquo;s Bilingual Agent Service for English-Speaking Candidates Salary negotiation in Japanese requires you to advocate for yourself through a recruiter intermediary—which is simultaneously an advantage and a trap. You cannot negotiate directly with the hiring manager in most cases; all compensation discussion goes through the agency. This makes choosing the right agency critical.\ndoda is one of Japan\u0026rsquo;s largest job-change platforms and has a dedicated bilingual service for non-Japanese candidates and internationally-minded Japanese professionals. Here is why it matters specifically for salary negotiation:\nBilingual Agents Who Understand Both Sides doda\u0026rsquo;s English-language advisors are familiar with the compensation expectations of candidates who have worked outside Japan. They can translate not just language but context—explaining to a domestic hiring company why an international candidate\u0026rsquo;s benchmark is different, and explaining to you how a Japanese offer compares to market rate for the role and industry.\nSalary Data Dossiers doda publishes annual salary benchmarks by industry, role, and region. When you work with a doda agent, you have access to real offer data from comparable placements—not just published survey ranges. This gives you a credible anchor in negotiation rather than guessing.\nThe Agent as Buffer Japanese companies expect negotiation to happen through the recruiter. A blunt direct ask from a candidate can read as aggressive in Japanese business culture. A skilled doda agent frames your ask diplomatically: \u0026ldquo;The candidate\u0026rsquo;s current package includes a December bonus of approximately ¥800,000 which will be forfeited; we are requesting a ¥500,000 sign-on adjustment to reflect this.\u0026rdquo; That framing works. The same message sent directly by the candidate often does not.\nFirst Registration Step Registration on doda is free. You create a profile, upload a Japanese-format resume (rirekisho and shokumukeireishosho), and an advisor contacts you within a few business days. The bilingual service is available by request at registration or when you first speak with your assigned advisor.\nRegister with doda (English-supported): doda bilingual/international service Putting It All Together: A Negotiation Checklist Before you accept any offer, work through this list:\nRun the take-home number. Use the Take-Home Pay Calculator with the gross figure offered. Confirm the net is livable. Identify the social insurance bracket. Ask HR for the hyōjun hōshū getsugaku that applies to your new salary. Quantify your forfeited bonus. Calculate what bonus (June or December) you will miss at your current company and request a sign-on offset. Check the minashi zangyō component. Remove any fixed overtime allowance from the headline figure before comparing base salaries. Confirm the residence tax handover. Find out whether your current employer will collect remaining residence tax in your final paycheck and plan cash flow accordingly. Negotiate start date for social insurance continuity. Aim for month-start at new employer, month-end departure from current. Model the retirement allowance gap. Use the Budget Planner if you are leaving a long-tenure position. Use a bilingual agent for the final ask. doda\u0026rsquo;s service is purpose-built for this. Managing Your Finances After the Switch The first six months after a job change tend to be financially turbulent even when the new salary is higher. Residence tax from the previous year, potential gaps in social insurance, and a delayed first bonus create temporary cash-flow pressure.\nIf you are setting up a new household budget or tracking expenses across the transition, freee offers personal and small-business accounting tools in Japan with Japanese tax integration. It is particularly useful for filing kakutei shinkoku if you change jobs mid-year and need to reconcile income across two employers.\nFinal Thoughts The single most important insight in this article is this: never evaluate a Japanese job offer on the gross annual figure alone. Japan\u0026rsquo;s layered deduction system, the one-year lag on residence tax, and the structural quirks of bonus timing mean that two offers with the same headline number can deliver very different lives.\nRun the real numbers first. Know which months will be tight. Understand where negotiation room actually exists in a Japanese offer structure. And if you are doing this in a second language in a culture that expects negotiation to be indirect and formal, use a professional who knows both sides of the table.\nRelated Tools Calculate percentages, discounts, and tips instantly → Percentage Calculator Calculate your take-home pay in Japan → Salary Calculator See your 2026 federal tax bracket and effective rate → Tax Bracket Calculator Compare side income tax implications → Side Hustle Tax Calculator See how your savings grow with compound interest → Compound Interest Calculator Convert hourly wage to salary → Hourly to Salary Calculator Affiliate Disclosure: This article contains affiliate links. If you register or make a purchase through these links, we may earn a commission at no additional cost to you. We only recommend services we believe are genuinely useful to our readers. Our editorial content is not influenced by affiliate relationships.\nYou May Also Like How to Use doda for Salary Negotiation in Japan: An English Guide for Career Changers (2026) Best Job Search Sites 2026: Where to Actually Find Good Jobs How to Calculate Your Take-Home Pay in Japan After Tax, Pension and Insurance (2026) ","permalink":"https://productivity-works.com/posts/salary-negotiation-japan-switching-jobs/","summary":"\u003cp\u003eSwitching jobs in Japan comes with a rite of passage that surprises nearly every foreign professional the first time they go through it: you negotiate hard, land a headline number that looks great, and then your first payslip arrives and the figure in your bank account is roughly ¥50,000–¥120,000 less than you expected. This guide is designed to close that gap between expectation and reality—and to give you practical leverage at the negotiating table.\u003c/p\u003e","title":"How to Negotiate a Higher Salary When Switching Jobs in Japan (With Take-Home Pay Reality Check)"},{"content":"This article contains affiliate links. We may earn a commission at no extra cost to you.\nIf you live in Japan as a foreign resident and have been thinking about getting started with FX (foreign exchange) trading, DMM FX is one of the most frequently recommended platforms for beginners. It has consistently ranked among Japan\u0026rsquo;s top FX brokers in terms of trading volume, and its simple interface, zero commission structure, and accessible customer support make it a reasonable starting point.\nThis guide walks through the entire account opening process in plain English — from checking your eligibility as a non-Japanese resident, to gathering the required documents, completing the online application, and making your first deposit. We also cover what happens after you open the account: the tax obligations that come with FX profits in Japan, and how to approach them as a salaried worker or self-employed resident.\nIf you are still comparing brokers and have not decided on DMM FX yet, see our comparison post: Best FX Brokers in Japan for English Speakers .\n1. Why DMM FX Is Popular in Japan DMM FX is operated by DMM.com Securities Co., Ltd., a subsidiary of the DMM Group, one of Japan\u0026rsquo;s larger internet conglomerates. DMM FX has built a strong retail trading user base in Japan over more than a decade of operation.\nSeveral features explain its popularity:\nZero trading commissions. DMM FX earns revenue through the bid-ask spread rather than per-trade commissions. For high-frequency traders and beginners alike, this keeps the cost structure simple and predictable.\nCompetitive spreads on major pairs. USD/JPY is one of the most actively traded pairs by Japanese retail investors, and DMM FX maintains tight spreads on it during standard market hours.\n24-hour customer support. DMM FX operates a support desk that is available around the clock on weekdays. For beginners who run into setup questions outside business hours, this is a practical advantage.\nSmartphone trading apps. DMM FX offers two dedicated apps — a standard app suited for straightforward order placement, and a more advanced app for traders who want charting tools and customizable layouts. Both are available for iOS and Android.\nDemo account. New users can practice with a demo environment before committing real funds, which lowers the barrier to learning the platform.\nPoint reward system. Active traders accumulate DMM FX points that can be redeemed for various rewards within the DMM ecosystem.\nThese features do not make DMM FX universally optimal for every trading strategy, but for a foreign resident in Japan who wants a well-established, regulated platform with a user-friendly interface, it is a solid choice to evaluate.\n2. Can Foreigners Open a DMM FX Account? This is the question most expats ask first, and the short answer is: yes, foreign residents of Japan can open a DMM FX account, provided they meet the eligibility criteria.\nResidency Requirement DMM FX requires applicants to be residents of Japan. Holding a valid residence status (在留資格) and being registered in the Japanese municipal system — meaning you have a registered address in Japan and a Residence Card (在留カード, zairyu card) — is necessary to apply. You do not need to be a Japanese citizen.\nTourists, visitors on short-stay visas, and people without a valid residence card are not eligible.\nAge Requirement Applicants must be at least 20 years old. (Note: Japan\u0026rsquo;s civil law lowered the adult age to 18 in 2022, but many financial institutions including FX brokers continue to set their own minimum at 20. Confirm the current age requirement on the DMM FX website at the time of your application, as policies may be updated.)\nMy Number Requirement Since 2016, Japan has required all financial account openings to be linked to the Individual Number system (マイナンバー). You will need to provide your My Number during the application. For foreign residents, your My Number is printed on your Residence Card or on a separate My Number notification letter. If you have a My Number Card (マイナンバーカード), that is the most straightforward document to use for verification.\nEmployment and Financial Situation DMM FX asks applicants to declare their employment status, annual income, total financial assets, and investment experience. This is a standard suitability assessment required by Japanese financial regulations. You do not need to meet a minimum income or asset threshold to open an account, but you should answer honestly — inconsistencies between your declared information and the documents you submit can delay or complicate the review process.\nTax Residency Because FX trading profits in Japan are subject to Japanese tax law, DMM FX will collect tax-related information during onboarding. If you are a resident of Japan for tax purposes (which generally means you have lived in Japan for more than a year, or have your principal residence there), your FX gains will be subject to Japanese domestic tax treatment. This is covered in more detail in Section 7 of this guide.\n3. Required Documents Before starting the online application, gather the following documents. Having everything ready in advance will make the process faster.\nIdentity Verification (one of the following) My Number Card (マイナンバーカード) — This is the most efficient option. It serves as both identity verification and My Number documentation in a single document. You will need to photograph both the front and back. Residence Card (在留カード) + My Number notification letter — If you do not have a My Number Card yet, you can use your Residence Card as the identity document and submit your My Number separately via the notification letter or certificate. Driver\u0026rsquo;s license (運転免許証) issued in Japan — Can be used as a supplementary identity document alongside My Number documentation. Passport — A valid passport can be used for identity verification. Note that if you use a passport, you will likely also need to submit your Residence Card to confirm your Japan address. Address Verification Your registered address in Japan must be confirmed. Documents that serve this purpose include:\nMy Number Card (the address is printed on the front) Residence Card A recent utility bill or bank statement with your name and Japanese address printed on it (within the past 3 months) Japanese Bank Account You will need a Japanese bank account to deposit and withdraw funds from your DMM FX account. Major Japanese banks (MUFG, SMBC, Mizuho, Japan Post Bank, etc.) and regional banks are generally accepted. Some online banks such as Rakuten Bank and PayPay Bank are also compatible. You will need your bank account number and the branch name ready during setup.\nIf you do not yet have a Japanese bank account, opening one is a prerequisite. Most major banks allow foreign residents with a valid Residence Card to open accounts.\nEmail Address A valid email address that you actively check is required. DMM FX will send your login credentials, verification links, and account status notifications to this address.\n4. Step-by-Step Application Process Step 1: Visit the Official DMM FX Website Navigate to the official DMM FX website and locate the \u0026ldquo;口座開設\u0026rdquo; (Account Opening) button. It is typically displayed prominently on the top page. Click it to begin the application.\nVisit DMM FX Official Site (URL TBD — affiliate link pending)\nMake sure you are on the official DMM.com Securities domain. Avoid clicking through third-party comparison sites that redirect to unofficial landing pages.\nStep 2: Enter Your Email Address and Create a Password The application begins by asking for your email address and a password for your new account. Enter a valid email address and choose a strong password. You will receive a confirmation email — click the link inside to verify your address and proceed.\nStep 3: Fill In Your Personal Information This is the most time-consuming part of the application. You will be asked to provide:\nName — Enter your name in both the Roman alphabet (as it appears on your passport or Residence Card) and in katakana (カタカナ). The katakana field is required by Japanese financial systems. If you are unsure how to write your name in katakana, use an online romanization-to-katakana converter, or spell it phonetically. For example, \u0026ldquo;Michael\u0026rdquo; would typically be written as マイケル. Date of birth Gender Registered address in Japan — Enter the address exactly as it appears on your Residence Card or My Number Card. Include the prefecture, city/ward, block number, and apartment number if applicable. Phone number — A Japanese mobile number is recommended, as DMM FX may use SMS for verification or account alerts. My Number — Enter your 12-digit Individual Number. Nationality Occupation and employer — Select the closest matching category. If you are employed by a company, enter your employer\u0026rsquo;s name. If you are self-employed, freelance, or on a specific visa type that affects employment status, select the appropriate option. Annual income — Choose from the provided income range brackets. Total financial assets — Choose the bracket that most accurately reflects your current savings and investments. Investment experience — Indicate whether you have previous experience with FX, stocks, or other financial instruments, and for how many years. Investment purpose and trading style — Select what best matches your goals (e.g., asset building, income supplementation) and anticipated trading frequency. Answer all fields carefully and honestly. The investment experience and purpose fields contribute to the suitability assessment DMM FX performs as a regulated broker.\nStep 4: Upload Identity Verification Documents After submitting your personal information, you will be prompted to upload your identity documents. DMM FX offers two verification methods:\nSmartphone photo verification (recommended): Using a smartphone, you photograph your documents in real-time through the DMM FX app or a guided web interface. This method is faster and typically results in quicker review times. You will be prompted to take photos of your document\u0026rsquo;s front and back, and in some cases a live selfie may be requested to match your face with the ID photo.\nPostal verification: If smartphone verification is not suitable for you, DMM FX can send a confirmation postcard to your registered address via Japan Post. You receive the postcard, enter the code on it into your account, and your address is confirmed. This method takes longer — typically 1 week or more for the postcard to arrive.\nFor foreign residents, using the My Number Card for smartphone verification is the most efficient path, as it combines identity and My Number confirmation into one document.\nStep 5: Wait for the Review Once your documents are submitted, DMM FX\u0026rsquo;s compliance team reviews your application. The standard review period is 1 to 3 business days for applications submitted via smartphone verification. Applications requiring postal verification or manual review may take longer.\nYou will receive email notifications updating you on your application status. During this waiting period, you do not need to take any action unless DMM FX contacts you requesting additional information.\nStep 6: Receive Login Credentials When your account is approved, you will receive an email with your login ID and instructions for accessing your trading account. Log in to the DMM FX platform using the credentials provided and complete any remaining setup steps (such as setting up two-factor authentication, which is strongly recommended).\nStep 7: Link Your Bank Account and Deposit Funds Before you can trade, you need to deposit funds into your DMM FX account. Navigate to the deposit section and register your Japanese bank account. DMM FX supports several deposit methods including bank transfer. The minimum deposit amount is shown on the deposit page — confirm the current minimum at the time of your application, as it may change.\nOnce your deposit is processed and reflected in your trading account balance, you are ready to place your first trade.\n5. Understanding the Review Process DMM FX\u0026rsquo;s account review is conducted by DMM.com Securities in compliance with Japan\u0026rsquo;s Financial Instruments and Exchange Act (金融商品取引法) and anti-money-laundering (AML) regulations.\nWhat Gets Reviewed Identity verification: Confirming that the documents you submitted are valid, clearly photographed, and match the personal information you entered. Address verification: Confirming that your registered address in Japan is current and verifiable. My Number: Confirming that the Individual Number provided matches the document. Suitability assessment: Evaluating whether FX trading is appropriate for your declared financial situation and experience level. This does not mean you need large assets — it is a regulatory documentation requirement. Common Reasons Applications Are Delayed or Rejected Blurry or incomplete document photos: Make sure all four corners of the document are visible, text is readable, and there is no glare from camera flash. Name mismatch: The name you enter must exactly match what appears on your documents, including the order of given name and family name. Address mismatch: If your registered address in Japan differs from what is printed on your documents (for example, if you recently moved and your Residence Card has not been updated yet), this can cause delays. Expired documents: Submit only currently valid documents. An expired Residence Card or passport will cause the application to be rejected. Incomplete My Number documentation: If you submit a Residence Card without a separate My Number document, you may receive a request for additional information. Tips for Smooth Approval Use your My Number Card for both identity and My Number verification — it reduces the number of documents needed. Take document photos in good natural lighting, on a plain dark background. Double-check that the katakana spelling of your name is consistent throughout the form. Ensure your registered address in the application matches your current Residence Card address exactly. 6. DMM FX Features Overview Once your account is open, here is what you will have access to:\nTrading Pairs DMM FX offers a range of currency pairs. The primary focus is on major pairs involving the Japanese yen (JPY), US dollar (USD), Euro (EUR), British pound (GBP), and others. Check the current pair lineup on the DMM FX website, as available instruments may be updated.\nSpreads DMM FX earns through the spread — the difference between the buy and sell price. For major pairs like USD/JPY, spreads are competitive during active market hours. Spreads widen during off-hours and around major economic announcements, as is standard across the industry.\nTrading Apps DMM FX STANDARD app: Designed for straightforward market and limit order placement. Well-suited for beginners who want a clean interface without too many options.\nDMM FX PLUS app: Offers more advanced charting, technical indicators, and layout customization. Suited for traders who want to conduct more detailed technical analysis on mobile.\nA web-based trading platform is also available for desktop users.\nDemo Account DMM FX provides a demo trading environment that simulates real market conditions using virtual funds. This is highly recommended for anyone new to FX trading. Practicing in the demo account allows you to learn how to place orders, set stop-losses, and read price charts without risking real money.\nLeverage FX trading in Japan is regulated, and retail FX brokers are subject to a maximum leverage cap of 25x on currency pairs as set by Japan\u0026rsquo;s Financial Services Agency (FSA). DMM FX operates within this regulatory framework.\nPoint Rewards DMM FX has a point program where active trading accumulates points redeemable within the DMM ecosystem. The details of the reward structure are on the DMM FX website.\n7. Tax Obligations for FX Profits in Japan This section is important. Many expats are caught off guard by Japan\u0026rsquo;s tax treatment of FX profits, especially if they are used to a tax system in their home country where capital gains are treated differently.\nHow FX Profits Are Taxed in Japan In Japan, FX trading profits are classified as miscellaneous income (雑所得) and are subject to separate self-assessment taxation (申告分離課税). The tax rate is a flat 20.315%, which breaks down as follows:\n15% national income tax 0.315%復興特別所得税 (reconstruction special income tax, a temporary surcharge) 5% local inhabitant tax (住民税) This flat rate applies regardless of your total income level, which is one reason FX is sometimes preferred over stock trading for tax planning purposes by high-income earners.\nFX losses can be carried forward for up to 3 years (損失の繰越控除) if you file a tax return, which means a losing year can offset gains in future years.\nWhen You Need to File If your FX profits during a calendar year (January to December) exceed 200,000 yen and you are a salaried employee whose employer handles your annual tax adjustment (年末調整), you are required to file a 確定申告 (kakuteishinkoku) — Japan\u0026rsquo;s annual self-assessment tax return — by the March 15 deadline.\nIf you are self-employed or a freelancer, you already file kakuteishinkoku, so your FX income simply gets added to your existing filing.\nEven if your FX profits are below the 200,000 yen threshold, you may still benefit from filing if you have FX losses to carry forward.\nUsing Freee for Tax Filing If you find the tax return process daunting — especially navigating Japanese tax forms in a second language — freee (フリー) is an accounting and tax filing platform that supports kakuteishinkoku. freee\u0026rsquo;s interface is more user-friendly than filing through the National Tax Agency\u0026rsquo;s e-Tax portal directly, and it supports income from multiple sources including FX trading.\nLearn more about freee and consider using it when tax season arrives in February-March.\nFor a detailed breakdown of how FX profits interact with salary income and the filing process, see our guide: FX Tax in Japan for Salaried Workers .\nRakuten Securities and NISA as a Complement FX trading profits do not qualify for Japan\u0026rsquo;s NISA (少額投資非課税制度) tax exemption — NISA covers stocks and investment trusts, not FX. However, if you are also building a long-term investment portfolio, opening a NISA account through Rakuten Securities (楽天証券) is a tax-efficient way to invest in index funds and ETFs alongside your FX activity. Rakuten Securities integrates with Rakuten Bank for seamless fund transfers and offers a wide selection of no-load investment trusts for NISA.\nThe two activities — FX for active trading, NISA-eligible funds for long-term accumulation — can complement each other as part of a broader financial strategy in Japan.\n8. Frequently Asked Questions Q: Can I open a DMM FX account if I am on a working holiday visa?\nA: Working holiday visa holders who are registered as residents in Japan (jyuminhyo registration) and have a valid Residence Card can generally apply. The key requirement is that you have a valid residence status and a Japanese address registration. Confirm the current eligibility terms on the DMM FX website.\nQ: What if I don\u0026rsquo;t have a My Number Card yet — only the notification letter?\nA: You can use the notification letter along with your Residence Card as an alternative to the My Number Card. However, the My Number Card is the most convenient single document for both identity and My Number verification. If you plan to open multiple financial accounts in Japan, applying for a My Number Card at your local municipal office is worth doing.\nQ: How long does the deposit take to reflect in my account?\nA: This depends on the deposit method. Bank transfers typically reflect within the same business day if initiated during banking hours. Check the DMM FX deposit page for up-to-date processing times.\nQ: Is there an English interface?\nA: DMM FX\u0026rsquo;s platform is primarily in Japanese. The trading apps and web platform are in Japanese. This guide is intended to help English speakers navigate the Japanese-language application process. Once you are familiar with the basic terminology — 買 (buy), 売 (sell), 注文 (order), 決済 (close/settle) — the interface becomes manageable. A basic working vocabulary of trading-related Japanese terms will serve you well.\nQ: What is the minimum deposit?\nA: DMM FX has a minimum deposit requirement. Check the current amount on the official DMM FX website, as this figure is subject to change.\nConclusion Opening a DMM FX account in Japan as a foreign resident is a straightforward process if you have the right documents ready and follow the steps carefully. The application is entirely online, review times are reasonable, and the platform itself is accessible for beginners.\nThe key steps are:\nConfirm your eligibility (valid residence status, Japanese address registration, age 20+) Gather your My Number Card or equivalent documents, your Residence Card, and your Japanese bank account details Complete the online application with accurate personal and financial information Upload clear photos of your documents for identity verification Wait for review (typically 1-3 business days), then log in and deposit funds Once your account is active, take time to explore the demo account before trading with real money. Understand the tax obligations that apply to your FX profits — the 20.315% flat tax rate and the kakuteishinkoku filing requirement — so that you are not caught off guard when tax season arrives. Tools like freee can make the filing process significantly easier.\nIf you are still weighing your options across different brokers, our comparison guide covers the major FX brokers available in Japan for English-speaking residents: Best FX Brokers in Japan for English Speakers .\nVisit DMM FX Official Site (URL TBD — affiliate link pending)\nRelated Tools Calculate potential FX profits → Forex Profit Calculator Check your take-home pay → Salary Calculator Disclosure: This article contains affiliate links. We may earn a commission at no extra cost to you. This article is for informational purposes only and does not constitute financial advice. FX trading involves significant risk of loss and is not suitable for all investors. Please ensure you fully understand the risks involved before trading.\nYou May Also Like Best FX Brokers for English Speakers in Japan 2026: What to Look For FX Spread Comparison Japan 2026: Finding the Lowest Cost Broker for USD/JPY How Much Tax Do You Pay on FX Profits in Japan? A Salary Worker\u0026rsquo;s Guide ","permalink":"https://productivity-works.com/posts/dmm-fx-account-opening-guide-japan-english-2026/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links. We may earn a commission at no extra cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eIf you live in Japan as a foreign resident and have been thinking about getting started with FX (foreign exchange) trading, DMM FX is one of the most frequently recommended platforms for beginners. It has consistently ranked among Japan\u0026rsquo;s top FX brokers in terms of trading volume, and its simple interface, zero commission structure, and accessible customer support make it a reasonable starting point.\u003c/p\u003e","title":"How to Open a DMM FX Account in Japan: Complete English Guide for Beginners (2026)"},{"content":"This article contains affiliate links.\nSwitching jobs in Japan is one of the most reliable paths to a significant salary increase — but only if you know how to negotiate. The Japanese job market has a language, a pace, and an etiquette all its own. For English-speaking professionals, navigating that process without a guide can mean leaving hundreds of thousands of yen on the table.\nThis article is your practical, step-by-step guide to using doda — one of Japan\u0026rsquo;s largest job platforms, operated by Persol Career Co., Ltd. — to find higher-paying opportunities and negotiate compensation effectively. Whether you are an experienced bilingual professional, a mid-career foreigner working in Japan, or a recent hire looking to move up faster, this guide will walk you through everything from understanding how Japanese salaries are structured to closing a counter-offer with confidence.\nRelated reading: Salary Negotiation in Japan When Switching Jobs | Best Job Search Sites in Japan 2026 1. doda by Persol Career: What It Is and Why It Matters doda (https://doda.jp ) is operated by Persol Career Co., Ltd., one of Japan\u0026rsquo;s largest human resources companies. Persol Career is a subsidiary of the Persol Group, a major staffing and HR solutions conglomerate listed on the Tokyo Stock Exchange.\ndoda is not simply a job board. It is a hybrid platform that combines:\nA self-serve job listing database with hundreds of thousands of active openings A career agent service with dedicated human advisors A scout (direct approach) function where companies proactively contact you Salary benchmarking tools backed by proprietary market data As of 2026, doda ranks consistently among the top two or three most-used転職 (job change) platforms in Japan alongside Rikunabi NEXT. It is particularly strong in mid-career hiring across IT, finance, manufacturing, consulting, and sales.\nFor English-speaking professionals, doda has expanded its support for bilingual candidates and offers job listings explicitly tagged for English-language work environments. The agent service, while conducted primarily in Japanese, can accommodate candidates who speak Japanese at a business level — and the structured process takes much of the awkward negotiation language off your plate.\nWhy use doda specifically for salary negotiation?\nBecause the agent service is designed to negotiate on your behalf. Unlike applying directly to a company\u0026rsquo;s HR portal, using a doda career advisor means a trained professional communicates salary expectations to the hiring company as a matter of standard procedure. The company expects it. The advisor has market data. The system is built for it.\n2. Understanding Japanese Salary Structure Before you can negotiate effectively, you need to understand what you are negotiating. Japanese salary packages are structured differently from those in many Western countries, and the details matter significantly for your actual take-home pay.\n2.1 Monthly Base Salary (基本給, Kihonkyu) This is the foundation of your compensation. In Japan, annual salary (年収, nenshu) is almost always quoted as the total of 12 monthly salaries plus bonuses. When a job posting says \u0026ldquo;年収 600万円,\u0026rdquo; that typically includes bonuses.\nAlways clarify: What is the monthly base salary (基本給)?\nThe base salary matters because bonuses, social insurance contributions, and severance pay are often calculated as multiples of it.\n2.2 Bonuses (賞与, Shoyo) Most full-time (正社員) positions at Japanese companies include biannual bonuses — typically in June and December. A common structure is \u0026ldquo;2 months + 2 months\u0026rdquo; of base salary, though this varies widely by company performance and individual rating.\nWhen evaluating an offer, ask:\nIs the bonus guaranteed or performance-based? What was the actual bonus paid last year? Is it included in the quoted 年収 figure? 2.3 Allowances (手当, Teate) Japanese employment packages often include a range of allowances that significantly affect real compensation:\nAllowance Japanese Typical Amount Commuting 通勤手当 Actual cost (up to company cap) Housing 住宅手当 10,000–50,000 JPY/month Family 家族手当 5,000–20,000 JPY/child Position 役職手当 Varies by title Skills/Certification 資格手当 Varies These are often non-negotiable line items, but understanding them helps you compare offers accurately. A job with a lower base salary but a 30,000 JPY/month housing allowance may be more attractive than it first appears.\n2.4 Fixed Overtime Inclusion (固定残業代, Kotei Zangyodai) This is a critical point that trips up many candidates. Many Japanese job postings include a \u0026ldquo;fixed overtime\u0026rdquo; component — a set number of overtime hours (e.g., 30 hours/month) already factored into the quoted salary. If you work less overtime, you still receive it. If you work more, you receive additional pay.\nAlways check: Does the quoted salary include fixed overtime? How many hours? This is not a red flag on its own — it is standard practice — but it affects your calculation of true hourly compensation and how meaningful future \u0026ldquo;raises\u0026rdquo; actually are.\n2.5 Social Insurance Deductions Japan has mandatory social insurance (社会保険) contributions covering health insurance, pension (厚生年金), unemployment insurance, and long-term care insurance (for those 40+). Combined employee-side deductions typically run 14–16% of gross salary, with income tax on top. This means a 600万円 annual salary translates to roughly 420–440万円 in take-home pay depending on your personal deductions. Keep this in mind when setting your negotiation floor.\n3. How doda\u0026rsquo;s Agent Service Helps You Negotiate The single most powerful tool doda offers for salary negotiation is not a calculator or a database — it is the human advisor.\n3.1 How the Agent Service Works When you register on doda and opt into the agent service (エージェントサービス), you are assigned a career advisor (キャリアアドバイザー). This is a professional whose job is to match you with appropriate roles and facilitate your job change process end-to-end.\nThe typical process:\nInitial consultation (面談, mendan): A 60–90 minute call or in-person meeting where the advisor learns your background, goals, and salary expectations Job recommendation: The advisor proposes openings suited to your profile, including those not publicly listed (非公開求人, hikokkai kyujin) Application support: Resume and職務経歴書 (work history document) feedback Interview preparation: Company-specific coaching Offer and negotiation: The advisor communicates your salary expectation to the company and manages the back-and-forth Acceptance and onboarding support: Including resigning from your current job 3.2 Salary Benchmarking with Real Data doda publishes extensive salary survey data drawn from its placement history. During the consultation, your advisor can tell you:\nThe typical salary range for someone with your experience and job category (職種) What competing companies are currently offering Whether your current salary is above or below market This data is a negotiation asset. When your advisor tells the hiring company \u0026ldquo;this candidate is currently at X and expects Y based on market data,\u0026rdquo; it is a professional framing that removes the awkwardness of a candidate personally demanding a higher number.\n3.3 Non-Listed Jobs Often Pay More A significant share of the roles doda advisors present are non-publicly listed positions — openings that companies share only with select agencies. These are often senior-level roles with greater compensation flexibility. By working with a doda advisor rather than applying through the public job board alone, you gain access to a segment of the market you simply cannot reach independently.\n3.4 The Advisor Negotiates on Your Behalf Japanese business culture values indirect communication. It can feel uncomfortable for a candidate to personally push back on a salary offer. The agent service exists precisely to solve this problem. Your advisor is trained and culturally expected to relay your salary expectations, ask for reconsideration, and probe for flexibility — all as a matter of standard professional practice. The company does not perceive this as aggressive; they expect it.\nPractical tip: Be explicit with your advisor about your target salary, your walk-away number, and what non-salary factors (remote work, title, bonus structure) you are willing to trade. The more information the advisor has, the more effectively they can represent you.\n4. doda Scout Feature: Let Higher Offers Come to You Beyond the agent service, doda operates a scout function (スカウトサービス) that can surface higher compensation opportunities you might never have found through active search.\n4.1 How doda Scout Works When you create a profile on doda (you do not need to apply to any jobs to participate), companies and headhunters can view a anonymized or semi-identified version of your profile and send you direct approach messages. These are categorized as:\nScout (スカウト): A direct approach with interview invitation guaranteed if you respond positively Interest (気になる): An early-stage signal of interest from a company The key difference from a standard job application is intent. A company that scouts you has already reviewed your background and decided they want to meet you. This shifts negotiating leverage in your favor from the very first contact.\n4.2 Why Scout Offers Tend to Pay More When a company actively recruits someone rather than waiting for applications, they typically:\nHave a more urgent hiring need Have already allocated budget for the role Are willing to offer above-standard compensation to attract a specific profile In practice, scout-sourced offers frequently include a higher starting salary than the same company would offer to an applicant who came through the standard pipeline.\n4.3 Optimizing Your doda Profile for Better Scouts To attract high-quality scouts, treat your doda profile as a targeted marketing document:\nBe specific about measurable achievements: \u0026ldquo;Reduced supply chain costs by 12% over 18 months\u0026rdquo; outperforms \u0026ldquo;managed procurement\u0026rdquo; Tag your language skills clearly: Bilingual professionals command a premium; make your Japanese and English proficiency explicit and quantified (JLPT level, TOEIC score, business-level notation) List certifications and technical skills: These are directly searchable by recruiters Set your desired salary range accurately but ambitiously: Underpricing yourself filters out companies with genuine budget; set a target that reflects market data, not modesty Update your profile regularly: Active, recently-updated profiles receive priority in recruiter search results 5. Salary Negotiation Strategies for Japan Understanding the tools is one thing. Knowing how and when to use them is another. Here are proven strategies tailored to the Japanese hiring context.\n5.1 Timing: The Offer Phase Is Your Window In Japan, salary negotiation almost exclusively takes place after a formal job offer (内定, naite) has been extended but before you accept. Attempting to negotiate salary during interviews is generally considered premature and can signal poor cultural fit.\nThe standard sequence:\nFinal Interview → Offer Extended → Negotiation Window (3–7 days typical) → Acceptance or Decline Once you receive an offer, you have a legitimate and expected window to ask questions and request adjustments. Do not let this window pass without engaging it.\n5.2 The Market Data Approach The most effective negotiation frame in Japanese professional culture is objective market data, not personal need. The statement \u0026ldquo;I need more because my rent is high\u0026rdquo; lands poorly. The statement \u0026ldquo;Based on market data for this role and experience level, the range is typically X–Y, and I was hoping to align closer to the upper end\u0026rdquo; lands well.\ndoda publishes its own salary data publicly. Use it. Referencing a credible third-party source — especially one associated with the platform you are applying through — is both tactically sound and culturally appropriate.\n5.3 The Counter-Offer Approach If the initial offer is below your target but the role is otherwise strong, a structured counter-offer works well:\nExpress genuine interest first: \u0026ldquo;I am very interested in this opportunity and am seriously considering it.\u0026rdquo; State the gap without ultimatum language: \u0026ldquo;The offered salary is slightly below what I was targeting based on my research and current compensation.\u0026rdquo; Propose a specific number: Vague requests (\u0026ldquo;can you do better?\u0026rdquo;) are harder to action than specific ones (\u0026ldquo;I was hoping for 550万円 rather than 520万円\u0026rdquo;). Leave room for alternatives: \u0026ldquo;If that is not possible on base salary, I would be open to discussing the bonus structure or a review timeline.\u0026rdquo; When using doda\u0026rsquo;s agent service, your advisor handles this framing on your behalf — but briefing them on this structure ensures they represent you precisely.\n5.4 Leveraging Competing Offers If you have received an offer from another company, this is legitimate negotiation leverage. Inform your doda advisor. They can communicate to the company that you are in active consideration elsewhere, which frequently accelerates timelines and can prompt improved terms.\nIn Japanese business culture, explicitly saying \u0026ldquo;Company B offered me X\u0026rdquo; can feel confrontational if done directly. The agent layer again serves a useful purpose here — the advisor can convey competitive pressure professionally without the candidate appearing to be playing companies against each other.\n5.5 Title and Grade as Negotiation Currency Japanese compensation is tightly linked to job grade (職級, shokukyu) and title. Sometimes a company cannot move the base salary figure for a given grade, but they can offer you a higher grade. Negotiate the grade, and the salary follows.\nAsk your advisor: \u0026ldquo;Is there flexibility in the job grade the offer is attached to?\u0026rdquo;\n5.6 Know Your Walk-Away Number Set a firm minimum before entering negotiation — not a range, a floor. If an offer cannot reach that number through any combination of base, bonus, and allowances, decline. Accepting a compensation package that does not meet your needs and resigning within a year is expensive, time-consuming, and damaging to your resume. The short-term discomfort of declining an offer is far smaller than the cost of repeating the job search process in 12 months.\n6. Tax Implications of a Higher Salary Successfully negotiating a salary increase is excellent news. Understanding how that increase translates to your actual bank account requires a basic grasp of Japanese tax and social insurance mechanics.\n6.1 Progressive Income Tax Japan uses a progressive income tax structure. As your salary increases, you move into higher brackets. In simplified terms:\nUp to 1.95M JPY: 5% 1.95M – 3.3M JPY: 10% 3.3M – 6.95M JPY: 20% 6.95M – 9M JPY: 23% 9M – 18M JPY: 33% Note that these are marginal rates — only the income within each bracket is taxed at that rate. A move from 500万円 to 600万円 in gross salary does not result in your entire income being taxed at a higher rate; only the incremental 100万円 (less deductions) hits the higher bracket.\nIn addition to national income tax, residents of Japan pay local resident tax (住民税) at a flat 10% of taxable income. This is assessed in the following year, so a salary increase in 2026 will increase your resident tax bill from June 2027 onward.\n6.2 Social Insurance and the Salary Increase Effect Your social insurance contributions are determined by your standard monthly remuneration (標準報酬月額), which is recalculated annually in September (based on April–June earnings) and also when your salary changes significantly. A higher salary means higher insurance deductions — health insurance, pension, and long-term care (for those 40+).\nWhile higher pension contributions do increase your eventual pension payout, the immediate effect is that the relationship between gross salary increase and net take-home is not linear. A 10% gross salary increase may translate to a 7–8% net increase once additional social insurance and tax effects are accounted for.\n6.3 For Freelancers and Side Business Owners If you are considering switching from employment to a higher-paying freelance or contract arrangement, or if you already run a side business alongside your employment, the tax picture becomes more complex. You become responsible for quarterly estimated tax payments, managing consumption tax registration, and optimizing business expense deductions.\nfreee is Japan\u0026rsquo;s leading cloud accounting platform for freelancers and small business owners. It handles Japanese tax filings including 確定申告 (kakutei shinkoku, annual tax return), invoicing, expense tracking, and payroll in a bilingual-friendly interface. If a salary negotiation leads you to consider the leap to freelancing, or if you are managing income from multiple sources, freee can dramatically simplify the administrative burden. (Affiliate link)\n7. Invest Your Raise: Building Wealth with NISA Negotiating a higher salary is the first step. Ensuring that additional income builds long-term wealth — rather than simply inflating your monthly spending — is the second.\n7.1 The Power of Recurring Investment Consider this: if your salary negotiation yields an increase of 50,000 JPY per month (a reasonable result from a well-executed job change), and you invest that increment rather than spend it, the compounding effect over time is substantial.\nMonthly investment: 50,000 JPY Annual return (est.): 5% (diversified index portfolio) Investment horizon: 20 years Approximate result: ~20.6 million JPY The key is starting immediately and automating the contribution so the increased take-home pay never gets \u0026ldquo;lifestyle inflated\u0026rdquo; away.\n7.2 NISA: Japan\u0026rsquo;s Tax-Free Investment Account The NISA (Nippon Individual Savings Account) system, expanded and reformed in 2024 into the new NISA structure, allows Japanese residents — including non-Japanese nationals with a valid residence card — to invest up to:\nTsumitate (growth) portion: 1.2 million JPY per year in regular (dollar-cost averaging) investments One-time (lump sum) portion: 2.4 million JPY per year in spot purchases Investment gains and dividends within a NISA account are completely tax-free. In a standard taxable account, capital gains and dividends are taxed at approximately 20.315% (15.315% national + 5% local). NISA eliminates this entirely, compounding your effective returns significantly over time.\nThe lifetime NISA limit is 18 million JPY per person. For a working professional in Japan planning for retirement or long-term financial independence, maxing out NISA contributions should be a primary financial goal.\n7.3 Rakuten Securities for NISA Rakuten Securities (楽天証券) is one of Japan\u0026rsquo;s largest online brokerages and consistently ranks as a top choice for NISA accounts. Key advantages:\nNo account opening or maintenance fees Rakuten Points integration: Use accumulated points for investment contributions Tsumitate NISA fund selection: Wide lineup of low-cost index funds including eMAXIS Slim series English-language support resources: More accessible for non-native Japanese speakers than many competitors Rakuten Bank linking: Seamless money movement and a higher savings rate for linked account holders Opening a NISA account with Rakuten Securities takes approximately 20 minutes online. Once set up, you can automate monthly contributions to align exactly with the amount of your salary increase — ensuring the raise goes directly to work building your future wealth.\nOpen a NISA account with Rakuten Securities (Affiliate link)\n8. Putting It All Together: Your Action Plan Here is a concrete step-by-step plan to use doda for salary negotiation starting today.\nStep 1: Register on doda and create a complete profile Fill in every section. Add quantified achievements. Set your desired salary range honestly and ambitiously. Upload a professional photo.\nStep 2: Activate the agent service Request a career consultation. Be prepared for a 60–90 minute session. Come with clear answers to: current salary, target salary, preferred industries, must-have and nice-to-have conditions.\nStep 3: Enable the scout feature Opt in to receiving scouts. Check your inbox regularly. Even if you do not actively apply, incoming scout quality tells you a great deal about how the market values your profile.\nStep 4: Review market data before your consultation Browse doda\u0026rsquo;s publicly available salary surveys for your job category. Come to the advisor consultation knowing what the market looks like so you can set a credible, data-backed target.\nStep 5: Apply with advisor support and negotiate deliberately Let your advisor handle the salary negotiation phase. Brief them thoroughly on your numbers and priorities. Do not undersell yourself by giving a modest range \u0026ldquo;to be safe\u0026rdquo; — give the number you actually want.\nStep 6: Receive the offer, use the negotiation window When an offer comes, take 24–48 hours before responding. Review all components: base salary, bonus, allowances, grade, remote policy, review timeline. Identify what to push on and brief your advisor.\nStep 7: Accept, then optimize your finances Once you have accepted and your new salary begins: calculate the after-tax take-home increase, open a NISA account if you do not have one, and automate investment of the incremental income. If your income situation has become complex (freelance work, side income), set up freee.\nConclusion Salary negotiation in Japan does not have to be opaque or uncomfortable — not when you use the right platform with the right strategy. doda by Persol Career gives English-speaking professionals access to Japan\u0026rsquo;s largest job market with a support structure specifically designed to handle the negotiation conversation that many candidates dread.\nThe combination of a dedicated career advisor with real market data, a scout function that surfaces premium opportunities, and a massive non-public job listing pool makes doda one of the strongest tools available for anyone serious about maximizing their compensation in a Japanese career transition.\nUnderstand your salary structure. Know your market value. Let your advisor negotiate. And when the raise arrives, invest it deliberately.\nRegister on doda and start your job search today (Affiliate link)\nThis article contains affiliate links. If you register or make a purchase through these links, we may receive a commission at no additional cost to you. We only recommend services we believe provide genuine value.\nRelated Tools Calculate your take-home pay → Salary Calculator Estimate your tax bracket → Tax Bracket Calculator Calculate your net worth → Net Worth Calculator Convert hourly wage to salary → Hourly to Salary Calculator Related Articles\nSalary Negotiation in Japan When Switching Jobs Best Job Search Sites in Japan 2026 You May Also Like How to Negotiate a Higher Salary When Switching Jobs in Japan (With Take-Home Pay Reality Check) Best Job Search Sites 2026: Where to Actually Find Good Jobs How to Calculate Your Take-Home Pay in Japan After Tax, Pension and Insurance (2026) ","permalink":"https://productivity-works.com/posts/doda-japan-salary-negotiation-english-guide/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eSwitching jobs in Japan is one of the most reliable paths to a significant salary increase — but only if you know how to negotiate. The Japanese job market has a language, a pace, and an etiquette all its own. For English-speaking professionals, navigating that process without a guide can mean leaving hundreds of thousands of yen on the table.\u003c/p\u003e\n\u003cp\u003eThis article is your practical, step-by-step guide to using \u003cstrong\u003edoda\u003c/strong\u003e — one of Japan\u0026rsquo;s largest job platforms, operated by Persol Career Co., Ltd. — to find higher-paying opportunities and negotiate compensation effectively. Whether you are an experienced bilingual professional, a mid-career foreigner working in Japan, or a recent hire looking to move up faster, this guide will walk you through everything from understanding how Japanese salaries are structured to closing a counter-offer with confidence.\u003c/p\u003e","title":"How to Use doda for Salary Negotiation in Japan: An English Guide for Career Changers (2026)"},{"content":"This article contains affiliate links.\nIncome Tax Simulator Enter your income to instantly calculate your federal income tax, state tax, social insurance, and take-home pay all at once. The progressive tax bracket breakdown is shown clearly so you can understand exactly where your money goes.\nGross Annual Income ($) Employment Type W-2 Employee Freelance / Self-Employed Business Expense Rate (%) 0%30%80% Home Office / QBI Deduction (~$6,500 equivalent) Filing Status / Dependents Single, no dependents Married filing jointly (no children) Married + 1 child Married + 2 children Marginal Tax Rate 22% Effective Tax Rate (All Taxes / Income) —% Federal Tax — State Tax — FICA / Social Ins. — Take-Home — Monthly Take-Home: — Income Breakdown Take-Home Federal Tax State Tax FICA / Social Insurance Show Federal Income Tax Brackets Rate Taxable Income Range Deduction Offset Tax in This Bracket Take-Home Pay at Different Income Levels How to Use This Tool Enter your gross income — Use your total annual compensation before any deductions Select employment type — W-2 employees get the standard deduction automatically; freelancers can enter business expense rates Select filing status — Married filing jointly and dependents increase your deductions and reduce taxable income US Federal Income Tax Brackets (2026) Taxable Income Rate Notes $0 – $11,600 10% Lowest bracket $11,600 – $47,150 12% $47,150 – $100,525 22% Most middle-class filers $100,525 – $191,950 24% $191,950 – $243,725 32% $243,725 – $609,350 35% $609,350+ 37% Top bracket State income taxes vary. An average of ~5% is used in this simulator. FICA = 6.2% Social Security + 1.45% Medicare for employees.\nHow to Reduce Your Tax Bill 1. Maximize 401(k) and IRA Contributions Traditional 401(k) and IRA contributions are pre-tax, directly reducing your taxable income. In 2026, the 401(k) limit is $23,500 (under 50) — maxing this can drop you into a lower bracket.\n2. Use an HSA (Health Savings Account) HSAs offer a triple tax advantage: contributions are pre-tax, growth is tax-free, and withdrawals for medical expenses are tax-free.\n3. Claim All Eligible Deductions Don\u0026rsquo;t miss student loan interest, mortgage interest, charitable contributions, or medical expenses above 7.5% of AGI.\n4. Freelancers: File as S-Corp When Income Is High Enough Above ~$60,000 in net self-employment income, electing S-Corp status can significantly reduce self-employment tax liability.\nRelated Tools Take-Home Pay Calculator — Quick net pay calculation from gross salary Job Change Salary Simulator — Compare taxes and take-home before and after a job change Budget Journal Generator — Allocate your take-home pay across spending categories All Productivity Works Free Tools All calculators on this site are completely free, run in your browser, and store no data whatsoever.\nThis article contains affiliate links. No additional cost to you.\n","permalink":"https://productivity-works.com/tools/income-tax-simulator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"income-tax-simulator\"\u003eIncome Tax Simulator\u003c/h1\u003e\n\u003cp\u003eEnter your income to instantly calculate your \u003cstrong\u003efederal income tax, state tax, social insurance, and take-home pay\u003c/strong\u003e all at once. The progressive tax bracket breakdown is shown clearly so you can understand exactly where your money goes.\u003c/p\u003e\n\u003cdiv id=\"st-calc\" style=\"max-width:720px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;color:#1e293b;\"\u003e\n\u003cdiv style=\"padding:24px;border:2px solid #b91c1c;border-radius:12px;background:#fef2f2;\"\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eGross Annual Income ($)\u003c/label\u003e\n\u003cinput type=\"number\" id=\"stIncome\" min=\"0\" max=\"10000000\" step=\"1000\" value=\"80000\" oninput=\"calcST()\" style=\"width:100%;padding:12px;border:1px solid #cbd5e1;border-radius:8px;font-size:18px;\"\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eEmployment Type\u003c/label\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:8px;\"\u003e\n\u003clabel style=\"display:flex;align-items:center;padding:12px;border:2px solid #b91c1c;border-radius:8px;cursor:pointer;background:#fef2f2;\" id=\"lbl-employee\"\u003e\n\u003cinput type=\"radio\" name=\"stType\" value=\"employee\" checked onchange=\"calcST()\" style=\"margin-right:8px;\"\u003e W-2 Employee\n\u003c/label\u003e\n\u003clabel style=\"display:flex;align-items:center;padding:12px;border:2px solid #e2e8f0;border-radius:8px;cursor:pointer;background:white;\" id=\"lbl-freelance\"\u003e\n\u003cinput type=\"radio\" name=\"stType\" value=\"freelance\" onchange=\"calcST()\" style=\"margin-right:8px;\"\u003e Freelance / Self-Employed\n\u003c/label\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"stFreelanceOpts\" style=\"display:none;margin-bottom:20px;padding:16px;background:white;border-radius:8px;border:1px solid #e2e8f0;\"\u003e\n\u003cdiv style=\"margin-bottom:12px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;font-size:14px;\"\u003eBusiness Expense Rate (%)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"stExpenseRate\" min=\"0\" max=\"80\" step=\"5\" value=\"30\" oninput=\"calcST()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e0%\u003c/span\u003e\u003cspan id=\"stExpenseVal\" style=\"font-weight:bold;color:#b91c1c;\"\u003e30%\u003c/span\u003e\u003cspan\u003e80%\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv\u003e\n\u003clabel style=\"display:flex;align-items:center;cursor:pointer;font-size:14px;\"\u003e\n\u003cinput type=\"checkbox\" id=\"stBlueReturn\" checked onchange=\"calcST()\" style=\"margin-right:6px;width:16px;height:16px;\"\u003e Home Office / QBI Deduction (~$6,500 equivalent)\n\u003c/label\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:8px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eFiling Status / Dependents\u003c/label\u003e\n\u003cselect id=\"stDependents\" onchange=\"calcST()\" style=\"width:100%;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:15px;\"\u003e\n\u003coption value=\"0\"\u003eSingle, no dependents\u003c/option\u003e\n\u003coption value=\"1\"\u003eMarried filing jointly (no children)\u003c/option\u003e\n\u003coption value=\"2\"\u003eMarried + 1 child\u003c/option\u003e\n\u003coption value=\"3\"\u003eMarried + 2 children\u003c/option\u003e\n\u003c/select\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Results --\u003e\n\u003cdiv id=\"stResults\" style=\"margin-top:24px;\"\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:12px;text-align:center;\"\u003e\n\u003cdiv style=\"padding:20px;background:#fef2f2;border-radius:12px;border:1px solid #fecaca;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eMarginal Tax Rate\u003c/div\u003e\n\u003cdiv id=\"stRate\" style=\"font-size:36px;font-weight:bold;color:#b91c1c;\"\u003e22%\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:20px;background:#f0fdf4;border-radius:12px;border:1px solid #bbf7d0;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eEffective Tax Rate (All Taxes / Income)\u003c/div\u003e\n\u003cdiv id=\"stEffective\" style=\"font-size:36px;font-weight:bold;color:#16a34a;\"\u003e—%\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:8px;margin-top:12px;text-align:center;\"\u003e\n\u003cdiv style=\"padding:14px;background:white;border-radius:8px;border:1px solid #e2e8f0;\"\u003e\n\u003cdiv style=\"font-size:11px;color:#64748b;\"\u003eFederal Tax\u003c/div\u003e\n\u003cdiv id=\"stTax\" style=\"font-size:18px;font-weight:bold;color:#b91c1c;\"\u003e—\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:14px;background:white;border-radius:8px;border:1px solid #e2e8f0;\"\u003e\n\u003cdiv style=\"font-size:11px;color:#64748b;\"\u003eState Tax\u003c/div\u003e\n\u003cdiv id=\"stResident\" style=\"font-size:18px;font-weight:bold;color:#ea580c;\"\u003e—\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:14px;background:white;border-radius:8px;border:1px solid #e2e8f0;\"\u003e\n\u003cdiv style=\"font-size:11px;color:#64748b;\"\u003eFICA / Social Ins.\u003c/div\u003e\n\u003cdiv id=\"stSocial\" style=\"font-size:18px;font-weight:bold;color:#7c3aed;\"\u003e—\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:14px;background:#f0fdf4;border-radius:8px;border:2px solid #16a34a;\"\u003e\n\u003cdiv style=\"font-size:11px;color:#64748b;\"\u003eTake-Home\u003c/div\u003e\n\u003cdiv id=\"stTakeHome\" style=\"font-size:18px;font-weight:bold;color:#16a34a;\"\u003e—\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-top:12px;text-align:center;padding:12px;background:white;border-radius:8px;border:1px solid #e2e8f0;\"\u003e\n\u003cspan style=\"font-size:13px;color:#64748b;\"\u003eMonthly Take-Home: \u003c/span\u003e\n\u003cspan id=\"stMonthly\" style=\"font-size:20px;font-weight:bold;color:#16a34a;\"\u003e—\u003c/span\u003e\n\u003c/div\u003e\n\u003c!-- Breakdown Bar --\u003e\n\u003cdiv style=\"margin-top:20px;padding:20px;background:#f8fafc;border-radius:12px;border:1px solid #e2e8f0;\"\u003e\n\u003ch3 style=\"margin:0 0 12px 0;font-size:16px;\"\u003eIncome Breakdown\u003c/h3\u003e\n\u003cdiv id=\"stBar\" style=\"height:32px;border-radius:8px;overflow:hidden;display:flex;margin-bottom:8px;\"\u003e\u003c/div\u003e\n\u003cdiv style=\"display:flex;flex-wrap:wrap;gap:10px;font-size:12px;\"\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#16a34a;border-radius:2px;\"\u003e\u003c/span\u003e Take-Home\u003c/span\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#b91c1c;border-radius:2px;\"\u003e\u003c/span\u003e Federal Tax\u003c/span\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#ea580c;border-radius:2px;\"\u003e\u003c/span\u003e State Tax\u003c/span\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#7c3aed;border-radius:2px;\"\u003e\u003c/span\u003e FICA / Social Insurance\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Tax Bracket Table --\u003e\n\u003cdetails style=\"margin-top:20px;\"\u003e\n\u003csummary style=\"cursor:pointer;font-weight:bold;color:#b91c1c;font-size:15px;\"\u003eShow Federal Income Tax Brackets\u003c/summary\u003e\n\u003cdiv style=\"overflow-x:auto;margin-top:12px;\"\u003e\n\u003ctable id=\"stTable\" style=\"width:100%;border-collapse:collapse;font-size:14px;\"\u003e\n\u003cthead\u003e\n\u003ctr style=\"background:#b91c1c;color:white;\"\u003e\n\u003cth style=\"padding:8px;text-align:left;\"\u003eRate\u003c/th\u003e\n\u003cth style=\"padding:8px;text-align:right;\"\u003eTaxable Income Range\u003c/th\u003e\n\u003cth style=\"padding:8px;text-align:right;\"\u003eDeduction Offset\u003c/th\u003e\n\u003cth style=\"padding:8px;text-align:right;\"\u003eTax in This Bracket\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody id=\"stTableBody\"\u003e\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\u003c/details\u003e\n\u003c!-- Income Comparison --\u003e\n\u003cdiv style=\"margin-top:20px;padding:20px;background:#fffbeb;border-radius:12px;border:1px solid #fde68a;\"\u003e\n\u003ch3 style=\"margin:0 0 12px 0;font-size:16px;\"\u003eTake-Home Pay at Different Income Levels\u003c/h3\u003e\n\u003cdiv id=\"stScenarios\" style=\"display:grid;gap:8px;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nconst US_BRACKETS=[\n  [0,11600,0.10,0],\n  [11600,47150,0.12,232],\n  [47150,100525,0.22,3479],\n  [100525,191950,0.24,5579],\n  [191950,243725,0.32,20725],\n  [243725,609350,0.35,28001],\n  [609350,Infinity,0.37,29211]\n];\n\nfunction getType(){return document.querySelector('input[name=\"stType\"]:checked').value;}\n\nfunction calcEmployeeDeduction(income){\n  // Standard deduction 2026\n  return 14600;\n}\n\nfunction calcSocialInsurance(income,type){\n  if(type==='employee'){\n    // Employee share: SS 6.2% up to $168,600 + Medicare 1.45%\n    return Math.round(Math.min(income,168600)*0.062+income*0.0145);\n  } else {\n    // Self-employed: full 15.3% (SS+Medicare), deduct half\n    return Math.round(Math.min(income,168600)*0.124+income*0.029);\n  }\n}\n\nfunction calcFederalTax(taxableIncome){\n  if(taxableIncome\u003c=0) return{tax:0,rate:0,details:[]};\n  let tax=0;let topRate=0;const details=[];\n  for(const[lo,hi,rate,deduction] of US_BRACKETS){\n    if(taxableIncome\u003c=lo) break;\n    topRate=rate;\n    const portion=Math.min(taxableIncome,hi)-lo;\n    const t=portion*rate;\n    details.push({lo,hi:Math.min(taxableIncome,hi),rate,portion,tax:t});\n  }\n  const bracket=US_BRACKETS.find(b=\u003eb[2]===topRate);\n  tax=Math.floor(taxableIncome*topRate-(bracket?bracket[3]:0));\n  return{tax:Math.max(0,tax),rate:topRate,details};\n}\n\nfunction calcStateTax(taxableIncome){\n  if(taxableIncome\u003c=0) return 0;\n  return Math.round(taxableIncome*0.05);\n}\n\nfunction calcST(){\n  const incomeRaw=parseFloat(document.getElementById('stIncome').value)||0;\n  const income=incomeRaw;\n  const type=getType();\n  const deps=parseInt(document.getElementById('stDependents').value);\n\n  ['employee','freelance'].forEach(t=\u003e{\n    document.getElementById('lbl-'+t).style.borderColor=t===type?'#b91c1c':'#e2e8f0';\n    document.getElementById('lbl-'+t).style.background=t===type?'#fef2f2':'white';\n  });\n  document.getElementById('stFreelanceOpts').style.display=type==='freelance'?'block':'none';\n\n  // Deductions\n  let deduction=0;\n  if(type==='employee'){\n    deduction=calcEmployeeDeduction(income);\n  }else{\n    const expRate=parseFloat(document.getElementById('stExpenseRate').value)/100;\n    document.getElementById('stExpenseVal').textContent=Math.round(expRate*100)+'%';\n    deduction=income*expRate;\n    if(document.getElementById('stBlueReturn').checked) deduction+=6500;\n  }\n\n  // Additional deductions for dependents\n  if(deps\u003e=1) deduction+=14600; // married filing jointly additional\n  if(deps\u003e=2) deduction+=2000;  // child tax credit equivalent\n  if(deps\u003e=3) deduction+=2000;\n\n  const earnedIncome=Math.max(0,income-deduction);\n  const social=calcSocialInsurance(income,type);\n\n  // For self-employed, deduct half of SE tax\n  let seDeduction=type==='freelance'?Math.round(social/2):0;\n  const taxableIncome=Math.max(0,earnedIncome-seDeduction);\n\n  const{tax:federalTax,rate:topRate,details}=calcFederalTax(taxableIncome);\n  const stateTax=calcStateTax(taxableIncome);\n\n  const totalTax=federalTax+stateTax+social;\n  const takeHome=income-totalTax;\n  const effectiveRate=income\u003e0?(totalTax/income)*100:0;\n\n  const fmt=v=\u003e'$'+Math.round(v).toLocaleString();\n\n  document.getElementById('stRate').textContent=Math.round(topRate*100)+'%';\n  document.getElementById('stEffective').textContent=effectiveRate.toFixed(1)+'%';\n  document.getElementById('stTax').textContent=fmt(federalTax);\n  document.getElementById('stResident').textContent=fmt(stateTax);\n  document.getElementById('stSocial').textContent=fmt(social);\n  document.getElementById('stTakeHome').textContent=fmt(Math.max(0,takeHome));\n  document.getElementById('stMonthly').textContent=fmt(Math.max(0,takeHome/12));\n\n  // Breakdown bar\n  if(income\u003e0){\n    const pTH=((Math.max(0,takeHome)/income)*100).toFixed(1);\n    const pIT=((federalTax/income)*100).toFixed(1);\n    const pRT=((stateTax/income)*100).toFixed(1);\n    const pSI=((social/income)*100).toFixed(1);\n    document.getElementById('stBar').innerHTML=\n      '\u003cdiv style=\"width:'+pTH+'%;background:#16a34a;height:100%;\" title=\"Take-Home\"\u003e\u003c/div\u003e'+\n      '\u003cdiv style=\"width:'+pIT+'%;background:#b91c1c;height:100%;\" title=\"Federal Tax\"\u003e\u003c/div\u003e'+\n      '\u003cdiv style=\"width:'+pRT+'%;background:#ea580c;height:100%;\" title=\"State Tax\"\u003e\u003c/div\u003e'+\n      '\u003cdiv style=\"width:'+pSI+'%;background:#7c3aed;height:100%;\" title=\"FICA\"\u003e\u003c/div\u003e';\n  }\n\n  // Bracket table\n  let tbody='';\n  US_BRACKETS.forEach((b,i)=\u003e{\n    const[lo,hi,rate,ded]=b;\n    const active=taxableIncome\u003elo;\n    const current=taxableIncome\u003elo\u0026\u0026(hi===Infinity||taxableIncome\u003c=hi);\n    const portion=active?Math.min(taxableIncome,hi)-lo:0;\n    const t=portion*rate;\n    const bg=current?'#fef2f2':i%2===0?'#f8fafc':'white';\n    const hiStr=hi===Infinity?'+':'–$'+Math.round(hi).toLocaleString();\n    const arrow=current?' \u0026larr;':'';\n    tbody+='\u003ctr style=\"background:'+bg+';'+(current?'font-weight:bold;':'')+'\"\u003e';\n    tbody+='\u003ctd style=\"padding:8px;\"\u003e'+Math.round(rate*100)+'%'+arrow+'\u003c/td\u003e';\n    tbody+='\u003ctd style=\"padding:8px;text-align:right;\"\u003e$'+Math.round(lo).toLocaleString()+hiStr+'\u003c/td\u003e';\n    tbody+='\u003ctd style=\"padding:8px;text-align:right;\"\u003e$'+Math.round(ded).toLocaleString()+'\u003c/td\u003e';\n    tbody+='\u003ctd style=\"padding:8px;text-align:right;'+(active?'':'color:#94a3b8;')+'\"\u003e$'+Math.round(t).toLocaleString()+'\u003c/td\u003e';\n    tbody+='\u003c/tr\u003e';\n  });\n  document.getElementById('stTableBody').innerHTML=tbody;\n\n  // Income comparison scenarios\n  const scenarios=[30000,50000,70000,90000,120000,150000,200000,300000];\n  let sHTML='';\n  const expRate=type==='freelance'?(parseFloat(document.getElementById('stExpenseRate').value)/100):0;\n  const blueReturn=type==='freelance'\u0026\u0026document.getElementById('stBlueReturn').checked;\n  scenarios.forEach(s=\u003e{\n    const sDed=type==='employee'?calcEmployeeDeduction(s):s*expRate+(blueReturn?6500:0);\n    const depDed=(deps\u003e=1?14600:0)+(deps\u003e=2?2000:0)+(deps\u003e=3?2000:0);\n    const sEarned=Math.max(0,s-sDed-depDed);\n    const sSocial=calcSocialInsurance(s,type);\n    const sSEDed=type==='freelance'?Math.round(sSocial/2):0;\n    const sTaxable=Math.max(0,sEarned-sSEDed);\n    const sFederal=calcFederalTax(sTaxable).tax;\n    const sState=calcStateTax(sTaxable);\n    const sTH=s-sFederal-sState-sSocial;\n    const current=s===income;\n    sHTML+='\u003cdiv style=\"display:flex;justify-content:space-between;align-items:center;padding:10px 12px;background:'+(current?'#fef2f2':'white')+';border-radius:8px;border:'+(current?'2px solid #b91c1c':'1px solid #e2e8f0')+';font-size:14px;\"\u003e';\n    sHTML+='\u003cdiv\u003e\u003cstrong\u003e$'+s.toLocaleString()+'\u003c/strong\u003e\u003c/div\u003e';\n    sHTML+='\u003cdiv style=\"text-align:right;\"\u003eTake-Home \u003cstrong style=\"color:#16a34a;\"\u003e$'+Math.max(0,Math.round(sTH)).toLocaleString()+'\u003c/strong\u003e ('+((Math.max(0,sTH)/s)*100).toFixed(0)+'%)\u003c/div\u003e';\n    sHTML+='\u003c/div\u003e';\n  });\n  document.getElementById('stScenarios').innerHTML=sHTML;\n}\n\ncalcST();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-to-use-this-tool\"\u003eHow to Use This Tool\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eEnter your gross income\u003c/strong\u003e — Use your total annual compensation before any deductions\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSelect employment type\u003c/strong\u003e — W-2 employees get the standard deduction automatically; freelancers can enter business expense rates\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSelect filing status\u003c/strong\u003e — Married filing jointly and dependents increase your deductions and reduce taxable income\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch2 id=\"us-federal-income-tax-brackets-2026\"\u003eUS Federal Income Tax Brackets (2026)\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eTaxable Income\u003c/th\u003e\n          \u003cth\u003eRate\u003c/th\u003e\n          \u003cth\u003eNotes\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e$0 – $11,600\u003c/td\u003e\n          \u003ctd\u003e10%\u003c/td\u003e\n          \u003ctd\u003eLowest bracket\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e$11,600 – $47,150\u003c/td\u003e\n          \u003ctd\u003e12%\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e$47,150 – $100,525\u003c/td\u003e\n          \u003ctd\u003e22%\u003c/td\u003e\n          \u003ctd\u003eMost middle-class filers\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e$100,525 – $191,950\u003c/td\u003e\n          \u003ctd\u003e24%\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e$191,950 – $243,725\u003c/td\u003e\n          \u003ctd\u003e32%\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e$243,725 – $609,350\u003c/td\u003e\n          \u003ctd\u003e35%\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e$609,350+\u003c/td\u003e\n          \u003ctd\u003e37%\u003c/td\u003e\n          \u003ctd\u003eTop bracket\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e\u003cem\u003eState income taxes vary. An average of ~5% is used in this simulator. FICA = 6.2% Social Security + 1.45% Medicare for employees.\u003c/em\u003e\u003c/p\u003e","title":"Income Tax Simulator | Calculate Federal Tax, State Tax \u0026 Take-Home Pay Free"},{"content":"Disclosure: Some links on this page may be affiliate links. We may earn a commission at no extra cost to you if you make a purchase through these links. We only recommend tools and resources we genuinely believe in.\nInflation Calculator Inflation silently erodes the value of your money every year. Whether you want to see what a sum of money will be worth in the future, or understand what a price from the past equals in today\u0026rsquo;s dollars, this calculator gives you a clear, honest picture of purchasing power over time.\nFuture Value Past Value (Historical CPI) Future Value Settings \u0026lt;div style=\u0026quot;margin-bottom: 1.25rem;\u0026quot;\u0026gt; \u0026lt;label style=\u0026quot;display:block; font-weight:600; margin-bottom:6px; color:#44403c;\u0026quot;\u0026gt; Current Amount \u0026lt;/label\u0026gt; \u0026lt;div style=\u0026quot;display:flex; align-items:center; gap:8px;\u0026quot;\u0026gt; \u0026lt;span style=\u0026quot;font-size:1.2rem; color:#d97706; font-weight:700;\u0026quot;\u0026gt;$\u0026lt;/span\u0026gt; \u0026lt;input id=\u0026quot;fv-amount\u0026quot; type=\u0026quot;number\u0026quot; value=\u0026quot;1000\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;10000000\u0026quot; step=\u0026quot;1\u0026quot; oninput=\u0026quot;calcFuture()\u0026quot; style=\u0026quot;width:160px; padding:8px 12px; border:1.5px solid #fcd34d; border-radius:7px; font-size:1rem; background:#fff; color:#1c1917; outline:none;\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div style=\u0026quot;margin-bottom: 1.25rem;\u0026quot;\u0026gt; \u0026lt;label style=\u0026quot;display:flex; justify-content:space-between; font-weight:600; margin-bottom:6px; color:#44403c;\u0026quot;\u0026gt; \u0026lt;span\u0026gt;Annual Inflation Rate\u0026lt;/span\u0026gt; \u0026lt;span id=\u0026quot;fv-rate-display\u0026quot; style=\u0026quot;color:#d97706;\u0026quot;\u0026gt;3.0%\u0026lt;/span\u0026gt; \u0026lt;/label\u0026gt; \u0026lt;input id=\u0026quot;fv-rate\u0026quot; type=\u0026quot;range\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;10\u0026quot; step=\u0026quot;0.5\u0026quot; value=\u0026quot;3\u0026quot; oninput=\u0026quot;document.getElementById('fv-rate-display').textContent=parseFloat(this.value).toFixed(1)+'%'; calcFuture();\u0026quot; style=\u0026quot;width:100%; accent-color:#d97706; cursor:pointer;\u0026quot;\u0026gt; \u0026lt;div style=\u0026quot;display:flex; justify-content:space-between; font-size:0.75rem; color:#a8a29e; margin-top:3px;\u0026quot;\u0026gt; \u0026lt;span\u0026gt;1%\u0026lt;/span\u0026gt;\u0026lt;span\u0026gt;5%\u0026lt;/span\u0026gt;\u0026lt;span\u0026gt;10%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div\u0026gt; \u0026lt;label style=\u0026quot;display:flex; justify-content:space-between; font-weight:600; margin-bottom:6px; color:#44403c;\u0026quot;\u0026gt; \u0026lt;span\u0026gt;Time Period\u0026lt;/span\u0026gt; \u0026lt;span id=\u0026quot;fv-years-display\u0026quot; style=\u0026quot;color:#d97706;\u0026quot;\u0026gt;10 years\u0026lt;/span\u0026gt; \u0026lt;/label\u0026gt; \u0026lt;input id=\u0026quot;fv-years\u0026quot; type=\u0026quot;range\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;40\u0026quot; step=\u0026quot;1\u0026quot; value=\u0026quot;10\u0026quot; oninput=\u0026quot;document.getElementById('fv-years-display').textContent=this.value+' year'+(this.value==1?'':'s'); calcFuture();\u0026quot; style=\u0026quot;width:100%; accent-color:#d97706; cursor:pointer;\u0026quot;\u0026gt; \u0026lt;div style=\u0026quot;display:flex; justify-content:space-between; font-size:0.75rem; color:#a8a29e; margin-top:3px;\u0026quot;\u0026gt; \u0026lt;span\u0026gt;1 yr\u0026lt;/span\u0026gt;\u0026lt;span\u0026gt;20 yrs\u0026lt;/span\u0026gt;\u0026lt;span\u0026gt;40 yrs\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Future Results --\u0026gt; \u0026lt;div id=\u0026quot;fv-results\u0026quot; style=\u0026quot;background:#fff; border:1.5px solid #fde68a; border-radius:12px; padding:1.5rem; margin-bottom:1.5rem;\u0026quot;\u0026gt; \u0026lt;h2 style=\u0026quot;margin:0 0 1rem; font-size:1.05rem; color:#92400e;\u0026quot;\u0026gt;Results\u0026lt;/h2\u0026gt; \u0026lt;div style=\u0026quot;display:grid; grid-template-columns:repeat(auto-fit,minmax(190px,1fr)); gap:1rem; margin-bottom:1.25rem;\u0026quot;\u0026gt; \u0026lt;div style=\u0026quot;background:#fffbeb; border-radius:9px; padding:1rem; text-align:center;\u0026quot;\u0026gt; \u0026lt;div style=\u0026quot;font-size:0.8rem; color:#a8a29e; margin-bottom:4px;\u0026quot;\u0026gt;Future Equivalent Price\u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;fv-future-price\u0026quot; style=\u0026quot;font-size:1.6rem; font-weight:700; color:#d97706;\u0026quot;\u0026gt;$1,344\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div style=\u0026quot;background:#fffbeb; border-radius:9px; padding:1rem; text-align:center;\u0026quot;\u0026gt; \u0026lt;div style=\u0026quot;font-size:0.8rem; color:#a8a29e; margin-bottom:4px;\u0026quot;\u0026gt;Purchasing Power Lost\u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;fv-power-lost\u0026quot; style=\u0026quot;font-size:1.6rem; font-weight:700; color:#dc2626;\u0026quot;\u0026gt;25.6%\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div style=\u0026quot;background:#fffbeb; border-radius:9px; padding:1rem; text-align:center;\u0026quot;\u0026gt; \u0026lt;div style=\u0026quot;font-size:0.8rem; color:#a8a29e; margin-bottom:4px;\u0026quot;\u0026gt;Needed to Maintain Power\u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;fv-maintain\u0026quot; style=\u0026quot;font-size:1.6rem; font-weight:700; color:#16a34a;\u0026quot;\u0026gt;$1,344\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Visual Bar --\u0026gt; \u0026lt;div style=\u0026quot;margin-bottom:0.5rem;\u0026quot;\u0026gt; \u0026lt;div style=\u0026quot;display:flex; justify-content:space-between; font-size:0.8rem; color:#78716c; margin-bottom:6px;\u0026quot;\u0026gt; \u0026lt;span\u0026gt;Today's $1,000\u0026lt;/span\u0026gt; \u0026lt;span id=\u0026quot;fv-bar-label\u0026quot;\u0026gt;Future purchasing power\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div style=\u0026quot;background:#e5e7eb; border-radius:99px; height:22px; overflow:hidden; position:relative;\u0026quot;\u0026gt; \u0026lt;div id=\u0026quot;fv-bar-fill\u0026quot; style=\u0026quot;height:100%; background:linear-gradient(90deg,#d97706,#f59e0b); border-radius:99px; transition:width 0.4s; width:74.4%; display:flex; align-items:center; padding-left:10px;\u0026quot;\u0026gt; \u0026lt;span id=\u0026quot;fv-bar-pct\u0026quot; style=\u0026quot;font-size:0.75rem; font-weight:700; color:#fff;\u0026quot;\u0026gt;74.4%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div style=\u0026quot;font-size:0.75rem; color:#a8a29e; margin-top:4px;\u0026quot;\u0026gt; Your money retains \u0026lt;span id=\u0026quot;fv-retain-pct\u0026quot; style=\u0026quot;color:#d97706; font-weight:600;\u0026quot;\u0026gt;74.4%\u0026lt;/span\u0026gt; of today's purchasing power after \u0026lt;span id=\u0026quot;fv-retain-years\u0026quot;\u0026gt;10\u0026lt;/span\u0026gt; years at \u0026lt;span id=\u0026quot;fv-retain-rate\u0026quot;\u0026gt;3.0%\u0026lt;/span\u0026gt; inflation. \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Historical CPI Settings \u0026lt;div style=\u0026quot;margin-bottom:1.25rem;\u0026quot;\u0026gt; \u0026lt;label style=\u0026quot;display:block; font-weight:600; margin-bottom:6px; color:#44403c;\u0026quot;\u0026gt; Amount in Past Year \u0026lt;/label\u0026gt; \u0026lt;div style=\u0026quot;display:flex; align-items:center; gap:8px;\u0026quot;\u0026gt; \u0026lt;span style=\u0026quot;font-size:1.2rem; color:#d97706; font-weight:700;\u0026quot;\u0026gt;$\u0026lt;/span\u0026gt; \u0026lt;input id=\u0026quot;pv-amount\u0026quot; type=\u0026quot;number\u0026quot; value=\u0026quot;1000\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;10000000\u0026quot; step=\u0026quot;1\u0026quot; oninput=\u0026quot;calcPast()\u0026quot; style=\u0026quot;width:160px; padding:8px 12px; border:1.5px solid #fcd34d; border-radius:7px; font-size:1rem; background:#fff; color:#1c1917; outline:none;\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div\u0026gt; \u0026lt;label style=\u0026quot;display:flex; justify-content:space-between; font-weight:600; margin-bottom:6px; color:#44403c;\u0026quot;\u0026gt; \u0026lt;span\u0026gt;Year\u0026lt;/span\u0026gt; \u0026lt;span id=\u0026quot;pv-year-display\u0026quot; style=\u0026quot;color:#d97706;\u0026quot;\u0026gt;1990\u0026lt;/span\u0026gt; \u0026lt;/label\u0026gt; \u0026lt;input id=\u0026quot;pv-year\u0026quot; type=\u0026quot;range\u0026quot; min=\u0026quot;1960\u0026quot; max=\u0026quot;2025\u0026quot; step=\u0026quot;1\u0026quot; value=\u0026quot;1990\u0026quot; oninput=\u0026quot;document.getElementById('pv-year-display').textContent=this.value; calcPast();\u0026quot; style=\u0026quot;width:100%; accent-color:#d97706; cursor:pointer;\u0026quot;\u0026gt; \u0026lt;div style=\u0026quot;display:flex; justify-content:space-between; font-size:0.75rem; color:#a8a29e; margin-top:3px;\u0026quot;\u0026gt; \u0026lt;span\u0026gt;1960\u0026lt;/span\u0026gt;\u0026lt;span\u0026gt;1990\u0026lt;/span\u0026gt;\u0026lt;span\u0026gt;2025\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Past Results --\u0026gt; \u0026lt;div id=\u0026quot;pv-results\u0026quot; style=\u0026quot;background:#fff; border:1.5px solid #fde68a; border-radius:12px; padding:1.5rem; margin-bottom:1.5rem;\u0026quot;\u0026gt; \u0026lt;h2 style=\u0026quot;margin:0 0 1rem; font-size:1.05rem; color:#92400e;\u0026quot;\u0026gt;Results\u0026lt;/h2\u0026gt; \u0026lt;div style=\u0026quot;display:grid; grid-template-columns:repeat(auto-fit,minmax(190px,1fr)); gap:1rem; margin-bottom:1.25rem;\u0026quot;\u0026gt; \u0026lt;div style=\u0026quot;background:#fffbeb; border-radius:9px; padding:1rem; text-align:center;\u0026quot;\u0026gt; \u0026lt;div style=\u0026quot;font-size:0.8rem; color:#a8a29e; margin-bottom:4px;\u0026quot;\u0026gt;Equivalent in Today's Dollars\u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;pv-today-value\u0026quot; style=\u0026quot;font-size:1.6rem; font-weight:700; color:#d97706;\u0026quot;\u0026gt;$2,131\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div style=\u0026quot;background:#fffbeb; border-radius:9px; padding:1rem; text-align:center;\u0026quot;\u0026gt; \u0026lt;div style=\u0026quot;font-size:0.8rem; color:#a8a29e; margin-bottom:4px;\u0026quot;\u0026gt;Total Inflation Since \u0026lt;span id=\u0026quot;pv-since-year-label\u0026quot;\u0026gt;1990\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;pv-total-inflation\u0026quot; style=\u0026quot;font-size:1.6rem; font-weight:700; color:#dc2626;\u0026quot;\u0026gt;113.1%\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div style=\u0026quot;background:#fffbeb; border-radius:9px; padding:1rem; text-align:center;\u0026quot;\u0026gt; \u0026lt;div style=\u0026quot;font-size:0.8rem; color:#a8a29e; margin-bottom:4px;\u0026quot;\u0026gt;Avg Annual Rate\u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;pv-avg-rate\u0026quot; style=\u0026quot;font-size:1.6rem; font-weight:700; color:#78716c;\u0026quot;\u0026gt;3.2%\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div style=\u0026quot;font-size:0.9rem; color:#78716c; background:#fffbeb; border-radius:8px; padding:0.75rem 1rem;\u0026quot;\u0026gt; \u0026lt;strong style=\u0026quot;color:#92400e;\u0026quot;\u0026gt;Note:\u0026lt;/strong\u0026gt; Based on approximate US CPI data. Values are estimates for educational purposes. \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Year-by-Year Value Erosion Show All Years Year Value of Original $ Purchasing Power Power Lost Beat Inflation: Required Investment Return To maintain your purchasing power, your investments need to earn at least as much as the inflation rate. To actually grow your wealth in real terms, you need to exceed it. Break Even (keep up) 3.0% annual return Modest Growth (+2%) 5.0% annual return Strong Growth (+5%) 8.0% annual return Want to see how compound growth can outpace inflation? Try the Compound Interest Calculator \u0026rarr; How to Protect Against Inflation Inflation is a permanent feature of modern economies. Here are four evidence-based strategies to protect your purchasing power:\n1. Invest in assets that historically outpace inflation. Broad stock market index funds have historically returned around 7–10% annually before inflation adjustments — well above the long-run US inflation rate of roughly 3%. Holding cash long-term is a guaranteed losing strategy in real terms.\n2. Consider I Bonds and TIPS. US Treasury Inflation-Protected Securities (TIPS) and Series I Savings Bonds are explicitly designed to rise with inflation. They won\u0026rsquo;t make you rich, but they preserve purchasing power with low risk — especially useful for emergency funds or short-term savings.\n3. Real assets provide a natural hedge. Real estate, commodities, and infrastructure tend to rise in nominal value alongside inflation, though they carry their own risks and illiquidity. REITs (Real Estate Investment Trusts) offer exposure without direct ownership.\n4. Avoid holding large amounts in low-yield savings accounts. Even a high-yield savings account at 4–5% barely keeps pace with moderate inflation. Money that won\u0026rsquo;t be needed for 5+ years should generally be invested, not saved. Review your allocation regularly as interest rates change.\nRelated Tools Calculate compound interest: Compound Interest Calculator Plan for retirement: Retirement Calculator Plan for early retirement: FIRE Calculator Calculate your net worth: Net Worth Calculator ","permalink":"https://productivity-works.com/tools/inflation-calculator/","summary":"\u003cp\u003e\u003cem\u003eDisclosure: Some links on this page may be affiliate links. We may earn a commission at no extra cost to you if you make a purchase through these links. We only recommend tools and resources we genuinely believe in.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"inflation-calculator\"\u003eInflation Calculator\u003c/h1\u003e\n\u003cp\u003eInflation silently erodes the value of your money every year. Whether you want to see what a sum of money will be worth in the future, or understand what a price from the past equals in today\u0026rsquo;s dollars, this calculator gives you a clear, honest picture of purchasing power over time.\u003c/p\u003e","title":"Inflation Calculator | See How Prices Change Over Time"},{"content":"This article contains affiliate links.\nJob Change Salary Simulator Enter your current and prospective salaries to automatically calculate the change in take-home pay, tax and insurance comparison, and lifetime earnings difference.\nCurrent Salary Annual Salary ($) $60,000 New Salary (After Job Change) Annual Salary ($) $75,000 Current Age 223059 Lifetime Earnings Simulation How Much Does Changing Jobs Actually Increase Salary? Research consistently shows that switching jobs is one of the most effective ways to increase compensation, with typical salary jumps of 10–20% for mid-career professionals. However, because higher income means higher taxes and insurance contributions, comparing on a take-home basis is what truly matters — which is exactly what this simulator shows.\nTips to Maximize Your Salary When Changing Jobs Know your market value — Use salary databases and recruiter outreach to benchmark your worth Quantify your achievements — Express accomplishments in numbers (revenue generated, costs cut, etc.) Run multiple processes in parallel — Compare at least 3 offers before deciding Factor in total compensation — Remote work, PTO, benefits, equity, and work hours all affect real value Related Tools Take-Home Pay Calculator — Calculate net pay from gross salary in detail Income Tax Simulator — See how taxes are calculated at any income level FIRE Retirement Simulator — Calculate years to financial independence ","permalink":"https://productivity-works.com/tools/job-change-salary-simulator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"job-change-salary-simulator\"\u003eJob Change Salary Simulator\u003c/h1\u003e\n\u003cp\u003eEnter your current and prospective salaries to automatically calculate the \u003cstrong\u003echange in take-home pay\u003c/strong\u003e, \u003cstrong\u003etax and insurance comparison\u003c/strong\u003e, and \u003cstrong\u003elifetime earnings difference\u003c/strong\u003e.\u003c/p\u003e\n\u003cdiv id=\"tc-calc\" style=\"max-width:680px;margin:0 auto;font-family:system-ui,sans-serif;\"\u003e\n\u003cdiv style=\"padding:24px;border:2px solid #2563eb;border-radius:12px;background:#f8fafc;\"\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:20px;\"\u003e\n\u003cdiv\u003e\n\u003ch3 style=\"margin:0 0 12px;color:#64748b;font-size:14px;text-align:center;\"\u003eCurrent Salary\u003c/h3\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:13px;\"\u003eAnnual Salary ($)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"curSalary\" min=\"20000\" max=\"300000\" step=\"1000\" value=\"60000\" oninput=\"calcTC()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"text-align:center;font-weight:bold;font-size:22px;color:#64748b;\" id=\"curSalVal\"\u003e$60,000\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv\u003e\n\u003ch3 style=\"margin:0 0 12px;color:#2563eb;font-size:14px;text-align:center;\"\u003eNew Salary (After Job Change)\u003c/h3\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:13px;\"\u003eAnnual Salary ($)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"newSalary\" min=\"20000\" max=\"300000\" step=\"1000\" value=\"75000\" oninput=\"calcTC()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"text-align:center;font-weight:bold;font-size:22px;color:#2563eb;\" id=\"newSalVal\"\u003e$75,000\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:13px;\"\u003eCurrent Age\u003c/label\u003e\n\u003cinput type=\"range\" id=\"age\" min=\"22\" max=\"59\" step=\"1\" value=\"30\" oninput=\"calcTC()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e22\u003c/span\u003e\u003cspan id=\"ageVal\" style=\"font-weight:bold;font-size:16px;color:#2563eb;\"\u003e30\u003c/span\u003e\u003cspan\u003e59\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"tcResult\" style=\"margin-top:20px;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"tcCompare\" style=\"margin-top:24px;padding:24px;border:2px solid #10b981;border-radius:12px;background:#f0fdf4;\"\u003e\u003c/div\u003e\n\u003cdiv style=\"margin-top:24px;padding:24px;border:2px solid #f59e0b;border-radius:12px;background:#fffbeb;\"\u003e\n\u003ch3 style=\"margin:0 0 12px;color:#92400e;font-size:16px;\"\u003eLifetime Earnings Simulation\u003c/h3\u003e\n\u003cdiv id=\"tcLifetime\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nfunction fmt(n){return Math.round(n).toLocaleString('en-US');}\n\nfunction calcTax(annual){\n  // US Federal income tax (2026 single filer brackets, approximate)\n  var income=annual;\n  var standardDeduction=14600;\n  var taxableIncome=Math.max(0,income-standardDeduction);\n\n  var incomeTax=0;\n  if(taxableIncome\u003c=11600) incomeTax=taxableIncome*0.10;\n  else if(taxableIncome\u003c=47150) incomeTax=1160+( taxableIncome-11600)*0.12;\n  else if(taxableIncome\u003c=100525) incomeTax=5426+(taxableIncome-47150)*0.22;\n  else if(taxableIncome\u003c=191950) incomeTax=17169+(taxableIncome-100525)*0.24;\n  else if(taxableIncome\u003c=243725) incomeTax=39111+(taxableIncome-191950)*0.32;\n  else if(taxableIncome\u003c=609350) incomeTax=55679+(taxableIncome-243725)*0.35;\n  else incomeTax=183647+(taxableIncome-609350)*0.37;\n\n  // State income tax (approximate average ~5%)\n  var stateTax=Math.max(0,taxableIncome*0.05);\n\n  // FICA (Social Security 6.2% up to $168,600 + Medicare 1.45%)\n  var socialIns=Math.min(income,168600)*0.062+income*0.0145;\n\n  var totalDeductions=incomeTax+stateTax+socialIns;\n  var takeHome=income-totalDeductions;\n\n  return {\n    gross:income,\n    incomeTax:Math.max(0,incomeTax),\n    residentTax:Math.max(0,stateTax),\n    socialIns:socialIns,\n    totalDeductions:totalDeductions,\n    takeHome:Math.max(0,takeHome)\n  };\n}\n\nfunction calcTC(){\n  var cur=+document.getElementById('curSalary').value;\n  var nw=+document.getElementById('newSalary').value;\n  var age=+document.getElementById('age').value;\n\n  document.getElementById('curSalVal').textContent='$'+fmt(cur);\n  document.getElementById('newSalVal').textContent='$'+fmt(nw);\n  document.getElementById('ageVal').textContent=age;\n\n  var curT=calcTax(cur);\n  var newT=calcTax(nw);\n  var diff=nw-cur;\n  var pct=cur\u003e0?((nw-cur)/cur*100):0;\n  var takeHomeDiff=newT.takeHome-curT.takeHome;\n\n  // Result summary\n  var color=diff\u003e=0?'#10b981':'#ef4444';\n  var arrow=diff\u003e=0?'+':'';\n  var html='\u003cdiv style=\"text-align:center;padding:16px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e';\n  html+='\u003cdiv style=\"font-size:13px;color:#64748b;\"\u003eSalary Change\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:32px;font-weight:bold;color:'+color+';\"\u003e'+arrow+'$'+fmt(Math.abs(diff))+' \u003cspan style=\"font-size:18px;\"\u003e('+arrow+pct.toFixed(1)+'%)\u003c/span\u003e\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:13px;color:#64748b;margin-top:4px;\"\u003eTake-Home Change: \u003cstrong style=\"color:'+color+';\"\u003e'+arrow+'$'+fmt(Math.round(takeHomeDiff))+'/year\u003c/strong\u003e \u0026nbsp; ($'+arrow+fmt(Math.round(takeHomeDiff/12))+'/month)\u003c/div\u003e';\n  html+='\u003c/div\u003e';\n  document.getElementById('tcResult').innerHTML=html;\n\n  // Comparison table\n  var chtml='\u003ch3 style=\"margin:0 0 12px;color:#166534;font-size:16px;\"\u003eTax \u0026 Insurance Comparison (Annual)\u003c/h3\u003e';\n  chtml+='\u003ctable style=\"width:100%;border-collapse:collapse;font-size:14px;\"\u003e';\n  chtml+='\u003ctr style=\"border-bottom:2px solid #10b981;\"\u003e\u003cth style=\"text-align:left;padding:8px;\"\u003eItem\u003c/th\u003e\u003cth style=\"text-align:right;padding:8px;\"\u003eCurrent\u003c/th\u003e\u003cth style=\"text-align:right;padding:8px;\"\u003eAfter Change\u003c/th\u003e\u003cth style=\"text-align:right;padding:8px;\"\u003eDifference\u003c/th\u003e\u003c/tr\u003e';\n\n  var rows=[\n    ['Gross Salary',curT.gross,newT.gross],\n    ['Federal Income Tax',curT.incomeTax,newT.incomeTax],\n    ['State Income Tax',curT.residentTax,newT.residentTax],\n    ['FICA / Social Insurance',curT.socialIns,newT.socialIns],\n    ['Take-Home Pay',curT.takeHome,newT.takeHome]\n  ];\n\n  for(var i=0;i\u003crows.length;i++){\n    var bg=i%2===0?'#fff':'#f0fdf4';\n    var bold=i===rows.length-1?'font-weight:bold;':'';\n    var d=rows[i][2]-rows[i][1];\n    var dc=d\u003e=0?'#10b981':'#ef4444';\n    var ds=d\u003e=0?'+':'';\n    chtml+='\u003ctr style=\"background:'+bg+';'+bold+'\"\u003e';\n    chtml+='\u003ctd style=\"padding:8px;\"\u003e'+rows[i][0]+'\u003c/td\u003e';\n    chtml+='\u003ctd style=\"padding:8px;text-align:right;\"\u003e$'+fmt(Math.round(rows[i][1]))+'\u003c/td\u003e';\n    chtml+='\u003ctd style=\"padding:8px;text-align:right;\"\u003e$'+fmt(Math.round(rows[i][2]))+'\u003c/td\u003e';\n    chtml+='\u003ctd style=\"padding:8px;text-align:right;color:'+dc+';\"\u003e'+ds+'$'+fmt(Math.round(Math.abs(d)))+'\u003c/td\u003e';\n    chtml+='\u003c/tr\u003e';\n  }\n  chtml+='\u003c/table\u003e';\n\n  var curRate=(curT.takeHome/curT.gross*100).toFixed(1);\n  var newRate=(newT.takeHome/newT.gross*100).toFixed(1);\n  chtml+='\u003cdiv style=\"margin-top:12px;text-align:center;font-size:13px;color:#64748b;\"\u003eTake-home rate: Current \u003cstrong\u003e'+curRate+'%\u003c/strong\u003e \u0026rarr; After Change \u003cstrong\u003e'+newRate+'%\u003c/strong\u003e\u003c/div\u003e';\n  document.getElementById('tcCompare').innerHTML=chtml;\n\n  // Lifetime income\n  var yearsToRetire=65-age;\n  if(yearsToRetire\u003c1)yearsToRetire=1;\n  var curLifetime=curT.takeHome*yearsToRetire;\n  var newLifetime=newT.takeHome*yearsToRetire;\n  var lifetimeDiff=newLifetime-curLifetime;\n  var lcolor=lifetimeDiff\u003e=0?'#10b981':'#ef4444';\n  var larrow=lifetimeDiff\u003e=0?'+':'';\n\n  var lhtml='\u003cdiv style=\"text-align:center;margin-bottom:16px;\"\u003e';\n  lhtml+='\u003cdiv style=\"font-size:13px;color:#64748b;\"\u003eCumulative take-home difference to age 65 ('+yearsToRetire+' years)\u003c/div\u003e';\n  lhtml+='\u003cdiv style=\"font-size:28px;font-weight:bold;color:'+lcolor+';\"\u003e'+larrow+'$'+fmt(Math.round(Math.abs(lifetimeDiff)))+'\u003c/div\u003e';\n  lhtml+='\u003c/div\u003e';\n\n  lhtml+='\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:12px;text-align:center;\"\u003e';\n  lhtml+='\u003cdiv style=\"padding:12px;background:#fff;border-radius:8px;\"\u003e\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eStay Current\u003c/div\u003e\u003cdiv style=\"font-size:18px;font-weight:bold;\"\u003e$'+fmt(Math.round(curLifetime))+'\u003c/div\u003e\u003c/div\u003e';\n  lhtml+='\u003cdiv style=\"padding:12px;background:#fff;border-radius:8px;\"\u003e\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eAfter Job Change\u003c/div\u003e\u003cdiv style=\"font-size:18px;font-weight:bold;color:#2563eb;\"\u003e$'+fmt(Math.round(newLifetime))+'\u003c/div\u003e\u003c/div\u003e';\n  lhtml+='\u003c/div\u003e';\n\n  lhtml+='\u003cdiv style=\"margin-top:12px;font-size:12px;color:#92400e;text-align:center;\"\u003e* Approximation. Raises, retirement benefits, inflation and investment returns are not included.\u003c/div\u003e';\n  document.getElementById('tcLifetime').innerHTML=lhtml;\n}\n\ndocument.addEventListener('DOMContentLoaded',function(){calcTC();});\ncalcTC();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-much-does-changing-jobs-actually-increase-salary\"\u003eHow Much Does Changing Jobs Actually Increase Salary?\u003c/h2\u003e\n\u003cp\u003eResearch consistently shows that switching jobs is one of the most effective ways to increase compensation, with typical salary jumps of 10–20% for mid-career professionals. However, because higher income means higher taxes and insurance contributions, \u003cstrong\u003ecomparing on a take-home basis\u003c/strong\u003e is what truly matters — which is exactly what this simulator shows.\u003c/p\u003e","title":"Job Change Salary Simulator | Compare Take-Home Pay, Taxes \u0026 Lifetime Earnings"},{"content":"This article contains affiliate links.\nJapan has one of the highest life insurance penetration rates in the world. Walk into any convenience store, and there is a chance the person ahead of you is also paying a life insurance premium through their bank account. Life insurance is deeply woven into the financial culture here — pushed aggressively by door-to-door agents, embedded in employer benefits packages, and marketed relentlessly at new parents and newlyweds.\nFor expats and foreign residents, this creates a specific kind of confusion. Should you buy into a system you may not fully understand, in a language you may not read fluently, for a product whose value depends heavily on your personal circumstances? Or should you assume Japan\u0026rsquo;s famously comprehensive public health system already has you covered?\nThe honest answer is: it depends — and the details matter a lot.\nThis guide cuts through the marketing noise. It explains what types of insurance exist in Japan, what the national health system already covers (more than most people realise), and how to think clearly about whether private life or medical insurance actually makes sense for your situation in 2026.\n1. Japan\u0026rsquo;s Insurance Culture — And Why Expats Feel Lost Japan\u0026rsquo;s life insurance industry is enormous. According to industry data, Japan consistently ranks among the top three countries globally for life insurance premium volume, and the per-capita spend on insurance is among the highest anywhere. This is partly cultural — life insurance was historically sold as a form of savings and social obligation, not just risk management — and partly structural, driven by a dense network of commissioned agents who visit homes and workplaces.\nThe result is that many Japanese households are significantly over-insured: paying premiums for products they do not fully understand and coverage they may never use.\nExpats face a compounded challenge. Insurance documents are dense even in English. In Japanese, they are a maze of technical vocabulary, contractual fine print, and actuarial tables that even fluent speakers find opaque. Many foreign residents default to one of two extremes: buying whatever their employer or a helpful agent suggests, or buying nothing at all.\nNeither extreme is usually right. The goal of this guide is to give you the framework to make a deliberate, informed choice — not to sell you something you do not need.\n2. Types of Private Insurance in Japan Before you can decide what you need, you need to understand what is actually available. Japanese private insurance broadly falls into five categories:\nSeimei Hoken (生命保険) — Life Insurance This pays a lump sum or income stream to your named beneficiaries if you die. It comes in several forms:\nTerm life (定期保険, teiki hoken): Covers a fixed period (e.g., 10 or 20 years). Premiums are relatively low. No cash value if you outlive the term. This is the most practical option for most people with dependents. Whole life (終身保険, shūshin hoken): Covers you until death, whenever that occurs, and builds a cash surrender value over time. Premiums are significantly higher. Often marketed as a savings product, but generally a poor wealth-building vehicle compared to dedicated investments. Endowment / savings-linked (養老保険, yōrō hoken): A hybrid that pays out either on death or at a maturity date. Widely sold in Japan, but often disappointing as both insurance and investment. Iryo Hoken (医療保険) — Private Medical Insurance This pays a daily hospital benefit (usually ¥5,000–10,000 per day) and a lump sum for surgery. It supplements Japan\u0026rsquo;s public health insurance, which already covers a very large share of medical costs. Whether you actually need this is a nuanced question — covered in detail in Section 3.\nGan Hoken (がん保険) — Cancer Insurance A specialised form of medical insurance that pays a lump sum on a cancer diagnosis, plus ongoing treatment benefits. Cancer is the leading cause of death in Japan, and treatment costs — even under the public system — can be substantial over multi-year treatment courses.\nShūgyō Funō Hoken (就業不能保険) — Disability / Income Replacement Insurance Pays a monthly benefit if you are unable to work due to illness or injury. This is chronically under-purchased in Japan relative to its actual importance. Most working-age people are far more likely to suffer a disabling illness than to die prematurely, yet most insurance spending focuses on death benefits.\nKojin Nenkin (個人年金保険) — Personal Pension Insurance A private annuity product that accumulates savings over your working years and pays out in retirement. These carry favourable tax treatment (covered in Section 7), but are generally less flexible and lower-return than building the equivalent savings inside a NISA account.\n3. What Japan\u0026rsquo;s National Health Insurance Already Covers This is the section most insurance agents would rather you did not read carefully.\nJapan\u0026rsquo;s public health insurance system — whether you are enrolled in the employer-based Shakai Hoken (社会保険) or the municipal Kokumin Kenkō Hoken (国民健康保険) — covers approximately 70% of most medical costs for most residents. You pay 30% out of pocket at the point of care.\nThat 30% co-pay sounds manageable for a GP visit. But what about a major surgery, a long hospitalisation, or cancer treatment? Could a single medical event bankrupt you?\nThis is where Japan\u0026rsquo;s high-cost medical care system (高額療養費制度, Kōgaku Ryōyōhi Seido) changes the equation entirely.\nUnder this government programme, your out-of-pocket medical expenses in any single calendar month are capped based on your income. For a person with a standard income of roughly ¥3.7–7.7M per year, the monthly cap works out to approximately ¥80,000–90,000 plus a small income-linked supplement. For lower incomes, the cap is lower. For higher incomes, it is higher, but still capped.\nIn practical terms: if you are hospitalised for a month and your total hospital bill is ¥2,000,000, you will not pay ¥600,000 (30% co-pay). You will pay somewhere in the range of ¥80,000–150,000, depending on your income bracket. The public system absorbs the rest.\nThis system is real, it applies to most legal residents of Japan, and it fundamentally changes the risk calculation for private medical insurance. You are not exposed to unlimited catastrophic medical costs in the same way that uninsured people in some other countries are.\nWhat the high-cost medical care system does NOT cover:\nDifference-fee billing (差額ベッド代, sagaraku beddo-dai): charges for private or semi-private hospital rooms, which can add ¥5,000–20,000 per day on top of the capped amount Some treatments classified as advanced medicine (先進医療, senshin iryō) that are not yet covered by the public system Lost income during hospitalisation or recovery Non-medical costs: transport to hospital, family members\u0026rsquo; accommodation near the hospital, childcare during your hospitalisation Private medical insurance is most useful for covering exactly these gaps — particularly the room upgrade costs and income replacement during a long hospital stay. For many single, healthy adults with an adequate emergency fund, it may still be unnecessary. For people with families, mortgages, or irregular income, the calculus can shift.\n4. When Expats DO Need Life Insurance Several situations make life insurance genuinely worth considering, regardless of whether you are a foreign national or a lifelong Japanese resident.\nYou Have Dependents Living in Japan If a spouse, partner, child, or other family member depends on your income — and they are based in Japan — a term life policy is a straightforward and relatively cheap way to ensure they are not suddenly unable to pay rent or school fees if you die. A ¥20–30 million term policy for a healthy 35-year-old non-smoker typically costs less than ¥3,000–5,000 per month. That is not a trivial amount, but it is a meaningful level of protection for a household that relies on one income.\nYou Have a Mortgage Without Group Credit Life Insurance Most Japanese home loans (住宅ローン, jūtaku rōn) come with mandatory group credit life insurance (団体信用生命保険, danshin) bundled in — meaning the outstanding mortgage balance is effectively wiped out if the borrower dies. If you have a mortgage with danshin, you may already have substantial death coverage in place just from your loan terms.\nHowever, some loan structures — particularly certain types of flat-rate loans, joint mortgages, or loans from foreign-headquartered lenders — may not include standard danshin. If you are unsure whether your mortgage is covered, check with your lender directly. If it is not, a term life policy equal to your outstanding mortgage balance is a sensible addition.\nYou Are Self-Employed or a Freelancer Employees at most medium-to-large Japanese companies benefit from group insurance coverage provided through their employer, often without realising it. When you leave a company to become self-employed, that coverage disappears overnight. Freelancers and sole proprietors in Japan have no employer-provided safety net for death benefits, disability income, or supplementary medical coverage — they need to build this from scratch.\nFor the self-employed, the combination of a modest term life policy (if you have dependents), an income replacement / disability policy, and adequate medical coverage is more important than it is for a salaried employee at a large company.\nYou Have Dependents in Another Country Some expats support family members in their home country — parents, siblings, or children who live outside Japan. Standard Japanese social security survivor benefits are calibrated around Japan-based households. A private life insurance policy with named international beneficiaries may be the cleanest way to ensure overseas dependents are protected. Confirm the policy terms allow for international beneficiary payments before purchasing.\n5. When Expats Do NOT Need Life Insurance Just as important as knowing when you need insurance is knowing when you probably do not.\nYou Are Single With No Dependents Life insurance pays out to the people who depend on your income. If no one does, there is no financial void to fill when you die. A healthy single person with no dependents and no debts has essentially no need for a death benefit policy. Spending ¥3,000–10,000 per month on life insurance premiums in this situation is money that could go directly into investments.\nYour Employer Provides Group Coverage Many medium and large Japanese employers provide some level of group life insurance as part of their compensation package. Review your employment contract and ask your HR department for details. If you already have ¥10–20M in death coverage through your employer, you may not need to purchase additional private coverage — or may only need a small top-up policy.\nNote that employer group coverage usually ends when your employment does. If you plan to stay at the same employer long-term, this is less of a concern. If you change jobs or leave Japan, check whether you have a gap.\nYou Are on a Short-Term Assignment or Plan to Leave Japan If you are posted to Japan for two to five years and have existing life insurance coverage in your home country, adding a Japanese policy may be redundant and complicate your financial picture when you eventually return. Check whether your existing policy provides international coverage. Many whole-of-life and term policies from English-speaking countries provide global coverage regardless of your country of residence.\nYou Have a Very Large Liquid Asset Base Life insurance is fundamentally about replacing income that others depend on. If you have accumulated enough invested assets that your dependents could live comfortably off the returns for the rest of their lives — without needing your continued income — you may be self-insured. Most expats in Japan are not in this position, but it is worth being honest about where you actually stand.\n6. What Does Insurance Actually Cost in Japan? Specific premium quotes depend on your age, health history, smoking status, coverage amount, policy term, and the insurer. Rather than fabricating fictional product names or company comparisons, here are realistic cost ranges based on publicly available market data for straightforward policies.\nPrivate Medical Insurance (医療保険) Profile Approximate Monthly Premium Non-smoking adult, 30s, basic hospitalisation ¥1,000 – ¥2,500 Non-smoking adult, 40s, basic hospitalisation ¥1,500 – ¥3,500 With cancer rider added Add ¥500 – ¥1,500 Smoker, same age bands Approximately 20–40% higher Basic policies typically pay ¥5,000–10,000 per inpatient day and a surgery lump sum. Higher-tier policies may include advanced cancer treatment riders, outpatient treatment benefits, and income replacement components.\nTerm Life Insurance (定期保険) Coverage of ¥20–30 million for a 20-year term:\nProfile Approximate Monthly Premium Non-smoking male, age 30, ¥20M coverage ¥2,000 – ¥3,500 Non-smoking female, age 30, ¥20M coverage ¥1,500 – ¥2,500 Non-smoking male, age 40, ¥20M coverage ¥3,500 – ¥6,000 Non-smoking female, age 40, ¥20M coverage ¥2,000 – ¥4,000 Smokers pay meaningfully higher premiums, and some insurers require a medical examination for coverage above certain thresholds (typically ¥30–50M or for older applicants).\nDisability / Income Replacement Insurance (就業不能保険) Profile Approximate Monthly Premium Monthly benefit of ¥100,000, non-smoker, 30s ¥3,000 – ¥6,000 Monthly benefit of ¥200,000, non-smoker, 30s ¥5,000 – ¥10,000 Disability coverage is often disproportionately under-prioritised relative to life insurance. Consider whether you would rather your family received a large lump sum after your death, or whether you would rather receive a monthly income replacement if you are sick or injured and unable to work for one, two, or ten years.\n7. Tax Deduction Benefits for Insurance Premiums One genuinely useful feature of the Japanese tax system for insurance buyers is the life insurance premium deduction (生命保険料控除, seimei hokenryō kōjo).\nThe deduction applies to three separate categories of qualifying insurance premiums, each with its own calculation:\nCategory What It Covers Max Deduction (Income Tax) General life insurance (一般生命保険料) Death benefit policies, endowment ¥40,000 Medical / nursing care insurance (介護医療保険料) Medical, cancer, disability, nursing care insurance ¥40,000 Personal pension insurance (個人年金保険料) Qualifying personal pension products ¥40,000 The maximum total income tax deduction is ¥120,000 per year (¥40,000 x 3 categories). Residence tax deductions are calculated separately at lower rates, with a combined maximum of ¥70,000.\nIn practical terms, if you are paying premiums in all three categories and your income tax rate is 20%, this deduction saves you up to ¥24,000 per year in income tax alone. It is not a huge sum, but it is a real benefit worth capturing.\nFor employees: The deduction is typically applied at year-end tax adjustment (年末調整, nenmatsuchōsei) by your employer. You submit your insurance premium certificates (保険料控除証明書, sent by your insurer each autumn) to your HR department.\nFor self-employed and freelancers: You claim this deduction on your annual tax return (確定申告, kakuteishinkoku). If you use accounting software to manage your business finances, tracking insurance premium deductions is straightforward.\nSelf-employed in Japan? freee is the leading cloud accounting platform for sole proprietors and small business owners. It helps you track deductible expenses — including insurance premiums — and guides you through the kakuteishinkoku process. Try freee for free. 8. Insurance vs. Investment: Why Whole Life Often Loses This comparison deserves its own section, because the blurring of insurance and investment in Japan causes a significant amount of financial harm to policyholders who do not fully understand the products they are buying.\nWhole life insurance (終身保険) and savings-type endowment products (養老保険) are heavily promoted in Japan partly because they generate higher commissions for agents and partly because the \u0026ldquo;get your money back at the end\u0026rdquo; framing feels emotionally reassuring. The reality is more complicated.\nThe structural problem with savings-type insurance:\nWhen you pay premiums into a whole life or endowment product, your money is split between the actual cost of insurance coverage (mortality charges) and a savings component that accumulates cash value. The effective return on the savings component is typically very low — often below 1% in real terms for yen-denominated products. You are being charged the cost of insurance, administrative fees, and agent commissions out of the nominal savings return before any growth reaches you.\nCompare this to investing in NISA:\nInside a NISA account, you can invest in broadly diversified index funds — domestic and international — with annual fees (expense ratios) typically below 0.2%. All capital gains and dividends are tax-free indefinitely. The money remains fully liquid and accessible whenever you need it. There is no mortality charge eating into your returns. There is no agent commission embedded in the product.\nA simplified comparison:\nIf you have ¥10,000/month to allocate, the question is: what is the most efficient way to build long-term financial security?\nOption A: Pay ¥10,000/month into a whole life policy. After 30 years, receive a guaranteed maturity value that may grow at roughly 0.5–1% annually after all costs, locked inside an insurance contract with surrender charges if you need the money early. Option B: Buy a ¥3,000/month term life policy (adequate death coverage if you have dependents) and invest the remaining ¥7,000/month in a NISA account in a global index fund. After 30 years at a conservative 5% annual return, the NISA portfolio would be worth approximately ¥5.8M — all tax-free. The term policy cost you ¥1.08M in total over the same period. Option B separates the insurance function (pure risk protection at minimal cost) from the investment function (maximising long-term growth). This \u0026ldquo;buy term and invest the difference\u0026rdquo; approach is not universally correct — it depends on your tax situation, health insurability, and investment discipline — but it is the framework that financial planners consistently recommend as a starting point.\nReady to start investing in NISA? Rakuten Securities (楽天証券) is one of Japan\u0026rsquo;s largest online brokers, offering a seamless NISA account with access to low-cost index funds, a Japanese and English interface, and a straightforward online application process. Open a Rakuten Securities NISA account. For a deeper dive into NISA accounts, contribution limits, and how to choose funds, see our guide: Best Investment Account for Beginners in Japan: NISA vs. iDeCo vs. Regular Brokerage .\n9. How to Get Free, Independent Insurance Advice in Japan Even with all of the above information, choosing the right combination of coverage — term amount, policy period, medical riders, disability coverage — is genuinely complex. The optimal answer depends on your age, income, family structure, mortgage, employer benefits, health history, and financial goals. A generalised guide cannot give you a personalised answer.\nThe good news is that several free multi-company comparison services exist in Japan specifically to help individuals navigate these decisions with the help of a qualified financial planner (FP) — at no cost to the consumer.\nHow Free FP Consultation Services Work Services like 保険マンモス (Hoken Mammoth) and 保険見直しラボ (Hoken Minaoshi Labo) operate on a matchmaking model: they connect you with independent FP consultants who are licensed to advise on products from multiple insurance companies (not just one). The consultation is free because the FP earns a commission if you purchase a policy through them — similar to a mortgage broker model.\nThis structure does create a potential incentive for the FP to recommend something over nothing. But the key advantage over buying directly from a single insurance company\u0026rsquo;s agent is that the FP can show you products across many providers and explain why one structure fits your situation better than another.\nWhat to expect from a free FP consultation:\nAn initial questionnaire covering your household structure, income, current coverage, and goals A 60–90 minute meeting (in person at a consultation office, or increasingly by video call) A personalised coverage recommendation across multiple companies No obligation to purchase anything Follow-up support if you have questions about specific policy terms Tips for getting the most out of a free consultation:\nBring documentation of any existing coverage (employer group insurance certificates, any policies you currently hold) Have a rough idea of your monthly budget for insurance Ask the FP to explain the difference between any whole life or endowment products they recommend versus a simpler term policy — and ask them to show you the difference in total premium cost over 20 years Ask whether there is a shorter-term coverage option that meets your needs for a lower premium If you speak limited Japanese: Some FP consultation services have English-speaking consultants available, particularly in Tokyo and Osaka. It is worth asking at booking whether an English-language consultation is possible. Alternatively, having a Japanese-speaking partner or friend present can help navigate more technical discussions.\nBook a free insurance consultation: Services like 保険マンモス and 保険見直しラボ let you compare plans from multiple insurers with guidance from a qualified FP — at no cost to you. (Affiliate links coming soon — check back for direct booking links.)\n10. Practical Checklist: What to Do Next Rather than a vague \u0026ldquo;talk to an expert\u0026rdquo; conclusion, here is a concrete checklist based on the most common expat situations:\nIf you have dependents in Japan: Review whether your employer provides group life insurance — get the certificate and note the coverage amount Calculate how much income your dependents would need for 10–20 years if you died tomorrow Subtract employer coverage and any existing policies; the gap is your target term life coverage amount Get quotes from a free FP service for a straightforward term policy covering that gap If you are a freelancer or self-employed, add a disability/income replacement policy to your research If you are single with no dependents: Confirm you are enrolled in Japan\u0026rsquo;s national health insurance (Shakai Hoken or Kokumin Kenkō Hoken) — this is legally required for most residents Consider whether a basic private medical policy (¥1,500–2,500/month) for hospital room costs is worth the peace of mind to you Strongly consider redirecting premium money toward a NISA investment account instead of whole life insurance Review your situation again if circumstances change (marriage, children, mortgage) If you are self-employed or freelance: Confirm you are paying into Kokumin Kenkō Hoken (and Kokumin Nenkin if applicable) Research the high-cost medical care system benefit amount for your income bracket Get quotes for a medical policy that covers room upgrades and income loss during hospitalisation Track your insurance premiums for the life insurance premium deduction on your kakuteishinkoku Consider whether iDeCo (personal pension) or NISA better fits your retirement savings strategy For a comprehensive look at retirement planning for foreign residents in Japan, see our guide: Retirement Planning in Japan for Foreign Residents .\nConclusion Japan\u0026rsquo;s insurance market is large, well-regulated, and — for the right person in the right situation — genuinely valuable. But the same cultural and commercial pressures that make Japan one of the world\u0026rsquo;s most insured nations also mean that many people end up holding coverage they do not need, in products that serve insurers and agents better than they serve policyholders.\nFor expats and foreign residents, the clearest framework is this:\nStart with what you have. Japan\u0026rsquo;s national health insurance system, combined with the high-cost medical care programme, provides a substantial floor of coverage. You are not starting from zero.\nInsure for catastrophic income loss, not incremental costs. Life insurance makes sense when people depend on your income and cannot replace it. Disability coverage makes sense when illness or injury could eliminate your income for months or years. Medical insurance makes sense for the specific gaps the public system leaves uncovered — primarily room costs and lost income — not for routine medical expenses.\nDo not use insurance as an investment vehicle. Whole life and savings-type products are rarely the most efficient way to build wealth. Separate your insurance needs (cheapest product that covers the risk) from your investment needs (NISA, iDeCo, brokerage), and you will almost certainly be better off.\nUse free resources. A qualified FP consultation through a multi-company comparison service costs you nothing and can clarify your specific situation better than any general guide.\nInsurance is not exciting. It is not meant to be. The goal is to have exactly as much coverage as you actually need — no more, no less — so that the rest of your money can work harder in places where it actually grows.\nRelated Tools Calculate your exact age instantly → Age Calculator Check your BMI and healthy weight range → BMI Calculator Calculate your ideal emergency fund → Emergency Fund Calculator Check your take-home pay → Salary Calculator Plan your savings goals → Savings Goal Calculator Calculate your mortgage payment → Mortgage Calculator This article contains affiliate links. If you use these links to open accounts or request consultations, we may earn a commission at no additional cost to you. All opinions and analysis are our own.\nYou May Also Like Best Life Insurance 2026: Compare Policies, Rates \u0026amp; Coverage Retirement Planning in Japan for Foreign Residents: What the Pension System Won\u0026rsquo;t Cover How to Build an Emergency Fund Fast (2026) ","permalink":"https://productivity-works.com/posts/life-insurance-japan-expat-guide-english/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eJapan has one of the highest life insurance penetration rates in the world. Walk into any convenience store, and there is a chance the person ahead of you is also paying a life insurance premium through their bank account. Life insurance is deeply woven into the financial culture here — pushed aggressively by door-to-door agents, embedded in employer benefits packages, and marketed relentlessly at new parents and newlyweds.\u003c/p\u003e","title":"Life Insurance in Japan for Expats: What You Actually Need in 2026"},{"content":"This article contains affiliate links. We may earn a commission at no extra cost to you.\nMortgage Calculator 2026 Enter your home price, down payment, and loan details to calculate your full monthly payment — including principal \u0026amp; interest, property taxes, insurance, and PMI.\nHome Price ($) Down Payment: 20% \u0026nbsp;($70,000) 0%30% Loan Amount $280,000 Interest Rate: 6.5% 2%10% Loan Term 15 Years 20 Years 30 Years Property Tax Rate: 1.2% \u0026nbsp;($350/mo) 0%3% Home Insurance ($/year) PMI automatically added — down payment is below 20%. PMI is removed once equity reaches 20%. Total Monthly Payment $2,212 Principal \u0026amp; Interest + Tax + Insurance Principal \u0026amp; Interest $1,770 Property Tax $350 Home Insurance $100 PMI $0 Total Payment Over Loan Life $637,320 Total Interest Paid $357,320 Monthly Payment Breakdown P\u0026amp;I Property Tax Insurance PMI Amortization Schedule Year Payment Principal Interest Balance Show remaining years Year Payment Principal Interest Balance Rate Comparison How to Use This Calculator Enter your home price — the purchase price or estimated value of the home you\u0026rsquo;re buying Set your down payment — drag the slider to your planned down payment percentage; 20% avoids PMI Choose your interest rate — use your pre-approval rate or current market rates; adjust the slider in 0.125% increments Select a loan term — 15 years means higher monthly payments but far less total interest; 30 years lowers your payment Add property tax rate — check your county assessor\u0026rsquo;s website for the local rate (national average is ~1.1%) Enter annual home insurance — get quotes from insurers or use $1,000–$2,000 as a starting estimate All results update instantly as you adjust inputs. No sign-up or data sharing required.\nUnderstanding Your Mortgage Payment Most homeowners are surprised that their mortgage payment covers far more than just repaying the loan. Here is what makes up your total monthly obligation:\nPrincipal \u0026amp; Interest (P\u0026amp;I) This is the core loan repayment calculated using standard amortization. Early payments are mostly interest; later payments shift toward principal. Your P\u0026amp;I payment stays fixed for the life of a fixed-rate mortgage — one of its key advantages over renting or adjustable-rate loans.\nProperty Tax Collected by local governments and typically rolled into your monthly payment through an escrow account. Rates vary widely — from under 0.5% in states like Hawaii to over 2% in New Jersey and Illinois. The national average is approximately 1.1% of home value per year.\nHome Insurance (Homeowner\u0026rsquo;s Insurance) Required by virtually all mortgage lenders. It covers damage from fire, storms, theft, and liability claims. Annual premiums typically run $1,000–$2,500 depending on your home\u0026rsquo;s value, location, and coverage level. Shop at least 3 insurers to compare.\nPMI (Private Mortgage Insurance) Required when your down payment is below 20% of the home price. PMI protects the lender — not you — in case of default. This calculator uses a standard rate of 0.5% of the loan amount annually, split into monthly installments. Once your equity reaches 20% (through payments or appreciation), you can request PMI removal from your lender. PMI typically cancels automatically at 22% equity under the Homeowners Protection Act.\nTips to Get a Better Mortgage Rate 1. Improve your credit score before applying Lenders offer their best rates to borrowers with scores above 740. Even a 20-point improvement can drop your rate by 0.125%–0.25%, saving tens of thousands over the life of the loan. Pay down credit card balances, dispute any errors, and avoid opening new accounts in the 6 months before applying.\n2. Compare at least 3–5 lenders Rates vary more than most buyers realize. Credit unions, regional banks, online lenders, and mortgage brokers all price risk differently. Shopping multiple lenders within a 14-day window counts as only one credit inquiry under FICO scoring rules.\n3. Consider paying points to buy down your rate One discount point costs 1% of the loan amount and typically reduces your rate by 0.25%. Use the rate comparison table above to estimate how much a lower rate saves you per month — then divide the cost of points by the monthly savings to find your break-even period. If you plan to stay in the home longer than the break-even, buying points usually makes sense.\n4. Time your lock strategically Mortgage rates move daily with bond markets. Once you find a rate you are comfortable with, lock it in writing — most locks are free for 30–60 days. Floating (not locking) only makes sense if you have strong conviction rates will fall before closing.\nRelated Tools Calculate your monthly budget after housing costs → Budget Planner Find out how long to save your down payment → Savings Goal Calculator Compare mortgage payoff with debt avalanche → Debt Payoff Calculator Calculate your net take-home pay → Salary Calculator See how investments grow alongside home equity → Compound Interest Calculator Related Articles How Much House Can I Afford in 2026? 30-Year vs 15-Year Mortgage: Which Is Right for You? Best Mortgage Lenders 2026: Rates, Fees \u0026amp; Reviews How to Improve Your Credit Score Before Buying a Home First-Time Homebuyer Guide: From Pre-Approval to Closing Productivity Works Free Tools All our financial calculators are 100% free, run entirely in your browser, and never store your data.\nBrowse all free tools This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/tools/mortgage-calculator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links. We may earn a commission at no extra cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"mortgage-calculator-2026\"\u003eMortgage Calculator 2026\u003c/h1\u003e\n\u003cp\u003eEnter your home price, down payment, and loan details to calculate your \u003cstrong\u003efull monthly payment\u003c/strong\u003e — including principal \u0026amp; interest, property taxes, insurance, and PMI.\u003c/p\u003e\n\u003cdiv id=\"mort-calc\" style=\"max-width:720px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:#1e293b;\"\u003e\n\u003c!-- Inputs --\u003e\n\u003cdiv style=\"padding:24px;border:2px solid #2563eb;border-radius:12px;background:#f8fafc;\"\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eHome Price ($)\u003c/label\u003e\n\u003cinput type=\"number\" id=\"homePrice\" min=\"50000\" max=\"5000000\" step=\"1000\" value=\"350000\" oninput=\"calcMort()\" style=\"width:100%;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:16px;box-sizing:border-box;\"\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eDown Payment: \u003cspan id=\"dpPct\" style=\"color:#2563eb;\"\u003e20%\u003c/span\u003e \u0026nbsp;\u003cspan id=\"dpAmt\" style=\"color:#64748b;font-size:14px;font-weight:normal;\"\u003e($70,000)\u003c/span\u003e\u003c/label\u003e\n\u003cinput type=\"range\" id=\"downPct\" min=\"0\" max=\"30\" step=\"1\" value=\"20\" oninput=\"calcMort()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e0%\u003c/span\u003e\u003cspan\u003e30%\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;padding:12px;background:#e0f2fe;border-radius:8px;\"\u003e\n\u003cdiv style=\"font-size:13px;color:#64748b;\"\u003eLoan Amount\u003c/div\u003e\n\u003cdiv id=\"loanAmtDisplay\" style=\"font-size:22px;font-weight:bold;color:#0369a1;\"\u003e$280,000\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eInterest Rate: \u003cspan id=\"ratePct\" style=\"color:#2563eb;\"\u003e6.5%\u003c/span\u003e\u003c/label\u003e\n\u003cinput type=\"range\" id=\"intRate\" min=\"2\" max=\"10\" step=\"0.125\" value=\"6.5\" oninput=\"calcMort()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e2%\u003c/span\u003e\u003cspan\u003e10%\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:10px;\"\u003eLoan Term\u003c/label\u003e\n\u003cdiv style=\"display:flex;gap:12px;\"\u003e\n\u003clabel style=\"flex:1;text-align:center;padding:10px;border:2px solid #e2e8f0;border-radius:8px;cursor:pointer;font-weight:bold;\" id=\"term15label\"\u003e\n  \u003cinput type=\"radio\" name=\"loanTerm\" value=\"15\" onchange=\"calcMort()\" style=\"display:none;\"\u003e 15 Years\n\u003c/label\u003e\n\u003clabel style=\"flex:1;text-align:center;padding:10px;border:2px solid #e2e8f0;border-radius:8px;cursor:pointer;font-weight:bold;\" id=\"term20label\"\u003e\n  \u003cinput type=\"radio\" name=\"loanTerm\" value=\"20\" onchange=\"calcMort()\" style=\"display:none;\"\u003e 20 Years\n\u003c/label\u003e\n\u003clabel style=\"flex:1;text-align:center;padding:10px;border:2px solid #2563eb;border-radius:8px;cursor:pointer;font-weight:bold;background:#eff6ff;color:#2563eb;\" id=\"term30label\"\u003e\n  \u003cinput type=\"radio\" name=\"loanTerm\" value=\"30\" checked onchange=\"calcMort()\" style=\"display:none;\"\u003e 30 Years\n\u003c/label\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eProperty Tax Rate: \u003cspan id=\"taxPct\" style=\"color:#2563eb;\"\u003e1.2%\u003c/span\u003e \u0026nbsp;\u003cspan id=\"taxAmt\" style=\"color:#64748b;font-size:14px;font-weight:normal;\"\u003e($350/mo)\u003c/span\u003e\u003c/label\u003e\n\u003cinput type=\"range\" id=\"propTax\" min=\"0\" max=\"3\" step=\"0.1\" value=\"1.2\" oninput=\"calcMort()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e0%\u003c/span\u003e\u003cspan\u003e3%\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eHome Insurance ($/year)\u003c/label\u003e\n\u003cinput type=\"number\" id=\"insurance\" min=\"0\" max=\"20000\" step=\"100\" value=\"1200\" oninput=\"calcMort()\" style=\"width:100%;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:16px;box-sizing:border-box;\"\u003e\n\u003c/div\u003e\n\u003cdiv id=\"pmiRow\" style=\"margin-bottom:4px;padding:10px 12px;background:#fef3c7;border:1px solid #fcd34d;border-radius:8px;font-size:14px;color:#92400e;display:none;\"\u003e\n  PMI automatically added — down payment is below 20%. PMI is removed once equity reaches 20%.\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Results --\u003e\n\u003cdiv style=\"margin-top:24px;padding:24px;border-radius:12px;background:#1e40af;color:white;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:15px;opacity:0.85;margin-bottom:4px;\"\u003eTotal Monthly Payment\u003c/div\u003e\n\u003cdiv id=\"totalMonthly\" style=\"font-size:52px;font-weight:bold;line-height:1.1;\"\u003e$2,212\u003c/div\u003e\n\u003cdiv style=\"font-size:14px;opacity:0.75;margin-top:6px;\"\u003ePrincipal \u0026amp; Interest + Tax + Insurance\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-top:16px;display:grid;grid-template-columns:1fr 1fr;gap:12px;\"\u003e\n\u003cdiv style=\"padding:16px;background:#e0f2fe;border-radius:10px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;margin-bottom:4px;\"\u003ePrincipal \u0026amp; Interest\u003c/div\u003e\n\u003cdiv id=\"piPayment\" style=\"font-size:22px;font-weight:bold;color:#0369a1;\"\u003e$1,770\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:16px;background:#fef3c7;border-radius:10px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;margin-bottom:4px;\"\u003eProperty Tax\u003c/div\u003e\n\u003cdiv id=\"taxPayment\" style=\"font-size:22px;font-weight:bold;color:#d97706;\"\u003e$350\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:16px;background:#f0fdf4;border-radius:10px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;margin-bottom:4px;\"\u003eHome Insurance\u003c/div\u003e\n\u003cdiv id=\"insPayment\" style=\"font-size:22px;font-weight:bold;color:#059669;\"\u003e$100\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"pmiCell\" style=\"padding:16px;background:#fef2f2;border-radius:10px;text-align:center;display:none;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;margin-bottom:4px;\"\u003ePMI\u003c/div\u003e\n\u003cdiv id=\"pmiPayment\" style=\"font-size:22px;font-weight:bold;color:#dc2626;\"\u003e$0\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-top:16px;display:grid;grid-template-columns:1fr 1fr;gap:12px;\"\u003e\n\u003cdiv style=\"padding:16px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:10px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;margin-bottom:4px;\"\u003eTotal Payment Over Loan Life\u003c/div\u003e\n\u003cdiv id=\"totalLifetime\" style=\"font-size:20px;font-weight:bold;color:#1e293b;\"\u003e$637,320\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:16px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:10px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;margin-bottom:4px;\"\u003eTotal Interest Paid\u003c/div\u003e\n\u003cdiv id=\"totalInterest\" style=\"font-size:20px;font-weight:bold;color:#dc2626;\"\u003e$357,320\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Stacked bar --\u003e\n\u003cdiv style=\"margin-top:20px;\"\u003e\n\u003cdiv style=\"font-size:13px;font-weight:bold;margin-bottom:8px;color:#475569;\"\u003eMonthly Payment Breakdown\u003c/div\u003e\n\u003cdiv style=\"height:28px;border-radius:8px;overflow:hidden;display:flex;\"\u003e\n\u003cdiv id=\"barPI\"  style=\"background:#2563eb;height:100%;transition:width 0.3s;\" title=\"Principal \u0026amp; Interest\"\u003e\u003c/div\u003e\n\u003cdiv id=\"barTax\" style=\"background:#f59e0b;height:100%;transition:width 0.3s;\" title=\"Property Tax\"\u003e\u003c/div\u003e\n\u003cdiv id=\"barIns\" style=\"background:#10b981;height:100%;transition:width 0.3s;\" title=\"Insurance\"\u003e\u003c/div\u003e\n\u003cdiv id=\"barPMI\" style=\"background:#ef4444;height:100%;transition:width 0.3s;\" title=\"PMI\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;flex-wrap:wrap;gap:12px;font-size:12px;color:#64748b;margin-top:8px;\"\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#2563eb;border-radius:2px;margin-right:4px;\"\u003e\u003c/span\u003eP\u0026amp;I\u003c/span\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#f59e0b;border-radius:2px;margin-right:4px;\"\u003e\u003c/span\u003eProperty Tax\u003c/span\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#10b981;border-radius:2px;margin-right:4px;\"\u003e\u003c/span\u003eInsurance\u003c/span\u003e\n\u003cspan id=\"pmiLegend\" style=\"display:none;\"\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#ef4444;border-radius:2px;margin-right:4px;\"\u003e\u003c/span\u003ePMI\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Amortization table --\u003e\n\u003cdiv style=\"margin-top:24px;\"\u003e\n\u003cdiv style=\"font-size:16px;font-weight:bold;margin-bottom:12px;color:#1e293b;\"\u003eAmortization Schedule\u003c/div\u003e\n\u003cdiv style=\"overflow-x:auto;\"\u003e\n\u003ctable style=\"width:100%;border-collapse:collapse;font-size:14px;\"\u003e\n\u003cthead\u003e\n\u003ctr style=\"background:#2563eb;color:white;\"\u003e\n\u003cth style=\"padding:10px 8px;text-align:left;\"\u003eYear\u003c/th\u003e\n\u003cth style=\"padding:10px 8px;text-align:right;\"\u003ePayment\u003c/th\u003e\n\u003cth style=\"padding:10px 8px;text-align:right;\"\u003ePrincipal\u003c/th\u003e\n\u003cth style=\"padding:10px 8px;text-align:right;\"\u003eInterest\u003c/th\u003e\n\u003cth style=\"padding:10px 8px;text-align:right;\"\u003eBalance\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody id=\"amortFirst5\"\u003e\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\u003cdetails id=\"amortDetails\" style=\"margin-top:0;\"\u003e\n\u003csummary style=\"cursor:pointer;font-weight:bold;color:#2563eb;font-size:14px;padding:10px 8px;background:#eff6ff;border-radius:0 0 8px 8px;\"\u003eShow remaining years\u003c/summary\u003e\n\u003cdiv style=\"overflow-x:auto;\"\u003e\n\u003ctable style=\"width:100%;border-collapse:collapse;font-size:14px;\"\u003e\n\u003cthead\u003e\n\u003ctr style=\"background:#2563eb;color:white;\"\u003e\n\u003cth style=\"padding:10px 8px;text-align:left;\"\u003eYear\u003c/th\u003e\n\u003cth style=\"padding:10px 8px;text-align:right;\"\u003ePayment\u003c/th\u003e\n\u003cth style=\"padding:10px 8px;text-align:right;\"\u003ePrincipal\u003c/th\u003e\n\u003cth style=\"padding:10px 8px;text-align:right;\"\u003eInterest\u003c/th\u003e\n\u003cth style=\"padding:10px 8px;text-align:right;\"\u003eBalance\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody id=\"amortRest\"\u003e\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\u003c/details\u003e\n\u003c/div\u003e\n\u003c!-- Rate comparison --\u003e\n\u003cdiv style=\"margin-top:24px;padding:20px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:12px;\"\u003e\n\u003cdiv style=\"font-size:16px;font-weight:bold;margin-bottom:14px;color:#1e293b;\"\u003eRate Comparison\u003c/div\u003e\n\u003cdiv id=\"rateComparison\" style=\"display:grid;gap:8px;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nfunction fmt(n){return '$'+Math.round(n).toLocaleString();}\nfunction fmtR(n){return n.toFixed(3)+'%';}\n\nfunction getTermYears(){\n  var radios=document.getElementsByName('loanTerm');\n  for(var i=0;i\u003cradios.length;i++){if(radios[i].checked)return parseInt(radios[i].value);}\n  return 30;\n}\n\nfunction updateTermLabels(selected){\n  var terms=[15,20,30];\n  terms.forEach(function(t){\n    var el=document.getElementById('term'+t+'label');\n    if(t===selected){\n      el.style.borderColor='#2563eb';\n      el.style.background='#eff6ff';\n      el.style.color='#2563eb';\n    } else {\n      el.style.borderColor='#e2e8f0';\n      el.style.background='white';\n      el.style.color='#1e293b';\n    }\n  });\n}\n\nfunction calcPI(principal, annualRate, years){\n  var r=annualRate/100/12;\n  var n=years*12;\n  if(r===0) return principal/n;\n  return principal*(r*Math.pow(1+r,n))/(Math.pow(1+r,n)-1);\n}\n\nfunction buildAmort(principal, annualRate, years){\n  var r=annualRate/100/12;\n  var n=years*12;\n  var payment=calcPI(principal,annualRate,years);\n  var balance=principal;\n  var rows=[];\n  for(var y=1;y\u003c=years;y++){\n    var yearPrincipal=0;\n    var yearInterest=0;\n    var yearPayment=0;\n    for(var m=0;m\u003c12;m++){\n      var interest=balance*r;\n      var princ=payment-interest;\n      if(princ\u003ebalance) princ=balance;\n      balance-=princ;\n      yearInterest+=interest;\n      yearPrincipal+=princ;\n      yearPayment+=payment;\n      if(balance\u003c0.01){balance=0;break;}\n    }\n    rows.push({year:y,payment:yearPayment,principal:yearPrincipal,interest:yearInterest,balance:Math.max(0,balance)});\n    if(balance===0) break;\n  }\n  return rows;\n}\n\nfunction amortRow(r,bg){\n  return '\u003ctr style=\"background:'+bg+'\"\u003e'+\n    '\u003ctd style=\"padding:8px;\"\u003eYear '+r.year+'\u003c/td\u003e'+\n    '\u003ctd style=\"padding:8px;text-align:right;\"\u003e'+fmt(r.payment)+'\u003c/td\u003e'+\n    '\u003ctd style=\"padding:8px;text-align:right;color:#0369a1;\"\u003e'+fmt(r.principal)+'\u003c/td\u003e'+\n    '\u003ctd style=\"padding:8px;text-align:right;color:#dc2626;\"\u003e'+fmt(r.interest)+'\u003c/td\u003e'+\n    '\u003ctd style=\"padding:8px;text-align:right;font-weight:bold;\"\u003e'+fmt(r.balance)+'\u003c/td\u003e'+\n    '\u003c/tr\u003e';\n}\n\nfunction calcMort(){\n  var homePrice=parseFloat(document.getElementById('homePrice').value)||350000;\n  var dpPct=parseFloat(document.getElementById('downPct').value)||0;\n  var rate=parseFloat(document.getElementById('intRate').value)||6.5;\n  var years=getTermYears();\n  var taxRate=parseFloat(document.getElementById('propTax').value)||0;\n  var insAnnual=parseFloat(document.getElementById('insurance').value)||0;\n\n  updateTermLabels(years);\n\n  var downAmt=homePrice*(dpPct/100);\n  var loanAmt=homePrice-downAmt;\n\n  document.getElementById('dpPct').textContent=dpPct+'%';\n  document.getElementById('dpAmt').textContent='('+fmt(downAmt)+')';\n  document.getElementById('loanAmtDisplay').textContent=fmt(loanAmt);\n  document.getElementById('ratePct').textContent=rate.toFixed(3).replace(/\\.?0+$/,'')+'%';\n  var taxMonthly=(homePrice*taxRate/100)/12;\n  document.getElementById('taxPct').textContent=taxRate.toFixed(1)+'%';\n  document.getElementById('taxAmt').textContent='('+fmt(taxMonthly)+'/mo)';\n\n  var hasPMI=dpPct\u003c20;\n  var pmiMonthly=hasPMI?(loanAmt*0.005/12):0;\n\n  document.getElementById('pmiRow').style.display=hasPMI?'block':'none';\n  document.getElementById('pmiCell').style.display=hasPMI?'block':'none';\n  document.getElementById('pmiLegend').style.display=hasPMI?'inline':'none';\n\n  var pi=calcPI(loanAmt,rate,years);\n  var insMonthly=insAnnual/12;\n  var total=pi+taxMonthly+insMonthly+pmiMonthly;\n\n  document.getElementById('totalMonthly').textContent=fmt(total);\n  document.getElementById('piPayment').textContent=fmt(pi);\n  document.getElementById('taxPayment').textContent=fmt(taxMonthly);\n  document.getElementById('insPayment').textContent=fmt(insMonthly);\n  document.getElementById('pmiPayment').textContent=fmt(pmiMonthly);\n\n  // Total over loan life (P\u0026I only for interest calc, full payment for lifetime)\n  var totalPI=pi*years*12;\n  var totalInterest=totalPI-loanAmt;\n  var totalLifetime=total*years*12;\n  document.getElementById('totalLifetime').textContent=fmt(totalLifetime);\n  document.getElementById('totalInterest').textContent=fmt(totalInterest);\n\n  // Bar\n  var piPct=(pi/total)*100;\n  var taxPct=(taxMonthly/total)*100;\n  var insPct=(insMonthly/total)*100;\n  var pmiPct=(pmiMonthly/total)*100;\n  document.getElementById('barPI').style.width=piPct+'%';\n  document.getElementById('barTax').style.width=taxPct+'%';\n  document.getElementById('barIns').style.width=insPct+'%';\n  document.getElementById('barPMI').style.width=pmiPct+'%';\n\n  // Amortization\n  var rows=buildAmort(loanAmt,rate,years);\n  var first5='';\n  var rest='';\n  rows.forEach(function(r,i){\n    var bg=i%2===0?'white':'#f8fafc';\n    if(i\u003c5) first5+=amortRow(r,bg);\n    else rest+=amortRow(r,bg);\n  });\n  document.getElementById('amortFirst5').innerHTML=first5;\n  document.getElementById('amortRest').innerHTML=rest;\n  document.getElementById('amortDetails').style.display=rows.length\u003e5?'block':'none';\n\n  // Rate comparison\n  var offsets=[-1.0,-0.5,0,0.5,1.0];\n  var compHTML='';\n  offsets.forEach(function(offset){\n    var r2=Math.max(0.125,rate+offset);\n    var pi2=calcPI(loanAmt,r2,years);\n    var total2=pi2+taxMonthly+insMonthly+pmiMonthly;\n    var diff=total2-total;\n    var isBase=offset===0;\n    var bg=isBase?'#eff6ff':'white';\n    var border=isBase?'2px solid #2563eb':'1px solid #e2e8f0';\n    var diffStr=isBase?'\u003cspan style=\"color:#2563eb;font-size:12px;\"\u003ecurrent rate\u003c/span\u003e'\n      :(diff\u003e0?'\u003cspan style=\"color:#dc2626;font-size:12px;\"\u003e+'+fmt(diff)+'/mo\u003c/span\u003e'\n              :'\u003cspan style=\"color:#059669;font-size:12px;\"\u003e'+fmt(diff)+'/mo\u003c/span\u003e');\n    compHTML+='\u003cdiv style=\"display:flex;justify-content:space-between;align-items:center;padding:12px;background:'+bg+';border:'+border+';border-radius:8px;\"\u003e';\n    compHTML+='\u003cdiv\u003e\u003cstrong\u003e'+r2.toFixed(3).replace(/0+$/,'').replace(/\\.$/,'')+'%\u003c/strong\u003e'+(offset!==0?' \u003cspan style=\"font-size:12px;color:#64748b;\"\u003e('+( offset\u003e0?'+':'')+offset.toFixed(1)+'%)\u003c/span\u003e':'')+'\u003c/div\u003e';\n    compHTML+='\u003cdiv style=\"text-align:right;\"\u003e\u003cspan style=\"font-size:18px;font-weight:bold;\"\u003e'+fmt(total2)+'/mo\u003c/span\u003e '+diffStr+'\u003c/div\u003e';\n    compHTML+='\u003c/div\u003e';\n  });\n  document.getElementById('rateComparison').innerHTML=compHTML;\n}\n\n// Attach term radio change to also update labels\ndocument.getElementsByName('loanTerm').forEach(function(r){\n  r.addEventListener('change',function(){calcMort();});\n});\n\n// Attach term label clicks\n[15,20,30].forEach(function(t){\n  document.getElementById('term'+t+'label').addEventListener('click',function(){\n    document.querySelector('input[name=\"loanTerm\"][value=\"'+t+'\"]').checked=true;\n    calcMort();\n  });\n});\n\ncalcMort();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-to-use-this-calculator\"\u003eHow to Use This Calculator\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eEnter your home price\u003c/strong\u003e — the purchase price or estimated value of the home you\u0026rsquo;re buying\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSet your down payment\u003c/strong\u003e — drag the slider to your planned down payment percentage; 20% avoids PMI\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eChoose your interest rate\u003c/strong\u003e — use your pre-approval rate or current market rates; adjust the slider in 0.125% increments\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSelect a loan term\u003c/strong\u003e — 15 years means higher monthly payments but far less total interest; 30 years lowers your payment\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAdd property tax rate\u003c/strong\u003e — check your county assessor\u0026rsquo;s website for the local rate (national average is ~1.1%)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eEnter annual home insurance\u003c/strong\u003e — get quotes from insurers or use $1,000–$2,000 as a starting estimate\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eAll results update instantly as you adjust inputs. No sign-up or data sharing required.\u003c/p\u003e","title":"Mortgage Calculator 2026 | Monthly Payment, Amortization \u0026 More"},{"content":"This article contains affiliate links. We may earn a commission at no extra cost to you.\nNet Worth Calculator Enter your assets and liabilities below to instantly calculate your net worth and see how your financial health stacks up.\nAssets Cash \u0026amp; Savings $ Checking account $ Savings account $ Emergency fund Investments $ 401(k) / 403(b) $ IRA / Roth IRA $ Brokerage account $ Crypto Property $ Home value $ Other real estate Other Assets $ Vehicles $ Valuables (jewelry, art, collectibles) $ Business equity Total Assets $0 Liabilities $ Mortgage balance $ Student loans $ Auto loans $ Credit card debt $ Other debt Total Liabilities $0 Your Net Worth $0 Debt-to-Asset Ratio: — Asset Allocation Cash \u0026amp; Savings 0% Investments 0% Property 0% Other 0% Liability Breakdown Mortgage 0% Student 0% Auto 0% Credit Cards 0% Other 0% How Do You Compare? Median net worth by age group in the United States (Federal Reserve data).\nAge Group Median Net Worth (US) Your Position Select your age group above or scroll to see all benchmarks. Data sourced from Federal Reserve Survey of Consumer Finances. Your Age Group — Select age group — Under 35 35–44 45–54 55–64 65–74 75+ Goal Tracker Net Worth Goal ($) $ Progress toward goal 0% You're 0% of the way there. How to Use This Calculator Enter your assets — fill in what you own: bank balances, investment accounts, property values, and other valuables. Use current market values, not purchase prices. Enter your liabilities — list every debt: mortgage balance, student loans, car loans, and credit card balances. Select your age group — see how your net worth compares to US median benchmarks. Set a goal — enter a target net worth to track your progress. The calculator updates instantly as you type. Your data never leaves your browser.\nWhat Is Net Worth and Why Does It Matter? Net worth is the single most important number in personal finance. It is simply what you own (assets) minus what you owe (liabilities). A positive net worth means your assets exceed your debts; a negative net worth — common for young adults with student loans — means you owe more than you own. Tracking this number over time is the best way to measure whether you are genuinely building wealth or just keeping up with payments.\nNet worth matters because income alone does not tell the full story. Someone earning $200,000 per year but spending $210,000 is losing ground, while someone earning $60,000 and investing consistently builds real wealth. By calculating your net worth regularly — once a quarter or once a year — you get an honest snapshot of your financial progress that no single paycheck or bank balance can provide.\nHow to Increase Your Net Worth 1. Earn more Every dollar of additional income that you do not spend goes directly toward building net worth. Negotiate your salary, develop a marketable skill, or start a side hustle. Even an extra $300 per month invested over 20 years at 7% becomes over $175,000.\n2. Spend less Reducing expenses is often faster than increasing income. Audit your subscriptions, refinance high-rate debt, and distinguish wants from needs. The goal is not deprivation — it is ensuring your spending is intentional and aligned with your values.\n3. Pay off high-interest debt Credit card balances at 20%+ APR destroy net worth faster than almost anything else. Prioritize eliminating high-interest debt using the avalanche method (highest rate first) before aggressively investing. Every dollar paid off high-interest debt is a guaranteed return equal to that interest rate.\n4. Invest consistently Wealth is built through ownership — of index funds, real estate, a business, or other appreciating assets. Automate contributions to your 401(k) and IRA every month, and let compound growth do the heavy lifting over time. Time in the market consistently outperforms trying to time the market.\nRelated Tools Plan your monthly spending to free up more to invest — Budget Planner Calculate how long to reach a savings target — Savings Goal Calculator See your fastest path to becoming debt-free — Debt Payoff Calculator Project your retirement nest egg — Retirement Calculator See how compound growth builds wealth over time — Compound Interest Calculator Estimate your monthly mortgage payment — Mortgage Calculator Plan your path to financial independence → FIRE Calculator Related Articles How to Start Investing With $100 How to Build an Emergency Fund Fast How to Pay Off Debt Fast Best Budgeting Apps 2026 Best High-Yield Savings Accounts 2026 Productivity Works Free Tools All our financial calculators are 100% free, run entirely in your browser, and never store your data.\nBrowse all free tools This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/tools/net-worth-calculator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links. We may earn a commission at no extra cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"net-worth-calculator\"\u003eNet Worth Calculator\u003c/h1\u003e\n\u003cp\u003eEnter your assets and liabilities below to instantly calculate your \u003cstrong\u003enet worth\u003c/strong\u003e and see how your financial health stacks up.\u003c/p\u003e\n\u003cdiv id=\"nw-calc\" style=\"max-width:760px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:#1e293b;\"\u003e\n\u003c!-- ASSETS SECTION --\u003e\n\u003cdiv style=\"padding:24px;border:2px solid #059669;border-radius:12px;background:#f0fdf4;margin-bottom:20px;\"\u003e\n\u003ch2 style=\"margin:0 0 20px 0;font-size:20px;color:#059669;\"\u003eAssets\u003c/h2\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003cdiv style=\"font-weight:bold;font-size:13px;color:#64748b;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:12px;\"\u003eCash \u0026amp; Savings\u003c/div\u003e\n\u003cdiv style=\"display:grid;gap:10px;\"\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cspan style=\"font-size:16px;color:#64748b;min-width:14px;\"\u003e$\u003c/span\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003clabel style=\"display:block;font-size:13px;color:#64748b;margin-bottom:4px;\"\u003eChecking account\u003c/label\u003e\u003cinput type=\"number\" id=\"aCash\" min=\"0\" step=\"100\" value=\"0\" oninput=\"calcNW()\" style=\"width:100%;padding:9px 12px;border:1px solid #a7f3d0;border-radius:8px;font-size:15px;box-sizing:border-box;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cspan style=\"font-size:16px;color:#64748b;min-width:14px;\"\u003e$\u003c/span\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003clabel style=\"display:block;font-size:13px;color:#64748b;margin-bottom:4px;\"\u003eSavings account\u003c/label\u003e\u003cinput type=\"number\" id=\"aSavings\" min=\"0\" step=\"100\" value=\"0\" oninput=\"calcNW()\" style=\"width:100%;padding:9px 12px;border:1px solid #a7f3d0;border-radius:8px;font-size:15px;box-sizing:border-box;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cspan style=\"font-size:16px;color:#64748b;min-width:14px;\"\u003e$\u003c/span\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003clabel style=\"display:block;font-size:13px;color:#64748b;margin-bottom:4px;\"\u003eEmergency fund\u003c/label\u003e\u003cinput type=\"number\" id=\"aEmergency\" min=\"0\" step=\"100\" value=\"0\" oninput=\"calcNW()\" style=\"width:100%;padding:9px 12px;border:1px solid #a7f3d0;border-radius:8px;font-size:15px;box-sizing:border-box;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003cdiv style=\"font-weight:bold;font-size:13px;color:#64748b;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:12px;\"\u003eInvestments\u003c/div\u003e\n\u003cdiv style=\"display:grid;gap:10px;\"\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cspan style=\"font-size:16px;color:#64748b;min-width:14px;\"\u003e$\u003c/span\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003clabel style=\"display:block;font-size:13px;color:#64748b;margin-bottom:4px;\"\u003e401(k) / 403(b)\u003c/label\u003e\u003cinput type=\"number\" id=\"a401k\" min=\"0\" step=\"100\" value=\"0\" oninput=\"calcNW()\" style=\"width:100%;padding:9px 12px;border:1px solid #a7f3d0;border-radius:8px;font-size:15px;box-sizing:border-box;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cspan style=\"font-size:16px;color:#64748b;min-width:14px;\"\u003e$\u003c/span\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003clabel style=\"display:block;font-size:13px;color:#64748b;margin-bottom:4px;\"\u003eIRA / Roth IRA\u003c/label\u003e\u003cinput type=\"number\" id=\"aIRA\" min=\"0\" step=\"100\" value=\"0\" oninput=\"calcNW()\" style=\"width:100%;padding:9px 12px;border:1px solid #a7f3d0;border-radius:8px;font-size:15px;box-sizing:border-box;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cspan style=\"font-size:16px;color:#64748b;min-width:14px;\"\u003e$\u003c/span\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003clabel style=\"display:block;font-size:13px;color:#64748b;margin-bottom:4px;\"\u003eBrokerage account\u003c/label\u003e\u003cinput type=\"number\" id=\"aBrokerage\" min=\"0\" step=\"100\" value=\"0\" oninput=\"calcNW()\" style=\"width:100%;padding:9px 12px;border:1px solid #a7f3d0;border-radius:8px;font-size:15px;box-sizing:border-box;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cspan style=\"font-size:16px;color:#64748b;min-width:14px;\"\u003e$\u003c/span\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003clabel style=\"display:block;font-size:13px;color:#64748b;margin-bottom:4px;\"\u003eCrypto\u003c/label\u003e\u003cinput type=\"number\" id=\"aCrypto\" min=\"0\" step=\"100\" value=\"0\" oninput=\"calcNW()\" style=\"width:100%;padding:9px 12px;border:1px solid #a7f3d0;border-radius:8px;font-size:15px;box-sizing:border-box;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003cdiv style=\"font-weight:bold;font-size:13px;color:#64748b;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:12px;\"\u003eProperty\u003c/div\u003e\n\u003cdiv style=\"display:grid;gap:10px;\"\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cspan style=\"font-size:16px;color:#64748b;min-width:14px;\"\u003e$\u003c/span\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003clabel style=\"display:block;font-size:13px;color:#64748b;margin-bottom:4px;\"\u003eHome value\u003c/label\u003e\u003cinput type=\"number\" id=\"aHome\" min=\"0\" step=\"1000\" value=\"0\" oninput=\"calcNW()\" style=\"width:100%;padding:9px 12px;border:1px solid #a7f3d0;border-radius:8px;font-size:15px;box-sizing:border-box;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cspan style=\"font-size:16px;color:#64748b;min-width:14px;\"\u003e$\u003c/span\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003clabel style=\"display:block;font-size:13px;color:#64748b;margin-bottom:4px;\"\u003eOther real estate\u003c/label\u003e\u003cinput type=\"number\" id=\"aRealEstate\" min=\"0\" step=\"1000\" value=\"0\" oninput=\"calcNW()\" style=\"width:100%;padding:9px 12px;border:1px solid #a7f3d0;border-radius:8px;font-size:15px;box-sizing:border-box;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003cdiv style=\"font-weight:bold;font-size:13px;color:#64748b;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:12px;\"\u003eOther Assets\u003c/div\u003e\n\u003cdiv style=\"display:grid;gap:10px;\"\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cspan style=\"font-size:16px;color:#64748b;min-width:14px;\"\u003e$\u003c/span\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003clabel style=\"display:block;font-size:13px;color:#64748b;margin-bottom:4px;\"\u003eVehicles\u003c/label\u003e\u003cinput type=\"number\" id=\"aVehicles\" min=\"0\" step=\"500\" value=\"0\" oninput=\"calcNW()\" style=\"width:100%;padding:9px 12px;border:1px solid #a7f3d0;border-radius:8px;font-size:15px;box-sizing:border-box;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cspan style=\"font-size:16px;color:#64748b;min-width:14px;\"\u003e$\u003c/span\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003clabel style=\"display:block;font-size:13px;color:#64748b;margin-bottom:4px;\"\u003eValuables (jewelry, art, collectibles)\u003c/label\u003e\u003cinput type=\"number\" id=\"aValuables\" min=\"0\" step=\"100\" value=\"0\" oninput=\"calcNW()\" style=\"width:100%;padding:9px 12px;border:1px solid #a7f3d0;border-radius:8px;font-size:15px;box-sizing:border-box;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cspan style=\"font-size:16px;color:#64748b;min-width:14px;\"\u003e$\u003c/span\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003clabel style=\"display:block;font-size:13px;color:#64748b;margin-bottom:4px;\"\u003eBusiness equity\u003c/label\u003e\u003cinput type=\"number\" id=\"aBusiness\" min=\"0\" step=\"1000\" value=\"0\" oninput=\"calcNW()\" style=\"width:100%;padding:9px 12px;border:1px solid #a7f3d0;border-radius:8px;font-size:15px;box-sizing:border-box;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:16px;background:#059669;border-radius:8px;display:flex;justify-content:space-between;align-items:center;\"\u003e\n\u003cspan style=\"color:white;font-weight:bold;font-size:16px;\"\u003eTotal Assets\u003c/span\u003e\n\u003cspan id=\"totalAssets\" style=\"color:white;font-weight:bold;font-size:24px;\"\u003e$0\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- LIABILITIES SECTION --\u003e\n\u003cdiv style=\"padding:24px;border:2px solid #dc2626;border-radius:12px;background:#fef2f2;margin-bottom:20px;\"\u003e\n\u003ch2 style=\"margin:0 0 20px 0;font-size:20px;color:#dc2626;\"\u003eLiabilities\u003c/h2\u003e\n\u003cdiv style=\"display:grid;gap:10px;margin-bottom:20px;\"\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cspan style=\"font-size:16px;color:#64748b;min-width:14px;\"\u003e$\u003c/span\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003clabel style=\"display:block;font-size:13px;color:#64748b;margin-bottom:4px;\"\u003eMortgage balance\u003c/label\u003e\u003cinput type=\"number\" id=\"lMortgage\" min=\"0\" step=\"1000\" value=\"0\" oninput=\"calcNW()\" style=\"width:100%;padding:9px 12px;border:1px solid #fca5a5;border-radius:8px;font-size:15px;box-sizing:border-box;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cspan style=\"font-size:16px;color:#64748b;min-width:14px;\"\u003e$\u003c/span\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003clabel style=\"display:block;font-size:13px;color:#64748b;margin-bottom:4px;\"\u003eStudent loans\u003c/label\u003e\u003cinput type=\"number\" id=\"lStudent\" min=\"0\" step=\"500\" value=\"0\" oninput=\"calcNW()\" style=\"width:100%;padding:9px 12px;border:1px solid #fca5a5;border-radius:8px;font-size:15px;box-sizing:border-box;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cspan style=\"font-size:16px;color:#64748b;min-width:14px;\"\u003e$\u003c/span\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003clabel style=\"display:block;font-size:13px;color:#64748b;margin-bottom:4px;\"\u003eAuto loans\u003c/label\u003e\u003cinput type=\"number\" id=\"lAuto\" min=\"0\" step=\"500\" value=\"0\" oninput=\"calcNW()\" style=\"width:100%;padding:9px 12px;border:1px solid #fca5a5;border-radius:8px;font-size:15px;box-sizing:border-box;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cspan style=\"font-size:16px;color:#64748b;min-width:14px;\"\u003e$\u003c/span\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003clabel style=\"display:block;font-size:13px;color:#64748b;margin-bottom:4px;\"\u003eCredit card debt\u003c/label\u003e\u003cinput type=\"number\" id=\"lCredit\" min=\"0\" step=\"100\" value=\"0\" oninput=\"calcNW()\" style=\"width:100%;padding:9px 12px;border:1px solid #fca5a5;border-radius:8px;font-size:15px;box-sizing:border-box;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;align-items:center;gap:8px;\"\u003e\n\u003cspan style=\"font-size:16px;color:#64748b;min-width:14px;\"\u003e$\u003c/span\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003clabel style=\"display:block;font-size:13px;color:#64748b;margin-bottom:4px;\"\u003eOther debt\u003c/label\u003e\u003cinput type=\"number\" id=\"lOther\" min=\"0\" step=\"100\" value=\"0\" oninput=\"calcNW()\" style=\"width:100%;padding:9px 12px;border:1px solid #fca5a5;border-radius:8px;font-size:15px;box-sizing:border-box;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:16px;background:#dc2626;border-radius:8px;display:flex;justify-content:space-between;align-items:center;\"\u003e\n\u003cspan style=\"color:white;font-weight:bold;font-size:16px;\"\u003eTotal Liabilities\u003c/span\u003e\n\u003cspan id=\"totalLiabilities\" style=\"color:white;font-weight:bold;font-size:24px;\"\u003e$0\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- RESULTS PANEL --\u003e\n\u003cdiv id=\"nwResults\" style=\"padding:28px;border-radius:12px;background:#eef2ff;border:2px solid #4f46e5;margin-bottom:20px;\"\u003e\n\u003cdiv style=\"text-align:center;margin-bottom:24px;\"\u003e\n\u003cdiv style=\"font-size:14px;color:#64748b;text-transform:uppercase;letter-spacing:0.08em;margin-bottom:6px;\"\u003eYour Net Worth\u003c/div\u003e\n\u003cdiv id=\"netWorthDisplay\" style=\"font-size:52px;font-weight:bold;color:#4f46e5;line-height:1.1;\"\u003e$0\u003c/div\u003e\n\u003cdiv id=\"debtRatioDisplay\" style=\"font-size:14px;color:#64748b;margin-top:8px;\"\u003eDebt-to-Asset Ratio: —\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Asset allocation breakdown --\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003cdiv style=\"font-weight:bold;font-size:13px;color:#64748b;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:8px;\"\u003eAsset Allocation\u003c/div\u003e\n\u003cdiv id=\"assetBar\" style=\"height:28px;border-radius:8px;overflow:hidden;display:flex;background:#e2e8f0;\"\u003e\n\u003cdiv id=\"barCash\" style=\"background:#059669;height:100%;transition:width 0.3s;\" title=\"Cash \u0026amp; Savings\"\u003e\u003c/div\u003e\n\u003cdiv id=\"barInvest\" style=\"background:#10b981;height:100%;transition:width 0.3s;\" title=\"Investments\"\u003e\u003c/div\u003e\n\u003cdiv id=\"barProperty\" style=\"background:#34d399;height:100%;transition:width 0.3s;\" title=\"Property\"\u003e\u003c/div\u003e\n\u003cdiv id=\"barOther\" style=\"background:#6ee7b7;height:100%;transition:width 0.3s;\" title=\"Other\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;flex-wrap:wrap;gap:12px;font-size:12px;color:#64748b;margin-top:8px;\"\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#059669;border-radius:2px;margin-right:4px;\"\u003e\u003c/span\u003eCash \u0026amp; Savings \u003cspan id=\"pctCash\" style=\"font-weight:bold;\"\u003e0%\u003c/span\u003e\u003c/span\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#10b981;border-radius:2px;margin-right:4px;\"\u003e\u003c/span\u003eInvestments \u003cspan id=\"pctInvest\" style=\"font-weight:bold;\"\u003e0%\u003c/span\u003e\u003c/span\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#34d399;border-radius:2px;margin-right:4px;\"\u003e\u003c/span\u003eProperty \u003cspan id=\"pctProperty\" style=\"font-weight:bold;\"\u003e0%\u003c/span\u003e\u003c/span\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#6ee7b7;border-radius:2px;margin-right:4px;\"\u003e\u003c/span\u003eOther \u003cspan id=\"pctOther\" style=\"font-weight:bold;\"\u003e0%\u003c/span\u003e\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Liability breakdown --\u003e\n\u003cdiv id=\"liabBreakdownWrap\" style=\"display:none;\"\u003e\n\u003cdiv style=\"font-weight:bold;font-size:13px;color:#64748b;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:8px;\"\u003eLiability Breakdown\u003c/div\u003e\n\u003cdiv id=\"liabBar\" style=\"height:28px;border-radius:8px;overflow:hidden;display:flex;background:#e2e8f0;\"\u003e\n\u003cdiv id=\"lbarMortgage\" style=\"background:#dc2626;height:100%;transition:width 0.3s;\" title=\"Mortgage\"\u003e\u003c/div\u003e\n\u003cdiv id=\"lbarStudent\" style=\"background:#ef4444;height:100%;transition:width 0.3s;\" title=\"Student Loans\"\u003e\u003c/div\u003e\n\u003cdiv id=\"lbarAuto\" style=\"background:#f87171;height:100%;transition:width 0.3s;\" title=\"Auto Loans\"\u003e\u003c/div\u003e\n\u003cdiv id=\"lbarCredit\" style=\"background:#fca5a5;height:100%;transition:width 0.3s;\" title=\"Credit Cards\"\u003e\u003c/div\u003e\n\u003cdiv id=\"lbarOther\" style=\"background:#fecaca;height:100%;transition:width 0.3s;\" title=\"Other\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;flex-wrap:wrap;gap:12px;font-size:12px;color:#64748b;margin-top:8px;\"\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#dc2626;border-radius:2px;margin-right:4px;\"\u003e\u003c/span\u003eMortgage \u003cspan id=\"lpctMortgage\" style=\"font-weight:bold;\"\u003e0%\u003c/span\u003e\u003c/span\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#ef4444;border-radius:2px;margin-right:4px;\"\u003e\u003c/span\u003eStudent \u003cspan id=\"lpctStudent\" style=\"font-weight:bold;\"\u003e0%\u003c/span\u003e\u003c/span\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#f87171;border-radius:2px;margin-right:4px;\"\u003e\u003c/span\u003eAuto \u003cspan id=\"lpctAuto\" style=\"font-weight:bold;\"\u003e0%\u003c/span\u003e\u003c/span\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#fca5a5;border-radius:2px;margin-right:4px;\"\u003e\u003c/span\u003eCredit Cards \u003cspan id=\"lpctCredit\" style=\"font-weight:bold;\"\u003e0%\u003c/span\u003e\u003c/span\u003e\n\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:#fecaca;border-radius:2px;margin-right:4px;\"\u003e\u003c/span\u003eOther \u003cspan id=\"lpctOther\" style=\"font-weight:bold;\"\u003e0%\u003c/span\u003e\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- BENCHMARK SECTION --\u003e\n\u003cdiv style=\"padding:24px;border-radius:12px;background:#f8fafc;border:1px solid #e2e8f0;margin-bottom:20px;\"\u003e\n\u003ch3 style=\"margin:0 0 16px 0;font-size:18px;color:#1e293b;\"\u003eHow Do You Compare?\u003c/h3\u003e\n\u003cp style=\"font-size:13px;color:#64748b;margin:0 0 16px 0;\"\u003eMedian net worth by age group in the United States (Federal Reserve data).\u003c/p\u003e","title":"Net Worth Calculator | Track Your Financial Health"},{"content":"This article contains affiliate links.\nNISA Investment Simulator Enter your monthly contribution, expected annual return, and investment horizon to instantly calculate your future asset value and tax-free benefit.\nMonthly Contribution 1,000 JPY30,000 JPY100,000 JPY Expected Annual Return (%) 1%5.0%10% Investment Period (years) 1 yr20 yrs30 yrs Total Future Value (pre-tax) 0 JPY Total Principal 0 JPY Investment Gains 0 JPY Tax-Free Benefit 0 JPY Assumptions: Monthly fixed contributions, compounded annually. Tax-free benefit = 20.315% of gains (Japan capital gains tax). Actual results depend on market conditions. What to Do After Running Your Simulation Once you see your projected results, the next step is to open a brokerage account that supports NISA and start investing in a low-cost index fund.\nNo brokerage account yet? Open a NISA-compatible account at a major online broker Unsure which fund to buy? Look for a global equity index fund (e.g. tracking MSCI ACWI) with annual fees below 0.2% Wondering about NISA limits? New NISA allows 1.2M JPY/yr in the tsumitate (accumulation) slot and 2.4M JPY/yr in the growth slot — 3.6M JPY/yr total with a 18M JPY lifetime cap Frequently Asked Questions Q: How accurate are these projections? This tool uses the standard future value of an annuity formula with monthly compounding. Real market returns fluctuate year to year; treat this as a directional estimate, not a guarantee.\nQ: Is a 5% annual return realistic? The S\u0026amp;P 500 has averaged roughly 10% annually over 30 years. A globally diversified index fund typically lands at 7–8%. After fees, 5% is a conservative baseline for long-term planning.\nQ: What are the NISA contribution limits? Under the New NISA (2024 onwards): Tsumitate (accumulation) slot — 1.2M JPY/year; Growth slot — 2.4M JPY/year; Combined — 3.6M JPY/year; Lifetime tax-free holding limit — 18M JPY.\nQ: Does this simulator work for non-NISA accounts? Yes. Use the \u0026ldquo;Investment Gains\u0026rdquo; figure as your gross profit. For a taxable account, subtract 20.315% from the gains. The \u0026ldquo;Tax-Free Benefit\u0026rdquo; card shows exactly how much you would save by using a NISA account instead.\nRelated Tools Compound Interest Calculator — Model any compound growth scenario with a full milestone table Furusato Tax Simulator — Combine NISA with Furusato Nozei for maximum tax efficiency Percentage Calculator — Calculate return rates, fee impacts, and more ","permalink":"https://productivity-works.com/tools/nisa-investment-simulator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"nisa-investment-simulator\"\u003eNISA Investment Simulator\u003c/h1\u003e\n\u003cp\u003eEnter your monthly contribution, expected annual return, and investment horizon to instantly calculate your \u003cstrong\u003efuture asset value\u003c/strong\u003e and \u003cstrong\u003etax-free benefit\u003c/strong\u003e.\u003c/p\u003e\n\u003cdiv id=\"nisa-simulator\" style=\"max-width:640px;margin:0 auto;padding:24px;border:2px solid #2563eb;border-radius:12px;background:#f8fafc;\"\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eMonthly Contribution\u003c/label\u003e\n\u003cinput type=\"range\" id=\"monthly\" min=\"1000\" max=\"100000\" step=\"1000\" value=\"30000\" oninput=\"calc()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e1,000 JPY\u003c/span\u003e\u003cspan id=\"monthlyVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e30,000 JPY\u003c/span\u003e\u003cspan\u003e100,000 JPY\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eExpected Annual Return (%)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"rate\" min=\"1\" max=\"10\" step=\"0.5\" value=\"5\" oninput=\"calc()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e1%\u003c/span\u003e\u003cspan id=\"rateVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e5.0%\u003c/span\u003e\u003cspan\u003e10%\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:24px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eInvestment Period (years)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"years\" min=\"1\" max=\"30\" step=\"1\" value=\"20\" oninput=\"calc()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e1 yr\u003c/span\u003e\u003cspan id=\"yearsVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e20 yrs\u003c/span\u003e\u003cspan\u003e30 yrs\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#1e40af;color:white;border-radius:8px;padding:20px;text-align:center;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"font-size:14px;margin-bottom:4px;\"\u003eTotal Future Value (pre-tax)\u003c/div\u003e\n\u003cdiv id=\"totalAsset\" style=\"font-size:36px;font-weight:bold;\"\u003e0 JPY\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"background:#e0f2fe;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eTotal Principal\u003c/div\u003e\n\u003cdiv id=\"principal\" style=\"font-size:16px;font-weight:bold;color:#0369a1;\"\u003e0 JPY\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#dcfce7;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eInvestment Gains\u003c/div\u003e\n\u003cdiv id=\"profit\" style=\"font-size:16px;font-weight:bold;color:#15803d;\"\u003e0 JPY\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#fef9c3;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eTax-Free Benefit\u003c/div\u003e\n\u003cdiv id=\"taxSaved\" style=\"font-size:16px;font-weight:bold;color:#a16207;\"\u003e0 JPY\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#f1f5f9;border-radius:8px;padding:12px;font-size:13px;color:#475569;\"\u003e\n\u003cstrong\u003eAssumptions:\u003c/strong\u003e Monthly fixed contributions, compounded annually. Tax-free benefit = 20.315% of gains (Japan capital gains tax). Actual results depend on market conditions.\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nfunction calc(){\n  var m=parseInt(document.getElementById('monthly').value);\n  var r=parseFloat(document.getElementById('rate').value)/100;\n  var y=parseInt(document.getElementById('years').value);\n  var mr=r/12;\n  var n=y*12;\n  var fv=m*((Math.pow(1+mr,n)-1)/mr);\n  var p=m*n;\n  var g=fv-p;\n  var tax=Math.floor(g*0.20315);\n  document.getElementById('monthlyVal').textContent=m.toLocaleString()+' JPY';\n  document.getElementById('rateVal').textContent=(r*100).toFixed(1)+'%';\n  document.getElementById('yearsVal').textContent=y+' yrs';\n  document.getElementById('totalAsset').textContent=Math.floor(fv).toLocaleString()+' JPY';\n  document.getElementById('principal').textContent=p.toLocaleString()+' JPY';\n  document.getElementById('profit').textContent=Math.floor(g).toLocaleString()+' JPY';\n  document.getElementById('taxSaved').textContent=tax.toLocaleString()+' JPY';\n}\ncalc();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"what-to-do-after-running-your-simulation\"\u003eWhat to Do After Running Your Simulation\u003c/h2\u003e\n\u003cp\u003eOnce you see your projected results, the next step is to open a brokerage account that supports NISA and start investing in a low-cost index fund.\u003c/p\u003e","title":"NISA Investment Simulator | Calculate Your Future Wealth for Free"},{"content":"Disclosure: This page may contain affiliate links. If you make a purchase through these links, we may earn a commission at no extra cost to you. We only recommend tools and services we genuinely find useful.\nPercentage Calculator Whether you\u0026rsquo;re figuring out a restaurant tip, calculating a sale discount, or tracking how much your expenses changed month over month, percentages come up constantly in everyday life. This free calculator gives you five dedicated modes so you always get the right answer — no mental math required.\nX% of Y X is ?% of Y % Change Tip Calculator Discount Calculator \u0026lt;!-- TAB 1: What is X% of Y? --\u0026gt; \u0026lt;div id=\u0026quot;t1\u0026quot; class=\u0026quot;pct-panel active\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;What is X% of Y?\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;pct-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Percentage (%)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;t1-pct\u0026quot; placeholder=\u0026quot;e.g. 15\u0026quot; min=\u0026quot;0\u0026quot; step=\u0026quot;any\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;unit\u0026quot;\u0026gt;%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pct-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;of Number\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;t1-num\u0026quot; placeholder=\u0026quot;e.g. 200\u0026quot; step=\u0026quot;any\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;pct-btn\u0026quot; onclick=\u0026quot;calcT1()\u0026quot;\u0026gt;Calculate\u0026lt;/button\u0026gt; \u0026lt;div class=\u0026quot;pct-result\u0026quot; id=\u0026quot;t1-result\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;res-main\u0026quot; id=\u0026quot;t1-main\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;res-formula\u0026quot; id=\u0026quot;t1-formula\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- TAB 2: X is what % of Y? --\u0026gt; \u0026lt;div id=\u0026quot;t2\u0026quot; class=\u0026quot;pct-panel\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;X is what % of Y?\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;pct-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Number X\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;t2-x\u0026quot; placeholder=\u0026quot;e.g. 30\u0026quot; step=\u0026quot;any\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pct-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Number Y\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;t2-y\u0026quot; placeholder=\u0026quot;e.g. 200\u0026quot; step=\u0026quot;any\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;pct-btn\u0026quot; onclick=\u0026quot;calcT2()\u0026quot;\u0026gt;Calculate\u0026lt;/button\u0026gt; \u0026lt;div class=\u0026quot;pct-result\u0026quot; id=\u0026quot;t2-result\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;res-main\u0026quot; id=\u0026quot;t2-main\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;res-formula\u0026quot; id=\u0026quot;t2-formula\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- TAB 3: % Change --\u0026gt; \u0026lt;div id=\u0026quot;t3\u0026quot; class=\u0026quot;pct-panel\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Percentage Change\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;pct-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Original Value\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;t3-orig\u0026quot; placeholder=\u0026quot;e.g. 80\u0026quot; step=\u0026quot;any\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pct-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;New Value\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;t3-new\u0026quot; placeholder=\u0026quot;e.g. 100\u0026quot; step=\u0026quot;any\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;pct-btn\u0026quot; onclick=\u0026quot;calcT3()\u0026quot;\u0026gt;Calculate\u0026lt;/button\u0026gt; \u0026lt;div class=\u0026quot;pct-result\u0026quot; id=\u0026quot;t3-result\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;res-main\u0026quot; id=\u0026quot;t3-main\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;res-formula\u0026quot; id=\u0026quot;t3-formula\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- TAB 4: Tip Calculator --\u0026gt; \u0026lt;div id=\u0026quot;t4\u0026quot; class=\u0026quot;pct-panel\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Tip Calculator\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;pct-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Bill Amount ($)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;t4-bill\u0026quot; placeholder=\u0026quot;e.g. 85.00\u0026quot; min=\u0026quot;0\u0026quot; step=\u0026quot;0.01\u0026quot; oninput=\u0026quot;calcT4()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;unit\u0026quot;\u0026gt;$\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pct-slider-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Tip Percentage\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;t4-tip-slider\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;30\u0026quot; value=\u0026quot;18\u0026quot; step=\u0026quot;1\u0026quot; oninput=\u0026quot;t4SliderUpdate()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;slider-val\u0026quot; id=\u0026quot;t4-tip-display\u0026quot;\u0026gt;18%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pct-slider-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Split Between\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;t4-people-slider\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;20\u0026quot; value=\u0026quot;1\u0026quot; step=\u0026quot;1\u0026quot; oninput=\u0026quot;t4PeopleUpdate()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;slider-val\u0026quot; id=\u0026quot;t4-people-display\u0026quot;\u0026gt;1 person\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;pct-btn\u0026quot; onclick=\u0026quot;calcT4()\u0026quot;\u0026gt;Calculate\u0026lt;/button\u0026gt; \u0026lt;div class=\u0026quot;pct-multi-result\u0026quot; id=\u0026quot;t4-result\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;pct-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;card-label\u0026quot;\u0026gt;Tip Amount\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;card-val\u0026quot; id=\u0026quot;t4-tip-amt\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pct-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;card-label\u0026quot;\u0026gt;Total Bill\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;card-val\u0026quot; id=\u0026quot;t4-total\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pct-card highlight\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;card-label\u0026quot;\u0026gt;Per Person\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;card-val\u0026quot; id=\u0026quot;t4-per-person\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- TAB 5: Discount Calculator --\u0026gt; \u0026lt;div id=\u0026quot;t5\u0026quot; class=\u0026quot;pct-panel\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Discount Calculator\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;pct-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Original Price ($)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;t5-price\u0026quot; placeholder=\u0026quot;e.g. 120.00\u0026quot; min=\u0026quot;0\u0026quot; step=\u0026quot;0.01\u0026quot; oninput=\u0026quot;calcT5()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;unit\u0026quot;\u0026gt;$\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pct-slider-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Discount\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;t5-disc-slider\u0026quot; min=\u0026quot;5\u0026quot; max=\u0026quot;90\u0026quot; value=\u0026quot;20\u0026quot; step=\u0026quot;1\u0026quot; oninput=\u0026quot;t5SliderUpdate()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;slider-val\u0026quot; id=\u0026quot;t5-disc-display\u0026quot;\u0026gt;20%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;pct-btn\u0026quot; onclick=\u0026quot;calcT5()\u0026quot;\u0026gt;Calculate\u0026lt;/button\u0026gt; \u0026lt;div class=\u0026quot;pct-multi-result\u0026quot; id=\u0026quot;t5-result\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;pct-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;card-label\u0026quot;\u0026gt;Discount Amount\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;card-val\u0026quot; id=\u0026quot;t5-disc-amt\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pct-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;card-label\u0026quot;\u0026gt;Final Price\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;card-val\u0026quot; id=\u0026quot;t5-final\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pct-card highlight\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;card-label\u0026quot;\u0026gt;You Save\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;card-val\u0026quot; id=\u0026quot;t5-save\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Quick Percentage Reference % of amount $50 $100 $500 $1,000 10%$5.00$10.00$50.00$100.00 15%$7.50$15.00$75.00$150.00 20%$10.00$20.00$100.00$200.00 25%$12.50$25.00$125.00$250.00 33%$16.50$33.00$165.00$330.00 50%$25.00$50.00$250.00$500.00 75%$37.50$75.00$375.00$750.00 Related Tools Create a monthly budget → Budget Planner Calculate compound interest → Compound Interest Calculator Estimate your tax bracket → Tax Bracket Calculator Related Articles Best Credit Cards 2026: Complete Comparison Guide by Category How to Use ChatGPT for Data Analysis 2026 Excel Skills That Will Double Your Salary ","permalink":"https://productivity-works.com/tools/percentage-calculator/","summary":"\u003cp\u003e\u003cem\u003eDisclosure: This page may contain affiliate links. If you make a purchase through these links, we may earn a commission at no extra cost to you. We only recommend tools and services we genuinely find useful.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"percentage-calculator\"\u003ePercentage Calculator\u003c/h1\u003e\n\u003cp\u003eWhether you\u0026rsquo;re figuring out a restaurant tip, calculating a sale discount, or tracking how much your expenses changed month over month, percentages come up constantly in everyday life. This free calculator gives you five dedicated modes so you always get the right answer — no mental math required.\u003c/p\u003e","title":"Percentage Calculator | Calculate Any Percentage Instantly"},{"content":"This article contains affiliate links. If you use a referral link to sign up for a service, we may earn a commission at no additional cost to you.\nYou have a stable job in Tokyo, you have been here a few years, and now the thought keeps surfacing: should I buy property in Japan? Maybe you have heard colleagues talk about Tokyo\u0026rsquo;s consistently high occupancy rates. Maybe a Japanese co-worker casually mentioned they bought a studio apartment in Shinjuku-ku as an investment. Or maybe you stumbled across RENOSY and wondered whether a foreigner can even participate.\nThis review is written for exactly that situation. We will walk through what RENOSY is, whether expats can realistically use it, what yields look like in practice, the tax side that most review articles ignore, and how property investment stacks up against alternatives like NISA.\nLet\u0026rsquo;s be direct upfront: Japanese real estate investment is not a get-rich-quick scheme. Done well, it is a legitimate long-term asset building strategy. Done poorly — or chosen for the wrong reasons — it can tie up capital in an illiquid asset with maintenance costs that erode your returns. This review will give you the honest picture.\n1. What Is RENOSY? RENOSY is a real estate investment platform operated by GA technologies Co., Ltd. (GA テクノロジーズ), a company listed on the Tokyo Stock Exchange Growth Market (ticker: 3491). GA technologies was founded in 2013 and has grown into one of the more prominent property technology (PropTech) firms in Japan.\nThe RENOSY brand sits at the intersection of two things that had traditionally been difficult to do simultaneously: invest in real estate and not spend enormous amounts of time managing it. The platform targets salaried workers — what the Japanese market calls \u0026ldquo;salary investors\u0026rdquo; (サラリーマン投資家) — who want property as an asset class but cannot function as active landlords.\nRENOSY\u0026rsquo;s core pitch is:\nAI-assisted property selection, leaning on data from GA technologies\u0026rsquo; transaction history and market analytics A fully managed end-to-end service covering property sourcing, purchase, loan arrangement, tenant management, and ongoing asset monitoring An app where investors can track their portfolio without needing to call an agent The properties RENOSY focuses on are predominantly urban compact apartments (ワンルーム・1K マンション) concentrated in Tokyo and other major metropolitan areas. These are typically pre-owned units in established buildings rather than newly built properties, which has pricing and depreciation implications we will discuss in the tax section.\nGA technologies also operates related services including RENOSY WEALTH for higher-end transactions and has expanded into several other real estate digital services. As of 2026, the company has processed substantial transaction volume and holds a recognized position in Japan\u0026rsquo;s PropTech sector.\n2. Can Expats Use RENOSY? Residency, Visa, and Language Considerations This is the question most expat-focused reviews either skip or answer vaguely. Here is a grounded breakdown.\nResidency Requirements To purchase real estate in Japan as a foreign national, you do not need Japanese citizenship. Foreign nationals — including non-permanent residents — can legally own property in Japan. This is a relatively open policy compared to many other countries.\nHowever, financing is a different matter entirely. Most Japanese banks and lending institutions require borrowers to have permanent residency (永住権) to qualify for a mortgage. Some regional banks and a handful of specialized lenders will work with visa holders on certain visa categories (e.g., Engineer/Specialist in Humanities, Highly Skilled Professional) with long remaining validity, but the options are more limited and the terms are often less favorable.\nRENOSY\u0026rsquo;s general framework works within this reality. If you have permanent residency, you have access to the broadest range of financing options. If you are on a work visa, your practical options narrow. It is worth having a candid conversation with RENOSY\u0026rsquo;s sales consultation team about your specific visa status before proceeding. Entering a consultation costs nothing, and getting clarity on financing eligibility early saves everyone time.\nAll-Cash Purchases If you have the capital to purchase without a loan, visa status becomes far less of a hurdle from a legal and financial standpoint. The properties RENOSY typically handles range from roughly ¥15 million to ¥30 million for compact urban apartments, which is meaningful capital to deploy in cash, but not impossible for some expats who have been saving for several years or who are coming from higher-cost-of-living markets.\nLanguage Support This is where we have to be honest: RENOSY\u0026rsquo;s primary language is Japanese. The app, contracts, management reports, and customer support are all in Japanese. There is no dedicated English-language service pathway as of 2026.\nThis creates a practical barrier. Purchasing real estate involves contracts with precise legal language. Rental management communications, repair notices, and annual statements all arrive in Japanese. If your Japanese reading level is intermediate or below, you will need to engage a translator or bilingual advisor for key documents.\nThat said, the managed nature of the service — where RENOSY handles tenant communication, building management liaison, and routine maintenance coordination — does reduce the volume of Japanese correspondence you personally need to act on compared to self-managing a property. The day-to-day operational load in Japanese is lower than it would be if you purchased independently.\nOur recommendation: if you are considering RENOSY but your Japanese is limited, factor in the cost of using a tax accountant (税理士) who is bilingual and familiar with rental income reporting. That cost is real but deductible, and the peace of mind it buys is substantial.\nPractical Checklist for Expat Eligibility Permanent residency: Broadest financing access, minimal barriers Long-term work visa (3-year or 5-year): Consult on financing options; all-cash purchase is feasible Short-term or student visa: Financing will be very difficult; property purchase not practically advisable Japanese language ability: Required to meaningfully engage with contracts and management; professional translation/interpretation is a workable substitute 3. Yield Expectations: What the Numbers Actually Look Like Yield is where real estate investment marketing gets sloppy, so let\u0026rsquo;s be careful with terms and data.\nSurface Yield vs. Net Yield Surface yield (表面利回り) is the raw annual rental income divided by the purchase price, expressed as a percentage. It does not account for any costs.\nNet yield (実質利回り) deducts operating expenses — management fees, building maintenance reserve contributions, property taxes, vacancy periods, and insurance — from the rental income before dividing by total acquisition cost (which includes purchase price plus transaction costs like agent fees and taxes).\nThe gap between these two figures is significant. A property advertised at a 5% surface yield may deliver a net yield of 3% or less once realistic costs are applied.\nTokyo Market Context (General Data) Tokyo\u0026rsquo;s urban compact apartment market has sustained relatively resilient occupancy rates driven by persistent urbanization, a shrinking national population concentrated increasingly in metropolitan areas, and structural undersupply of well-located smaller units near transit hubs.\nFor the Tokyo market broadly, industry data and real estate research reports have typically indicated:\nSurface yields: Approximately 4–6% for compact urban apartments in central and near-central wards Net yields: Approximately 2–3.5% after realistic operating costs Occupancy rates: High in prime transit-accessible locations, historically cited in official figures at strong levels in core Tokyo wards These are general market figures, not figures specific to RENOSY\u0026rsquo;s portfolio performance. RENOSY does not publicly publish audited average yield data for its managed properties that we can independently verify, and we will not fabricate a specific number. When you enter a consultation, ask RENOSY to show you historical yield data for comparable properties they have sold and managed. That is the right data to evaluate.\nThe Appreciation Question Japanese real estate has a different relationship with land and building value compared to, say, US or Australian markets. Buildings in Japan depreciate in assessed value over time, while land value fluctuates based on location fundamentals. A reinforced concrete (RC) structure has a legal useful life of 47 years for tax purposes; a light steel structure has 19 years.\nThis means many of the properties RENOSY handles — pre-owned compact apartments in older buildings — may have limited remaining depreciable life for tax purposes, which cuts both ways: less depreciation to write off, but the asset may already reflect that in pricing. For brand-new properties, depreciation is more generous but purchase prices are higher and surface yields are typically lower.\nCapital appreciation in Tokyo\u0026rsquo;s prime wards has been real over the past decade, but it is location-specific, not guaranteed, and the market cycle matters. Buying with the expectation of capital gain rather than rental yield is speculation, not investment. Approach yield as the investment thesis and any appreciation as a potential bonus.\nCash Flow Reality Check Run a rough monthly cash flow scenario:\nExample (illustrative, not actual RENOSY property): Purchase price: ¥22,000,000 Mortgage (35yr, 2.5%): ¥78,000/month (approximate) Monthly rent: ¥85,000 Management fee (5%): ¥4,250/month Building maintenance: ¥8,000/month (reserve fund contribution) Property tax (monthly): ¥5,500 (annualized ÷ 12) Vacancy allowance (5%): ¥4,250/month Net monthly cash flow: ¥85,000 - ¥78,000 - ¥4,250 - ¥8,000 - ¥5,500 - ¥4,250 = -¥15,000/month (negative cash flow before tax benefits) Negative monthly cash flow is not uncommon in Tokyo compact apartment investment, particularly when leveraged. The investment case typically rests on three legs: tax benefits during the holding period (covered in Section 5), asset accumulation as the mortgage is paid down, and terminal value when the property is eventually sold. This is a different mental model than a high-yield dividend stock. It requires understanding the full picture, not just the monthly rent figure.\n4. Pros and Cons for Expats Pros Turnkey passive management. The biggest genuine advantage of RENOSY for expats is not having to manage a tenant relationship, arrange repairs, or communicate with building management in Japanese. For someone working full-time with limited Japanese language capacity, the managed service model dramatically reduces friction.\nAI-assisted selection. GA technologies has invested in data infrastructure. Their property sourcing uses analytics on location fundamentals, rental demand, and comparable transactions. This does not guarantee good outcomes, but it is more systematic than choosing a property based on intuition alone.\nUrban market focus. Compact apartments in Tokyo and major cities have structural demand support. This is not investing in a rural property with uncertain future rental demand.\nApp-based monitoring. Being able to check your asset status in an app without calling an agent is a genuine quality-of-life improvement for anyone who has dealt with traditional Japanese real estate agents.\nFinancing coordination. RENOSY works with lending partners and facilitates loan arrangement as part of their service, which reduces the burden on you to independently approach multiple banks.\nPortfolio building over time. RENOSY\u0026rsquo;s model is built around the concept of building a portfolio — starting with one unit and potentially adding more over time. Their consultants are set up to support multi-property strategies.\nCons No English-language service. As noted, this is a real operational barrier. Contracts, management reports, tax documentation — all in Japanese.\nHigh minimum commitment. A single compact Tokyo apartment typically starts at ¥15–20 million or more. This is a large capital commitment with significant illiquidity. Unlike NISA investments, you cannot partially liquidate.\nTransaction costs are high. Buying real estate in Japan involves substantial one-time costs: agent fee (仲介手数料, typically 3% + ¥60,000 + consumption tax), registration taxes, stamp duty, mortgage arrangement fees, and sundry administrative costs. These can add 5–8% to your effective acquisition cost and must be recovered before you are in positive return territory.\nExit is not guaranteed. When you decide to sell, finding a buyer at your target price takes time and depends on market conditions. The compact apartment market in Tokyo is relatively liquid compared to other Japanese real estate, but it is nothing like selling a stock.\nConsultation sales model. RENOSY uses a consultative sales approach where you schedule a meeting and a salesperson walks you through options. This is standard for this type of product, but it means you should come prepared with questions and not feel pressured to commit quickly. Take your time.\nLimited secondary research in English. Unlike mainstream Japanese investment products, finding independent third-party RENOSY reviews in English is difficult. Your due diligence will require engaging with Japanese-language material or working with a bilingual advisor.\n5. Tax Implications for Foreign Residents This section is important and routinely undercovered in expat real estate guides. We are covering general principles; consult a qualified tax accountant (税理士) for your specific situation.\nRental Income is Taxable Rental income from Japanese property is subject to Japanese income tax, regardless of your nationality. If you are a tax resident of Japan (which most long-term expats are), your rental income is aggregated with your employment income and taxed at your marginal income tax rate, which ranges from 5% to 45% plus a 10% resident tax (住民税).\nThis matters enormously for your effective return. If your marginal rate is 33%, paying 33% of your net rental income in tax substantially changes the investment economics.\nDepreciation: The Key Tax Strategy Here is where it gets more interesting. You can deduct depreciation (減価償却費) on the building portion of a property purchase against your rental income and, in some cases, against your employment income.\nFor pre-owned reinforced concrete apartments — the type RENOSY commonly handles — the remaining depreciable life can be short. A simplified formula for used buildings:\nRemaining depreciable years = Statutory life × 20% (for a building that has exceeded its statutory life) Example: RC building, statutory life 47 years, building age 30 years: Remaining life = (47 - 30) + (30 × 0.2) = 17 + 6 = 23 years Annual depreciation = Building value ÷ 23 years If the building value is ¥6,000,000 and remaining life is 23 years, annual depreciation is approximately ¥260,000. This reduces your taxable rental income by that amount each year.\nFor older buildings that have exceeded their statutory life, depreciation can be more aggressive (remaining life = statutory life × 20%), which has historically created tax benefits for high-income earners. However, tax law changes and the NTA (National Tax Agency) scrutiny of real estate tax shelters have tightened some of the aggressive strategies used in prior years. Work with a qualified tax accountant.\nRental Income Filing If your rental income (or the combination of rental and other side income) exceeds ¥200,000 in a calendar year, you are required to file a 確定申告 (kakutei shinkoku) — an annual tax return — by March 15 of the following year.\nThis is where freee becomes highly relevant for foreign residents investing in real estate. freee is one of Japan\u0026rsquo;s leading cloud-based accounting and tax filing platforms. Its interface is available in English for basic navigation, and it supports rental income tracking, depreciation calculations, and tax return preparation.\nFor expats managing rental income alongside employment income, freee\u0026rsquo;s features for tracking income and expenses, categorizing deductions, and generating the forms needed for the kakutei shinkoku are genuinely useful. It reduces the complexity of what would otherwise require either a specialist accountant for routine filing or a significant investment of time learning Japanese tax forms.\nTry freee for your rental income filing → (affiliate link)\nProperty Tax (固定資産税) Property owners pay an annual fixed asset tax (固定資産税) and, in urban planning areas, an urban planning tax (都市計画税). These are assessed on the assessed value of the land and building, not the market value. For a compact Tokyo apartment, this typically runs in the range of ¥60,000–¥120,000 per year depending on the property. It is a deductible expense against rental income.\nInheritance and Exit Tax Considerations For foreign residents with assets in multiple jurisdictions, owning Japanese real estate adds complexity to estate planning. Japan\u0026rsquo;s inheritance tax applies to Japanese real estate regardless of the decedent\u0026rsquo;s or heir\u0026rsquo;s nationality if the property is in Japan. Exit tax (国外転出時課税) applies to financial assets over ¥100 million but not directly to real estate. If you anticipate leaving Japan, discuss the exit planning implications with a tax advisor before committing to a property purchase.\n6. RENOSY vs. NISA and iDeCo: Building a Balanced Strategy Real estate investment and securities investment serve different purposes in a portfolio. They are not directly competing products, but when capital is finite, you need to prioritize.\nFor a deeper look at NISA and iDeCo specifically, see our guide: Best Investment Account in Japan for Foreign Residents: NISA and iDeCo Explained .\nAnd for long-term financial planning as a foreign resident more broadly: Retirement Planning in Japan as a Foreign Resident .\nHere is a framework for thinking about the two approaches:\nNISA: Accessible, Liquid, Tax-Advantaged Japan\u0026rsquo;s New NISA (introduced in 2024 and continuing through 2026) provides:\nAnnual contribution limit of ¥3.6 million (¥1.2M Tsumitate + ¥2.4M Growth) Lifetime limit of ¥18 million Completely tax-free returns — no tax on dividends, interest, or capital gains within the account Full liquidity: you can sell at any time and your annual contribution allowance is restored upon sale For most expats in Japan at the beginning of their investment journey, maximizing NISA before moving to real estate is a strong default position. The tax-free compounding within NISA is a concrete, guaranteed benefit. With index funds tracking global equities, you get broad diversification in a single vehicle.\n楽天証券 (Rakuten Securities) is one of the leading platforms for NISA investing in Japan. It offers a wide fund selection, competitive costs, and an interface that is navigable for English speakers. For expats new to Japanese investment accounts, it is frequently recommended as a starting point.\nOpen a Rakuten Securities NISA account → (affiliate link)\niDeCo: Tax-Deferred Retirement Savings iDeCo (individual-type Defined Contribution pension) provides a tax deduction on contributions now, with tax on withdrawal at retirement. Contribution limits vary by employment status (employed at a company with no corporate pension: ¥23,000/month; self-employed: ¥68,000/month). The trade-off is that funds are locked until age 60 (or 75 for recent rule changes).\nFor expats who plan to remain in Japan long-term and are looking at retirement planning, iDeCo is worth including in the strategy. For those uncertain about their tenure in Japan, the lock-up period is a genuine concern.\nA Possible Framework by Stage Stage 1 (Early years in Japan, building foundation): - Maximize NISA Tsumitate slot (¥1.2M/year) with a global index fund - Build emergency fund (6-12 months expenses in liquid JPY) - Consider iDeCo if long-term Japan residence is planned Stage 2 (Established, stable income, PR or long-term visa): - Continue NISA (Growth slot for lump sum equity positions) - Evaluate real estate if capital allows AND financing is accessible - Use freee for tax management as complexity increases Stage 3 (Multiple assets, portfolio management): - Real estate as one component, not the whole portfolio - NISA assets provide liquid counterbalance to illiquid property - Annual kakutei shinkoku with real estate deductions via freee Real estate and index fund investing are not mutually exclusive. The risk is letting an illiquid property purchase crowd out the tax-advantaged space in NISA, which is capped annually and cannot be retroactively maximized. Always fill NISA before considering leveraged real estate, unless you have highly specific reasons to do otherwise.\n7. The Honest Bottom Line: Who Should Consider RENOSY? RENOSY is a professionally run platform in a legitimate asset class. GA technologies is a publicly listed company with a track record and regulatory obligations. The service model addresses real pain points for investors who want property exposure without active management.\nThat said, it is not the right product for everyone.\nRENOSY is likely worth exploring if:\nYou have permanent residency or strong financing eligibility You have ¥20–30 million or more in capital, or solid financing capacity alongside an existing financial cushion You are already maximizing NISA and looking for additional asset class diversification You understand and accept illiquidity as a trade-off for the property\u0026rsquo;s characteristics You have (or can hire) sufficient Japanese language support for contracts and tax filing You are thinking in a 10–20+ year time horizon, not 3–5 years RENOSY is likely not appropriate if:\nYou are new to investing and haven\u0026rsquo;t maximized tax-advantaged accounts (NISA/iDeCo) Your visa status makes financing very difficult and you don\u0026rsquo;t have substantial cash to deploy You might leave Japan within the next 5 years You cannot comfortably engage with Japanese-language documents or afford professional support You are looking for high-liquidity returns The Tokyo compact apartment market\u0026rsquo;s fundamentals — urban concentration, persistent rental demand in transit-accessible areas, limited land supply in core wards — are real. They are not fiction. But they also don\u0026rsquo;t override the basics of investment discipline: know your time horizon, understand your costs, don\u0026rsquo;t over-leverage, and don\u0026rsquo;t invest money you cannot afford to have illiquid.\nGet Started with RENOSY If you are at the stage where a consultation makes sense — you have the residency status, the capital capacity, and you want to hear what\u0026rsquo;s actually available in the market — RENOSY offers free consultations with no obligation to proceed.\n[Book a free RENOSY consultation →] (URL TBD — affiliate link coming soon)\nGoing into the consultation prepared with specific questions — about financing options for your visa category, historical yield data on managed properties, management fee structure, and exit processes — will make the conversation far more productive.\nManage Your Rental Income and Tax Filing Once you own a rental property, you are in the tax filing business whether you like it or not. freee makes the annual kakutei shinkoku significantly less painful, with support for rental income categorization, depreciation tracking, and tax return generation.\nStart managing rental income with freee → (affiliate link)\nCalculate your mortgage payment → Mortgage Calculator Build Your Investment Foundation with NISA Before or alongside real estate, NISA is the most efficient savings vehicle available to residents of Japan. Rakuten Securities offers a strong platform for index fund investing within NISA, with low costs and a solid selection of domestic and international funds.\nOpen a NISA account at Rakuten Securities → (affiliate link)\nRelated Tools Estimate your mortgage payments → Mortgage Calculator Calculate your net worth → Net Worth Calculator Calculate compound interest → Compound Interest Calculator This article contains affiliate links. If you sign up for a service through a link in this article, we may earn a commission at no additional cost to you. All opinions are our own. This article is for informational purposes only and does not constitute financial or tax advice. Consult a qualified financial advisor and tax accountant before making investment decisions.\nYou May Also Like Real Estate Investment in Japan for Salaried Workers: How It Compares to NISA and iDeCo Best Index Funds for Beginners 2026 Passive Income Ideas That Actually Work 2026 ","permalink":"https://productivity-works.com/posts/renosy-real-estate-investment-review-expat-japan/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links. If you use a referral link to sign up for a service, we may earn a commission at no additional cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eYou have a stable job in Tokyo, you have been here a few years, and now the thought keeps surfacing: should I buy property in Japan? Maybe you have heard colleagues talk about Tokyo\u0026rsquo;s consistently high occupancy rates. Maybe a Japanese co-worker casually mentioned they bought a studio apartment in Shinjuku-ku as an investment. Or maybe you stumbled across RENOSY and wondered whether a foreigner can even participate.\u003c/p\u003e","title":"RENOSY Review for Expats: Is Real Estate Investment in Japan Worth It? (2026)"},{"content":"Japan is one of the most expensive countries in the world to retire in — and one of the least discussed when it comes to financial planning for foreign residents. Whether you arrived on a work visa and quietly became a permanent resident, or you are actively considering making Japan your long-term home, the gap between what the public pension system will pay and what you actually need is almost certainly larger than you think.\nThis guide breaks down the numbers honestly, shows you how to run your own projections with free tools, and gives you a concrete starting portfolio strategy using accounts available to non-Japanese residents.\n1. How Much the Japanese Public Pension Actually Pays Out Japan\u0026rsquo;s public pension system consists of two main layers:\nKokumin Nenkin (National Pension — Tier 1) Every resident of Japan aged 20–59, including foreign nationals, is legally required to enroll in the National Pension (kokumin nenkin). Contributions are flat-rate: around ¥16,980 per month as of 2024. In exchange, after age 65, you receive a fixed annual payout.\nThe full kokumin nenkin benefit requires 40 years of contributions (480 months). The 2024 full amount is approximately ¥816,000 per year — that is ¥68,000 per month. If you arrived in Japan at 30 and plan to stay until 65, you have a maximum of 35 years of coverage, which translates to roughly 35/40 of the full amount, or about ¥714,000 per year (¥59,500/month).\nIf you only spent 10 years in Japan before moving abroad, you receive 10/40 of the full amount — ¥204,000 per year — provided you have the minimum 10 years of coverage required to receive any benefit at all.\nKosei Nenkin (Employees\u0026rsquo; Pension — Tier 2) If you are employed by a Japanese company, you are also enrolled in the Employees\u0026rsquo; Pension Insurance (kosei nenkin), which is an earnings-related scheme on top of the national pension. Contributions are split with your employer. The average monthly payout for people who spent a full career contributing is roughly ¥145,000 per month (¥1.74 million per year), but this figure assumes 40 years of employment in Japan at average earnings. Most foreign residents will receive considerably less.\nWhat about leaving Japan before retirement? If you are not planning to stay in Japan permanently, there is a lump-sum withdrawal mechanism called the dassoku ichijikin (脱退一時金). This lets you reclaim a portion of your pension contributions within two years of leaving Japan. However, the maximum refund period is capped at five years of contributions regardless of how long you actually paid in. You also forfeit any future pension rights. If you have a long career in Japan, this is almost always a bad deal.\nJapan also has social security agreements with over 20 countries — including the United States, Germany, France, South Korea, Australia, and the UK — that allow pension contribution periods to be totalized. This means contributions paid in Japan can count toward the minimum coverage requirement in your home country and vice versa. If your home country has a totalization agreement with Japan, you may be able to preserve both pension entitlements without double-paying contributions.\nThe uncomfortable bottom line: Even under the most optimistic scenario — a full career in Japan, enrolled in kosei nenkin, retiring at 65 — the public pension will deliver somewhere between ¥145,000 and ¥200,000 per month before tax. Japan\u0026rsquo;s average household expenditure for a retired couple is over ¥250,000 per month. The gap is real, structural, and will not close by itself.\n2. Running the Numbers with the Retirement Calculator — The Gap Most People Ignore Before building a strategy, you need to understand your personal numbers. The public pension payout figures above are national averages. Your actual situation depends on your age, years of coverage, current salary, expected retirement age, and target monthly spending in retirement.\nStart by plugging your numbers into the Retirement Calculator to get a personalized estimate of:\nYour projected pension payout based on actual years of coverage in Japan Your target retirement corpus (the lump sum you need invested to bridge the gap) Your required monthly savings rate to reach that corpus Here is an illustrative example:\nVariable Value Current age 35 Target retirement age 65 Years enrolled in kokumin nenkin 30 (if staying until 65) Expected kokumin nenkin payout ~¥612,000/year (¥51,000/month) Expected kosei nenkin payout (10 years at avg salary) ~¥435,000/year (¥36,250/month) Total public pension estimate ~¥87,250/month Target monthly retirement spending ¥250,000/month Monthly gap ¥162,750/month Annual gap ¥1,953,000/year Years in retirement (65–90) 25 years Total gap (undiscounted) ~¥48.8 million To fund ¥48.8 million over 25 years of retirement, assuming a 3% annual drawdown rate, you need a retirement portfolio of roughly ¥65 million at age 65.\nThat sounds enormous. But over 30 years, with consistent investing and compound returns, it is achievable — and the Compound Interest Calculator makes the math visible. Investing ¥70,000 per month for 30 years at a 6% annual return produces a corpus of approximately ¥70 million. The numbers work. The discipline is what most people lack.\nThe gap most foreign residents ignore is not the pension shortfall itself — it is the failure to start modeling it early. A 10-year delay in starting contributions roughly doubles the monthly savings required to reach the same target. Use the calculator now, not at 50.\n3. NISA + iDeCo as Gap-Fillers: Contribution Rules for Non-Japanese Residents Japan offers two powerful tax-advantaged investment accounts: NISA (Nippon Individual Savings Account) and iDeCo (Individual-type Defined Contribution pension plan). Both are available to foreign residents under specific conditions.\nNISA: The Core Tool The 2024 NISA reform dramatically improved the system. Key parameters:\nAnnual contribution limit: ¥3.6 million (¥1.2 million tsumitate/growth investing slot + ¥2.4 million seichyo/growth stock slot) Lifetime contribution limit: ¥18 million Tax benefit: All capital gains and dividends within the NISA account are completely tax-free, with no time limit Investment options: Index funds, ETFs, individual stocks, REITs (options depend on the slot used) Eligibility for foreign residents: You must have an address registered in Japan (i.e., be enrolled in the jumin kihon daicho — the basic resident register) and be aged 18 or over. Foreign residents with a valid residence card who are registered residents in Japan are fully eligible. There is no citizenship requirement.\nImportant caveat: If you leave Japan permanently, your NISA account must generally be closed or transferred within a specific window. Gains accrued up to that point remain tax-free, but you cannot continue contributing from overseas. Plan your NISA usage around your expected timeline in Japan.\nFor the tsumitate (accumulative) slot, focus on low-cost global index funds. The standard recommendation is a combination of:\nA global all-market or MSCI World index fund (e.g., eMAXIS Slim All Country) A domestic bond or money market fund as a ballast (especially if you are within 10 years of retirement) Use the NISA Simulator to model how ¥100,000/month in the tsumitate slot compounds over 10, 20, and 30 years at different assumed return rates.\niDeCo: The Pension Supplement iDeCo is a self-directed defined contribution pension that allows you to invest in mutual funds with full income-tax deductibility on contributions, tax-free compounding during accumulation, and favorable lump-sum or annuity treatment on withdrawal at age 60 or later.\nContribution limits depend on your employment status:\nStatus Monthly limit Company employee (with company DC plan) ¥12,000–¥20,000 Company employee (no company DC plan) ¥23,000 Self-employed / freelancer ¥68,000 Homemaker (depending on situation) ¥23,000 Eligibility for foreign residents: The same residency requirement applies — you need to be a registered resident in Japan. Foreign nationals with permanent residency or a long-term work visa are fully eligible. You must be under 65 to contribute (from 2022, the upper age limit was raised from 60 to 65).\nThe income tax advantage is significant. If you are in the 20% income tax bracket and contribute ¥23,000/month to iDeCo (¥276,000/year), you save approximately ¥55,200 in income tax annually. That is an immediate 20% return before any investment performance — something no investment product can match.\nCaveat for those leaving Japan: iDeCo funds are locked until age 60 under most circumstances. If you leave Japan before 60, your options are limited: you can generally leave the account in a preserved state (maintaining the balance without adding contributions) until you reach withdrawal age. You cannot simply cash out. This makes iDeCo less suitable for residents who are uncertain about their long-term stay.\nStrategy summary: For foreign residents with a clear long-term horizon in Japan (aiming for permanent residency or planning to stay 10+ years), the optimal tax-advantaged stack is:\nMax out iDeCo for the income tax deduction Max out NISA tsumitate slot (¥100,000/month) for tax-free growth Invest any surplus in a taxable brokerage account using the same low-cost index fund strategy 4. Building a Simple Three-Bucket Portfolio with Rakuten Securities Once you have the tax-advantaged account structure in place, the next step is choosing where to hold your investments and how to allocate them. Rakuten Securities is one of the two dominant online brokers in Japan (alongside SBI Securities) and offers a clean, English-friendly interface with excellent fund selection, zero trading commissions on domestic stocks, and integration with Rakuten Bank for seamless cash management.\nOpening a Rakuten Securities account gives you access to NISA, iDeCo, and a standard taxable brokerage account under one roof. For foreign residents, you will need your residence card, My Number (Individual Number), and a Japanese bank account. The entire process can be completed online.\nOpen a Rakuten Securities account and start investing in your retirement today. Sign up here The Three-Bucket Framework A three-bucket approach separates your assets by time horizon and function, making it easier to manage both accumulation and decumulation without panicking during market downturns.\nBucket 1: Cash / Emergency Reserve (0–2 years)\nPurpose: Cover 6–12 months of living expenses plus any foreseeable large expenses (housing repairs, medical, flights home) Where to hold: Rakuten Bank savings account (linked to Rakuten Securities for seamless transfer) Target size: ¥1.5–3 million depending on your situation Return expectation: Minimal. This bucket is not for growth; it is for stability. Bucket 2: Medium-Term Growth (2–10 years)\nPurpose: Bridge funding and opportunistic growth — money you may need in 2–10 years but want working harder than cash Where to hold: NISA seichyo (growth) slot or taxable brokerage Allocation: Balanced between global equity index funds and domestic or foreign bond funds. A 60/40 or 70/30 equity/bond split is typical for this bucket. Rebalance: Once per year, or when allocation drifts more than 5% from target Bucket 3: Long-Term Compounding (10+ years)\nPurpose: Your core retirement corpus — money you will not touch for at least a decade Where to hold: iDeCo + NISA tsumitate slot Allocation: 100% global equity index funds for the first 15+ years of accumulation, gradually shifting toward a 70/30 or 60/40 mix as you approach retirement Recommended fund: eMAXIS Slim All Country (world equity), with an expense ratio under 0.06% Putting it together: A sample monthly allocation for a 35-year-old foreign resident earning ¥600,000/month:\nBucket Account Monthly contribution Cash reserve (top-up until target reached) Rakuten Bank ¥50,000 iDeCo (income tax deduction) iDeCo via Rakuten ¥23,000 NISA tsumitate NISA via Rakuten ¥100,000 Taxable brokerage (surplus) Rakuten brokerage ¥30,000–50,000 Total ¥203,000–223,000 At a 6% average annual return, ¥123,000/month (iDeCo + NISA) invested for 30 years compounds to approximately ¥122 million — well above the ¥65 million target corpus calculated in Section 2. Even at a conservative 4% return, the result is around ¥85 million. The math strongly favors starting early and staying consistent.\nTools to Track Your Progress Retirement Calculator — Model your pension gap and required corpus in under 5 minutes Compound Interest Calculator — See exactly how your monthly contributions grow over time at different return assumptions NISA Simulator — Optimize your NISA contribution allocation between the tsumitate and seichyo slots Take-home Pay Calculator — Calculate your actual net monthly income after pension, health insurance, and income tax deductions to know your true investable surplus Frequently Asked Questions Q: I am on a work visa that expires in 3 years. Should I still be contributing to the pension? Yes. Kokumin nenkin and kosei nenkin enrollment is mandatory for all residents regardless of visa type. If you leave before reaching 10 years of coverage, you will not be eligible for pension benefits at retirement, but you may be able to claim the dassoku ichijikin (lump-sum withdrawal) for up to 5 years of contributions. Check whether your home country has a social security totalization agreement with Japan — if so, your Japanese contribution period may count toward your home country pension.\nQ: Can I open a NISA account if I only have a 1-year visa? Yes, as long as you are a registered resident in Japan (registered in the jumin kihon daicho), you are eligible. Visa duration does not affect NISA eligibility. However, if you leave Japan permanently, you will need to close or freeze the account.\nQ: My employer contributes to kosei nenkin. Do I need iDeCo as well? Possibly. If your employer has a corporate defined contribution pension plan (kigyougata DC), your iDeCo contribution limit is reduced (to ¥12,000–¥20,000/month). But if they do not, you can contribute up to ¥23,000/month to iDeCo on top of kosei nenkin. Given the income tax deduction, it is almost always worth maximizing iDeCo first.\nQ: Are my NISA gains taxable in my home country? This depends entirely on your home country\u0026rsquo;s tax rules and any tax treaty between your home country and Japan. Japan will not tax your NISA gains. However, the United States, for instance, taxes its citizens on worldwide income regardless of residence — meaning US citizens may owe US tax on gains that Japan exempts. Consult a tax advisor familiar with both jurisdictions before making large NISA contributions.\nFinal Thoughts Japan\u0026rsquo;s pension system is structurally sound but underpowered relative to the actual cost of retirement here. For foreign residents — who almost always accumulate fewer years of coverage than a lifetime Japanese worker — the gap is wider still. The good news is that the combination of NISA and iDeCo, used consistently over a 20–30 year horizon, is more than sufficient to close that gap and build genuine financial security.\nThe key insight from running the numbers: the Japanese pension is a floor, not a plan. Your NISA, your iDeCo, and your disciplined monthly investment habit are the actual plan.\nStart with the Retirement Calculator to know your number. Then use Rakuten Securities to build the portfolio that reaches it.\nReady to open your Rakuten Securities account and start your retirement portfolio today? Get started here Related Tools Calculate your exact age instantly → Age Calculator Retirement Calculator — Project your pension gap and required savings corpus Compound Interest Calculator — Model long-term growth of your monthly investments NISA Simulator — Optimize your NISA tsumitate and seichyo slot allocation Take-home Pay Calculator — Calculate net monthly income after all Japanese deductions Savings Goal Calculator — Calculate how long to reach any savings target Calculate your mortgage payment → Mortgage Calculator Plan your path to financial independence → FIRE Calculator See how inflation affects your money → Inflation Calculator Affiliate Disclosure: This article contains affiliate links. If you open an account or make a purchase through these links, we may receive a commission at no additional cost to you. All product recommendations are based on independent research and editorial judgment. This article is for informational purposes only and does not constitute financial or investment advice. Please consult a qualified financial advisor before making investment decisions.\nYou May Also Like iDeCo Tax Deduction Calculator: How Much Can You Actually Save in Japan? Best Investment Account for Beginners in Japan: NISA vs. iDeCo vs. Regular Brokerage 401k vs IRA Differences Explained (2026) ","permalink":"https://productivity-works.com/posts/retirement-planning-japan-foreign-resident/","summary":"\u003cp\u003eJapan is one of the most expensive countries in the world to retire in — and one of the least discussed when it comes to financial planning for foreign residents. Whether you arrived on a work visa and quietly became a permanent resident, or you are actively considering making Japan your long-term home, the gap between what the public pension system will pay and what you actually need is almost certainly larger than you think.\u003c/p\u003e","title":"Retirement Planning in Japan for Foreign Residents: What the Pension System Won't Cover"},{"content":"This article contains affiliate links. We may earn a commission at no extra cost to you.\nSide Hustle Tax Calculator Enter your W-2 salary and side hustle income to estimate your total tax burden, self-employment tax, and actual take-home from your side gig.\nW-2 Salary Annual Salary ($) $65,000 Side Hustle Gross Revenue ($) $15,000 Business Expenses ($) $0$3,000$50,000 Filing Status Single Married Filing Jointly Head of Household Quarterly Estimated Tax Payments How Side Hustle Income Is Taxed When you earn money from a side hustle — freelancing, gig work, selling online, consulting — the IRS treats it as self-employment income. This means you pay two types of tax:\n1. Self-Employment Tax (15.3%) This is the Social Security (12.4%) and Medicare (2.9%) tax that employers normally split with you. As a self-employed person, you pay both halves. The good news: you can deduct half of this tax from your adjusted gross income.\n2. Federal Income Tax Your side hustle profit is added to your W-2 income, so it\u0026rsquo;s taxed at your marginal tax rate — the rate for the next dollar you earn. This is often higher than your average tax rate.\nReducing Your Tax Bill Track all business expenses — home office, equipment, software, mileage, supplies Open a Solo 401(k) or SEP-IRA — contribute pre-tax dollars from self-employment income Deduct health insurance premiums if you\u0026rsquo;re self-employed and not covered by an employer plan Use accounting software to categorize expenses and generate Schedule C reports When to Pay If you expect to owe $1,000+ in taxes, make quarterly estimated payments using Form 1040-ES. Missing these deadlines can result in underpayment penalties.\nWant to see your full salary picture? Try our US Salary Calculator for W-2 take-home pay.\nBuilding your emergency fund from side hustle profits? Our Emergency Fund Calculator shows how long it takes.\nRelated Tools\nUS Salary Calculator Budget Planner Debt Payoff Calculator Compound Interest Calculator ","permalink":"https://productivity-works.com/tools/side-hustle-tax-calculator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links. We may earn a commission at no extra cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"side-hustle-tax-calculator\"\u003eSide Hustle Tax Calculator\u003c/h1\u003e\n\u003cp\u003eEnter your W-2 salary and side hustle income to estimate your \u003cstrong\u003etotal tax burden\u003c/strong\u003e, \u003cstrong\u003eself-employment tax\u003c/strong\u003e, and \u003cstrong\u003eactual take-home\u003c/strong\u003e from your side gig.\u003c/p\u003e\n\u003cdiv id=\"sh-calc\" style=\"max-width:680px;margin:0 auto;font-family:Inter,sans-serif;\"\u003e\n\u003cdiv style=\"padding:24px;border:2px solid #2563eb;border-radius:12px;background:#f8fafc;\"\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:20px;\"\u003e\n\u003cdiv\u003e\n\u003ch3 style=\"margin:0 0 12px;color:#64748b;font-size:14px;text-align:center;\"\u003eW-2 Salary\u003c/h3\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:13px;\"\u003eAnnual Salary ($)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"w2Salary\" min=\"0\" max=\"250000\" step=\"5000\" value=\"65000\" oninput=\"calcSH()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"text-align:center;font-weight:bold;font-size:20px;color:#64748b;\" id=\"w2Val\"\u003e$65,000\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv\u003e\n\u003ch3 style=\"margin:0 0 12px;color:#2563eb;font-size:14px;text-align:center;\"\u003eSide Hustle\u003c/h3\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:13px;\"\u003eGross Revenue ($)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"shRevenue\" min=\"0\" max=\"100000\" step=\"1000\" value=\"15000\" oninput=\"calcSH()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"text-align:center;font-weight:bold;font-size:20px;color:#2563eb;\" id=\"shRevVal\"\u003e$15,000\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:13px;\"\u003eBusiness Expenses ($)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"shExpenses\" min=\"0\" max=\"50000\" step=\"500\" value=\"3000\" oninput=\"calcSH()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e$0\u003c/span\u003e\u003cspan id=\"shExpVal\" style=\"font-weight:bold;font-size:16px;color:#ef4444;\"\u003e$3,000\u003c/span\u003e\u003cspan\u003e$50,000\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:4px;font-size:13px;\"\u003eFiling Status\u003c/label\u003e\n\u003cselect id=\"shFiling\" onchange=\"calcSH()\" style=\"width:100%;padding:10px;border:1px solid #cbd5e1;border-radius:8px;font-size:15px;\"\u003e\n\u003coption value=\"single\"\u003eSingle\u003c/option\u003e\n\u003coption value=\"married\"\u003eMarried Filing Jointly\u003c/option\u003e\n\u003coption value=\"hoh\"\u003eHead of Household\u003c/option\u003e\n\u003c/select\u003e\n\u003c/div\u003e\n\u003cdiv id=\"shResult\" style=\"margin-top:20px;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"shBreakdown\" style=\"margin-top:24px;padding:24px;border:2px solid #10b981;border-radius:12px;background:#f0fdf4;\"\u003e\u003c/div\u003e\n\u003cdiv style=\"margin-top:24px;padding:24px;border:2px solid #f59e0b;border-radius:12px;background:#fffbeb;\"\u003e\n\u003ch3 style=\"margin:0 0 12px;color:#92400e;font-size:16px;\"\u003eQuarterly Estimated Tax Payments\u003c/h3\u003e\n\u003cdiv id=\"shQuarterly\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nfunction fmt(n){return Math.round(n).toLocaleString('en-US');}\n\nfunction calcFedTax(taxable,filing){\n  var brackets;\n  if(filing==='married'){\n    brackets=[[23200,0.10],[94300,0.12],[201050,0.22],[383900,0.24],[487450,0.32],[731200,0.35],[Infinity,0.37]];\n  } else if(filing==='hoh'){\n    brackets=[[16550,0.10],[63100,0.12],[100500,0.22],[191950,0.24],[243725,0.32],[609350,0.35],[Infinity,0.37]];\n  } else {\n    brackets=[[11600,0.10],[47150,0.12],[100525,0.22],[191950,0.24],[243725,0.32],[609350,0.35],[Infinity,0.37]];\n  }\n  var tax=0,prev=0;\n  for(var i=0;i\u003cbrackets.length;i++){\n    var upper=brackets[i][0],rate=brackets[i][1];\n    var amt=Math.min(taxable,upper)-prev;\n    if(amt\u003c=0) break;\n    tax+=amt*rate;\n    prev=upper;\n  }\n  return Math.max(0,tax);\n}\n\nfunction getStdDeduction(filing){\n  if(filing==='married') return 29200;\n  if(filing==='hoh') return 21900;\n  return 14600;\n}\n\nfunction calcSH(){\n  var w2=+document.getElementById('w2Salary').value;\n  var rev=+document.getElementById('shRevenue').value;\n  var exp=+document.getElementById('shExpenses').value;\n  var filing=document.getElementById('shFiling').value;\n\n  document.getElementById('w2Val').textContent='$'+fmt(w2);\n  document.getElementById('shRevVal').textContent='$'+fmt(rev);\n  document.getElementById('shExpVal').textContent='$'+fmt(exp);\n\n  var netProfit=Math.max(0,rev-exp);\n\n  // Self-employment tax (15.3% on 92.35% of net profit)\n  var seTaxBase=netProfit*0.9235;\n  var ssWageBase=168600; // 2025 SS wage base\n  var ssFromW2=Math.min(w2,ssWageBase);\n  var ssFromSE=Math.min(Math.max(0,ssWageBase-ssFromW2),seTaxBase);\n  var ssTax=ssFromSE*0.124;\n  var medicareTax=seTaxBase*0.029;\n  var seTax=ssTax+medicareTax;\n\n  // SE tax deduction (half of SE tax)\n  var seDeduction=seTax/2;\n\n  // Federal income tax\n  var stdDed=getStdDeduction(filing);\n  var totalIncome=w2+netProfit;\n  var agi=totalIncome-seDeduction;\n  var taxable=Math.max(0,agi-stdDed);\n  var fedTax=calcFedTax(taxable,filing);\n\n  // Tax WITHOUT side hustle (for comparison)\n  var taxableNoSH=Math.max(0,w2-stdDed);\n  var fedTaxNoSH=calcFedTax(taxableNoSH,filing);\n\n  // Additional tax from side hustle\n  var additionalFed=fedTax-fedTaxNoSH;\n  var totalSHTax=seTax+additionalFed;\n  var effectiveRate=netProfit\u003e0?(totalSHTax/netProfit*100):0;\n  var takeHome=netProfit-totalSHTax;\n\n  // Result summary\n  var color=takeHome\u003e=0?'#10b981':'#ef4444';\n  var html='\u003cdiv style=\"text-align:center;padding:16px;background:#fff;border-radius:8px;border:1px solid #e2e8f0;\"\u003e';\n  html+='\u003cdiv style=\"font-size:13px;color:#64748b;\"\u003eSide Hustle Take-Home\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:36px;font-weight:bold;color:'+color+';\"\u003e$'+fmt(takeHome)+'\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:14px;color:#64748b;margin-top:4px;\"\u003eout of $'+fmt(netProfit)+' net profit\u003c/div\u003e';\n  html+='\u003cdiv style=\"font-size:13px;color:#64748b;margin-top:8px;\"\u003eEffective tax rate on side income: \u003cstrong style=\"color:#ef4444;\"\u003e'+effectiveRate.toFixed(1)+'%\u003c/strong\u003e\u003c/div\u003e';\n  html+='\u003c/div\u003e';\n  document.getElementById('shResult').innerHTML=html;\n\n  // Breakdown table\n  var bhtml='\u003ch3 style=\"margin:0 0 12px;color:#166534;font-size:16px;\"\u003eTax Breakdown\u003c/h3\u003e';\n  bhtml+='\u003ctable style=\"width:100%;border-collapse:collapse;font-size:14px;\"\u003e';\n  bhtml+='\u003ctr style=\"border-bottom:2px solid #10b981;\"\u003e\u003cth style=\"text-align:left;padding:8px;\"\u003eItem\u003c/th\u003e\u003cth style=\"text-align:right;padding:8px;\"\u003eAmount\u003c/th\u003e\u003c/tr\u003e';\n\n  var rows=[\n    ['Side hustle gross revenue','$'+fmt(rev)],\n    ['Business expenses','-$'+fmt(exp)],\n    ['Net profit (Schedule C)','$'+fmt(netProfit)],\n    ['',''],\n    ['Self-employment tax (15.3%)','$'+fmt(Math.round(seTax))],\n    ['  Social Security (12.4%)','$'+fmt(Math.round(ssTax))],\n    ['  Medicare (2.9%)','$'+fmt(Math.round(medicareTax))],\n    ['',''],\n    ['Additional federal income tax','$'+fmt(Math.round(additionalFed))],\n    ['SE tax deduction (above-the-line)','-$'+fmt(Math.round(seDeduction))],\n    ['',''],\n    ['Total tax on side income','$'+fmt(Math.round(totalSHTax))],\n    ['Take-home from side hustle','$'+fmt(Math.round(takeHome))]\n  ];\n\n  for(var i=0;i\u003crows.length;i++){\n    if(rows[i][0]===''){bhtml+='\u003ctr\u003e\u003ctd colspan=\"2\" style=\"padding:4px;\"\u003e\u003c/td\u003e\u003c/tr\u003e';continue;}\n    var bg=i%2===0?'#fff':'#f0fdf4';\n    var bold=(i===rows.length-1||i===rows.length-2)?'font-weight:bold;':'';\n    var indent=rows[i][0].startsWith('  ')?'padding-left:24px;':'';\n    bhtml+='\u003ctr style=\"background:'+bg+';'+bold+'\"\u003e';\n    bhtml+='\u003ctd style=\"padding:8px;'+indent+'\"\u003e'+rows[i][0].trim()+'\u003c/td\u003e';\n    bhtml+='\u003ctd style=\"padding:8px;text-align:right;\"\u003e'+rows[i][1]+'\u003c/td\u003e';\n    bhtml+='\u003c/tr\u003e';\n  }\n  bhtml+='\u003c/table\u003e';\n  document.getElementById('shBreakdown').innerHTML=bhtml;\n\n  // Quarterly estimates\n  var quarterly=Math.round(totalSHTax/4);\n  var qhtml='\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:8px;text-align:center;\"\u003e';\n  var dates=['Apr 15','Jun 15','Sep 15','Jan 15'];\n  for(var i=0;i\u003c4;i++){\n    qhtml+='\u003cdiv style=\"padding:12px;background:#fff;border-radius:8px;\"\u003e';\n    qhtml+='\u003cdiv style=\"font-size:11px;color:#64748b;\"\u003eQ'+(i+1)+' ('+dates[i]+')\u003c/div\u003e';\n    qhtml+='\u003cdiv style=\"font-size:18px;font-weight:bold;color:#92400e;\"\u003e$'+fmt(quarterly)+'\u003c/div\u003e';\n    qhtml+='\u003c/div\u003e';\n  }\n  qhtml+='\u003c/div\u003e';\n  qhtml+='\u003cdiv style=\"margin-top:12px;font-size:12px;color:#92400e;text-align:center;\"\u003ePay quarterly estimated taxes to avoid underpayment penalties. Use IRS Form 1040-ES.\u003c/div\u003e';\n  document.getElementById('shQuarterly').innerHTML=qhtml;\n}\n\ndocument.addEventListener('DOMContentLoaded',function(){calcSH();});\ncalcSH();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-side-hustle-income-is-taxed\"\u003eHow Side Hustle Income Is Taxed\u003c/h2\u003e\n\u003cp\u003eWhen you earn money from a side hustle — freelancing, gig work, selling online, consulting — the IRS treats it as \u003cstrong\u003eself-employment income\u003c/strong\u003e. This means you pay two types of tax:\u003c/p\u003e","title":"Side Hustle Tax Calculator | Estimate Self-Employment \u0026 Income Tax"},{"content":"This article contains affiliate links. We may earn a commission at no extra cost to you.\nSubscription Cost Calculator Check the subscriptions you pay for, enter your monthly costs, and instantly see your true annual spend — plus what that money could grow to if you invested it instead.\nYour Subscriptions Streaming \u0026amp; Entertainment Productivity \u0026amp; Cloud Fitness \u0026amp; Health News \u0026amp; Education Other + Add Custom Subscription\nTotal Monthly Cost $0.00 Annual Cost $0.00 Daily Cost $0.00 If you invested this instead (7% annual return): In 5 years $0 In 10 years $0 In 20 years $0 Assumes annual cost invested as a lump sum at start of each year, compounded monthly. Spending by Category How to Cut Subscription Costs 1. Audit every subscription — even the tiny ones Small monthly charges add up fast. Check your bank and credit card statements for the past 90 days and list every recurring charge. Many people find subscriptions they forgot they signed up for.\n2. Cancel anything you haven\u0026rsquo;t used in 30 days If you haven\u0026rsquo;t opened an app or visited a service in the past month, cancel it. You can almost always resubscribe if you miss it, and most services offer re-engagement deals to lapsed subscribers.\n3. Share plans and family tiers Many services offer family or group plans at a fraction of the per-person cost. Netflix, Spotify, Apple One, Google One, and Microsoft 365 all have multi-user tiers. Split with trusted family members and cut your per-person cost by 50-75%.\n4. Time your cancellations around free trials and promotions Services frequently offer discounted annual plans or free trial extensions, especially at the end of a billing cycle. Cancel, wait for a win-back offer, and resubscribe at a lower rate.\n5. Switch to annual billing for services you definitely use Annual plans typically cost 20-40% less than paying month-to-month. For services you use every day — like cloud storage or a productivity suite — switching to annual billing is an easy win.\nRelated Tools Create a monthly budget → Budget Planner Build your emergency fund → Emergency Fund Calculator Calculate your net worth → Net Worth Calculator Set a savings goal → Savings Goal Calculator Related Articles Best Budgeting Apps 2026 How to Build an Emergency Fund Fast How to Cut Monthly Expenses Without Feeling Deprived Best High-Yield Savings Accounts 2026 This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/tools/subscription-cost-calculator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links. We may earn a commission at no extra cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"subscription-cost-calculator\"\u003eSubscription Cost Calculator\u003c/h1\u003e\n\u003cp\u003eCheck the subscriptions you pay for, enter your monthly costs, and instantly see your \u003cstrong\u003etrue annual spend\u003c/strong\u003e — plus what that money could grow to if you invested it instead.\u003c/p\u003e\n\u003cdiv id=\"sub-calc\" style=\"max-width:720px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:#1e293b;\"\u003e\n\u003c!-- Subscription list --\u003e\n\u003cdiv style=\"padding:24px;border:2px solid #e11d48;border-radius:12px;background:#fff1f2;\"\u003e\n\u003ch3 style=\"margin:0 0 16px 0;font-size:18px;color:#e11d48;\"\u003eYour Subscriptions\u003c/h3\u003e\n\u003c!-- Streaming --\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003cdiv style=\"font-size:13px;font-weight:bold;text-transform:uppercase;letter-spacing:0.05em;color:#64748b;margin-bottom:8px;padding-bottom:4px;border-bottom:1px solid #fecdd3;\"\u003eStreaming \u0026amp; Entertainment\u003c/div\u003e\n\u003cdiv id=\"rows-streaming\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Productivity --\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003cdiv style=\"font-size:13px;font-weight:bold;text-transform:uppercase;letter-spacing:0.05em;color:#64748b;margin-bottom:8px;padding-bottom:4px;border-bottom:1px solid #fecdd3;\"\u003eProductivity \u0026amp; Cloud\u003c/div\u003e\n\u003cdiv id=\"rows-productivity\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Fitness --\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003cdiv style=\"font-size:13px;font-weight:bold;text-transform:uppercase;letter-spacing:0.05em;color:#64748b;margin-bottom:8px;padding-bottom:4px;border-bottom:1px solid #fecdd3;\"\u003eFitness \u0026amp; Health\u003c/div\u003e\n\u003cdiv id=\"rows-fitness\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- News / Education --\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003cdiv style=\"font-size:13px;font-weight:bold;text-transform:uppercase;letter-spacing:0.05em;color:#64748b;margin-bottom:8px;padding-bottom:4px;border-bottom:1px solid #fecdd3;\"\u003eNews \u0026amp; Education\u003c/div\u003e\n\u003cdiv id=\"rows-news\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Other --\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003cdiv style=\"font-size:13px;font-weight:bold;text-transform:uppercase;letter-spacing:0.05em;color:#64748b;margin-bottom:8px;padding-bottom:4px;border-bottom:1px solid #fecdd3;\"\u003eOther\u003c/div\u003e\n\u003cdiv id=\"rows-other\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Add custom --\u003e\n\u003cp\u003e\u003cbutton onclick=\"addCustomRow()\" style=\"display:inline-flex;align-items:center;gap:8px;padding:10px 20px;background:#e11d48;color:white;border:none;border-radius:8px;font-size:15px;font-weight:bold;cursor:pointer;\"\u003e+ Add Custom Subscription\u003c/button\u003e\u003c/p\u003e","title":"Subscription Cost Calculator | Track Your Monthly Subscriptions"},{"content":"This article contains affiliate links. We may earn a commission at no extra cost to you.\nUS Tax Bracket Calculator 2026 Enter your taxable income and filing status to see your federal tax bracket, effective tax rate, and total tax owed — with a visual breakdown of how each dollar is taxed.\nAnnual Taxable Income ($) Filing Status Single Married Filing Jointly Married Filing Separately Head of Household Standard Deduction Apply standard deduction ($15,000) Marginal Tax Bracket 22% Effective Tax Rate 14.3% Total Federal Tax $10,725 After-Tax Income $64,275 Monthly Take-Home (Federal Only) $5,356 How your income is taxed 2026 Federal Tax Brackets Rate Income Range Tax in Bracket Cumulative Tax What if your income changed? How US Tax Brackets Work The US uses a progressive tax system — your income is split into portions (\u0026ldquo;brackets\u0026rdquo;), and each portion is taxed at a different rate. Only the income within each bracket is taxed at that bracket\u0026rsquo;s rate.\nCommon misconception: \u0026ldquo;If I earn $1 more and move into a higher bracket, all my income gets taxed at the higher rate.\u0026rdquo; This is false. Only the additional dollar is taxed at the higher rate. Your overall (effective) tax rate always stays lower than your marginal bracket.\n2026 Federal Tax Brackets (Single Filer) Rate Taxable Income 10% $0 – $11,925 12% $11,925 – $48,475 22% $48,475 – $103,350 24% $103,350 – $197,300 32% $197,300 – $250,525 35% $250,525 – $626,350 37% $626,350+ Standard deduction for 2026: $15,000 (Single), $30,000 (Married Filing Jointly), $22,500 (Head of Household).\nWays to Lower Your Tax Bracket 1. Maximize retirement contributions 401(k) contributions (up to $23,500 in 2026) reduce your taxable income dollar-for-dollar. If you\u0026rsquo;re in the 22% bracket, a $10,000 contribution saves $2,200 in federal taxes.\n2. Use the standard deduction or itemize Most filers benefit from the standard deduction. If your mortgage interest, state taxes, and charitable donations exceed the standard deduction, itemizing saves more.\n3. Contribute to an HSA Health Savings Account contributions ($4,300 individual / $8,550 family in 2026) are tax-deductible, grow tax-free, and can be withdrawn tax-free for medical expenses.\n4. Harvest investment losses Capital losses offset capital gains dollar-for-dollar, plus up to $3,000 of ordinary income per year.\nRelated Tools Calculate your full take-home pay after all taxes → Salary Calculator See how savings grow with compound interest → Compound Interest Calculator Calculate how long to reach any savings target → Savings Goal Calculator Plan your monthly budget after taxes → Budget Planner Estimate freelance/side hustle taxes → Side Hustle Tax Calculator Related Articles Freelance Tax Guide 2026 401(k) vs IRA Differences Explained Roth IRA vs Traditional IRA Best Budgeting Apps 2026 How to Start Freelancing 2026 Productivity Works Free Tools All our financial calculators are 100% free, run entirely in your browser, and never store your data.\nBrowse all free tools This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/tools/tax-bracket-calculator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links. We may earn a commission at no extra cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"us-tax-bracket-calculator-2026\"\u003eUS Tax Bracket Calculator 2026\u003c/h1\u003e\n\u003cp\u003eEnter your taxable income and filing status to see your \u003cstrong\u003efederal tax bracket, effective tax rate, and total tax owed\u003c/strong\u003e — with a visual breakdown of how each dollar is taxed.\u003c/p\u003e\n\u003cdiv id=\"tb-calc\" style=\"max-width:720px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:#1e293b;\"\u003e\n\u003cdiv style=\"padding:24px;border:2px solid #dc2626;border-radius:12px;background:#fef2f2;\"\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eAnnual Taxable Income ($)\u003c/label\u003e\n\u003cinput type=\"number\" id=\"tbIncome\" min=\"0\" max=\"50000000\" step=\"1000\" value=\"75000\" oninput=\"calcTB()\" style=\"width:100%;padding:12px;border:1px solid #cbd5e1;border-radius:8px;font-size:18px;\"\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eFiling Status\u003c/label\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:8px;\"\u003e\n\u003clabel style=\"display:flex;align-items:center;padding:12px;border:2px solid #e2e8f0;border-radius:8px;cursor:pointer;background:white;\" id=\"lbl-single\"\u003e\n\u003cinput type=\"radio\" name=\"tbStatus\" value=\"single\" checked onchange=\"calcTB()\" style=\"margin-right:8px;\"\u003e Single\n\u003c/label\u003e\n\u003clabel style=\"display:flex;align-items:center;padding:12px;border:2px solid #e2e8f0;border-radius:8px;cursor:pointer;background:white;\" id=\"lbl-mfj\"\u003e\n\u003cinput type=\"radio\" name=\"tbStatus\" value=\"mfj\" onchange=\"calcTB()\" style=\"margin-right:8px;\"\u003e Married Filing Jointly\n\u003c/label\u003e\n\u003clabel style=\"display:flex;align-items:center;padding:12px;border:2px solid #e2e8f0;border-radius:8px;cursor:pointer;background:white;\" id=\"lbl-mfs\"\u003e\n\u003cinput type=\"radio\" name=\"tbStatus\" value=\"mfs\" onchange=\"calcTB()\" style=\"margin-right:8px;\"\u003e Married Filing Separately\n\u003c/label\u003e\n\u003clabel style=\"display:flex;align-items:center;padding:12px;border:2px solid #e2e8f0;border-radius:8px;cursor:pointer;background:white;\" id=\"lbl-hoh\"\u003e\n\u003cinput type=\"radio\" name=\"tbStatus\" value=\"hoh\" onchange=\"calcTB()\" style=\"margin-right:8px;\"\u003e Head of Household\n\u003c/label\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:8px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eStandard Deduction\u003c/label\u003e\n\u003cdiv style=\"display:flex;gap:12px;align-items:center;\"\u003e\n\u003clabel style=\"display:flex;align-items:center;cursor:pointer;\"\u003e\n\u003cinput type=\"checkbox\" id=\"tbDeduction\" checked onchange=\"calcTB()\" style=\"margin-right:6px;width:18px;height:18px;\"\u003e Apply standard deduction\n\u003c/label\u003e\n\u003cspan id=\"tbDeductionAmt\" style=\"color:#64748b;font-size:14px;\"\u003e($15,000)\u003c/span\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Results --\u003e\n\u003cdiv id=\"tbResults\" style=\"margin-top:24px;\"\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;text-align:center;\"\u003e\n\u003cdiv style=\"padding:20px;background:#fef2f2;border-radius:12px;border:1px solid #fecaca;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eMarginal Tax Bracket\u003c/div\u003e\n\u003cdiv id=\"tbBracket\" style=\"font-size:36px;font-weight:bold;color:#dc2626;\"\u003e22%\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:20px;background:#f0fdf4;border-radius:12px;border:1px solid #bbf7d0;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eEffective Tax Rate\u003c/div\u003e\n\u003cdiv id=\"tbEffective\" style=\"font-size:36px;font-weight:bold;color:#16a34a;\"\u003e14.3%\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:20px;background:#f8fafc;border-radius:12px;border:1px solid #e2e8f0;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eTotal Federal Tax\u003c/div\u003e\n\u003cdiv id=\"tbTotal\" style=\"font-size:28px;font-weight:bold;color:#1e293b;\"\u003e$10,725\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:12px;text-align:center;\"\u003e\n\u003cdiv style=\"padding:16px;background:white;border-radius:8px;border:1px solid #e2e8f0;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eAfter-Tax Income\u003c/div\u003e\n\u003cdiv id=\"tbAfterTax\" style=\"font-size:22px;font-weight:bold;color:#1e293b;\"\u003e$64,275\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"padding:16px;background:white;border-radius:8px;border:1px solid #e2e8f0;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eMonthly Take-Home (Federal Only)\u003c/div\u003e\n\u003cdiv id=\"tbMonthly\" style=\"font-size:22px;font-weight:bold;color:#1e293b;\"\u003e$5,356\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Bracket breakdown bar --\u003e\n\u003cdiv style=\"margin-top:24px;padding:20px;background:#f8fafc;border-radius:12px;border:1px solid #e2e8f0;\"\u003e\n\u003ch3 style=\"margin:0 0 12px 0;font-size:16px;\"\u003eHow your income is taxed\u003c/h3\u003e\n\u003cdiv id=\"tbBar\" style=\"height:32px;border-radius:8px;overflow:hidden;display:flex;margin-bottom:8px;\"\u003e\u003c/div\u003e\n\u003cdiv id=\"tbLegend\" style=\"display:flex;flex-wrap:wrap;gap:8px;font-size:12px;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Bracket table --\u003e\n\u003cdiv style=\"margin-top:20px;padding:20px;background:white;border-radius:12px;border:1px solid #e2e8f0;\"\u003e\n\u003ch3 style=\"margin:0 0 12px 0;font-size:16px;\"\u003e2026 Federal Tax Brackets\u003c/h3\u003e\n\u003cdiv style=\"overflow-x:auto;\"\u003e\n\u003ctable id=\"tbTable\" style=\"width:100%;border-collapse:collapse;font-size:14px;\"\u003e\n\u003cthead\u003e\n\u003ctr style=\"background:#dc2626;color:white;\"\u003e\n\u003cth style=\"padding:8px;text-align:left;\"\u003eRate\u003c/th\u003e\n\u003cth style=\"padding:8px;text-align:right;\"\u003eIncome Range\u003c/th\u003e\n\u003cth style=\"padding:8px;text-align:right;\"\u003eTax in Bracket\u003c/th\u003e\n\u003cth style=\"padding:8px;text-align:right;\"\u003eCumulative Tax\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody id=\"tbTableBody\"\u003e\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Comparison --\u003e\n\u003cdiv style=\"margin-top:20px;padding:20px;background:#fffbeb;border-radius:12px;border:1px solid #fde68a;\"\u003e\n\u003ch3 style=\"margin:0 0 12px 0;font-size:16px;\"\u003eWhat if your income changed?\u003c/h3\u003e\n\u003cdiv id=\"tbScenarios\" style=\"display:grid;gap:8px;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nconst BRACKETS={\n  single:[\n    [0,11925,0.10],[11925,48475,0.12],[48475,103350,0.22],\n    [103350,197300,0.24],[197300,250525,0.32],[250525,626350,0.35],[626350,Infinity,0.37]\n  ],\n  mfj:[\n    [0,23850,0.10],[23850,96950,0.12],[96950,206700,0.22],\n    [206700,394600,0.24],[394600,501050,0.32],[501050,751600,0.35],[751600,Infinity,0.37]\n  ],\n  mfs:[\n    [0,11925,0.10],[11925,48475,0.12],[48475,103350,0.22],\n    [103350,197300,0.24],[197300,250525,0.32],[250525,375800,0.35],[375800,Infinity,0.37]\n  ],\n  hoh:[\n    [0,17000,0.10],[17000,64850,0.12],[64850,103350,0.22],\n    [103350,197300,0.24],[197300,250500,0.32],[250500,626350,0.35],[626350,Infinity,0.37]\n  ]\n};\nconst STD_DED={single:15000,mfj:30000,mfs:15000,hoh:22500};\nconst COLORS=['#dbeafe','#bfdbfe','#93c5fd','#60a5fa','#3b82f6','#2563eb','#1d4ed8'];\n\nfunction getStatus(){\n  return document.querySelector('input[name=\"tbStatus\"]:checked').value;\n}\n\nfunction computeTax(income,status){\n  const brackets=BRACKETS[status];\n  let tax=0;const details=[];\n  for(const[lo,hi,rate]of brackets){\n    if(income\u003c=lo)break;\n    const taxable=Math.min(income,hi)-lo;\n    const t=taxable*rate;\n    tax+=t;\n    details.push({lo,hi:Math.min(income,hi),rate,taxable,tax:t,cumTax:tax});\n  }\n  return{tax,details};\n}\n\nfunction calcTB(){\n  const grossIncome=parseFloat(document.getElementById('tbIncome').value)||0;\n  const status=getStatus();\n  const useDeduction=document.getElementById('tbDeduction').checked;\n  const deduction=useDeduction?STD_DED[status]:0;\n  const taxableIncome=Math.max(0,grossIncome-deduction);\n\n  document.getElementById('tbDeductionAmt').textContent='($'+deduction.toLocaleString()+')';\n\n  // Highlight selected radio\n  ['single','mfj','mfs','hoh'].forEach(s=\u003e{\n    document.getElementById('lbl-'+s).style.borderColor=s===status?'#dc2626':'#e2e8f0';\n    document.getElementById('lbl-'+s).style.background=s===status?'#fef2f2':'white';\n  });\n\n  const{tax,details}=computeTax(taxableIncome,status);\n  const marginalRate=details.length\u003e0?details[details.length-1].rate:0;\n  const effectiveRate=grossIncome\u003e0?(tax/grossIncome)*100:0;\n  const afterTax=grossIncome-tax;\n\n  document.getElementById('tbBracket').textContent=Math.round(marginalRate*100)+'%';\n  document.getElementById('tbEffective').textContent=effectiveRate.toFixed(1)+'%';\n  document.getElementById('tbTotal').textContent='$'+Math.round(tax).toLocaleString();\n  document.getElementById('tbAfterTax').textContent='$'+Math.round(afterTax).toLocaleString();\n  document.getElementById('tbMonthly').textContent='$'+Math.round(afterTax/12).toLocaleString();\n\n  // Bar\n  let barHTML='';let legendHTML='';\n  if(taxableIncome\u003e0){\n    details.forEach((d,i)=\u003e{\n      const pct=(d.taxable/taxableIncome)*100;\n      if(pct\u003e0){\n        barHTML+='\u003cdiv style=\"width:'+pct+'%;background:'+COLORS[i%COLORS.length]+';height:100%;\" title=\"'+Math.round(d.rate*100)+'%: $'+d.taxable.toLocaleString()+'\"\u003e\u003c/div\u003e';\n        legendHTML+='\u003cspan\u003e\u003cspan style=\"display:inline-block;width:10px;height:10px;background:'+COLORS[i%COLORS.length]+';border-radius:2px;\"\u003e\u003c/span\u003e '+Math.round(d.rate*100)+'%\u003c/span\u003e';\n      }\n    });\n  }\n  document.getElementById('tbBar').innerHTML=barHTML;\n  document.getElementById('tbLegend').innerHTML=legendHTML;\n\n  // Table\n  const allBrackets=BRACKETS[status];\n  let tbody='';let cumTax=0;\n  allBrackets.forEach((b,i)=\u003e{\n    const[lo,hi,rate]=b;\n    const taxable=taxableIncome\u003elo?Math.min(taxableIncome,hi)-lo:0;\n    const t=taxable*rate;\n    cumTax+=t;\n    const active=taxableIncome\u003elo;\n    const current=taxableIncome\u003elo\u0026\u0026(hi===Infinity||taxableIncome\u003c=hi);\n    const bg=current?'#fef2f2':i%2===0?'#f8fafc':'white';\n    const hiStr=hi===Infinity?'+':'$'+hi.toLocaleString();\n    const arrow=current?' ←':'';\n    tbody+='\u003ctr style=\"background:'+bg+';'+(current?'font-weight:bold;':'')+'\"\u003e';\n    tbody+='\u003ctd style=\"padding:8px;\"\u003e'+Math.round(rate*100)+'%'+arrow+'\u003c/td\u003e';\n    tbody+='\u003ctd style=\"padding:8px;text-align:right;\"\u003e$'+lo.toLocaleString()+' – '+hiStr+'\u003c/td\u003e';\n    tbody+='\u003ctd style=\"padding:8px;text-align:right;'+(active?'':'color:#94a3b8;')+'\"\u003e$'+Math.round(t).toLocaleString()+'\u003c/td\u003e';\n    tbody+='\u003ctd style=\"padding:8px;text-align:right;'+(active?'':'color:#94a3b8;')+'\"\u003e$'+Math.round(cumTax).toLocaleString()+'\u003c/td\u003e';\n    tbody+='\u003c/tr\u003e';\n  });\n  document.getElementById('tbTableBody').innerHTML=tbody;\n\n  // Scenarios\n  const changes=[10000,25000,50000];\n  let sHTML='';\n  changes.forEach(c=\u003e{\n    const newGross=grossIncome+c;\n    const newTaxable=Math.max(0,newGross-deduction);\n    const r=computeTax(newTaxable,status);\n    const diff=r.tax-tax;\n    const newEff=newGross\u003e0?(r.tax/newGross)*100:0;\n    sHTML+='\u003cdiv style=\"display:flex;justify-content:space-between;align-items:center;padding:12px;background:white;border-radius:8px;border:1px solid #e2e8f0;\"\u003e';\n    sHTML+='\u003cdiv\u003e\u003cstrong\u003e$'+newGross.toLocaleString()+'\u003c/strong\u003e \u003cspan style=\"color:#64748b;font-size:13px;\"\u003e(+$'+c.toLocaleString()+')\u003c/span\u003e\u003c/div\u003e';\n    sHTML+='\u003cdiv style=\"text-align:right;font-size:14px;\"\u003eTax: $'+Math.round(r.tax).toLocaleString()+' \u003cspan style=\"color:#dc2626;\"\u003e(+$'+Math.round(diff).toLocaleString()+')\u003c/span\u003e | Eff: '+newEff.toFixed(1)+'%\u003c/div\u003e';\n    sHTML+='\u003c/div\u003e';\n  });\n  document.getElementById('tbScenarios').innerHTML=sHTML;\n}\n\ncalcTB();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-us-tax-brackets-work\"\u003eHow US Tax Brackets Work\u003c/h2\u003e\n\u003cp\u003eThe US uses a \u003cstrong\u003eprogressive tax system\u003c/strong\u003e — your income is split into portions (\u0026ldquo;brackets\u0026rdquo;), and each portion is taxed at a different rate. Only the income \u003cstrong\u003ewithin\u003c/strong\u003e each bracket is taxed at that bracket\u0026rsquo;s rate.\u003c/p\u003e","title":"US Tax Bracket Calculator 2026 | Federal Income Tax Estimator"},{"content":" — Select a city to add — + Add City Reset Digital Clock Analog Clock Meeting Planner Source time zone: Compare Convert timestamps → Timestamp Converter \u0026nbsp;|\u0026nbsp; Compare time zones → Timezone Converter ","permalink":"https://productivity-works.com/tools/world-clock/","summary":"\u003cdiv id=\"wc-app\"\u003e\n\u003cstyle\u003e\n#wc-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 1100px;\n  margin: 0 auto;\n  color: #1e293b;\n}\n#wc-app * { box-sizing: border-box; }\n#wc-app h2 {\n  font-size: 1.3rem;\n  font-weight: 700;\n  margin: 0 0 14px 0;\n  color: #0f172a;\n}\n#wc-app .wc-controls {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  margin-bottom: 20px;\n  align-items: center;\n}\n#wc-app .wc-search {\n  flex: 1;\n  min-width: 200px;\n  padding: 9px 14px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 14px;\n  outline: none;\n  transition: border-color 0.2s;\n}\n#wc-app .wc-search:focus { border-color: #3b82f6; }\n#wc-app .wc-city-select {\n  flex: 1;\n  min-width: 200px;\n  padding: 9px 14px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 14px;\n  background: #fff;\n  outline: none;\n  cursor: pointer;\n}\n#wc-app .wc-btn {\n  padding: 9px 18px;\n  background: #3b82f6;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.2s;\n  white-space: nowrap;\n}\n#wc-app .wc-btn:hover { background: #2563eb; }\n#wc-app .wc-btn-secondary {\n  background: #f1f5f9;\n  color: #334155;\n  border: 1.5px solid #cbd5e1;\n}\n#wc-app .wc-btn-secondary:hover { background: #e2e8f0; }\n#wc-app .wc-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));\n  gap: 16px;\n  margin-bottom: 28px;\n}\n#wc-app .wc-card {\n  background: #fff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 16px 18px;\n  position: relative;\n  transition: box-shadow 0.2s;\n}\n#wc-app .wc-card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.08); }\n#wc-app .wc-card.day { border-top: 3px solid #f59e0b; }\n#wc-app .wc-card.night { border-top: 3px solid #6366f1; background: #f8f7ff; }\n#wc-app .wc-card-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: flex-start;\n  margin-bottom: 8px;\n}\n#wc-app .wc-city-name {\n  font-size: 15px;\n  font-weight: 700;\n  color: #0f172a;\n}\n#wc-app .wc-day-night {\n  font-size: 20px;\n  line-height: 1;\n}\n#wc-app .wc-tz-label {\n  font-size: 11px;\n  color: #94a3b8;\n  margin-bottom: 4px;\n}\n#wc-app .wc-time {\n  font-size: 28px;\n  font-weight: 800;\n  color: #0f172a;\n  letter-spacing: -0.5px;\n  font-variant-numeric: tabular-nums;\n}\n#wc-app .wc-date {\n  font-size: 12px;\n  color: #64748b;\n  margin-top: 2px;\n}\n#wc-app .wc-diff {\n  font-size: 12px;\n  color: #3b82f6;\n  margin-top: 4px;\n  font-weight: 600;\n}\n#wc-app .wc-remove-btn {\n  position: absolute;\n  top: 8px;\n  right: 8px;\n  background: none;\n  border: none;\n  color: #94a3b8;\n  cursor: pointer;\n  font-size: 16px;\n  padding: 2px 5px;\n  border-radius: 4px;\n  line-height: 1;\n  transition: color 0.2s, background 0.2s;\n}\n#wc-app .wc-remove-btn:hover { color: #ef4444; background: #fee2e2; }\n#wc-app .wc-canvas-wrap {\n  display: flex;\n  justify-content: center;\n  margin-top: 8px;\n}\n#wc-app canvas.wc-analog {\n  display: block;\n}\n#wc-app .wc-section {\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 20px 22px;\n  margin-bottom: 24px;\n}\n#wc-app .wc-planner-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 12px;\n  align-items: center;\n  margin-bottom: 16px;\n}\n#wc-app .wc-planner-select,\n#wc-app .wc-planner-input {\n  padding: 8px 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 14px;\n  background: #fff;\n  outline: none;\n}\n#wc-app .wc-planner-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 13px;\n}\n#wc-app .wc-planner-table th {\n  text-align: left;\n  padding: 6px 10px;\n  background: #e2e8f0;\n  color: #475569;\n  font-weight: 600;\n  border-radius: 4px;\n}\n#wc-app .wc-planner-table td {\n  padding: 7px 10px;\n  border-bottom: 1px solid #f1f5f9;\n  color: #334155;\n}\n#wc-app .wc-planner-table tr:last-child td { border-bottom: none; }\n#wc-app .wc-toggle-row {\n  display: flex;\n  gap: 10px;\n  margin-bottom: 16px;\n  flex-wrap: wrap;\n}\n#wc-app .wc-toggle-btn {\n  padding: 6px 14px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 20px;\n  background: #fff;\n  font-size: 13px;\n  cursor: pointer;\n  transition: all 0.2s;\n  color: #475569;\n}\n#wc-app .wc-toggle-btn.active {\n  background: #3b82f6;\n  border-color: #3b82f6;\n  color: #fff;\n}\n#wc-app .wc-crosslinks {\n  font-size: 13px;\n  color: #64748b;\n  margin-top: 10px;\n  padding-top: 10px;\n  border-top: 1px solid #e2e8f0;\n}\n#wc-app .wc-crosslinks a { color: #3b82f6; text-decoration: none; }\n#wc-app .wc-crosslinks a:hover { text-decoration: underline; }\n@media (max-width: 600px) {\n  #wc-app .wc-grid { grid-template-columns: 1fr 1fr; gap: 10px; }\n  #wc-app .wc-time { font-size: 22px; }\n  #wc-app .wc-controls { flex-direction: column; }\n}\n@media (max-width: 380px) {\n  #wc-app .wc-grid { grid-template-columns: 1fr; }\n}\n\u003c/style\u003e\n\u003cdiv class=\"wc-controls\"\u003e\n  \u003cinput type=\"text\" id=\"wc-search\" class=\"wc-search\" placeholder=\"Search cities...\" /\u003e\n  \u003cselect id=\"wc-city-select\" class=\"wc-city-select\"\u003e\n    \u003coption value=\"\"\u003e— Select a city to add —\u003c/option\u003e\n  \u003c/select\u003e\n  \u003cbutton id=\"wc-add-btn\" class=\"wc-btn\"\u003e+ Add City\u003c/button\u003e\n  \u003cbutton id=\"wc-reset-btn\" class=\"wc-btn wc-btn-secondary\"\u003eReset\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"wc-toggle-row\"\u003e\n  \u003cbutton class=\"wc-toggle-btn active\" data-view=\"digital\"\u003eDigital Clock\u003c/button\u003e\n  \u003cbutton class=\"wc-toggle-btn\" data-view=\"analog\"\u003eAnalog Clock\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv id=\"wc-grid\" class=\"wc-grid\"\u003e\u003c/div\u003e\n\u003cdiv class=\"wc-section\"\u003e\n  \u003ch2\u003eMeeting Planner\u003c/h2\u003e\n  \u003cdiv class=\"wc-planner-row\"\u003e\n    \u003clabel style=\"font-size:14px;color:#475569;font-weight:600;\"\u003eSource time zone:\u003c/label\u003e\n    \u003cselect id=\"wc-planner-tz\" class=\"wc-planner-select\"\u003e\u003c/select\u003e\n    \u003cinput type=\"time\" id=\"wc-planner-time\" class=\"wc-planner-input\" value=\"09:00\" /\u003e\n    \u003cbutton id=\"wc-planner-calc\" class=\"wc-btn\"\u003eCompare\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv id=\"wc-planner-result\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"wc-crosslinks\"\u003e\n  Convert timestamps → \u003ca href=\"/tools/timestamp-converter/\"\u003eTimestamp Converter\u003c/a\u003e \u0026nbsp;|\u0026nbsp;\n  Compare time zones → \u003ca href=\"/tools/timezone-converter/\"\u003eTimezone Converter\u003c/a\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  'use strict';\n\n  var CITIES = [\n    { name: 'Tokyo', tz: 'Asia/Tokyo', flag: '🇯🇵' },\n    { name: 'Seoul', tz: 'Asia/Seoul', flag: '🇰🇷' },\n    { name: 'Beijing', tz: 'Asia/Shanghai', flag: '🇨🇳' },\n    { name: 'Shanghai', tz: 'Asia/Shanghai', flag: '🇨🇳' },\n    { name: 'Hong Kong', tz: 'Asia/Hong_Kong', flag: '🇭🇰' },\n    { name: 'Singapore', tz: 'Asia/Singapore', flag: '🇸🇬' },\n    { name: 'Bangkok', tz: 'Asia/Bangkok', flag: '🇹🇭' },\n    { name: 'Kolkata', tz: 'Asia/Kolkata', flag: '🇮🇳' },\n    { name: 'Mumbai', tz: 'Asia/Kolkata', flag: '🇮🇳' },\n    { name: 'Dubai', tz: 'Asia/Dubai', flag: '🇦🇪' },\n    { name: 'Istanbul', tz: 'Europe/Istanbul', flag: '🇹🇷' },\n    { name: 'Moscow', tz: 'Europe/Moscow', flag: '🇷🇺' },\n    { name: 'Nairobi', tz: 'Africa/Nairobi', flag: '🇰🇪' },\n    { name: 'Cairo', tz: 'Africa/Cairo', flag: '🇪🇬' },\n    { name: 'Johannesburg', tz: 'Africa/Johannesburg', flag: '🇿🇦' },\n    { name: 'Frankfurt', tz: 'Europe/Berlin', flag: '🇩🇪' },\n    { name: 'Berlin', tz: 'Europe/Berlin', flag: '🇩🇪' },\n    { name: 'Paris', tz: 'Europe/Paris', flag: '🇫🇷' },\n    { name: 'Amsterdam', tz: 'Europe/Amsterdam', flag: '🇳🇱' },\n    { name: 'Zurich', tz: 'Europe/Zurich', flag: '🇨🇭' },\n    { name: 'London', tz: 'Europe/London', flag: '🇬🇧' },\n    { name: 'Lisbon', tz: 'Europe/Lisbon', flag: '🇵🇹' },\n    { name: 'Reykjavik', tz: 'Atlantic/Reykjavik', flag: '🇮🇸' },\n    { name: 'New York', tz: 'America/New_York', flag: '🇺🇸' },\n    { name: 'Washington DC', tz: 'America/New_York', flag: '🇺🇸' },\n    { name: 'Chicago', tz: 'America/Chicago', flag: '🇺🇸' },\n    { name: 'Denver', tz: 'America/Denver', flag: '🇺🇸' },\n    { name: 'Los Angeles', tz: 'America/Los_Angeles', flag: '🇺🇸' },\n    { name: 'San Francisco', tz: 'America/Los_Angeles', flag: '🇺🇸' },\n    { name: 'Toronto', tz: 'America/Toronto', flag: '🇨🇦' },\n    { name: 'Vancouver', tz: 'America/Vancouver', flag: '🇨🇦' },\n    { name: 'Mexico City', tz: 'America/Mexico_City', flag: '🇲🇽' },\n    { name: 'São Paulo', tz: 'America/Sao_Paulo', flag: '🇧🇷' },\n    { name: 'Buenos Aires', tz: 'America/Argentina/Buenos_Aires', flag: '🇦🇷' },\n    { name: 'Sydney', tz: 'Australia/Sydney', flag: '🇦🇺' },\n    { name: 'Melbourne', tz: 'Australia/Melbourne', flag: '🇦🇺' },\n    { name: 'Auckland', tz: 'Pacific/Auckland', flag: '🇳🇿' },\n    { name: 'Honolulu', tz: 'Pacific/Honolulu', flag: '🇺🇸' },\n    { name: 'Anchorage', tz: 'America/Anchorage', flag: '🇺🇸' },\n    { name: 'UTC', tz: 'UTC', flag: '🌐' },\n  ];\n\n  var DEFAULT_CITIES = ['Tokyo', 'New York', 'London', 'Sydney', 'Dubai', 'Los Angeles'];\n  var STORAGE_KEY = 'wc_selected_cities';\n  var VIEW_KEY = 'wc_view_mode';\n\n  var viewMode = localStorage.getItem(VIEW_KEY) || 'digital';\n  var selectedCities = [];\n  var tickInterval = null;\n\n  function loadCities() {\n    try {\n      var stored = JSON.parse(localStorage.getItem(STORAGE_KEY));\n      if (Array.isArray(stored) \u0026\u0026 stored.length \u003e 0) return stored;\n    } catch (e) {}\n    return DEFAULT_CITIES.slice();\n  }\n\n  function saveCities() {\n    localStorage.setItem(STORAGE_KEY, JSON.stringify(selectedCities));\n  }\n\n  function getLocalHour(tz) {\n    try {\n      var d = new Date();\n      var h = parseInt(new Intl.DateTimeFormat('en-US', { hour: 'numeric', hour12: false, timeZone: tz }).format(d), 10);\n      return h;\n    } catch (e) { return 12; }\n  }\n\n  function isDay(tz) {\n    var h = getLocalHour(tz);\n    return h \u003e= 6 \u0026\u0026 h \u003c 20;\n  }\n\n  function getTimeInTz(tz) {\n    var d = new Date();\n    var opts = { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false, timeZone: tz };\n    return new Intl.DateTimeFormat('en-US', opts).format(d);\n  }\n\n  function getDateInTz(tz) {\n    var d = new Date();\n    var opts = { weekday: 'short', month: 'short', day: 'numeric', timeZone: tz };\n    return new Intl.DateTimeFormat('en-US', opts).format(d);\n  }\n\n  function getTzLabel(tz) {\n    try {\n      var d = new Date();\n      var parts = new Intl.DateTimeFormat('en-US', { timeZoneName: 'short', timeZone: tz }).formatToParts(d);\n      var p = parts.find(function (x) { return x.type === 'timeZoneName'; });\n      return p ? p.value : tz;\n    } catch (e) { return tz; }\n  }\n\n  function getOffsetDiff(tz) {\n    try {\n      var d = new Date();\n      var localOffset = -d.getTimezoneOffset();\n      var tzStr = new Intl.DateTimeFormat('en-US', { timeZone: tz, timeZoneName: 'longOffset' })\n        .formatToParts(d).find(function (p) { return p.type === 'timeZoneName'; });\n      if (!tzStr) return '';\n      var m = tzStr.value.match(/GMT([+-])(\\d{2}):(\\d{2})/);\n      if (!m) return tz === 'UTC' ? 'UTC±0' : '';\n      var sign = m[1] === '+' ? 1 : -1;\n      var tzOffset = sign * (parseInt(m[2], 10) * 60 + parseInt(m[3], 10));\n      var diff = tzOffset - localOffset;\n      if (diff === 0) return 'Local';\n      var h = Math.floor(Math.abs(diff) / 60);\n      var min = Math.abs(diff) % 60;\n      var str = diff \u003e 0 ? '+' : '-';\n      str += h + (min ? ':' + String(min).padStart(2, '0') : '') + 'h from local';\n      return str;\n    } catch (e) { return ''; }\n  }\n\n  function drawAnalog(canvas, tz) {\n    var ctx = canvas.getContext('2d');\n    var size = canvas.width;\n    var cx = size / 2, cy = size / 2, r = size / 2 - 4;\n    ctx.clearRect(0, 0, size, size);\n\n    // face\n    ctx.beginPath();\n    ctx.arc(cx, cy, r, 0, Math.PI * 2);\n    ctx.fillStyle = '#f8fafc';\n    ctx.fill();\n    ctx.strokeStyle = '#cbd5e1';\n    ctx.lineWidth = 2;\n    ctx.stroke();\n\n    // hour marks\n    for (var i = 0; i \u003c 12; i++) {\n      var ang = (i / 12) * Math.PI * 2 - Math.PI / 2;\n      var x1 = cx + Math.cos(ang) * (r - 6);\n      var y1 = cy + Math.sin(ang) * (r - 6);\n      var x2 = cx + Math.cos(ang) * (r - 14);\n      var y2 = cy + Math.sin(ang) * (r - 14);\n      ctx.beginPath();\n      ctx.moveTo(x1, y1);\n      ctx.lineTo(x2, y2);\n      ctx.strokeStyle = '#94a3b8';\n      ctx.lineWidth = i % 3 === 0 ? 2.5 : 1.5;\n      ctx.stroke();\n    }\n\n    // get time\n    var d = new Date();\n    var parts = new Intl.DateTimeFormat('en-US', {\n      hour: '2-digit', minute: '2-digit', second: '2-digit',\n      hour12: false, timeZone: tz\n    }).formatToParts(d);\n    var hh = 0, mm = 0, ss = 0;\n    parts.forEach(function (p) {\n      if (p.type === 'hour') hh = parseInt(p.value, 10) % 12;\n      if (p.type === 'minute') mm = parseInt(p.value, 10);\n      if (p.type === 'second') ss = parseInt(p.value, 10);\n    });\n\n    function drawHand(angle, length, width, color) {\n      ctx.beginPath();\n      ctx.moveTo(cx, cy);\n      ctx.lineTo(cx + Math.cos(angle) * length, cy + Math.sin(angle) * length);\n      ctx.strokeStyle = color;\n      ctx.lineWidth = width;\n      ctx.lineCap = 'round';\n      ctx.stroke();\n    }\n\n    var hourAng = ((hh + mm / 60 + ss / 3600) / 12) * Math.PI * 2 - Math.PI / 2;\n    var minAng = ((mm + ss / 60) / 60) * Math.PI * 2 - Math.PI / 2;\n    var secAng = (ss / 60) * Math.PI * 2 - Math.PI / 2;\n\n    drawHand(hourAng, r * 0.5, 4, '#0f172a');\n    drawHand(minAng, r * 0.72, 3, '#334155');\n    drawHand(secAng, r * 0.82, 1.5, '#ef4444');\n\n    // center dot\n    ctx.beginPath();\n    ctx.arc(cx, cy, 4, 0, Math.PI * 2);\n    ctx.fillStyle = '#0f172a';\n    ctx.fill();\n  }\n\n  function renderCard(cityName) {\n    var city = CITIES.find(function (c) { return c.name === cityName; });\n    if (!city) return '';\n    var day = isDay(city.tz);\n    var canvasId = 'wc-canvas-' + cityName.replace(/\\s+/g, '-');\n    return '\u003cdiv class=\"wc-card ' + (day ? 'day' : 'night') + '\" data-city=\"' + cityName + '\"\u003e' +\n      '\u003cbutton class=\"wc-remove-btn\" data-remove=\"' + cityName + '\" title=\"Remove\"\u003e×\u003c/button\u003e' +\n      '\u003cdiv class=\"wc-card-header\"\u003e' +\n        '\u003cdiv\u003e' +\n          '\u003cdiv class=\"wc-city-name\"\u003e' + city.flag + ' ' + cityName + '\u003c/div\u003e' +\n          '\u003cdiv class=\"wc-tz-label\"\u003e' + getTzLabel(city.tz) + '\u003c/div\u003e' +\n        '\u003c/div\u003e' +\n        '\u003cdiv class=\"wc-day-night\"\u003e' + (day ? '☀️' : '🌙') + '\u003c/div\u003e' +\n      '\u003c/div\u003e' +\n      '\u003cdiv class=\"wc-time\" id=\"wc-time-' + cityName.replace(/\\s+/g, '-') + '\"\u003e' + getTimeInTz(city.tz) + '\u003c/div\u003e' +\n      '\u003cdiv class=\"wc-date\" id=\"wc-date-' + cityName.replace(/\\s+/g, '-') + '\"\u003e' + getDateInTz(city.tz) + '\u003c/div\u003e' +\n      '\u003cdiv class=\"wc-diff\"\u003e' + getOffsetDiff(city.tz) + '\u003c/div\u003e' +\n      (viewMode === 'analog' ? '\u003cdiv class=\"wc-canvas-wrap\"\u003e\u003ccanvas class=\"wc-analog\" id=\"' + canvasId + '\" width=\"90\" height=\"90\"\u003e\u003c/canvas\u003e\u003c/div\u003e' : '') +\n    '\u003c/div\u003e';\n  }\n\n  function renderGrid() {\n    var grid = document.getElementById('wc-grid');\n    grid.innerHTML = selectedCities.map(renderCard).join('');\n    if (viewMode === 'analog') {\n      selectedCities.forEach(function (cityName) {\n        var city = CITIES.find(function (c) { return c.name === cityName; });\n        if (!city) return;\n        var canvasId = 'wc-canvas-' + cityName.replace(/\\s+/g, '-');\n        var canvas = document.getElementById(canvasId);\n        if (canvas) drawAnalog(canvas, city.tz);\n      });\n    }\n  }\n\n  function tick() {\n    selectedCities.forEach(function (cityName) {\n      var city = CITIES.find(function (c) { return c.name === cityName; });\n      if (!city) return;\n      var key = cityName.replace(/\\s+/g, '-');\n      var timeEl = document.getElementById('wc-time-' + key);\n      var dateEl = document.getElementById('wc-date-' + key);\n      if (timeEl) timeEl.textContent = getTimeInTz(city.tz);\n      if (dateEl) dateEl.textContent = getDateInTz(city.tz);\n      if (viewMode === 'analog') {\n        var canvas = document.getElementById('wc-canvas-' + key);\n        if (canvas) drawAnalog(canvas, city.tz);\n      }\n    });\n  }\n\n  function startTick() {\n    if (tickInterval) clearInterval(tickInterval);\n    tickInterval = setInterval(tick, 1000);\n  }\n\n  function populateSelect(filter) {\n    var sel = document.getElementById('wc-city-select');\n    var val = sel.value;\n    var opts = CITIES.filter(function (c) {\n      if (filter \u0026\u0026 !c.name.toLowerCase().includes(filter.toLowerCase())) return false;\n      return !selectedCities.includes(c.name);\n    });\n    sel.innerHTML = '\u003coption value=\"\"\u003e— Select a city to add —\u003c/option\u003e' +\n      opts.map(function (c) { return '\u003coption value=\"' + c.name + '\"\u003e' + c.flag + ' ' + c.name + '\u003c/option\u003e'; }).join('');\n    if (val) sel.value = val;\n  }\n\n  function populatePlannerTz() {\n    var sel = document.getElementById('wc-planner-tz');\n    sel.innerHTML = CITIES.map(function (c) {\n      return '\u003coption value=\"' + c.tz + '\"\u003e' + c.flag + ' ' + c.name + ' (' + getTzLabel(c.tz) + ')\u003c/option\u003e';\n    }).join('');\n    sel.value = 'Asia/Tokyo';\n  }\n\n  function runPlanner() {\n    var tz = document.getElementById('wc-planner-tz').value;\n    var timeVal = document.getElementById('wc-planner-time').value;\n    if (!timeVal) return;\n    var parts = timeVal.split(':');\n    var hh = parseInt(parts[0], 10);\n    var mm = parseInt(parts[1], 10);\n\n    // build a date in the source tz\n    var now = new Date();\n    // get offset of source tz\n    var srcFormatter = new Intl.DateTimeFormat('en-US', {\n      timeZone: tz, timeZoneName: 'longOffset'\n    });\n    var srcParts = srcFormatter.formatToParts(now);\n    var srcTzName = (srcParts.find(function (p) { return p.type === 'timeZoneName'; }) || {}).value || '';\n    var m2 = srcTzName.match(/GMT([+-])(\\d{2}):(\\d{2})/);\n    var srcOffsetMin = 0;\n    if (m2) {\n      var s2 = m2[1] === '+' ? 1 : -1;\n      srcOffsetMin = s2 * (parseInt(m2[2], 10) * 60 + parseInt(m2[3], 10));\n    }\n\n    // build UTC date for source time today\n    var utcMs = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), hh - srcOffsetMin / 60 | 0, mm - (srcOffsetMin % 60), 0);\n    var utcDate = new Date(utcMs);\n\n    var rows = CITIES.map(function (city) {\n      var opts2 = { hour: '2-digit', minute: '2-digit', hour12: false, timeZone: city.tz };\n      var t = new Intl.DateTimeFormat('en-US', opts2).format(utcDate);\n      var hCity = parseInt(t.split(':')[0], 10);\n      var isWorkHour = hCity \u003e= 9 \u0026\u0026 hCity \u003c 18;\n      return '\u003ctr\u003e\u003ctd\u003e' + city.flag + ' ' + city.name + '\u003c/td\u003e\u003ctd\u003e' + t + ' ' + getTzLabel(city.tz) +\n        '\u003c/td\u003e\u003ctd style=\"color:' + (isWorkHour ? '#16a34a' : '#dc2626') + ';font-weight:600;\"\u003e' +\n        (isWorkHour ? '✓ Business hours' : '✗ Outside hours') + '\u003c/td\u003e\u003c/tr\u003e';\n    });\n\n    document.getElementById('wc-planner-result').innerHTML =\n      '\u003ctable class=\"wc-planner-table\"\u003e\u003cthead\u003e\u003ctr\u003e\u003cth\u003eCity\u003c/th\u003e\u003cth\u003eLocal Time\u003c/th\u003e\u003cth\u003eStatus\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e\u003ctbody\u003e' +\n      rows.join('') + '\u003c/tbody\u003e\u003c/table\u003e';\n  }\n\n  function init() {\n    selectedCities = loadCities();\n    populateSelect('');\n    populatePlannerTz();\n    renderGrid();\n    startTick();\n\n    // search\n    document.getElementById('wc-search').addEventListener('input', function () {\n      populateSelect(this.value);\n    });\n\n    // add city\n    document.getElementById('wc-add-btn').addEventListener('click', function () {\n      var sel = document.getElementById('wc-city-select');\n      var name = sel.value;\n      if (!name) return;\n      if (!selectedCities.includes(name)) {\n        selectedCities.push(name);\n        saveCities();\n        populateSelect(document.getElementById('wc-search').value);\n        renderGrid();\n        startTick();\n      }\n    });\n\n    // reset\n    document.getElementById('wc-reset-btn').addEventListener('click', function () {\n      selectedCities = DEFAULT_CITIES.slice();\n      saveCities();\n      populateSelect(document.getElementById('wc-search').value);\n      renderGrid();\n      startTick();\n    });\n\n    // remove city (delegated)\n    document.getElementById('wc-grid').addEventListener('click', function (e) {\n      var btn = e.target.closest('[data-remove]');\n      if (!btn) return;\n      var name = btn.getAttribute('data-remove');\n      selectedCities = selectedCities.filter(function (c) { return c !== name; });\n      saveCities();\n      populateSelect(document.getElementById('wc-search').value);\n      renderGrid();\n      startTick();\n    });\n\n    // view toggle\n    document.querySelectorAll('#wc-app .wc-toggle-btn').forEach(function (btn) {\n      btn.addEventListener('click', function () {\n        document.querySelectorAll('#wc-app .wc-toggle-btn').forEach(function (b) { b.classList.remove('active'); });\n        btn.classList.add('active');\n        viewMode = btn.getAttribute('data-view');\n        localStorage.setItem(VIEW_KEY, viewMode);\n        renderGrid();\n        startTick();\n      });\n    });\n\n    // planner\n    document.getElementById('wc-planner-calc').addEventListener('click', runPlanner);\n    document.getElementById('wc-planner-time').addEventListener('change', runPlanner);\n    document.getElementById('wc-planner-tz').addEventListener('change', runPlanner);\n    runPlanner();\n  }\n\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', init);\n  } else {\n    init();\n  }\n})();\n\u003c/script\u003e\n\u003c/div\u003e","title":"World Clock - Time Zone Comparison"},{"content":" \u0026#9654; Format \u0026#9646;\u0026#9646; Minify \u0026#10003; Validate \u0026#123;\u0026#125; To JSON 2 Spaces 4 Spaces Tabs \u0026#128196; Sample \u0026#215; Clear \u0026#128203; Copy Input Paste XML above and click Format or Validate. Input XML Output Copied! Formatted Tree View JSON \u0026#128203; Copy Output Related Tools\nFormat JSON \u0026rarr; JSON Formatter \u0026nbsp;|\u0026nbsp; Convert CSV \u0026harr; JSON \u0026rarr; CSV to JSON Converter \u0026nbsp;|\u0026nbsp; Format SQL \u0026rarr; SQL Formatter ","permalink":"https://productivity-works.com/tools/xml-formatter/","summary":"\u003cdiv id=\"xf-app\"\u003e\n\u003cstyle\u003e\n#xf-app *,#xf-app *::before,#xf-app *::after{box-sizing:border-box;margin:0;padding:0}\n#xf-app{font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;font-size:15px;color:#1e293b;line-height:1.6}\n#xf-app .xf-toolbar{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:10px;align-items:center}\n#xf-app .xf-btn{display:inline-flex;align-items:center;gap:5px;padding:7px 14px;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;transition:background .15s,transform .1s;white-space:nowrap}\n#xf-app .xf-btn:active{transform:scale(.97)}\n#xf-app .xf-btn-primary{background:#2563eb;color:#fff}\n#xf-app .xf-btn-primary:hover{background:#1d4ed8}\n#xf-app .xf-btn-secondary{background:#f1f5f9;color:#334155;border:1px solid #cbd5e1}\n#xf-app .xf-btn-secondary:hover{background:#e2e8f0}\n#xf-app .xf-btn-success{background:#16a34a;color:#fff}\n#xf-app .xf-btn-success:hover{background:#15803d}\n#xf-app .xf-btn-warning{background:#d97706;color:#fff}\n#xf-app .xf-btn-warning:hover{background:#b45309}\n#xf-app .xf-btn-info{background:#0891b2;color:#fff}\n#xf-app .xf-btn-info:hover{background:#0e7490}\n#xf-app .xf-btn-danger{background:#dc2626;color:#fff}\n#xf-app .xf-btn-danger:hover{background:#b91c1c}\n#xf-app .xf-select{padding:7px 10px;border:1px solid #cbd5e1;border-radius:6px;font-size:13px;background:#fff;color:#334155;cursor:pointer}\n#xf-app .xf-panels{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:12px}\n@media(max-width:700px){#xf-app .xf-panels{grid-template-columns:1fr}}\n#xf-app .xf-panel{display:flex;flex-direction:column;gap:6px}\n#xf-app .xf-panel-label{font-size:12px;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.05em}\n#xf-app .xf-editor-wrap{position:relative;border:1.5px solid #cbd5e1;border-radius:8px;overflow:hidden;background:#fafafa}\n#xf-app .xf-editor-wrap.xf-error-border{border-color:#ef4444}\n#xf-app .xf-editor-wrap.xf-ok-border{border-color:#22c55e}\n#xf-app .xf-with-lines{display:flex}\n#xf-app .xf-line-nums{width:36px;min-width:36px;background:#f1f5f9;color:#94a3b8;font-family:\"JetBrains Mono\",\"Fira Code\",\"Cascadia Code\",monospace;font-size:12px;line-height:1.6;padding:10px 0;text-align:right;padding-right:6px;user-select:none;overflow:hidden;border-right:1px solid #e2e8f0}\n#xf-app .xf-line-nums span{display:block}\n#xf-app .xf-textarea{width:100%;min-height:280px;padding:10px 12px;border:none;background:transparent;font-family:\"JetBrains Mono\",\"Fira Code\",\"Cascadia Code\",monospace;font-size:13px;line-height:1.6;color:#1e293b;resize:vertical;outline:none}\n#xf-app .xf-output-pre{width:100%;min-height:280px;padding:10px 12px;font-family:\"JetBrains Mono\",\"Fira Code\",\"Cascadia Code\",monospace;font-size:13px;line-height:1.6;overflow:auto;white-space:pre;background:transparent}\n#xf-app .xf-status{display:flex;align-items:center;gap:8px;padding:9px 14px;border-radius:8px;font-size:13px;font-weight:600;min-height:40px}\n#xf-app .xf-status.xf-idle{background:#f8fafc;color:#64748b;border:1px solid #e2e8f0}\n#xf-app .xf-status.xf-ok{background:#f0fdf4;color:#16a34a;border:1px solid #bbf7d0}\n#xf-app .xf-status.xf-err{background:#fef2f2;color:#dc2626;border:1px solid #fecaca}\n#xf-app .xf-status-dot{width:8px;height:8px;border-radius:50%;background:currentColor;flex-shrink:0}\n#xf-app .xf-tab-bar{display:flex;gap:4px;margin-bottom:8px}\n#xf-app .xf-tab{padding:6px 14px;border-radius:6px 6px 0 0;border:1.5px solid #e2e8f0;border-bottom:none;font-size:13px;font-weight:600;cursor:pointer;background:#f8fafc;color:#64748b;transition:background .15s}\n#xf-app .xf-tab.xf-tab-active{background:#fff;color:#2563eb;border-color:#2563eb;border-bottom:2px solid #fff}\n#xf-app .xf-tab-content{display:none}\n#xf-app .xf-tab-content.xf-tab-visible{display:block}\n#xf-app .xf-tree{padding:10px 12px;min-height:200px;font-family:\"JetBrains Mono\",\"Fira Code\",monospace;font-size:12.5px;line-height:1.7;overflow:auto}\n#xf-app .xf-tree-node{cursor:pointer;user-select:none}\n#xf-app .xf-tree-node:hover{background:#f0f9ff;border-radius:3px}\n#xf-app .xf-tree-toggle{display:inline-block;width:14px;text-align:center;color:#94a3b8;font-size:10px}\n#xf-app .xf-tree-children{margin-left:18px}\n#xf-app .xf-tree-tag{color:#2563eb;font-weight:600}\n#xf-app .xf-tree-attr{color:#7c3aed}\n#xf-app .xf-tree-text{color:#374151}\n#xf-app .xf-tree-comment{color:#6b7280;font-style:italic}\n#xf-app .xf-hl-tag{color:#1d4ed8;font-weight:600}\n#xf-app .xf-hl-attr{color:#7c3aed}\n#xf-app .xf-hl-val{color:#15803d}\n#xf-app .xf-hl-cdata{color:#b45309}\n#xf-app .xf-hl-comment{color:#6b7280;font-style:italic}\n#xf-app .xf-hl-pi{color:#0e7490}\n#xf-app .xf-hl-text{color:#374151}\n#xf-app .xf-copy-tip{font-size:12px;color:#16a34a;font-weight:600;opacity:0;transition:opacity .3s}\n#xf-app .xf-copy-tip.xf-show{opacity:1}\n#xf-app .xf-section-title{font-size:14px;font-weight:700;color:#475569;margin:16px 0 8px}\n#xf-app .xf-related{margin-top:20px;padding:14px 16px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;font-size:13px}\n#xf-app .xf-related p{margin:0 0 6px;font-weight:600;color:#475569}\n#xf-app .xf-related a{color:#2563eb;text-decoration:none;margin-right:12px}\n#xf-app .xf-related a:hover{text-decoration:underline}\n\u003c/style\u003e\n\u003cdiv class=\"xf-toolbar\"\u003e\n  \u003cbutton class=\"xf-btn xf-btn-primary\" id=\"xf-btn-format\"\u003e\u0026#9654; Format\u003c/button\u003e\n  \u003cbutton class=\"xf-btn xf-btn-secondary\" id=\"xf-btn-minify\"\u003e\u0026#9646;\u0026#9646; Minify\u003c/button\u003e\n  \u003cbutton class=\"xf-btn xf-btn-success\" id=\"xf-btn-validate\"\u003e\u0026#10003; Validate\u003c/button\u003e\n  \u003cbutton class=\"xf-btn xf-btn-info\" id=\"xf-btn-to-json\"\u003e\u0026#123;\u0026#125; To JSON\u003c/button\u003e\n  \u003cselect class=\"xf-select\" id=\"xf-indent\"\u003e\n    \u003coption value=\"2\"\u003e2 Spaces\u003c/option\u003e\n    \u003coption value=\"4\" selected\u003e4 Spaces\u003c/option\u003e\n    \u003coption value=\"tab\"\u003eTabs\u003c/option\u003e\n  \u003c/select\u003e\n  \u003cbutton class=\"xf-btn xf-btn-secondary\" id=\"xf-btn-sample\"\u003e\u0026#128196; Sample\u003c/button\u003e\n  \u003cbutton class=\"xf-btn xf-btn-danger\" id=\"xf-btn-clear\"\u003e\u0026#215; Clear\u003c/button\u003e\n  \u003cbutton class=\"xf-btn xf-btn-secondary\" id=\"xf-btn-copy-in\"\u003e\u0026#128203; Copy Input\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv id=\"xf-status\" class=\"xf-status xf-idle\"\u003e\u003cspan class=\"xf-status-dot\"\u003e\u003c/span\u003e\u003cspan id=\"xf-status-msg\"\u003ePaste XML above and click Format or Validate.\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"xf-panels\" style=\"margin-top:10px\"\u003e\n  \u003cdiv class=\"xf-panel\"\u003e\n    \u003cdiv class=\"xf-panel-label\"\u003eInput XML\u003c/div\u003e\n    \u003cdiv class=\"xf-editor-wrap\" id=\"xf-in-wrap\"\u003e\n      \u003cdiv class=\"xf-with-lines\"\u003e\n        \u003cdiv class=\"xf-line-nums\" id=\"xf-line-nums\"\u003e\u003c/div\u003e\n        \u003ctextarea class=\"xf-textarea\" id=\"xf-input\" spellcheck=\"false\" placeholder=\"Paste your XML here...\"\u003e\u003c/textarea\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"xf-panel\"\u003e\n    \u003cdiv class=\"xf-panel-label\" style=\"display:flex;justify-content:space-between;align-items:center\"\u003e\n      \u003cspan id=\"xf-out-label\"\u003eOutput\u003c/span\u003e\n      \u003cspan class=\"xf-copy-tip\" id=\"xf-copy-tip\"\u003eCopied!\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"xf-tab-bar\"\u003e\n      \u003cdiv class=\"xf-tab xf-tab-active\" data-tab=\"formatted\"\u003eFormatted\u003c/div\u003e\n      \u003cdiv class=\"xf-tab\" data-tab=\"tree\"\u003eTree View\u003c/div\u003e\n      \u003cdiv class=\"xf-tab\" data-tab=\"json\"\u003eJSON\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"xf-editor-wrap\" id=\"xf-out-wrap\"\u003e\n      \u003cdiv class=\"xf-tab-content xf-tab-visible\" id=\"xf-tab-formatted\"\u003e\n        \u003cpre class=\"xf-output-pre\" id=\"xf-output\"\u003e\u003c/pre\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"xf-tab-content\" id=\"xf-tab-tree\"\u003e\n        \u003cdiv class=\"xf-tree\" id=\"xf-tree-view\"\u003e\u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"xf-tab-content\" id=\"xf-tab-json\"\u003e\n        \u003cpre class=\"xf-output-pre\" id=\"xf-json-output\"\u003e\u003c/pre\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cbutton class=\"xf-btn xf-btn-secondary\" id=\"xf-btn-copy-out\" style=\"align-self:flex-start;margin-top:4px\"\u003e\u0026#128203; Copy Output\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"xf-related\"\u003e\n  \u003cp\u003eRelated Tools\u003c/p\u003e","title":"XML Formatter \u0026 Validator"},{"content":"Japan\u0026rsquo;s individual-defined-contribution pension plan — called iDeCo (individual-type Defined Contribution pension, or 個人型確定拠出年金) — is one of the most powerful tax-reduction tools available to anyone earning income in Japan. Unlike an ISA or a standard brokerage account, every yen you contribute to iDeCo comes straight off your taxable income with no cap on the deduction rate. If you have never run an iDeCo tax deduction calculator, the numbers can surprise you.\nThis guide walks through the mechanics in plain English, shows simulated savings across three income brackets, explains contribution limits by employment type, and ends with a practical step-by-step for opening an account at Rakuten Securities — consistently rated one of Japan\u0026rsquo;s best iDeCo providers for low fees and wide fund selection.\n1. How iDeCo\u0026rsquo;s Income Deduction Works The legal category: 小規模企業共済等掛金控除 iDeCo contributions fall under a specific deduction category called 小規模企業共済等掛金控除 (Small Enterprise Mutual Aid Premium Deduction). The name matters because this deduction is treated differently from most others in the Japanese tax code:\n100% of your annual contribution is deducted from your gross taxable income. There is no phase-out, no income limit, and no percentage cap. The deduction applies to both income tax (所得税) and residence tax (住民税), so you save at two levels simultaneously. Japan\u0026rsquo;s progressive income tax + reconstruction surcharge Japan\u0026rsquo;s national income tax runs on a progressive schedule from 5% to 45%:\nTaxable Income (after deductions) Rate Up to ¥1.95M 5% ¥1.95M – ¥3.3M 10% ¥3.3M – ¥6.95M 20% ¥6.95M – ¥9M 23% ¥9M – ¥18M 33% ¥18M – ¥40M 40% Over ¥40M 45% On top of the base rate, a 2.1% reconstruction surtax is applied to the income tax amount (not the income itself). Additionally, residence tax (住民税) is a flat 10% of taxable income, assessed by your municipality.\nHow the deduction reduces your tax bill Suppose your marginal income tax rate is 20% and you contribute ¥23,000 per month (¥276,000 per year). The calculation is:\nIncome tax saving: ¥276,000 × 20% × 1.021 (reconstruction surtax) = ¥56,359 Residence tax saving: ¥276,000 × 10% = ¥27,600 Total annual saving: ¥83,959 That is money that stays in your pocket each year — before any investment growth inside the iDeCo account. Over 20 years the cumulative tax saving from contributions alone can exceed ¥1.5 million, not counting compounding.\nInvestment growth is also tax-free Inside the account, dividends and capital gains accumulate without annual tax. When you eventually withdraw, a lump-sum withdrawal qualifies for the retirement income deduction (退職所得控除), which is extremely generous — ¥800,000 plus ¥700,000 for every year of contributions beyond 20 years. In many cases the effective tax on the withdrawal approaches zero.\nThe one hard constraint: you cannot withdraw until age 60. iDeCo is a lock-in instrument. If liquidity matters, pair it with NISA rather than substituting for it.\n2. Tax Saving Walk-Through by Income Bracket The figures below use the standard salary earner\u0026rsquo;s deduction (給与所得控除) for each bracket and Japan\u0026rsquo;s current tax rates. They assume the contributor has no other deductions beyond the standard ones.\nRun these numbers interactively with the iDeCo Simulator and cross-check your take-home with the Take-Home Pay Calculator .\nBracket A — Gross Annual Income ¥3,000,000 A ¥3M earner sits at the lower end of the tax table, but iDeCo still delivers meaningful savings.\nItem Value Gross income ¥3,000,000 Salary income deduction ¥980,000 Taxable income (approx.) ¥2,020,000 Marginal income tax rate 10% Residence tax rate 10% Monthly iDeCo contribution (employee) ¥23,000 Annual contribution ¥276,000 Income tax saving (with reconstruction surtax) ¥28,180 Residence tax saving ¥27,600 Total annual tax saving ¥55,780 Monthly effective saving ¥4,648 Over 30 years of contributions, the cumulative tax saving from the deduction alone is approximately ¥1.67 million — before any investment return.\nBracket B — Gross Annual Income ¥5,000,000 The ¥5M bracket is Japan\u0026rsquo;s statistical median for full-time employees. The marginal rate here straddles the 20% band, generating noticeably larger savings.\nItem Value Gross income ¥5,000,000 Salary income deduction ¥1,440,000 Taxable income (approx.) ¥3,560,000 Marginal income tax rate 20% Residence tax rate 10% Monthly iDeCo contribution (employee) ¥23,000 Annual contribution ¥276,000 Income tax saving (with reconstruction surtax) ¥56,359 Residence tax saving ¥27,600 Total annual tax saving ¥83,959 Monthly effective saving ¥6,997 Framed differently: your ¥23,000 monthly contribution effectively costs you only about ¥16,003 net after the tax saving is factored in.\nBracket C — Gross Annual Income ¥7,000,000 At ¥7M, the marginal rate reaches 23%. Managers, engineers with a few years of experience, and many dual-income households land in this range.\nItem Value Gross income ¥7,000,000 Salary income deduction ¥1,610,000 Taxable income (approx.) ¥5,390,000 Marginal income tax rate 23% Residence tax rate 10% Monthly iDeCo contribution (employee) ¥23,000 Annual contribution ¥276,000 Income tax saving (with reconstruction surtax) ¥64,813 Residence tax saving ¥27,600 Total annual tax saving ¥92,413 Monthly effective saving ¥7,701 A ¥7M earner who maximises contributions for 25 years would accumulate over ¥2.3 million in tax savings from the deduction alone. Add investment growth at even a modest 3% annual return and the total advantage becomes substantial.\nSelf-employed — Gross ¥5,000,000 Self-employed individuals (フリーランス, sole proprietors) have the highest contribution limit and often the most to gain because they pay income tax directly through the annual tax filing (確定申告) rather than via payroll withholding.\nItem Value Monthly iDeCo contribution (self-employed) ¥68,000 Annual contribution ¥816,000 Marginal income tax rate 20% Income tax saving ¥166,666 Residence tax saving ¥81,600 Total annual tax saving ¥248,266 At this contribution level, iDeCo alone reduces a ¥5M self-employed earner\u0026rsquo;s annual tax burden by roughly 25%. Combined with the freee accounting platform for tracking deductible expenses and preparing the 確定申告, the total tax reduction can be significantly larger.\n3. Contribution Limits by Employment Type iDeCo limits depend on whether your employer runs a corporate pension scheme. Always confirm your situation with your HR department before setting up contributions.\nEmployment Type Monthly Limit Annual Limit Company employee — no corporate DC or DB plan ¥23,000 ¥276,000 Company employee — with corporate DC plan ¥20,000 ¥240,000 Company employee — with DB plan only ¥12,000 ¥144,000 Company employee — with both DC and DB ¥12,000 ¥144,000 Civil servant (公務員) ¥12,000 ¥144,000 Self-employed / freelancer (国民年金第1号被保険者) ¥68,000 ¥816,000 Housewife / dependent spouse (第3号被保険者) ¥23,000 ¥276,000 Key notes:\nCivil servants gained iDeCo access in 2017; their limit is ¥12,000/month. Self-employed contributors may also be enrolled in the National Pension Fund (国民年金基金); combined contributions to both cannot exceed ¥68,000/month. If your company introduced a iDeCo+ (中小事業主掛金納付制度) scheme, employer contributions count toward your limit. Limits are set by the Defined Contribution Pension Act and are reviewed periodically; check the Japan Pension Service (日本年金機構) website for the latest figures. Need to estimate how iDeCo fits into your overall retirement picture? The Retirement Calculator lets you model contribution scenarios alongside your expected pension and other assets.\n4. Step-by-Step: Opening an iDeCo Account at Rakuten Securities Rakuten Securities (楽天証券) is a strong choice for most iDeCo savers because it charges no account administration fee for the majority of account holders, offers a wide fund lineup including low-cost index funds, and integrates smoothly with the Rakuten ecosystem if you already bank or invest there.\nStep 1 — Check your eligibility You must be a Japanese resident aged 20–64 enrolled in the national pension system (国民年金). Confirm whether your employer has a corporate pension; this determines your contribution limit (see the table above).\nStep 2 — Gather your documents You will need:\nMy Number (個人番号) — your 12-digit individual identification number Basic Resident Register Card or My Number Card (for identity verification) Pension book or Kinen-teichyo showing your pension category (第1号 / 第2号 / 第3号) If employed: a \u0026ldquo;Third party certification form\u0026rdquo; (事業主の証明書) completed by your HR department confirming the corporate pension status at your employer. Download the form from the Rakuten Securities iDeCo page; your HR team fills it in. Step 3 — Apply online at Rakuten Securities Visit the Rakuten Securities iDeCo page and click 「iDeCoに申し込む」. Log in or create a Rakuten Securities account if you do not already have one. Complete the online application form: personal details, pension category, desired monthly contribution amount. Upload scans or photographs of your My Number document and identity verification document. If you are employed, either upload the employer certification form or request that Rakuten Securities sends a paper form to your employer directly. Step 4 — Wait for approval The National Pension Fund Association (国民年金基金連合会) processes all iDeCo applications centrally. Approval typically takes 1–2 months. You will receive a notification letter and your account number by post.\nStep 5 — Select your funds Once the account is active, log in to Rakuten Securities and navigate to the iDeCo fund selection screen. A sensible starting point for most long-term investors:\n楽天・オールカントリー株式インデックス・ファンド (global equities, low cost) for the growth portion 三菱UFJ国内債券インデックスファンド (domestic bonds) for stability if you are within 10 years of 60 Rebalance annually or when your target allocation drifts by more than 5 percentage points.\nStep 6 — Claim the deduction on your tax return Salaried employees: Rakuten Securities will send a 「小規模企業共済等掛金払込証明書」 in October each year. Submit it with your year-end adjustment form (年末調整) to your employer — you do not need to file a separate tax return. Self-employed / freelancers: Enter the contribution amount on Schedule B (第二表) of your 確定申告 under 小規模企業共済等掛金控除. The freee accounting service can pre-populate this figure automatically from your records. Frequently Asked Questions Can I change my monthly contribution amount after enrolling? Yes, once per year. Submit a change request to your financial institution; the new amount takes effect from the following month\u0026rsquo;s contribution.\nWhat happens to my iDeCo if I switch jobs? You must notify the plan within a specified period and either continue contributions under the new employer\u0026rsquo;s rules or temporarily suspend contributions. The accumulated balance remains yours and continues to grow.\nIs there a minimum contribution? Yes — ¥5,000 per month, in ¥1,000 increments.\nCan I lose money in iDeCo? Yes. iDeCo is an investment account, not a guaranteed-return product. However, you can choose capital-guaranteed insurance-type options if you prefer zero investment risk. The tax deduction benefit applies regardless of which funds you choose.\nWhat if I withdraw before age 60? Withdrawal before 60 is generally not permitted except in cases of disability or death. This is the main trade-off versus NISA, which has no lock-in period.\nSummary iDeCo is one of the most effective legal tax-reduction strategies available in Japan. The combination of a full income deduction, tax-free compounding, and a generous retirement deduction on withdrawal makes it uniquely powerful — particularly for self-employed individuals who can contribute up to ¥68,000 per month.\nThe main points to remember:\nContributions qualify for 100% income deduction under 小規模企業共済等掛金控除, reducing both income tax and residence tax. Tax savings range from roughly ¥56,000 to ¥248,000 per year depending on income and contribution limit. Contribution limits vary from ¥12,000 to ¥68,000 per month depending on employment type. Opening an account at Rakuten Securities takes about 1–2 months and is fully online. The hard constraint is the no-withdrawal-until-60 rule — plan your liquidity needs with NISA and other accounts accordingly. Use the iDeCo Simulator to model your personal numbers, compare with the NISA Simulator for the flexible counterpart, and check your monthly take-home with the Take-Home Pay Calculator .\nRelated Tools Estimate your tax bracket → Tax Bracket Calculator Plan your retirement → Retirement Calculator Calculate your take-home pay → Salary Calculator Affiliate Disclosure: This article contains affiliate links. If you sign up for services through links on this page, Productivity Works may receive a commission at no additional cost to you. All product recommendations are based on independent research. Tax figures are based on Japan\u0026rsquo;s tax code as of 2026 and are provided for informational purposes only; consult a licensed tax advisor (税理士) for advice specific to your situation.\nYou May Also Like Best Investment Account for Beginners in Japan: NISA vs. iDeCo vs. Regular Brokerage Retirement Planning in Japan for Foreign Residents: What the Pension System Won\u0026rsquo;t Cover 401k vs IRA Differences Explained (2026) ","permalink":"https://productivity-works.com/posts/ideco-tax-deduction-calculator-japan/","summary":"\u003cp\u003eJapan\u0026rsquo;s individual-defined-contribution pension plan — called \u003cstrong\u003eiDeCo\u003c/strong\u003e (individual-type Defined Contribution pension, or 個人型確定拠出年金) — is one of the most powerful tax-reduction tools available to anyone earning income in Japan. Unlike an ISA or a standard brokerage account, every yen you contribute to iDeCo comes straight off your taxable income with no cap on the deduction rate. If you have never run an iDeCo tax deduction calculator, the numbers can surprise you.\u003c/p\u003e","title":"iDeCo Tax Deduction Calculator: How Much Can You Actually Save in Japan?"},{"content":"Going freelance in Japan as a foreigner sounds daunting. There are unfamiliar forms, a tax system that rewards preparation, and a language barrier that can make even a trip to the city office feel like an adventure. But the core setup — picking a legal structure, securing a web presence, filing your registration, and getting your accounting in order — can genuinely be completed over a single weekend if you know what you are doing.\nThis guide walks you through each step in plain English, with factual information about the forms required, the deadlines that matter, and the tools that will save you hours every tax season.\n1. Legal Structure Options for Foreigners Freelancing in Japan Before you buy a domain or open a bank account, you need to decide what kind of business entity you are operating as. For most foreigners starting out, the choice comes down to two options: sole proprietor (個人事業主, kojin jigyo nushi) or a joint-stock company (株式会社, kabushiki kaisha, commonly abbreviated KK).\nSole Proprietor (個人事業主) A sole proprietorship is the simplest and fastest way to become self-employed in Japan. There is no minimum capital requirement, no notary, and no incorporation filing with the Legal Affairs Bureau. You register at your local tax office (税務署, zeimusho) by submitting a single form — the 開業届 — and you are legally operating.\nFor most freelancers — designers, engineers, translators, consultants, writers — sole proprietor status is entirely sufficient. Your business income and personal income are the same legal entity, which keeps things simple.\nVisa consideration: If you are on an Engineer/Specialist in Humanities/International Services visa (技術・人文知識・国際業務, commonly called \u0026ldquo;Engineer/Humanities\u0026rdquo;), you are generally permitted to do freelance work in a field that matches your visa category. However, you must confirm with your immigration lawyer or the Immigration Services Agency that the specific work you plan to do is covered. Freelancing outside your visa category without a status change can jeopardize your residency.\nKabushiki Kaisha (株式会社, KK) A KK is a full incorporated company. Since a 2006 amendment to the Companies Act, the minimum required capital is just ¥1, which makes incorporation technically affordable. In practice, you will want at least ¥100,000–¥500,000 to cover incorporation costs (notary fees, registration tax, seal creation) and initial operating capital.\nThe main advantages of a KK over a sole proprietorship are improved credibility with Japanese corporate clients, greater flexibility in bringing on co-founders or investors, and certain tax planning advantages once your revenue grows above roughly ¥5–6 million per year.\nVisa consideration: To serve as a representative director (代表取締役) of a KK, foreigners typically need a Business Manager visa (経営・管理). This visa has its own requirements, including a physical business address and evidence of business viability. Switching from an existing work visa to a Business Manager visa requires advance planning.\nWhich Should You Choose? For a first-time freelancer in Japan, sole proprietor is almost always the right starting point. You can always incorporate later. The administrative overhead of a KK — corporate tax filings, board resolutions, statutory audits at scale — adds cost and complexity that a solo operator rarely needs in the early stages.\nUse our Budget Planner to model your projected income and expenses before deciding, since the break-even point for KK incorporation varies significantly depending on your revenue level and business type.\n2. Getting a Domain and Basic Web Presence via お名前.com Before you file anything official, securing your domain is a smart first move. It costs almost nothing, takes fifteen minutes, and gives you a professional email address and online home that you can reference on your business card and official documents.\nWhy a Domain Matters for Japanese Freelancers Japanese corporate clients — and increasingly individual clients — expect a degree of formality. Having yourname@gmail.com on an invoice is a red flag; having hello@yourname.com or contact@yourstudio.jp signals that you are a serious operator. A .com domain works fine for most purposes; a .jp domain can signal local credibility if your target clients are Japanese companies.\nRegistering via お名前.com お名前.com is Japan\u0026rsquo;s largest domain registrar and one of the most widely used by small businesses and freelancers. The interface is available in Japanese, but navigation is straightforward even for non-Japanese readers.\nStep-by-step:\nGo to お名前.com and search for your desired domain name. Add the domain to your cart. .com domains are typically around ¥1,400–¥1,500 per year; .jp domains cost more (around ¥3,000–¥4,000/year). Create an account using your email address. You will need a Japanese phone number or a valid international number for SMS verification. At checkout, you can add optional services like Whois privacy protection (recommended — this hides your personal address from public domain lookup records) and DNS hosting. Complete payment. お名前.com accepts major credit cards and convenience store payment (コンビニ決済). Once your domain is registered, you can set up a simple website using a static site generator, a hosted WordPress instance, or even a single landing page. For most early-stage freelancers, a one-page site with your services, contact form, and a professional headshot is more than sufficient.\nProfessional Email With your domain registered, configure a professional email address. Options include:\nGoogle Workspace (from around ¥680/month) — familiar interface, excellent deliverability. Zoho Mail (free tier available for one user) — a solid zero-cost option when you are just starting. お名前.com\u0026rsquo;s bundled mail service, which is included with some hosting plans. 3. Filing the Business Registration (開業届) — What Forms, Which Office This is the step that officially makes you a sole proprietor in the eyes of the Japanese tax authorities. It is simpler than most people expect.\nWhat Is the 開業届? The 開業届 (kojin jigyo no kaigyou todoke), formally called the 個人事業の開業・廃業等届出書, is a notification — not an application — that you have started a business. You are not asking for permission; you are informing the National Tax Agency (国税庁) through your local tax office.\nDeadline: You must submit the 開業届 within one month of the date you started your business activity. There is no penalty for late filing (the system does not automatically fine you), but filing on time is important for starting the clock on your 青色申告 application window.\nWhat You Need Your My Number (マイナンバー) — the 12-digit individual number issued to all residents of Japan. Your registered seal (印鑑) — or, in many offices today, a signature is accepted. Your address as registered on your residence card (在留カード). The name of your business (屋号, yago) — this is optional but recommended. Choose something simple and professional; it will appear on your invoices and bank account. Your business category (事業の概要) — a brief description in Japanese of what you do. \u0026ldquo;Webデザイン・コンサルティング\u0026rdquo; or \u0026ldquo;翻訳・通訳サービス\u0026rdquo; are typical examples. Where to File You file at the 税務署 (zeimusho) — the local branch of the National Tax Agency — that has jurisdiction over your residential address. To find the correct office, search \u0026ldquo;税務署 [your city/ward name]\u0026rdquo; or use the NTA\u0026rsquo;s official office locator at nta.go.jp.\nYou can file:\nIn person at the zeimusho (no appointment needed; bring your documents and My Number). By post (registered mail to the zeimusho\u0026rsquo;s address). Electronically via the e-Tax system (e-Tax requires a My Number Card and a card reader or compatible smartphone). The form itself is available on the NTA website (search \u0026ldquo;個人事業の開業・廃業等届出書\u0026rdquo;) or you can pick one up at the zeimusho counter. Fill it out, keep a copy with the received stamp (受付印), and you are done.\nFiling for 青色申告 (Blue Return) at the Same Time This is critical: on the same day you file your 開業届, also file the 青色申告承認申請書 (application for approval to use the blue return filing method).\nThe 青色申告 (Blue Return) system gives you access to significant tax deductions not available to white return (白色申告) filers:\nUp to ¥650,000 deduction from your business income if you use double-entry bookkeeping AND file electronically. Up to ¥550,000 if you use double-entry bookkeeping but file on paper. Up to ¥100,000 if you use simplified (single-entry) bookkeeping. Deadline for Blue Return application: Within two months of your business start date (or, if you start between January 1 and January 15, by March 15 of the same year).\nMissing this deadline means you default to white return for your first year, losing tens of thousands of yen in potential deductions. File both forms on the same day to avoid this completely.\n4. Using freee to Handle Invoices and Prep for Kakuteishinkoku from Day One Tax season in Japan — 確定申告 (kakuteishinkoku) — runs from February 16 to March 15 each year. As a sole proprietor, you are required to file your own income tax return covering the previous calendar year. The paperwork is manageable, but only if your records are in order throughout the year. This is where accounting software makes an enormous difference.\nWhy Start Accounting Software Immediately The single most common mistake new freelancers make is treating accounting as something to worry about in February. By then, you have twelve months of bank statements, receipts, and invoices to reconstruct from memory. Starting freee on day one means your books are always current, and kakuteishinkoku becomes a matter of reviewing and submitting rather than a week-long audit of your own finances.\nWhat freee Does freee (pronounced \u0026ldquo;free\u0026rdquo;) is Japan\u0026rsquo;s leading cloud accounting platform for small businesses and sole proprietors. Its core functions include:\nAutomated bank and credit card synchronization — connect your business bank account and freee automatically imports and categorizes transactions. Invoice creation and management — generate professional Japanese-format invoices (請求書) with your registered invoice number if applicable, send them by email or PDF, and track payment status. Expense tracking — photograph receipts with your phone; freee\u0026rsquo;s OCR reads the amounts and suggests categories. Payroll — if you hire employees or pay yourself a director\u0026rsquo;s salary in a KK structure. Kakuteishinkoku filing — freee guides you through the annual tax return step by step and supports e-Tax electronic filing, which is required to claim the full ¥650,000 Blue Return deduction. Setting Up freee as a New Sole Proprietor Step 1: Create your account. Go to freee and sign up. Select \u0026ldquo;個人事業主\u0026rdquo; as your business type. The interface has an English-language option, though some tax-specific terminology remains in Japanese.\nStep 2: Enter your business details. Input your business name (屋号), your business start date, your address, and your My Number. Select 青色申告 as your filing method.\nStep 3: Connect your bank account. freee supports connections to most Japanese major banks (Mitsubishi UFJ, Mizuho, Sumitomo Mitsui, Japan Post Bank, Rakuten Bank, PayPay Bank, and many regional banks). Once connected, transactions sync automatically.\nStep 4: Set up your invoice template. Under the invoicing section, enter your business name, address, bank account details for payment, and — critically — your 登録番号 (invoice registration number) if you have registered under the Invoice System (インボイス制度).\nStep 5: Categorize your first transactions. freee will prompt you to categorize imported transactions. Common categories for freelancers include: 売上 (sales), 通信費 (communication expenses — phone, internet), 旅費交通費 (travel), 消耗品費 (supplies), 外注費 (subcontracting), and 広告宣伝費 (advertising).\nThe Invoice System (インボイス制度) — What Foreigners Need to Know As of October 2023, Japan introduced the 適格請求書等保存方式 (Invoice System). Under this system, businesses that are registered as 適格請求書発行事業者 (qualified invoice issuers) can issue invoices with a unique registration number (T + 13 digits). Clients who are themselves consumption tax payers can only claim input tax credits on invoices that carry this number.\nPractical implication for freelancers:\nIf your clients are individuals or small businesses that are themselves tax-exempt, the invoice system may not matter to them. You can operate without registering. If your clients are large corporations or B2B operators who file consumption tax returns, they will likely require a registered invoice number from you. Without one, they lose the ability to claim input tax credits on what they pay you, which makes you less attractive as a vendor. Registering as a qualified invoice issuer means you must collect and remit 消費税 (consumption tax, currently 10%), even if your annual revenue is under ¥10 million (the threshold that normally triggers consumption tax liability for small businesses). For B2B freelancers, registering under the invoice system is increasingly a practical necessity. You can apply through the NTA\u0026rsquo;s e-Tax portal. freee walks you through setting up consumption tax handling once you enter your registration number.\nPlanning Your Finances: The Budget Planner Once your bookkeeping is running, take thirty minutes to map your financial projections using our Budget Planner . Input your expected monthly revenue, your fixed costs (domain, software subscriptions, phone, coworking space), and your variable costs (travel, equipment). The planner will show you your projected net income and help you estimate quarterly tax payment obligations.\nFor a clearer picture of what you will actually take home after income tax and resident tax, use the Take-home Pay calculator . Japan\u0026rsquo;s tax brackets and resident tax (住民税) can take a significant share of income above ¥3–4 million, so understanding your effective take-home rate early helps you price your services correctly.\nWeekend Action Plan: From Zero to Operational in 48 Hours Here is a compressed checklist for turning a weekend into a fully operational freelance setup:\nSaturday Morning\nDecide on sole proprietor vs. KK structure based on visa status and revenue projections. Register your domain at お名前.com . Enable Whois privacy. Set up professional email (Google Workspace or Zoho Mail). Saturday Afternoon\nDownload and fill in the 開業届 (個人事業の開業・廃業等届出書) from nta.go.jp. Download and fill in the 青色申告承認申請書. Confirm your local 税務署 address and opening hours (most are open weekdays 8:30–17:00; some have Saturday hours). Sunday Morning\nVisit the 税務署 and submit both forms. Keep the stamped copies. Open a business bank account (Rakuten Bank and PayPay Bank offer online applications with fast turnaround for sole proprietors). Sunday Afternoon\nCreate your freee account and complete initial setup. Connect your bank account. Create your first invoice template. Run your numbers through the Budget Planner and Take-home Pay calculator . By Sunday evening, you have a registered business, a professional web presence, and accounting software tracking every yen from day one.\nCommon Mistakes to Avoid Missing the Blue Return application window. This is the single costliest error. The ¥650,000 deduction available to Blue Return filers using double-entry bookkeeping and e-filing can translate to tens of thousands of yen in saved taxes annually. File the application simultaneously with your 開業届.\nUsing a personal bank account for business transactions. Mixing personal and business finances creates a bookkeeping nightmare at tax time. Open a dedicated business account immediately, even if it is just a free online bank account.\nIgnoring consumption tax planning. Even if you are under the ¥10 million threshold and technically exempt, if you plan to do significant B2B work, consider registering under the invoice system from the start to avoid losing clients who require registered invoices.\nNot keeping receipts. Under Blue Return rules, you can deduct business expenses — but only with documentation. freee\u0026rsquo;s receipt scanning feature removes the excuse for not keeping records. Photograph every receipt the day you receive it.\nAssuming your current visa covers all freelance activities. Check with an immigration lawyer or administrative scrivener (行政書士) before you start. Some visa categories are narrowly defined. Working outside your permitted scope is a serious immigration violation.\nLong-Term: Growing Your Freelance Business in Japan Once you are operational, the next milestones to plan for are:\nIncorporating as a KK when your annual revenue consistently exceeds ¥5–6 million and the tax planning advantages of a corporation begin to outweigh the administrative overhead. Hiring — even one part-time assistant changes your compliance obligations significantly. freee\u0026rsquo;s payroll module handles withholding tax and social insurance calculations. Expanding savings and investment — as a sole proprietor, you can contribute to an iDeCo (individual defined contribution pension) account, which offers significant income deductions. Use the Compound Interest calculator to model the long-term impact of consistent iDeCo contributions. Final Thoughts Setting up a freelance business in Japan as a foreigner is far less complicated than the language barrier makes it appear. The forms are standardized, the process is predictable, and the tools — from お名前.com for your web presence to freee for your accounting — are designed to be used by non-accountants.\nThe keys are: choose the right structure for your visa status, file your 開業届 and Blue Return application within the deadline windows, and start your bookkeeping on day one rather than scrambling in February. A weekend of focused action gets all of this done.\nUse お名前.com to secure your domain and professional email, freee to keep your books clean from the first invoice, and our Budget Planner to make sure your pricing covers both your costs and your taxes.\nGood luck — and welcome to self-employment in Japan.\nRelated Tools Calculate your ideal freelance hourly rate → Freelance Rate Calculator This article provides general informational guidance only and does not constitute legal, tax, or immigration advice. Regulations change; verify current rules with a qualified tax accountant (税理士), immigration lawyer, or the relevant government authorities before making decisions.\nAffiliate Disclosure: This article contains affiliate links. If you register for a service through these links, we may earn a commission at no additional cost to you. We only recommend services we believe are genuinely useful for freelancers in Japan.\nYou May Also Like How to Start Freelancing in 2026: Complete Beginner\u0026rsquo;\u0026rsquo;s Guide Freelance in Japan: How to Register as Kojin Jigyonushi and File Taxes With freee Freelance Tax Guide 2026: Everything You Need to Know ","permalink":"https://productivity-works.com/posts/freelance-business-setup-japan-foreigner/","summary":"\u003cp\u003eGoing freelance in Japan as a foreigner sounds daunting. There are unfamiliar forms, a tax system that rewards preparation, and a language barrier that can make even a trip to the city office feel like an adventure. But the core setup — picking a legal structure, securing a web presence, filing your registration, and getting your accounting in order — can genuinely be completed over a single weekend if you know what you are doing.\u003c/p\u003e","title":"Setting Up a Freelance Business in Japan: Domain, Registration, and Accounting in One Weekend"},{"content":" Your Info Annual Gross Salary ($) Filing Status Single Married Filing Jointly Head of Household Pay Frequency Annual Monthly Bi-weekly Weekly State California (CA) New York (NY) Texas (TX) Florida (FL) Washington (WA) Illinois (IL) Pennsylvania (PA) Ohio (OH) Georgia (GA) North Carolina (NC) New Jersey (NJ) Virginia (VA) Massachusetts (MA) Arizona (AZ) Colorado (CO) Alabama (AL) Alaska (AK) Arkansas (AR) Connecticut (CT) Delaware (DE) Hawaii (HI) Idaho (ID) Indiana (IN) Iowa (IA) Kansas (KS) Kentucky (KY) Louisiana (LA) Maine (ME) Maryland (MD) Michigan (MI) Minnesota (MN) Mississippi (MS) Missouri (MO) Montana (MT) Nebraska (NE) Nevada (NV) New Hampshire (NH) New Mexico (NM) North Dakota (ND) Oklahoma (OK) Oregon (OR) Rhode Island (RI) South Carolina (SC) South Dakota (SD) Tennessee (TN) Utah (UT) Vermont (VT) West Virginia (WV) Wisconsin (WI) Wyoming (WY) Washington D.C. (DC) Other (enter rate manually) State Tax Rate (%) 401(k) Contribution (%) Additional Pre-tax Deductions ($) Annual Take-Home Pay — Monthly: — Bi-weekly: — Weekly: — Effective Tax Rate — Marginal Tax Rate — Total Deductions — Tax Breakdown ItemAnnual Amount Federal Income Tax — State Income Tax — Social Security (6.2%) — Medicare — 401(k) Contribution — Total Deductions — Paycheck Breakdown Take-home Federal State SS Medicare 401(k) 2026 tax brackets applied. Standard deduction: Single $15,200 / MFJ $30,400 / HoH $22,800. Social Security wage base $176,100. 401(k) contribution reduces federal \u0026amp; state taxable income. State rates are simplified effective rates. Does not include local taxes, AMT, credits, or itemized deductions. For official tax advice consult a CPA. Sponsored Links\nFind higher-paying jobs on doda Try freee for easy tax filing Other Free Financial Tools\nCompound Interest Calculator Budget Planner Loan Repayment Calculator Retirement Calculator Forex Profit Calculator Calculate compound interest → Compound Interest Calculator Estimate mortgage payments → Mortgage Calculator Related Articles How Much Tax Do You Pay on FX Profits in Japan? A Salary Worker\u0026rsquo;s Guide How to Calculate Your Take-Home Pay in Japan After Tax, Pension and Insurance (2026) Side Job Tax Rules in Japan: When Do You Need to File and How to Avoid Penalties? ","permalink":"https://productivity-works.com/tools/salary-calculator/","summary":"\u003cstyle\u003e\n.sc-wrap {\n  max-width: 720px;\n  margin: 0 auto;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  color: #1e293b;\n}\n.sc-card {\n  background: #ffffff;\n  border: 1px solid #e2e8f0;\n  border-radius: 14px;\n  padding: 28px;\n  margin-bottom: 20px;\n  box-shadow: 0 2px 8px rgba(74,158,255,0.07);\n}\n.sc-section-title {\n  font-size: 15px;\n  font-weight: 700;\n  color: #4a9eff;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  margin: 0 0 18px 0;\n}\n.sc-field {\n  margin-bottom: 18px;\n}\n.sc-label {\n  display: block;\n  font-size: 14px;\n  font-weight: 600;\n  color: #475569;\n  margin-bottom: 8px;\n}\n.sc-range-row {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n}\n.sc-range-row input[type=\"range\"] {\n  flex: 1;\n  accent-color: #4a9eff;\n  cursor: pointer;\n}\n.sc-number-input {\n  width: 110px;\n  padding: 7px 10px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  font-size: 15px;\n  font-weight: 600;\n  color: #1e293b;\n  text-align: right;\n  transition: border-color .2s;\n}\n.sc-number-input:focus {\n  outline: none;\n  border-color: #4a9eff;\n  box-shadow: 0 0 0 3px rgba(74,158,255,0.15);\n}\n.sc-select {\n  width: 100%;\n  padding: 9px 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  font-size: 15px;\n  color: #1e293b;\n  background: #fff;\n  cursor: pointer;\n  transition: border-color .2s;\n}\n.sc-select:focus {\n  outline: none;\n  border-color: #4a9eff;\n  box-shadow: 0 0 0 3px rgba(74,158,255,0.15);\n}\n.sc-grid-2 {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 14px;\n}\n@media (max-width: 500px) {\n  .sc-grid-2 { grid-template-columns: 1fr; }\n}\n.sc-result-hero {\n  background: linear-gradient(135deg, #1d4ed8 0%, #4a9eff 100%);\n  border-radius: 12px;\n  padding: 28px 20px;\n  text-align: center;\n  color: #fff;\n  margin-bottom: 20px;\n}\n.sc-result-hero .label {\n  font-size: 14px;\n  opacity: .85;\n  margin-bottom: 6px;\n  letter-spacing: .04em;\n}\n.sc-result-hero .amount {\n  font-size: 48px;\n  font-weight: 800;\n  letter-spacing: -1px;\n  line-height: 1;\n}\n.sc-result-hero .sub {\n  margin-top: 14px;\n  font-size: 15px;\n  opacity: .9;\n  display: flex;\n  justify-content: center;\n  gap: 24px;\n  flex-wrap: wrap;\n}\n.sc-result-hero .sub span { font-weight: 700; }\n.sc-rate-row {\n  display: flex;\n  gap: 14px;\n  margin-bottom: 20px;\n}\n.sc-rate-box {\n  flex: 1;\n  border-radius: 10px;\n  padding: 14px 12px;\n  text-align: center;\n}\n.sc-rate-box .rl { font-size: 12px; color: #64748b; margin-bottom: 4px; }\n.sc-rate-box .rv { font-size: 20px; font-weight: 700; }\n.sc-breakdown-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 14px;\n}\n.sc-breakdown-table th {\n  text-align: left;\n  font-size: 12px;\n  font-weight: 700;\n  color: #94a3b8;\n  text-transform: uppercase;\n  letter-spacing: .06em;\n  padding: 0 0 8px 0;\n  border-bottom: 1px solid #e2e8f0;\n}\n.sc-breakdown-table th:last-child { text-align: right; }\n.sc-breakdown-table td {\n  padding: 10px 0;\n  border-bottom: 1px solid #f1f5f9;\n  color: #334155;\n}\n.sc-breakdown-table td:last-child {\n  text-align: right;\n  font-weight: 600;\n  color: #dc2626;\n}\n.sc-breakdown-table tr.total-row td {\n  font-weight: 700;\n  font-size: 15px;\n  color: #1e293b;\n  border-bottom: none;\n  padding-top: 12px;\n}\n.sc-breakdown-table tr.total-row td:last-child { color: #dc2626; }\n.sc-dot {\n  display: inline-block;\n  width: 10px;\n  height: 10px;\n  border-radius: 50%;\n  margin-right: 7px;\n  vertical-align: middle;\n}\n.sc-bar-wrap {\n  display: flex;\n  height: 20px;\n  border-radius: 6px;\n  overflow: hidden;\n  margin-bottom: 10px;\n}\n.sc-bar-legend {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  font-size: 12px;\n  color: #475569;\n}\n.sc-other-row {\n  display: none;\n  margin-top: 10px;\n}\n.sc-note {\n  background: #f8fafc;\n  border-left: 3px solid #4a9eff;\n  border-radius: 0 8px 8px 0;\n  padding: 12px 16px;\n  font-size: 13px;\n  color: #64748b;\n  line-height: 1.6;\n  margin-top: 8px;\n}\n.sc-affiliate {\n  background: #fffbeb;\n  border: 1px solid #fde68a;\n  border-radius: 10px;\n  padding: 18px 20px;\n  margin-bottom: 20px;\n}\n.sc-affiliate p { margin: 0 0 8px 0; font-size: 14px; font-weight: 600; color: #92400e; }\n.sc-affiliate a { color: #b45309; font-weight: 600; }\n.sc-crosslinks {\n  background: #f0f9ff;\n  border: 1px solid #bae6fd;\n  border-radius: 10px;\n  padding: 18px 20px;\n}\n.sc-crosslinks p { margin: 0 0 10px 0; font-size: 14px; font-weight: 700; color: #0369a1; }\n.sc-crosslinks ul { margin: 0; padding: 0 0 0 18px; }\n.sc-crosslinks ul li { margin-bottom: 6px; font-size: 14px; }\n.sc-crosslinks ul li a { color: #0284c7; font-weight: 500; }\n\u003c/style\u003e\n\u003cdiv class=\"sc-wrap\"\u003e\n\u003c!-- ===== INPUTS ===== --\u003e\n\u003cdiv class=\"sc-card\"\u003e\n  \u003cdiv class=\"sc-section-title\"\u003eYour Info\u003c/div\u003e\n  \u003cdiv class=\"sc-field\"\u003e\n    \u003clabel class=\"sc-label\" for=\"sc-salary\"\u003eAnnual Gross Salary ($)\u003c/label\u003e\n    \u003cdiv class=\"sc-range-row\"\u003e\n      \u003cinput type=\"range\" id=\"sc-salary-range\" min=\"10000\" max=\"500000\" step=\"1000\" value=\"75000\"\u003e\n      \u003cinput type=\"number\" id=\"sc-salary\" class=\"sc-number-input\" min=\"10000\" max=\"500000\" value=\"75000\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"sc-grid-2\"\u003e\n    \u003cdiv class=\"sc-field\"\u003e\n      \u003clabel class=\"sc-label\" for=\"sc-filing\"\u003eFiling Status\u003c/label\u003e\n      \u003cselect id=\"sc-filing\" class=\"sc-select\"\u003e\n        \u003coption value=\"single\"\u003eSingle\u003c/option\u003e\n        \u003coption value=\"married\"\u003eMarried Filing Jointly\u003c/option\u003e\n        \u003coption value=\"head\"\u003eHead of Household\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"sc-field\"\u003e\n      \u003clabel class=\"sc-label\" for=\"sc-freq\"\u003ePay Frequency\u003c/label\u003e\n      \u003cselect id=\"sc-freq\" class=\"sc-select\"\u003e\n        \u003coption value=\"1\"\u003eAnnual\u003c/option\u003e\n        \u003coption value=\"12\"\u003eMonthly\u003c/option\u003e\n        \u003coption value=\"26\" selected\u003eBi-weekly\u003c/option\u003e\n        \u003coption value=\"52\"\u003eWeekly\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"sc-field\"\u003e\n    \u003clabel class=\"sc-label\" for=\"sc-state\"\u003eState\u003c/label\u003e\n    \u003cselect id=\"sc-state\" class=\"sc-select\"\u003e\n      \u003coption value=\"CA\"\u003eCalifornia (CA)\u003c/option\u003e\n      \u003coption value=\"NY\"\u003eNew York (NY)\u003c/option\u003e\n      \u003coption value=\"TX\"\u003eTexas (TX)\u003c/option\u003e\n      \u003coption value=\"FL\"\u003eFlorida (FL)\u003c/option\u003e\n      \u003coption value=\"WA\"\u003eWashington (WA)\u003c/option\u003e\n      \u003coption value=\"IL\"\u003eIllinois (IL)\u003c/option\u003e\n      \u003coption value=\"PA\"\u003ePennsylvania (PA)\u003c/option\u003e\n      \u003coption value=\"OH\"\u003eOhio (OH)\u003c/option\u003e\n      \u003coption value=\"GA\"\u003eGeorgia (GA)\u003c/option\u003e\n      \u003coption value=\"NC\"\u003eNorth Carolina (NC)\u003c/option\u003e\n      \u003coption value=\"NJ\"\u003eNew Jersey (NJ)\u003c/option\u003e\n      \u003coption value=\"VA\"\u003eVirginia (VA)\u003c/option\u003e\n      \u003coption value=\"MA\"\u003eMassachusetts (MA)\u003c/option\u003e\n      \u003coption value=\"AZ\"\u003eArizona (AZ)\u003c/option\u003e\n      \u003coption value=\"CO\"\u003eColorado (CO)\u003c/option\u003e\n      \u003coption value=\"AL\"\u003eAlabama (AL)\u003c/option\u003e\n      \u003coption value=\"AK\"\u003eAlaska (AK)\u003c/option\u003e\n      \u003coption value=\"AR\"\u003eArkansas (AR)\u003c/option\u003e\n      \u003coption value=\"CT\"\u003eConnecticut (CT)\u003c/option\u003e\n      \u003coption value=\"DE\"\u003eDelaware (DE)\u003c/option\u003e\n      \u003coption value=\"HI\"\u003eHawaii (HI)\u003c/option\u003e\n      \u003coption value=\"ID\"\u003eIdaho (ID)\u003c/option\u003e\n      \u003coption value=\"IN\"\u003eIndiana (IN)\u003c/option\u003e\n      \u003coption value=\"IA\"\u003eIowa (IA)\u003c/option\u003e\n      \u003coption value=\"KS\"\u003eKansas (KS)\u003c/option\u003e\n      \u003coption value=\"KY\"\u003eKentucky (KY)\u003c/option\u003e\n      \u003coption value=\"LA\"\u003eLouisiana (LA)\u003c/option\u003e\n      \u003coption value=\"ME\"\u003eMaine (ME)\u003c/option\u003e\n      \u003coption value=\"MD\"\u003eMaryland (MD)\u003c/option\u003e\n      \u003coption value=\"MI\"\u003eMichigan (MI)\u003c/option\u003e\n      \u003coption value=\"MN\"\u003eMinnesota (MN)\u003c/option\u003e\n      \u003coption value=\"MS\"\u003eMississippi (MS)\u003c/option\u003e\n      \u003coption value=\"MO\"\u003eMissouri (MO)\u003c/option\u003e\n      \u003coption value=\"MT\"\u003eMontana (MT)\u003c/option\u003e\n      \u003coption value=\"NE\"\u003eNebraska (NE)\u003c/option\u003e\n      \u003coption value=\"NV\"\u003eNevada (NV)\u003c/option\u003e\n      \u003coption value=\"NH\"\u003eNew Hampshire (NH)\u003c/option\u003e\n      \u003coption value=\"NM\"\u003eNew Mexico (NM)\u003c/option\u003e\n      \u003coption value=\"ND\"\u003eNorth Dakota (ND)\u003c/option\u003e\n      \u003coption value=\"OK\"\u003eOklahoma (OK)\u003c/option\u003e\n      \u003coption value=\"OR\"\u003eOregon (OR)\u003c/option\u003e\n      \u003coption value=\"RI\"\u003eRhode Island (RI)\u003c/option\u003e\n      \u003coption value=\"SC\"\u003eSouth Carolina (SC)\u003c/option\u003e\n      \u003coption value=\"SD\"\u003eSouth Dakota (SD)\u003c/option\u003e\n      \u003coption value=\"TN\"\u003eTennessee (TN)\u003c/option\u003e\n      \u003coption value=\"UT\"\u003eUtah (UT)\u003c/option\u003e\n      \u003coption value=\"VT\"\u003eVermont (VT)\u003c/option\u003e\n      \u003coption value=\"WV\"\u003eWest Virginia (WV)\u003c/option\u003e\n      \u003coption value=\"WI\"\u003eWisconsin (WI)\u003c/option\u003e\n      \u003coption value=\"WY\"\u003eWyoming (WY)\u003c/option\u003e\n      \u003coption value=\"DC\"\u003eWashington D.C. (DC)\u003c/option\u003e\n      \u003coption value=\"OTHER\"\u003eOther (enter rate manually)\u003c/option\u003e\n    \u003c/select\u003e\n    \u003cdiv class=\"sc-other-row\" id=\"sc-other-row\"\u003e\n      \u003clabel class=\"sc-label\" for=\"sc-other-rate\"\u003eState Tax Rate (%)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"sc-other-rate\" class=\"sc-number-input\" min=\"0\" max=\"20\" step=\"0.1\" value=\"5\" style=\"width:90px;\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"sc-grid-2\"\u003e\n    \u003cdiv class=\"sc-field\"\u003e\n      \u003clabel class=\"sc-label\" for=\"sc-401k\"\u003e401(k) Contribution (%)\u003c/label\u003e\n      \u003cdiv class=\"sc-range-row\"\u003e\n        \u003cinput type=\"range\" id=\"sc-401k-range\" min=\"0\" max=\"100\" step=\"1\" value=\"6\"\u003e\n        \u003cinput type=\"number\" id=\"sc-401k\" class=\"sc-number-input\" min=\"0\" max=\"100\" value=\"6\" style=\"width:80px;\"\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"sc-field\"\u003e\n      \u003clabel class=\"sc-label\" for=\"sc-pretax\"\u003eAdditional Pre-tax Deductions ($)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"sc-pretax\" class=\"sc-number-input\" min=\"0\" value=\"0\" style=\"width:100%;\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ===== RESULTS ===== --\u003e\n\u003cdiv class=\"sc-result-hero\" id=\"sc-hero\"\u003e\n  \u003cdiv class=\"label\"\u003eAnnual Take-Home Pay\u003c/div\u003e\n  \u003cdiv class=\"amount\" id=\"sc-takehome-annual\"\u003e—\u003c/div\u003e\n  \u003cdiv class=\"sub\"\u003e\n    \u003cdiv\u003eMonthly: \u003cspan id=\"sc-monthly\"\u003e—\u003c/span\u003e\u003c/div\u003e\n    \u003cdiv\u003eBi-weekly: \u003cspan id=\"sc-biweekly\"\u003e—\u003c/span\u003e\u003c/div\u003e\n    \u003cdiv\u003eWeekly: \u003cspan id=\"sc-weekly\"\u003e—\u003c/span\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv style=\"margin-top:14px;font-size:14px;opacity:.85;\" id=\"sc-per-period-label\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"sc-rate-row\"\u003e\n  \u003cdiv class=\"sc-rate-box\" style=\"background:#dcfce7;\"\u003e\n    \u003cdiv class=\"rl\"\u003eEffective Tax Rate\u003c/div\u003e\n    \u003cdiv class=\"rv\" style=\"color:#15803d;\" id=\"sc-eff-rate\"\u003e—\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"sc-rate-box\" style=\"background:#fef3c7;\"\u003e\n    \u003cdiv class=\"rl\"\u003eMarginal Tax Rate\u003c/div\u003e\n    \u003cdiv class=\"rv\" style=\"color:#b45309;\" id=\"sc-marg-rate\"\u003e—\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"sc-rate-box\" style=\"background:#fee2e2;\"\u003e\n    \u003cdiv class=\"rl\"\u003eTotal Deductions\u003c/div\u003e\n    \u003cdiv class=\"rv\" style=\"color:#dc2626;\" id=\"sc-total-ded\"\u003e—\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"sc-card\"\u003e\n  \u003cdiv class=\"sc-section-title\"\u003eTax Breakdown\u003c/div\u003e\n  \u003ctable class=\"sc-breakdown-table\"\u003e\n    \u003cthead\u003e\n      \u003ctr\u003e\u003cth\u003eItem\u003c/th\u003e\u003cth\u003eAnnual Amount\u003c/th\u003e\u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody\u003e\n      \u003ctr\u003e\n        \u003ctd\u003e\u003cspan class=\"sc-dot\" style=\"background:#dc2626;\"\u003e\u003c/span\u003eFederal Income Tax\u003c/td\u003e\n        \u003ctd id=\"sc-fed-tax\"\u003e—\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n        \u003ctd\u003e\u003cspan class=\"sc-dot\" style=\"background:#d97706;\"\u003e\u003c/span\u003eState Income Tax\u003c/td\u003e\n        \u003ctd id=\"sc-state-tax\"\u003e—\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n        \u003ctd\u003e\u003cspan class=\"sc-dot\" style=\"background:#0369a1;\"\u003e\u003c/span\u003eSocial Security (6.2%)\u003c/td\u003e\n        \u003ctd id=\"sc-ss\"\u003e—\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n        \u003ctd\u003e\u003cspan class=\"sc-dot\" style=\"background:#7c3aed;\"\u003e\u003c/span\u003eMedicare\u003c/td\u003e\n        \u003ctd id=\"sc-med\"\u003e—\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n        \u003ctd\u003e\u003cspan class=\"sc-dot\" style=\"background:#0891b2;\"\u003e\u003c/span\u003e401(k) Contribution\u003c/td\u003e\n        \u003ctd id=\"sc-401k-amt\"\u003e—\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr class=\"total-row\"\u003e\n        \u003ctd\u003e\u003cstrong\u003eTotal Deductions\u003c/strong\u003e\u003c/td\u003e\n        \u003ctd id=\"sc-total-ded-2\"\u003e—\u003c/td\u003e\n      \u003c/tr\u003e\n    \u003c/tbody\u003e\n  \u003c/table\u003e\n  \u003c!-- Stacked bar --\u003e\n  \u003cdiv style=\"margin-top:20px;\"\u003e\n    \u003cdiv style=\"font-size:12px;font-weight:600;color:#94a3b8;margin-bottom:8px;\"\u003ePaycheck Breakdown\u003c/div\u003e\n    \u003cdiv class=\"sc-bar-wrap\"\u003e\n      \u003cdiv id=\"sc-bar-take\" style=\"background:#16a34a;\"\u003e\u003c/div\u003e\n      \u003cdiv id=\"sc-bar-fed\"  style=\"background:#dc2626;\"\u003e\u003c/div\u003e\n      \u003cdiv id=\"sc-bar-state\" style=\"background:#d97706;\"\u003e\u003c/div\u003e\n      \u003cdiv id=\"sc-bar-ss\"  style=\"background:#0369a1;\"\u003e\u003c/div\u003e\n      \u003cdiv id=\"sc-bar-med\"  style=\"background:#7c3aed;\"\u003e\u003c/div\u003e\n      \u003cdiv id=\"sc-bar-401k\" style=\"background:#0891b2;\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"sc-bar-legend\"\u003e\n      \u003cspan\u003e\u003cspan class=\"sc-dot\" style=\"background:#16a34a;\"\u003e\u003c/span\u003eTake-home\u003c/span\u003e\n      \u003cspan\u003e\u003cspan class=\"sc-dot\" style=\"background:#dc2626;\"\u003e\u003c/span\u003eFederal\u003c/span\u003e\n      \u003cspan\u003e\u003cspan class=\"sc-dot\" style=\"background:#d97706;\"\u003e\u003c/span\u003eState\u003c/span\u003e\n      \u003cspan\u003e\u003cspan class=\"sc-dot\" style=\"background:#0369a1;\"\u003e\u003c/span\u003eSS\u003c/span\u003e\n      \u003cspan\u003e\u003cspan class=\"sc-dot\" style=\"background:#7c3aed;\"\u003e\u003c/span\u003eMedicare\u003c/span\u003e\n      \u003cspan\u003e\u003cspan class=\"sc-dot\" style=\"background:#0891b2;\"\u003e\u003c/span\u003e401(k)\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"sc-note\"\u003e\n  \u003cstrong\u003e2026 tax brackets applied.\u003c/strong\u003e Standard deduction: Single $15,200 / MFJ $30,400 / HoH $22,800. Social Security wage base $176,100. 401(k) contribution reduces federal \u0026amp; state taxable income. State rates are simplified effective rates. Does not include local taxes, AMT, credits, or itemized deductions. For official tax advice consult a CPA.\n\u003c/div\u003e\n\u003c!-- ===== AFFILIATE ===== --\u003e\n\u003cdiv class=\"sc-affiliate\" style=\"margin-top:24px;\"\u003e\n  \u003cp\u003eSponsored Links\u003c/p\u003e","title":"US Salary Calculator — Take-Home Pay After Taxes"},{"content":"About Productivity Works Productivity Works is a media platform built around one idea: you should be in control of how you work.\nWe publish practical, actionable guides on productivity, AI tools, side income, and career development — because better information leads to better decisions, and better decisions lead to a better life.\nOur Mission Empower every individual to make the most of their time and abilities.\nThe rise of AI and remote work has opened up more opportunities than ever before for individuals to build income streams, grow skills, and design their own working lives. But the information landscape is noisy.\nWe cut through the noise. Every article we publish is tested, researched, and written with one goal: to give you something you can actually use.\nWhat We Cover Productivity \u0026amp; Efficiency Time management and task management systems Remote work and home office optimization Focus, habits, and deep work strategies AI Tools \u0026amp; Automation Practical guides to ChatGPT, Claude, Gemini, and more Using AI to automate repetitive tasks and save hours each week Monetizing AI skills as a freelancer or side hustler Side Income \u0026amp; Career How to start a side hustle while working a full-time job Freelancing and building an independent income Career transitions and job hunting in the AI era Our Editorial Standards We hold ourselves to high standards because our readers\u0026rsquo; time is valuable:\nHands-on testing: We don\u0026rsquo;t write about tools we haven\u0026rsquo;t used ourselves No fluff: Every article is structured to get to the point quickly Regular updates: Outdated articles are revised to reflect current realities Balanced perspective: We cover the pros and cons honestly Get in Touch Have feedback on an article? Want to collaborate? We\u0026rsquo;d love to hear from you.\nVisit our Contact page or email us directly at kagakusha22@gmail.com .\nProductivity Works — Work smarter, live better.\n","permalink":"https://productivity-works.com/about/","summary":"\u003ch2 id=\"about-productivity-works\"\u003eAbout Productivity Works\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eProductivity Works\u003c/strong\u003e is a media platform built around one idea: \u003cem\u003eyou should be in control of how you work.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eWe publish practical, actionable guides on productivity, AI tools, side income, and career development — because better information leads to better decisions, and better decisions lead to a better life.\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"our-mission\"\u003eOur Mission\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003eEmpower every individual to make the most of their time and abilities.\u003c/strong\u003e\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eThe rise of AI and remote work has opened up more opportunities than ever before for individuals to build income streams, grow skills, and design their own working lives. But the information landscape is noisy.\u003c/p\u003e","title":"About Productivity Works"},{"content":"This article contains affiliate links.\nBudget Journal Generator | Ideal Spending Allocation Calculator Enter your monthly take-home income and instantly get your ideal budget breakdown. Allocation ratios are based on guidelines recommended by certified financial planners.\nMonthly Take-Home Pay (USD) $1,000$3,500$15,000 Household Type Single Couple (Dual Income) Family (with Children) Monthly Savings \u0026 Investment Target $700 Annual: $8,400 Allocation basis: Ratios are based on CFP-recommended spending guidelines. Actual budgets vary by individual circumstances. Use this as a general reference. Action Steps to Grow Your Savings Use your simulation results to start building savings more efficiently.\nCut fixed expenses — Review your phone plan, insurance, and subscriptions to potentially save $100–200/month Invest your savings — Index funds and ETFs offer higher expected returns than a savings account Increase income with a side hustle — An extra $300–500/month can double your savings rate Use accounting software — Automate tracking by linking your bank accounts to a budgeting app Frequently Asked Questions Q. What percentage of take-home pay should go to housing? A general guideline is 25–30% of your take-home pay. Aim for around 28% if you live alone, and under 25% for families.\nQ. Is a 20% savings rate realistic? For a single person earning $3,500/month, that\u0026rsquo;s $700/month in savings. Combining fixed-cost reduction with automatic savings transfers makes this very achievable.\nQ. How should I handle bonuses? Ideally, put 50% or more of any bonus straight into savings or investments. Manage bonuses separately from your monthly budget.\nRelated Tools Job Change Salary Simulator — Compare take-home pay before and after a job change FIRE Retirement Simulator — Calculate years to financial independence Take-Home Pay Calculator — Calculate net pay from your gross salary ","permalink":"https://productivity-works.com/tools/budget-journal-generator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"budget-journal-generator--ideal-spending-allocation-calculator\"\u003eBudget Journal Generator | Ideal Spending Allocation Calculator\u003c/h1\u003e\n\u003cp\u003eEnter your monthly take-home income and instantly get your \u003cstrong\u003eideal budget breakdown\u003c/strong\u003e. Allocation ratios are based on guidelines recommended by certified financial planners.\u003c/p\u003e\n\u003cdiv id=\"kakeibo-calc\" style=\"max-width:640px;margin:0 auto;padding:24px;border:2px solid #2563eb;border-radius:12px;background:#f8fafc;\"\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eMonthly Take-Home Pay (USD)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"monthlyIncome\" min=\"1000\" max=\"15000\" step=\"100\" value=\"3500\" oninput=\"calcBudget()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e$1,000\u003c/span\u003e\u003cspan id=\"incomeVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e$3,500\u003c/span\u003e\u003cspan\u003e$15,000\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:24px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eHousehold Type\u003c/label\u003e\n\u003cselect id=\"household\" onchange=\"calcBudget()\" style=\"width:100%;padding:8px;border:1px solid #cbd5e1;border-radius:6px;font-size:16px;\"\u003e\n\u003coption value=\"single\"\u003eSingle\u003c/option\u003e\n\u003coption value=\"couple\"\u003eCouple (Dual Income)\u003c/option\u003e\n\u003coption value=\"family\"\u003eFamily (with Children)\u003c/option\u003e\n\u003c/select\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#1e40af;color:white;border-radius:8px;padding:20px;text-align:center;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"font-size:14px;margin-bottom:4px;\"\u003eMonthly Savings \u0026 Investment Target\u003c/div\u003e\n\u003cdiv id=\"savingsTarget\" style=\"font-size:36px;font-weight:bold;\"\u003e$700\u003c/div\u003e\n\u003cdiv style=\"font-size:13px;margin-top:4px;\"\u003eAnnual: \u003cspan id=\"savingsYearly\"\u003e$8,400\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"budgetBreakdown\" style=\"margin-bottom:16px;\"\u003e\u003c/div\u003e\n\u003cdiv style=\"background:#f1f5f9;border-radius:8px;padding:12px;font-size:13px;color:#475569;\"\u003e\n\u003cstrong\u003eAllocation basis:\u003c/strong\u003e Ratios are based on CFP-recommended spending guidelines. Actual budgets vary by individual circumstances. Use this as a general reference.\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nfunction calcBudget(){\n  var income=parseInt(document.getElementById('monthlyIncome').value);\n  var type=document.getElementById('household').value;\n\n  var ratios;\n  if(type==='single'){\n    ratios=[\n      {name:'Housing',rate:0.28,color:'#ef4444'},\n      {name:'Food \u0026 Groceries',rate:0.15,color:'#f97316'},\n      {name:'Utilities',rate:0.05,color:'#eab308'},\n      {name:'Phone \u0026 Internet',rate:0.05,color:'#22c55e'},\n      {name:'Personal \u0026 Clothing',rate:0.05,color:'#06b6d4'},\n      {name:'Social \u0026 Entertainment',rate:0.08,color:'#8b5cf6'},\n      {name:'Transportation',rate:0.04,color:'#ec4899'},\n      {name:'Insurance \u0026 Healthcare',rate:0.03,color:'#6366f1'},\n      {name:'Self-Development',rate:0.05,color:'#14b8a6'},\n      {name:'Emergency Fund',rate:0.02,color:'#94a3b8'},\n      {name:'Savings \u0026 Investing',rate:0.20,color:'#2563eb'}\n    ];\n  } else if(type==='couple'){\n    ratios=[\n      {name:'Housing',rate:0.25,color:'#ef4444'},\n      {name:'Food \u0026 Groceries',rate:0.15,color:'#f97316'},\n      {name:'Utilities',rate:0.05,color:'#eab308'},\n      {name:'Phone \u0026 Internet',rate:0.05,color:'#22c55e'},\n      {name:'Personal \u0026 Clothing',rate:0.05,color:'#06b6d4'},\n      {name:'Social \u0026 Entertainment',rate:0.07,color:'#8b5cf6'},\n      {name:'Transportation',rate:0.04,color:'#ec4899'},\n      {name:'Insurance \u0026 Healthcare',rate:0.04,color:'#6366f1'},\n      {name:'Self-Development',rate:0.03,color:'#14b8a6'},\n      {name:'Emergency Fund',rate:0.02,color:'#94a3b8'},\n      {name:'Savings \u0026 Investing',rate:0.25,color:'#2563eb'}\n    ];\n  } else {\n    ratios=[\n      {name:'Housing',rate:0.25,color:'#ef4444'},\n      {name:'Food \u0026 Groceries',rate:0.18,color:'#f97316'},\n      {name:'Utilities',rate:0.06,color:'#eab308'},\n      {name:'Phone \u0026 Internet',rate:0.04,color:'#22c55e'},\n      {name:'Personal \u0026 Clothing',rate:0.06,color:'#06b6d4'},\n      {name:'Education \u0026 Childcare',rate:0.08,color:'#8b5cf6'},\n      {name:'Transportation',rate:0.04,color:'#ec4899'},\n      {name:'Insurance \u0026 Healthcare',rate:0.06,color:'#6366f1'},\n      {name:'Entertainment \u0026 Social',rate:0.05,color:'#14b8a6'},\n      {name:'Emergency Fund',rate:0.03,color:'#94a3b8'},\n      {name:'Savings \u0026 Investing',rate:0.15,color:'#2563eb'}\n    ];\n  }\n\n  var savings=0;\n  var html='';\n  for(var i=0;i\u003cratios.length;i++){\n    var amt=Math.floor(income*ratios[i].rate);\n    var pct=Math.round(ratios[i].rate*100);\n    if(ratios[i].name==='Savings \u0026 Investing') savings=amt;\n    html+='\u003cdiv style=\"display:flex;align-items:center;padding:8px 0;border-bottom:1px solid #e2e8f0;\"\u003e';\n    html+='\u003cdiv style=\"width:12px;height:12px;border-radius:50%;background:'+ratios[i].color+';margin-right:10px;flex-shrink:0;\"\u003e\u003c/div\u003e';\n    html+='\u003cdiv style=\"flex:1;font-size:14px;\"\u003e'+ratios[i].name+' \u003cspan style=\"color:#94a3b8;\"\u003e('+pct+'%)\u003c/span\u003e\u003c/div\u003e';\n    html+='\u003cdiv style=\"font-weight:bold;font-size:15px;\"\u003e$'+amt.toLocaleString()+'\u003c/div\u003e';\n    html+='\u003c/div\u003e';\n  }\n\n  document.getElementById('incomeVal').textContent='$'+income.toLocaleString();\n  document.getElementById('savingsTarget').textContent='$'+savings.toLocaleString();\n  document.getElementById('savingsYearly').textContent='$'+(savings*12).toLocaleString();\n  document.getElementById('budgetBreakdown').innerHTML=html;\n}\ncalcBudget();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"action-steps-to-grow-your-savings\"\u003eAction Steps to Grow Your Savings\u003c/h2\u003e\n\u003cp\u003eUse your simulation results to start building savings more efficiently.\u003c/p\u003e","title":"Budget Journal Generator | Auto-Calculate Ideal Monthly Spending Allocation"},{"content":"This page contains affiliate links.\nBudget Planner | 50/30/20 Rule Calculator Enter your monthly take-home pay to get a recommended spending breakdown based on the 50/30/20 budgeting rule.\nMonthly Take-Home Pay ($) $1,000$5,000$15,000 Monthly Savings Target (20%) $1,000 Annual savings: $12,000 Needs (50%)\nHousing, food, utilities, insurance, transport $2,500 Wants (30%)\nDining out, entertainment, shopping, hobbies $1,500 Savings \u0026 Debt (20%)\nEmergency fund, investments, debt repayment $1,000 If you invest your 20% savings: In 10 years (7% return)$173,085 In 30 years (7% return)$1,219,971 The 50/30/20 Rule: Popularized by Senator Elizabeth Warren, this simple framework allocates 50% of after-tax income to needs, 30% to wants, and 20% to savings/debt repayment. How To Use Your Budget Needs (50%) Breakdown Category Typical % of Income Housing (rent/mortgage) 25-30% Groceries 10-15% Utilities 3-5% Transportation 5-10% Insurance 3-5% Where To Put Your 20% Savings Emergency fund first — 3-6 months of expenses in a high-yield savings account Max out employer 401(k) match — Free money, don\u0026rsquo;t leave it on the table Invest the rest — Low-cost index funds through a brokerage account Use our Compound Interest Calculator to see how your savings grow over time.\nFAQ Q: What if I can\u0026rsquo;t save 20%? Start with whatever you can — even 5% is better than nothing. Automate your savings and increase by 1% each month.\nQ: Should I include debt payments in the 20%? Yes. Minimum debt payments go under \u0026ldquo;Needs\u0026rdquo; (50%), but extra payments toward debt reduction count as part of your 20%.\nQ: What if my needs exceed 50%? This is common in high cost-of-living areas. Look for ways to reduce your biggest expense (usually housing) or increase income.\nRecommended Next Steps Based on your budget results, here are the most impactful actions you can take:\nStart investing your savings — Even $100/month in index funds can grow to $76,000+ in 20 years at 7% returns. Use our Compound Interest Calculator to see how Pay off high-interest debt first — Use our Loan Repayment Calculator to create a payoff plan Calculate your real take-home pay — Make sure you\u0026rsquo;re budgeting from the right number. Try our Salary Calculator Explore forex trading — If you have extra investment capital, learn about forex markets with our Forex Profit Calculator Related Tools Retirement Savings Calculator — Estimate your 401(k)/IRA nest egg Compound Interest Calculator — See your investment growth Loan Repayment Calculator — Plan your debt payoff Salary Calculator — Know your real take-home pay Forex Profit Calculator — Calculate trading profits Related Articles Beginner Investing Guide 2026: How to Start Building Wealth Today How to Start Investing with $100 in 2026 7 Best Budgeting Apps in 2026 (Free \u0026amp; Paid Compared) ","permalink":"https://productivity-works.com/tools/budget-planner/","summary":"\u003cp\u003e\u003cem\u003eThis page contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"budget-planner--503020-rule-calculator\"\u003eBudget Planner | 50/30/20 Rule Calculator\u003c/h1\u003e\n\u003cp\u003eEnter your monthly take-home pay to get a \u003cstrong\u003erecommended spending breakdown\u003c/strong\u003e based on the 50/30/20 budgeting rule.\u003c/p\u003e\n\u003cdiv id=\"budget-calc\" style=\"max-width:640px;margin:0 auto;padding:24px;border:2px solid #2563eb;border-radius:12px;background:#f8fafc;\"\u003e\n\u003cdiv style=\"margin-bottom:24px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eMonthly Take-Home Pay ($)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"monthlyPay\" min=\"1000\" max=\"15000\" step=\"100\" value=\"5000\" oninput=\"calcBudget()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e$1,000\u003c/span\u003e\u003cspan id=\"payVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e$5,000\u003c/span\u003e\u003cspan\u003e$15,000\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#1e40af;color:white;border-radius:8px;padding:20px;text-align:center;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"font-size:14px;margin-bottom:4px;\"\u003eMonthly Savings Target (20%)\u003c/div\u003e\n\u003cdiv id=\"savingsAmt\" style=\"font-size:36px;font-weight:bold;\"\u003e$1,000\u003c/div\u003e\n\u003cdiv style=\"font-size:13px;margin-top:4px;\"\u003eAnnual savings: \u003cspan id=\"savingsYearly\" style=\"font-weight:bold;\"\u003e$12,000\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:16px;\"\u003e\n\u003cdiv style=\"display:flex;align-items:center;padding:12px 0;border-bottom:1px solid #e2e8f0;\"\u003e\n\u003cdiv style=\"width:16px;height:16px;border-radius:50%;background:#ef4444;margin-right:12px;flex-shrink:0;\"\u003e\u003c/div\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003cstrong\u003eNeeds (50%)\u003c/strong\u003e\u003cbr\u003e\u003cspan style=\"font-size:13px;color:#64748b;\"\u003eHousing, food, utilities, insurance, transport\u003c/span\u003e\u003c/div\u003e\n\u003cdiv style=\"font-weight:bold;font-size:18px;\" id=\"needsAmt\"\u003e$2,500\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;align-items:center;padding:12px 0;border-bottom:1px solid #e2e8f0;\"\u003e\n\u003cdiv style=\"width:16px;height:16px;border-radius:50%;background:#f59e0b;margin-right:12px;flex-shrink:0;\"\u003e\u003c/div\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003cstrong\u003eWants (30%)\u003c/strong\u003e\u003cbr\u003e\u003cspan style=\"font-size:13px;color:#64748b;\"\u003eDining out, entertainment, shopping, hobbies\u003c/span\u003e\u003c/div\u003e\n\u003cdiv style=\"font-weight:bold;font-size:18px;\" id=\"wantsAmt\"\u003e$1,500\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:flex;align-items:center;padding:12px 0;border-bottom:1px solid #e2e8f0;\"\u003e\n\u003cdiv style=\"width:16px;height:16px;border-radius:50%;background:#2563eb;margin-right:12px;flex-shrink:0;\"\u003e\u003c/div\u003e\n\u003cdiv style=\"flex:1;\"\u003e\u003cstrong\u003eSavings \u0026 Debt (20%)\u003c/strong\u003e\u003cbr\u003e\u003cspan style=\"font-size:13px;color:#64748b;\"\u003eEmergency fund, investments, debt repayment\u003c/span\u003e\u003c/div\u003e\n\u003cdiv style=\"font-weight:bold;font-size:18px;\" id=\"saveAmt\"\u003e$1,000\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#dcfce7;border-radius:8px;padding:16px;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"font-size:13px;color:#15803d;margin-bottom:4px;\"\u003e\u003cstrong\u003eIf you invest your 20% savings:\u003c/strong\u003e\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:8px;\"\u003e\n\u003cdiv style=\"text-align:center;\"\u003e\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eIn 10 years (7% return)\u003c/div\u003e\u003cdiv id=\"invest10\" style=\"font-weight:bold;color:#15803d;font-size:16px;\"\u003e$173,085\u003c/div\u003e\u003c/div\u003e\n\u003cdiv style=\"text-align:center;\"\u003e\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eIn 30 years (7% return)\u003c/div\u003e\u003cdiv id=\"invest30\" style=\"font-weight:bold;color:#15803d;font-size:16px;\"\u003e$1,219,971\u003c/div\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#f1f5f9;border-radius:8px;padding:12px;font-size:13px;color:#475569;\"\u003e\n\u003cstrong\u003eThe 50/30/20 Rule:\u003c/strong\u003e Popularized by Senator Elizabeth Warren, this simple framework allocates 50% of after-tax income to needs, 30% to wants, and 20% to savings/debt repayment.\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nfunction calcBudget(){\n  var pay=parseInt(document.getElementById('monthlyPay').value);\n  var needs=Math.floor(pay*0.5);\n  var wants=Math.floor(pay*0.3);\n  var save=Math.floor(pay*0.2);\n\n  // Investment projections at 7% annual return\n  var mr=0.07/12;\n  var fv10=save*((Math.pow(1+mr,120)-1)/mr);\n  var fv30=save*((Math.pow(1+mr,360)-1)/mr);\n\n  document.getElementById('payVal').textContent='$'+pay.toLocaleString();\n  document.getElementById('savingsAmt').textContent='$'+save.toLocaleString();\n  document.getElementById('savingsYearly').textContent='$'+(save*12).toLocaleString();\n  document.getElementById('needsAmt').textContent='$'+needs.toLocaleString();\n  document.getElementById('wantsAmt').textContent='$'+wants.toLocaleString();\n  document.getElementById('saveAmt').textContent='$'+save.toLocaleString();\n  document.getElementById('invest10').textContent='$'+Math.floor(fv10).toLocaleString();\n  document.getElementById('invest30').textContent='$'+Math.floor(fv30).toLocaleString();\n}\ncalcBudget();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-to-use-your-budget\"\u003eHow To Use Your Budget\u003c/h2\u003e\n\u003ch3 id=\"needs-50-breakdown\"\u003eNeeds (50%) Breakdown\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eCategory\u003c/th\u003e\n          \u003cth\u003eTypical % of Income\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eHousing (rent/mortgage)\u003c/td\u003e\n          \u003ctd\u003e25-30%\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eGroceries\u003c/td\u003e\n          \u003ctd\u003e10-15%\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eUtilities\u003c/td\u003e\n          \u003ctd\u003e3-5%\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eTransportation\u003c/td\u003e\n          \u003ctd\u003e5-10%\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eInsurance\u003c/td\u003e\n          \u003ctd\u003e3-5%\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"where-to-put-your-20-savings\"\u003eWhere To Put Your 20% Savings\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eEmergency fund first\u003c/strong\u003e — 3-6 months of expenses in a high-yield savings account\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eMax out employer 401(k) match\u003c/strong\u003e — Free money, don\u0026rsquo;t leave it on the table\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eInvest the rest\u003c/strong\u003e — Low-cost index funds through a brokerage account\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eUse our \u003ca href=\"https://productivity-works.com/tools/compound-interest-calculator/\"\u003eCompound Interest Calculator\u003c/a\u003e\n to see how your savings grow over time.\u003c/p\u003e","title":"Budget Planner | Free Monthly Budget Calculator"},{"content":"This page contains affiliate links.\nCompound Interest Calculator Enter your monthly contribution, expected annual return, and investment timeline to calculate your future portfolio value.\nMonthly Contribution ($) $50$500$5,000 Expected Annual Return (%) 1%7.0%12% Investment Period (years) 1 yr20 years40 yrs Future Portfolio Value $260,464 Total Contributions $120,000 Investment Gains $140,464 Tax-Advantaged (Roth IRA/401k) $0 No tax on gains Taxable Account (15% cap gains) $0 Tax savings: $0 Growth Timeline Year Contributions Portfolio Value Gains Assumptions: Monthly contributions at end of each month, compounded monthly. Actual investment returns will vary based on market conditions. Past performance does not guarantee future results. What To Do Next Ready to start investing? Here are your next steps:\nHow to Start Investing as a Beginner Best Index Funds for Long-Term Growth 2026 Best Robo-Advisors 2026 Start Investing Today Ready to put compound interest to work? Here are the best ways to get started:\nOpen a brokerage account — Low-cost platforms like Fidelity, Schwab, or Vanguard make it easy to start with index funds Max out tax-advantaged accounts — 401(k) and IRA contributions grow tax-free or tax-deferred, supercharging compound growth Automate your investments — Set up automatic monthly contributions so you never miss a month of compounding Track your budget — Use our Budget Planner to find extra money to invest each month FAQ Q: Is 7% annual return realistic? The S\u0026amp;P 500 has returned approximately 10% annually over the past 30 years before inflation. A 7% return is a conservative estimate after accounting for inflation and fees.\nQ: Does this account for taxes? This calculator shows pre-tax returns. Using tax-advantaged accounts (401k, IRA, Roth IRA) can significantly reduce your tax burden on investment gains.\nQ: Should I invest a lump sum or dollar-cost average? Historically, lump-sum investing outperforms dollar-cost averaging about 2/3 of the time. However, regular monthly contributions are more practical for most people and reduce timing risk.\nRelated Tools Retirement Savings Calculator — Estimate your 401(k)/IRA nest egg Budget Planner — Find extra money to invest each month Salary Calculator — Calculate your take-home pay Loan Repayment Calculator — Plan your debt payoff Dividend Income Calculator — Calculate annual dividend income Forex Profit Calculator — Calculate trading profits Plan your path to financial independence → FIRE Calculator Related Articles NISA vs FX in Japan: Which Is Better for Growing Your Money Tax-Efficiently? Beginner Investing Guide 2026: How to Start Building Wealth Today Best Index Funds for Beginners 2026 ","permalink":"https://productivity-works.com/tools/compound-interest-calculator/","summary":"\u003cp\u003e\u003cem\u003eThis page contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"compound-interest-calculator\"\u003eCompound Interest Calculator\u003c/h1\u003e\n\u003cp\u003eEnter your monthly contribution, expected annual return, and investment timeline to calculate your \u003cstrong\u003efuture portfolio value\u003c/strong\u003e.\u003c/p\u003e\n\u003cdiv id=\"calc-app\" style=\"max-width:640px;margin:0 auto;padding:24px;border:2px solid #2563eb;border-radius:12px;background:#f8fafc;\"\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eMonthly Contribution ($)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"monthly\" min=\"50\" max=\"5000\" step=\"50\" value=\"500\" oninput=\"calc()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e$50\u003c/span\u003e\u003cspan id=\"monthlyVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e$500\u003c/span\u003e\u003cspan\u003e$5,000\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eExpected Annual Return (%)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"rate\" min=\"1\" max=\"12\" step=\"0.5\" value=\"7\" oninput=\"calc()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e1%\u003c/span\u003e\u003cspan id=\"rateVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e7.0%\u003c/span\u003e\u003cspan\u003e12%\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:24px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eInvestment Period (years)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"years\" min=\"1\" max=\"40\" step=\"1\" value=\"20\" oninput=\"calc()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e1 yr\u003c/span\u003e\u003cspan id=\"yearsVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e20 years\u003c/span\u003e\u003cspan\u003e40 yrs\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#1e40af;color:white;border-radius:8px;padding:20px;text-align:center;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"font-size:14px;margin-bottom:4px;\"\u003eFuture Portfolio Value\u003c/div\u003e\n\u003cdiv id=\"totalAsset\" style=\"font-size:36px;font-weight:bold;\"\u003e$260,464\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"background:#e0f2fe;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eTotal Contributions\u003c/div\u003e\n\u003cdiv id=\"principal\" style=\"font-size:16px;font-weight:bold;color:#0369a1;\"\u003e$120,000\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#dcfce7;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eInvestment Gains\u003c/div\u003e\n\u003cdiv id=\"profit\" style=\"font-size:16px;font-weight:bold;color:#15803d;\"\u003e$140,464\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"background:#fef3c7;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eTax-Advantaged (Roth IRA/401k)\u003c/div\u003e\n\u003cdiv id=\"taxFreeNet\" style=\"font-size:16px;font-weight:bold;color:#b45309;\"\u003e$0\u003c/div\u003e\n\u003cdiv style=\"font-size:11px;color:#92400e;\"\u003eNo tax on gains\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#fce7f3;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eTaxable Account (15% cap gains)\u003c/div\u003e\n\u003cdiv id=\"taxedNet\" style=\"font-size:16px;font-weight:bold;color:#be185d;\"\u003e$0\u003c/div\u003e\n\u003cdiv style=\"font-size:11px;color:#9d174d;\" id=\"taxSaved\"\u003eTax savings: $0\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003ch3 style=\"margin:20px 0 12px;font-size:16px;\"\u003eGrowth Timeline\u003c/h3\u003e\n\u003cdiv style=\"overflow-x:auto;\"\u003e\n\u003ctable id=\"timeline\" style=\"width:100%;border-collapse:collapse;font-size:13px;\"\u003e\n\u003cthead\u003e\u003ctr style=\"background:#1e3a5f;color:white;\"\u003e\n\u003cth style=\"padding:6px 8px;text-align:center;\"\u003eYear\u003c/th\u003e\n\u003cth style=\"padding:6px 8px;text-align:right;\"\u003eContributions\u003c/th\u003e\n\u003cth style=\"padding:6px 8px;text-align:right;\"\u003ePortfolio Value\u003c/th\u003e\n\u003cth style=\"padding:6px 8px;text-align:right;\"\u003eGains\u003c/th\u003e\n\u003c/tr\u003e\u003c/thead\u003e\n\u003ctbody id=\"tbody\"\u003e\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#f1f5f9;border-radius:8px;padding:12px;font-size:13px;color:#475569;margin-top:16px;\"\u003e\n\u003cstrong\u003eAssumptions:\u003c/strong\u003e Monthly contributions at end of each month, compounded monthly. Actual investment returns will vary based on market conditions. Past performance does not guarantee future results.\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nfunction calc(){\n  var m=parseInt(document.getElementById('monthly').value);\n  var r=parseFloat(document.getElementById('rate').value)/100;\n  var y=parseInt(document.getElementById('years').value);\n  var mr=r/12;\n  var n=y*12;\n  var fv=m*((Math.pow(1+mr,n)-1)/mr);\n  var p=m*n;\n  var g=fv-p;\n  var tax=g*0.15;\n  document.getElementById('monthlyVal').textContent='$'+m.toLocaleString();\n  document.getElementById('rateVal').textContent=(r*100).toFixed(1)+'%';\n  document.getElementById('yearsVal').textContent=y+(y===1?' year':' years');\n  document.getElementById('totalAsset').textContent='$'+Math.floor(fv).toLocaleString();\n  document.getElementById('principal').textContent='$'+p.toLocaleString();\n  document.getElementById('profit').textContent='$'+Math.floor(g).toLocaleString();\n  document.getElementById('taxFreeNet').textContent='$'+Math.floor(fv).toLocaleString();\n  document.getElementById('taxedNet').textContent='$'+Math.floor(fv-tax).toLocaleString();\n  document.getElementById('taxSaved').textContent='Tax savings: $'+Math.floor(tax).toLocaleString();\n  var tb=document.getElementById('tbody');\n  tb.innerHTML='';\n  var milestones=[1,3,5,10,15,20,25,30,35,40];\n  for(var i=0;i\u003cmilestones.length;i++){\n    var yr=milestones[i];\n    if(yr\u003ey)break;\n    var nm=yr*12;\n    var fvY=m*((Math.pow(1+mr,nm)-1)/mr);\n    var pY=m*nm;\n    var gY=fvY-pY;\n    var bg=i%2===0?'#f8fafc':'#ffffff';\n    tb.innerHTML+='\u003ctr style=\"background:'+bg+'\"\u003e\u003ctd style=\"padding:6px 8px;text-align:center;border-bottom:1px solid #e2e8f0;\"\u003e'+yr+'\u003c/td\u003e\u003ctd style=\"padding:6px 8px;text-align:right;border-bottom:1px solid #e2e8f0;\"\u003e$'+pY.toLocaleString()+'\u003c/td\u003e\u003ctd style=\"padding:6px 8px;text-align:right;border-bottom:1px solid #e2e8f0;font-weight:bold;\"\u003e$'+Math.floor(fvY).toLocaleString()+'\u003c/td\u003e\u003ctd style=\"padding:6px 8px;text-align:right;border-bottom:1px solid #e2e8f0;color:#15803d;\"\u003e+$'+Math.floor(gY).toLocaleString()+'\u003c/td\u003e\u003c/tr\u003e';\n  }\n}\ncalc();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"what-to-do-next\"\u003eWhat To Do Next\u003c/h2\u003e\n\u003cp\u003eReady to start investing? Here are your next steps:\u003c/p\u003e","title":"Compound Interest Calculator | Free Investment Growth Simulator"},{"content":"Contact Us We\u0026rsquo;d love to hear from you. Reach out to the Productivity Works team using the information below.\nGet in Touch Email: kagakusha22@gmail.com To help us respond faster, please include one of the following in your subject line:\nArticle feedback or corrections → Subject: \u0026ldquo;Article Feedback\u0026rdquo; Collaboration or partnership inquiry → Subject: \u0026ldquo;Collaboration Inquiry\u0026rdquo; Advertising or sponsored content → Subject: \u0026ldquo;Advertising Inquiry\u0026rdquo; General questions → Subject: \u0026ldquo;General Inquiry\u0026rdquo; Frequently Asked Questions Q. I found an error in one of your articles. How do I report it? Please email us with the article title and a description of the issue. We take accuracy seriously and will review and correct errors promptly.\nQ. Can I republish or translate your content? Short quotes with a link back to the original article are welcome. Full republication or translation requires prior written permission. See our Privacy Policy for more details.\nQ. Do you accept guest posts or contributor articles? We are currently considering opening up contributor submissions. If you\u0026rsquo;re interested, please reach out via email with your topic ideas and writing background.\nQ. Do you accept sponsored content or advertising? We may consider sponsored content that aligns with our editorial values and is clearly disclosed to readers. Contact us to discuss.\nResponse Time We aim to respond to all inquiries within 2–5 business days. During busy periods, responses may take longer. Thank you for your patience.\nProductivity Works Editorial Team\n","permalink":"https://productivity-works.com/contact/","summary":"\u003ch2 id=\"contact-us\"\u003eContact Us\u003c/h2\u003e\n\u003cp\u003eWe\u0026rsquo;d love to hear from you. Reach out to the Productivity Works team using the information below.\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"get-in-touch\"\u003eGet in Touch\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eEmail\u003c/strong\u003e: \u003ca href=\"mailto:kagakusha22@gmail.com\"\u003ekagakusha22@gmail.com\u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003eTo help us respond faster, please include one of the following in your subject line:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eArticle feedback or corrections → Subject: \u0026ldquo;Article Feedback\u0026rdquo;\u003c/li\u003e\n\u003cli\u003eCollaboration or partnership inquiry → Subject: \u0026ldquo;Collaboration Inquiry\u0026rdquo;\u003c/li\u003e\n\u003cli\u003eAdvertising or sponsored content → Subject: \u0026ldquo;Advertising Inquiry\u0026rdquo;\u003c/li\u003e\n\u003cli\u003eGeneral questions → Subject: \u0026ldquo;General Inquiry\u0026rdquo;\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch2 id=\"frequently-asked-questions\"\u003eFrequently Asked Questions\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eQ. I found an error in one of your articles. How do I report it?\u003c/strong\u003e\nPlease email us with the article title and a description of the issue. We take accuracy seriously and will review and correct errors promptly.\u003c/p\u003e","title":"Contact"},{"content":"Forex Profit Calculator Enter your trade details below to instantly calculate profit/loss, margin required, and tax on gains.\nCurrency Pair EUR/USD GBP/USD USD/JPY AUD/USD USD/CAD Position Buy (Long) Sell (Short) Lot Size (1 standard lot = 100,000 units) 0.011.00 lot10.00 Entry Price Exit Price Spread (pips) 01.0 pips5.0 Profit / Loss +$900.00 Net pips: +90.0 Margin Required $2,168 Leverage 50:1 ROI +41.5% Note: Calculations use 50:1 leverage (US standard). Pip values are approximate. Actual results vary by broker. Swap/rollover fees not included. How Forex Profit is Calculated The basic formula for forex profit:\nProfit = (Price Change in Pips) x (Pip Value) x (Lot Size) - Spread Cost\nPair Pip Size Pip Value (1 std lot) EUR/USD 0.0001 $10 GBP/USD 0.0001 $10 USD/JPY 0.01 ~$6.45 AUD/USD 0.0001 $10 Risk Management Tips Never risk more than 1-2% of your account on a single trade Always use stop losses to limit potential downside Start with micro lots (0.01) if you\u0026rsquo;re a beginner Higher leverage = higher risk — use lower leverage until you\u0026rsquo;re experienced Keep a trading journal to track and improve your performance FAQ Q: What is a pip? A pip is the smallest price move in a currency pair. For EUR/USD it\u0026rsquo;s 0.0001 (the fourth decimal). For USD/JPY it\u0026rsquo;s 0.01 (the second decimal).\nQ: How much money do I need to start trading forex? With a micro account (0.01 lots), you can start with as little as $50-100. However, $500-1,000 is recommended for proper risk management.\nQ: What\u0026rsquo;s the difference between a standard lot and a mini lot? A standard lot is 100,000 units, a mini lot is 10,000 units, and a micro lot is 1,000 units.\nRelated Tools Compound Interest Calculator — Long-term investment growth Loan Repayment Calculator — Debt payoff planning Budget Planner — Monthly budget optimization Related Articles Best Forex Brokers 2026: Platforms for Beginners \u0026amp; Traders Best FX Brokers for English Speakers in Japan 2026: What to Look For How to Open a DMM FX Account in Japan: Complete English Guide for Beginners (2026) ","permalink":"https://productivity-works.com/tools/forex-profit-calculator/","summary":"\u003ch1 id=\"forex-profit-calculator\"\u003eForex Profit Calculator\u003c/h1\u003e\n\u003cp\u003eEnter your trade details below to instantly calculate \u003cstrong\u003eprofit/loss\u003c/strong\u003e, \u003cstrong\u003emargin required\u003c/strong\u003e, and \u003cstrong\u003etax on gains\u003c/strong\u003e.\u003c/p\u003e\n\u003cdiv id=\"fx-calc-en\" style=\"max-width:640px;margin:0 auto;padding:24px;border:2px solid #2563eb;border-radius:12px;background:#f8fafc;\"\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eCurrency Pair\u003c/label\u003e\n\u003cselect id=\"pairEn\" onchange=\"fxCalcEn()\" style=\"width:100%;padding:10px;border:1px solid #cbd5e1;border-radius:6px;font-size:16px;\"\u003e\n\u003coption value=\"EURUSD\" data-pip=\"0.0001\" data-pipval=\"10\"\u003eEUR/USD\u003c/option\u003e\n\u003coption value=\"GBPUSD\" data-pip=\"0.0001\" data-pipval=\"10\"\u003eGBP/USD\u003c/option\u003e\n\u003coption value=\"USDJPY\" data-pip=\"0.01\" data-pipval=\"6.45\"\u003eUSD/JPY\u003c/option\u003e\n\u003coption value=\"AUDUSD\" data-pip=\"0.0001\" data-pipval=\"10\"\u003eAUD/USD\u003c/option\u003e\n\u003coption value=\"USDCAD\" data-pip=\"0.0001\" data-pipval=\"7.35\"\u003eUSD/CAD\u003c/option\u003e\n\u003c/select\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003ePosition\u003c/label\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:8px;\"\u003e\n\u003cbutton id=\"btnBuyEn\" onclick=\"setDirEn('buy')\" style=\"padding:12px;border:2px solid #2563eb;border-radius:8px;background:#2563eb;color:white;font-weight:bold;font-size:16px;cursor:pointer;\"\u003eBuy (Long)\u003c/button\u003e\n\u003cbutton id=\"btnSellEn\" onclick=\"setDirEn('sell')\" style=\"padding:12px;border:2px solid #cbd5e1;border-radius:8px;background:white;color:#475569;font-weight:bold;font-size:16px;cursor:pointer;\"\u003eSell (Short)\u003c/button\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eLot Size (1 standard lot = 100,000 units)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"lotsEn\" min=\"0.01\" max=\"10\" step=\"0.01\" value=\"1\" oninput=\"fxCalcEn()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e0.01\u003c/span\u003e\u003cspan id=\"lotsValEn\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e1.00 lot\u003c/span\u003e\u003cspan\u003e10.00\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:20px;\"\u003e\n\u003cdiv\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eEntry Price\u003c/label\u003e\n\u003cinput type=\"number\" id=\"entryPriceEn\" value=\"1.0840\" step=\"0.0001\" oninput=\"fxCalcEn()\" style=\"width:100%;padding:10px;border:1px solid #cbd5e1;border-radius:6px;font-size:16px;box-sizing:border-box;\"\u003e\n\u003c/div\u003e\n\u003cdiv\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eExit Price\u003c/label\u003e\n\u003cinput type=\"number\" id=\"exitPriceEn\" value=\"1.0940\" step=\"0.0001\" oninput=\"fxCalcEn()\" style=\"width:100%;padding:10px;border:1px solid #cbd5e1;border-radius:6px;font-size:16px;box-sizing:border-box;\"\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:24px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eSpread (pips)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"spreadEn\" min=\"0\" max=\"5\" step=\"0.1\" value=\"1.0\" oninput=\"fxCalcEn()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e0\u003c/span\u003e\u003cspan id=\"spreadValEn\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e1.0 pips\u003c/span\u003e\u003cspan\u003e5.0\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"plBoxEn\" style=\"background:#16a34a;color:white;border-radius:8px;padding:20px;text-align:center;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"font-size:14px;margin-bottom:4px;\"\u003eProfit / Loss\u003c/div\u003e\n\u003cdiv id=\"plAmountEn\" style=\"font-size:36px;font-weight:bold;\"\u003e+$900.00\u003c/div\u003e\n\u003cdiv style=\"font-size:13px;margin-top:4px;\"\u003eNet pips: \u003cspan id=\"plPipsEn\" style=\"font-weight:bold;\"\u003e+90.0\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"background:#e0f2fe;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eMargin Required\u003c/div\u003e\n\u003cdiv id=\"marginEn\" style=\"font-size:14px;font-weight:bold;color:#0369a1;\"\u003e$2,168\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#fef9c3;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eLeverage\u003c/div\u003e\n\u003cdiv id=\"leverageEn\" style=\"font-size:14px;font-weight:bold;color:#a16207;\"\u003e50:1\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#dcfce7;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eROI\u003c/div\u003e\n\u003cdiv id=\"roiEn\" style=\"font-size:14px;font-weight:bold;color:#15803d;\"\u003e+41.5%\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#f1f5f9;border-radius:8px;padding:12px;font-size:13px;color:#475569;\"\u003e\n\u003cstrong\u003eNote:\u003c/strong\u003e Calculations use 50:1 leverage (US standard). Pip values are approximate. Actual results vary by broker. Swap/rollover fees not included.\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nvar fxDirEn='buy';\nfunction setDirEn(d){\n  fxDirEn=d;\n  document.getElementById('btnBuyEn').style.background=d==='buy'?'#2563eb':'white';\n  document.getElementById('btnBuyEn').style.color=d==='buy'?'white':'#475569';\n  document.getElementById('btnBuyEn').style.borderColor=d==='buy'?'#2563eb':'#cbd5e1';\n  document.getElementById('btnSellEn').style.background=d==='sell'?'#dc2626':'white';\n  document.getElementById('btnSellEn').style.color=d==='sell'?'white':'#475569';\n  document.getElementById('btnSellEn').style.borderColor=d==='sell'?'#dc2626':'#cbd5e1';\n  fxCalcEn();\n}\nfunction fxCalcEn(){\n  var sel=document.getElementById('pairEn');\n  var opt=sel.options[sel.selectedIndex];\n  var pipSize=parseFloat(opt.getAttribute('data-pip'));\n  var pipValPer=parseFloat(opt.getAttribute('data-pipval'));\n  var lots=parseFloat(document.getElementById('lotsEn').value);\n  var entry=parseFloat(document.getElementById('entryPriceEn').value);\n  var exit=parseFloat(document.getElementById('exitPriceEn').value);\n  var spreadPips=parseFloat(document.getElementById('spreadEn').value);\n  if(isNaN(entry)||isNaN(exit)||entry\u003c=0||exit\u003c=0){return;}\n  var priceDiff=fxDirEn==='buy'?(exit-entry):(entry-exit);\n  var pips=priceDiff/pipSize;\n  var netPips=pips-spreadPips;\n  var pl=netPips*pipValPer*lots;\n  var plRound=Math.round(pl*100)/100;\n  var notional=entry*100000*lots;\n  var marginReq=Math.ceil(notional/50);\n  var lev=notional/marginReq;\n  var roiPct=marginReq\u003e0?((plRound/marginReq)*100):0;\n  var plBox=document.getElementById('plBoxEn');\n  plBox.style.background=plRound\u003e=0?'#16a34a':'#dc2626';\n  document.getElementById('lotsValEn').textContent=lots.toFixed(2)+' lot'+(lots!==1?'s':'');\n  document.getElementById('spreadValEn').textContent=spreadPips.toFixed(1)+' pips';\n  document.getElementById('plAmountEn').textContent=(plRound\u003e=0?'+':'')+plRound.toLocaleString('en-US',{style:'currency',currency:'USD'});\n  document.getElementById('plPipsEn').textContent=(netPips\u003e=0?'+':'')+netPips.toFixed(1);\n  document.getElementById('marginEn').textContent='$'+marginReq.toLocaleString();\n  document.getElementById('leverageEn').textContent=Math.round(lev)+':1';\n  document.getElementById('roiEn').textContent=(roiPct\u003e=0?'+':'')+roiPct.toFixed(1)+'%';\n}\nfxCalcEn();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-forex-profit-is-calculated\"\u003eHow Forex Profit is Calculated\u003c/h2\u003e\n\u003cp\u003eThe basic formula for forex profit:\u003c/p\u003e","title":"Forex Profit Calculator | Free FX Trading Simulator"},{"content":"This article contains affiliate links.\niDeCo Tax Savings Simulator [2026] Just enter your income, occupation, and monthly contribution to automatically calculate your tax savings and projected payout at age 60.\nAnnual Income (gross, in 10,000s JPY) ¥2M¥5,000,000¥15M Occupation (affects contribution limit) Company employee (no corporate pension) — limit ¥23,000/mo Company employee (with corporate DC plan) — limit ¥20,000/mo Company employee (with DB / welfare pension fund) — limit ¥12,000/mo Public servant — limit ¥12,000/mo Self-employed / Freelancer — limit ¥68,000/mo Homemaker (non-working spouse) — limit ¥23,000/mo Monthly Contribution (JPY) ¥5,000¥23,000¥68,000 Expected Annual Return (%) 1%4.0%8% Investment Period (until age 60) 1 yr25 years35 yrs Projected Payout at Age 60 (pre-tax) ¥11,896,000 of which investment gains: ¥4,996,000 Annual Tax Savings ¥55,200 Cumulative Tax Savings ¥1,380,000 Total Contributions (principal) ¥6,900,000 Effective Return (incl. tax savings) +78.2% Calculation notes: Uses a simplified model applying employment income deduction, basic deduction, and estimated social insurance premiums (~14.7%). Actual tax savings vary by individual circumstances. iDeCo contributions are fully deductible from taxable income. iDeCo\u0026rsquo;s 3 Tax Benefits iDeCo offers tax advantages at 3 stages, making it one of the most powerful tax-saving vehicles available.\nStage Tax Benefit Effect Contribution Full income deduction Reduces income \u0026amp; resident tax every year Growth phase Tax-free investment gains Eliminates the usual 20.315% tax on gains Withdrawal Retirement income deduction / public pension deduction Favorable treatment whether taken as lump sum or annuity How to Start iDeCo Choose a brokerage — Compare fees and fund lineup Set your monthly contribution — Limit depends on your occupation (use the simulator above) Select investment products — Index funds are recommended for beginners Set up auto-deduction — Once configured, contributions run on autopilot Tax Savings by Income Level Annual Income Contribution (employee) Annual Tax Savings 30-Year Total Savings ¥3,000,000 ¥23,000/mo ~¥41,400 ~¥1,242,000 ¥5,000,000 ¥23,000/mo ~¥55,200 ~¥1,656,000 ¥7,000,000 ¥23,000/mo ~¥55,200 ~¥1,656,000 ¥10,000,000 ¥23,000/mo ~¥82,800 ~¥2,484,000 Estimates for single employee with no dependents and no corporate pension.\nFrequently Asked Questions Q. Should I start with iDeCo or NISA first? Generally NISA first. Because iDeCo funds are locked until age 60, it is recommended to secure liquidity with NISA before adding iDeCo contributions.\nQ. Why do self-employed people have a higher iDeCo limit? Self-employed individuals are not covered by the corporate pension system, so they have less retirement security. To compensate, their iDeCo contribution ceiling is ¥68,000/month (¥816,000/year).\nQ. Can I change my contribution amount later? Yes, once per year. You can also set contributions to ¥0 and continue managing investments as a \u0026ldquo;non-contributing member.\u0026rdquo;\nQ. Are withdrawals taxed? Lump-sum withdrawals qualify for the retirement income deduction; annuity withdrawals qualify for the public pension deduction. Not the full amount is taxed.\nRelated Tools Savings Goal Calculator — Calculate how long to reach any savings target Asset Allocation Simulator — Track your total net worth Dividend Growth Simulator — Model dividend income and reinvestment ","permalink":"https://productivity-works.com/tools/ideco-simulator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"ideco-tax-savings-simulator-2026\"\u003eiDeCo Tax Savings Simulator [2026]\u003c/h1\u003e\n\u003cp\u003eJust enter your income, occupation, and monthly contribution to automatically calculate your \u003cstrong\u003etax savings\u003c/strong\u003e and \u003cstrong\u003eprojected payout at age 60\u003c/strong\u003e.\u003c/p\u003e\n\u003cdiv id=\"ideco-calc\" style=\"max-width:640px;margin:0 auto;padding:24px;border:2px solid #2563eb;border-radius:12px;background:#f8fafc;\"\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eAnnual Income (gross, in 10,000s JPY)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"income\" min=\"200\" max=\"1500\" step=\"10\" value=\"500\" oninput=\"calcIdeco()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e¥2M\u003c/span\u003e\u003cspan id=\"incomeVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e¥5,000,000\u003c/span\u003e\u003cspan\u003e¥15M\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eOccupation (affects contribution limit)\u003c/label\u003e\n\u003cselect id=\"jobType\" onchange=\"calcIdeco()\" style=\"width:100%;padding:8px;border:1px solid #cbd5e1;border-radius:6px;font-size:16px;\"\u003e\n\u003coption value=\"employee\"\u003eCompany employee (no corporate pension) — limit ¥23,000/mo\u003c/option\u003e\n\u003coption value=\"employee_dc\"\u003eCompany employee (with corporate DC plan) — limit ¥20,000/mo\u003c/option\u003e\n\u003coption value=\"employee_db\"\u003eCompany employee (with DB / welfare pension fund) — limit ¥12,000/mo\u003c/option\u003e\n\u003coption value=\"civil\"\u003ePublic servant — limit ¥12,000/mo\u003c/option\u003e\n\u003coption value=\"self\"\u003eSelf-employed / Freelancer — limit ¥68,000/mo\u003c/option\u003e\n\u003coption value=\"homemaker\"\u003eHomemaker (non-working spouse) — limit ¥23,000/mo\u003c/option\u003e\n\u003c/select\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eMonthly Contribution (JPY)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"contribution\" min=\"5000\" max=\"68000\" step=\"1000\" value=\"23000\" oninput=\"calcIdeco()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e¥5,000\u003c/span\u003e\u003cspan id=\"contribVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e¥23,000\u003c/span\u003e\u003cspan id=\"contribMax\"\u003e¥68,000\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eExpected Annual Return (%)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"rate\" min=\"1\" max=\"8\" step=\"0.5\" value=\"4\" oninput=\"calcIdeco()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e1%\u003c/span\u003e\u003cspan id=\"rateVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e4.0%\u003c/span\u003e\u003cspan\u003e8%\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:24px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eInvestment Period (until age 60)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"years\" min=\"1\" max=\"35\" step=\"1\" value=\"25\" oninput=\"calcIdeco()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e1 yr\u003c/span\u003e\u003cspan id=\"yearsVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e25 years\u003c/span\u003e\u003cspan\u003e35 yrs\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#1e40af;color:white;border-radius:8px;padding:20px;text-align:center;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"font-size:14px;margin-bottom:4px;\"\u003eProjected Payout at Age 60 (pre-tax)\u003c/div\u003e\n\u003cdiv id=\"totalAsset\" style=\"font-size:36px;font-weight:bold;\"\u003e¥11,896,000\u003c/div\u003e\n\u003cdiv style=\"font-size:13px;margin-top:4px;\"\u003eof which investment gains: \u003cspan id=\"profitAmt\" style=\"font-weight:bold;\"\u003e¥4,996,000\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:12px;\"\u003e\n\u003cdiv style=\"background:#dcfce7;border-radius:8px;padding:16px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eAnnual Tax Savings\u003c/div\u003e\n\u003cdiv id=\"taxSaveYear\" style=\"font-size:22px;font-weight:bold;color:#15803d;\"\u003e¥55,200\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#fef9c3;border-radius:8px;padding:16px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eCumulative Tax Savings\u003c/div\u003e\n\u003cdiv id=\"taxSaveTotal\" style=\"font-size:22px;font-weight:bold;color:#a16207;\"\u003e¥1,380,000\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"background:#e0f2fe;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eTotal Contributions (principal)\u003c/div\u003e\n\u003cdiv id=\"principal\" style=\"font-size:16px;font-weight:bold;color:#0369a1;\"\u003e¥6,900,000\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#f3e8ff;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eEffective Return (incl. tax savings)\u003c/div\u003e\n\u003cdiv id=\"realReturn\" style=\"font-size:16px;font-weight:bold;color:#7c3aed;\"\u003e+78.2%\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#f1f5f9;border-radius:8px;padding:12px;font-size:13px;color:#475569;\"\u003e\n\u003cstrong\u003eCalculation notes:\u003c/strong\u003e Uses a simplified model applying employment income deduction, basic deduction, and estimated social insurance premiums (~14.7%). Actual tax savings vary by individual circumstances. iDeCo contributions are fully deductible from taxable income.\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nfunction calcIdeco(){\n  var inc=parseInt(document.getElementById('income').value);\n  var job=document.getElementById('jobType').value;\n  var contrib=parseInt(document.getElementById('contribution').value);\n  var r=parseFloat(document.getElementById('rate').value)/100;\n  var y=parseInt(document.getElementById('years').value);\n\n  var maxContrib;\n  if(job==='self') maxContrib=68000;\n  else if(job==='employee') maxContrib=23000;\n  else if(job==='employee_dc') maxContrib=20000;\n  else if(job==='employee_db'||job==='civil') maxContrib=12000;\n  else maxContrib=23000;\n\n  if(contrib\u003emaxContrib) contrib=maxContrib;\n  var slider=document.getElementById('contribution');\n  slider.max=maxContrib;\n  if(parseInt(slider.value)\u003emaxContrib) slider.value=maxContrib;\n\n  var annual=inc*10000;\n  var annualContrib=contrib*12;\n\n  var deduction;\n  if(annual\u003c=1625000) deduction=550000;\n  else if(annual\u003c=1800000) deduction=annual*0.4-100000;\n  else if(annual\u003c=3600000) deduction=annual*0.3+80000;\n  else if(annual\u003c=6600000) deduction=annual*0.2+440000;\n  else if(annual\u003c=8500000) deduction=annual*0.1+1100000;\n  else deduction=1950000;\n\n  if(job==='self') deduction=0;\n\n  var shotoku=annual-deduction;\n\n  var socialIns=Math.floor(annual*0.147);\n  if(job==='self') socialIns=Math.floor(annual*0.10);\n  var basicDeduction=480000;\n  var totalDeduction=basicDeduction+socialIns;\n\n  var taxableWithout=Math.max(shotoku-totalDeduction,0);\n  var taxableWith=Math.max(shotoku-totalDeduction-annualContrib,0);\n\n  function getIncomeTax(taxable){\n    if(taxable\u003c=1950000) return taxable*0.05;\n    else if(taxable\u003c=3300000) return taxable*0.1-97500;\n    else if(taxable\u003c=6950000) return taxable*0.2-427500;\n    else if(taxable\u003c=9000000) return taxable*0.23-636000;\n    else if(taxable\u003c=18000000) return taxable*0.33-1536000;\n    else if(taxable\u003c=40000000) return taxable*0.40-2796000;\n    else return taxable*0.45-4796000;\n  }\n\n  var taxWithout=Math.floor(getIncomeTax(taxableWithout)*1.021)+Math.floor(taxableWithout*0.1)+5000;\n  var taxWith=Math.floor(getIncomeTax(taxableWith)*1.021)+Math.floor(taxableWith*0.1)+5000;\n\n  var taxSaveYear=Math.max(taxWithout-taxWith,0);\n  var taxSaveTotal=taxSaveYear*y;\n\n  var mr=r/12;\n  var n=y*12;\n  var fv=contrib*((Math.pow(1+mr,n)-1)/mr);\n  var p=contrib*n;\n  var profit=fv-p;\n\n  var realRet=((fv+taxSaveTotal-p)/p*100);\n\n  document.getElementById('incomeVal').textContent='¥'+( inc*10000).toLocaleString();\n  document.getElementById('contribVal').textContent='¥'+contrib.toLocaleString();\n  document.getElementById('contribMax').textContent='¥'+maxContrib.toLocaleString();\n  document.getElementById('rateVal').textContent=(r*100).toFixed(1)+'%';\n  document.getElementById('yearsVal').textContent=y+' years';\n  document.getElementById('totalAsset').textContent='¥'+Math.floor(fv).toLocaleString();\n  document.getElementById('profitAmt').textContent='¥'+Math.floor(profit).toLocaleString();\n  document.getElementById('taxSaveYear').textContent='¥'+taxSaveYear.toLocaleString();\n  document.getElementById('taxSaveTotal').textContent='¥'+taxSaveTotal.toLocaleString();\n  document.getElementById('principal').textContent='¥'+p.toLocaleString();\n  document.getElementById('realReturn').textContent='+'+realRet.toFixed(1)+'%';\n}\ncalcIdeco();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"idecos-3-tax-benefits\"\u003eiDeCo\u0026rsquo;s 3 Tax Benefits\u003c/h2\u003e\n\u003cp\u003eiDeCo offers tax advantages at \u003cstrong\u003e3 stages\u003c/strong\u003e, making it one of the most powerful tax-saving vehicles available.\u003c/p\u003e","title":"iDeCo Simulator | Calculate Tax Savings \u0026 Future Assets Automatically [2026]"},{"content":"This page contains affiliate links.\nLoan Repayment Calculator Enter your loan amount, interest rate, and repayment term to calculate your monthly payment and total interest cost.\nLoan Amount ($) $5,000$200,000$500,000 Annual Interest Rate (%) 1%6.5%15% Loan Term (years) 1 yr15 years30 yrs Monthly Payment $1,742 Principal $200,000 Total Interest $113,538 Total Cost $313,538 Assumptions: Fixed-rate loan with equal monthly payments (amortization). Does not include property taxes, insurance, or PMI for mortgages. How To Save On Interest Make extra payments: Even $100/month extra can save thousands in interest and shorten your loan term significantly Refinance when rates drop: If rates fall 1%+ below your current rate, refinancing could save you money Choose a shorter term: A 15-year mortgage has higher payments but dramatically less total interest than a 30-year Improve your credit score: Better credit = lower interest rates on future loans FAQ Q: What types of loans does this calculator work for? This calculator works for any fixed-rate amortizing loan: mortgages, auto loans, personal loans, and student loans.\nQ: Why is my actual payment different from this estimate? Your actual payment may include property taxes, homeowner\u0026rsquo;s insurance, and PMI (for mortgages) which are not included in this calculation. For a full mortgage breakdown, use our Mortgage Calculator .\nQ: Is it better to pay off debt or invest? Generally, if your loan interest rate is higher than expected investment returns (historically ~7-10% for stocks), pay off the debt first. If your rate is low (under 4-5%), investing may yield better long-term results.\nWhat To Do After Paying Off Debt Once your loans are paid off, redirect those payments toward building wealth:\nBuild an emergency fund — Save 3-6 months of expenses in a high-yield savings account Start investing — Use our Compound Interest Calculator to see how your freed-up payments could grow Create a budget — Use our Budget Planner to allocate your new surplus wisely Calculate your take-home pay — Know exactly what you\u0026rsquo;re working with. Try our Salary Calculator Related Tools Retirement Savings Calculator — Estimate your 401(k)/IRA nest egg Compound Interest Calculator — See how your investments grow over time Budget Planner — Optimize your monthly spending Salary Calculator — Calculate your take-home pay Mortgage Calculator — Full mortgage payment with taxes, insurance \u0026amp; PMI Forex Profit Calculator — Explore trading opportunities ","permalink":"https://productivity-works.com/tools/loan-repayment-calculator/","summary":"\u003cp\u003e\u003cem\u003eThis page contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"loan-repayment-calculator\"\u003eLoan Repayment Calculator\u003c/h1\u003e\n\u003cp\u003eEnter your loan amount, interest rate, and repayment term to calculate your \u003cstrong\u003emonthly payment\u003c/strong\u003e and \u003cstrong\u003etotal interest cost\u003c/strong\u003e.\u003c/p\u003e\n\u003cdiv id=\"loan-calc\" style=\"max-width:640px;margin:0 auto;padding:24px;border:2px solid #2563eb;border-radius:12px;background:#f8fafc;\"\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eLoan Amount ($)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"loanAmt\" min=\"5000\" max=\"500000\" step=\"5000\" value=\"200000\" oninput=\"calcLoan()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e$5,000\u003c/span\u003e\u003cspan id=\"loanAmtVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e$200,000\u003c/span\u003e\u003cspan\u003e$500,000\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eAnnual Interest Rate (%)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"loanRate\" min=\"1\" max=\"15\" step=\"0.25\" value=\"6.5\" oninput=\"calcLoan()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e1%\u003c/span\u003e\u003cspan id=\"loanRateVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e6.5%\u003c/span\u003e\u003cspan\u003e15%\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:24px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eLoan Term (years)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"loanYears\" min=\"1\" max=\"30\" step=\"1\" value=\"15\" oninput=\"calcLoan()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e1 yr\u003c/span\u003e\u003cspan id=\"loanYearsVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e15 years\u003c/span\u003e\u003cspan\u003e30 yrs\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#1e40af;color:white;border-radius:8px;padding:20px;text-align:center;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"font-size:14px;margin-bottom:4px;\"\u003eMonthly Payment\u003c/div\u003e\n\u003cdiv id=\"monthlyPayment\" style=\"font-size:36px;font-weight:bold;\"\u003e$1,742\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"background:#e0f2fe;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003ePrincipal\u003c/div\u003e\n\u003cdiv id=\"loanPrincipal\" style=\"font-size:16px;font-weight:bold;color:#0369a1;\"\u003e$200,000\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#fee2e2;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eTotal Interest\u003c/div\u003e\n\u003cdiv id=\"totalInterest\" style=\"font-size:16px;font-weight:bold;color:#dc2626;\"\u003e$113,538\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#fef3c7;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eTotal Cost\u003c/div\u003e\n\u003cdiv id=\"totalCost\" style=\"font-size:16px;font-weight:bold;color:#d97706;\"\u003e$313,538\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#f1f5f9;border-radius:8px;padding:12px;font-size:13px;color:#475569;\"\u003e\n\u003cstrong\u003eAssumptions:\u003c/strong\u003e Fixed-rate loan with equal monthly payments (amortization). Does not include property taxes, insurance, or PMI for mortgages.\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nfunction calcLoan(){\n  var P=parseInt(document.getElementById('loanAmt').value);\n  var r=parseFloat(document.getElementById('loanRate').value)/100/12;\n  var n=parseInt(document.getElementById('loanYears').value)*12;\n  var payment=P*(r*Math.pow(1+r,n))/(Math.pow(1+r,n)-1);\n  var totalCost=payment*n;\n  var totalInterest=totalCost-P;\n\n  document.getElementById('loanAmtVal').textContent='$'+P.toLocaleString();\n  document.getElementById('loanRateVal').textContent=(r*1200).toFixed(2)+'%';\n  document.getElementById('loanYearsVal').textContent=document.getElementById('loanYears').value+(document.getElementById('loanYears').value==='1'?' year':' years');\n  document.getElementById('monthlyPayment').textContent='$'+Math.floor(payment).toLocaleString();\n  document.getElementById('loanPrincipal').textContent='$'+P.toLocaleString();\n  document.getElementById('totalInterest').textContent='$'+Math.floor(totalInterest).toLocaleString();\n  document.getElementById('totalCost').textContent='$'+Math.floor(totalCost).toLocaleString();\n}\ncalcLoan();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-to-save-on-interest\"\u003eHow To Save On Interest\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eMake extra payments\u003c/strong\u003e: Even $100/month extra can save thousands in interest and shorten your loan term significantly\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eRefinance when rates drop\u003c/strong\u003e: If rates fall 1%+ below your current rate, refinancing could save you money\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eChoose a shorter term\u003c/strong\u003e: A 15-year mortgage has higher payments but dramatically less total interest than a 30-year\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eImprove your credit score\u003c/strong\u003e: Better credit = lower interest rates on future loans\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch2 id=\"faq\"\u003eFAQ\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eQ: What types of loans does this calculator work for?\u003c/strong\u003e\nThis calculator works for any fixed-rate amortizing loan: mortgages, auto loans, personal loans, and student loans.\u003c/p\u003e","title":"Loan Repayment Calculator | Free Monthly Payment Estimator"},{"content":"Privacy Policy Productivity Works (\u0026ldquo;this site\u0026rdquo;) takes the protection of user privacy seriously. This Privacy Policy explains how we collect, use, and safeguard your personal information.\n1. Collection of Personal Information When you contact us through our contact form or email, we may collect personal information such as your name and email address. This information is used solely to respond to your inquiries and provide requested information.\nWe will not share your personal information with third parties without your consent, except as required by law.\n2. Google AdSense and Advertising This site may display advertisements through Google AdSense. Google AdSense uses cookies to serve ads based on your prior visits to this website and other websites on the internet.\nGoogle uses cookies to serve ads based on your interests You may opt out of personalized advertising by visiting Google Ads Settings For more information, see the Google Privacy Policy To opt out of a third-party vendor\u0026rsquo;s use of cookies, visit the Network Advertising Initiative opt-out page 3. Google Analytics This site may use Google Analytics, a web analytics service provided by Google. Google Analytics uses cookies to collect information about how visitors use this site. All data is collected anonymously and does not identify individual users.\nData collected: Page views, session duration, traffic sources, etc. IP Anonymization: Enabled Purpose: To improve our content and user experience Review the Google Analytics Terms of Service You can opt out of Google Analytics by installing the Google Analytics Opt-out Browser Add-on .\n4. Cookies This site uses cookies through Google AdSense and Google Analytics as described above. Cookies are small data files stored on your browser by websites you visit.\nYou can disable cookies through your browser settings. Please note that disabling cookies may affect the functionality of some services on this site.\n5. Disclaimer While we strive to provide accurate and up-to-date information, we make no warranties or representations regarding the completeness, accuracy, or reliability of any content on this site. We are not liable for any losses or damages arising from the use of information provided on this site.\nWe are not responsible for the content of external websites linked from this site.\n6. Copyright All content on this site, including text and images, is the intellectual property of Productivity Works unless otherwise stated. Unauthorized reproduction or distribution is prohibited. When citing our content, please include a link to the original article.\n7. Changes to This Privacy Policy We reserve the right to update this Privacy Policy at any time. Changes will be posted on this page with an updated revision date. We encourage you to review this page periodically.\n8. Contact Us If you have any questions about this Privacy Policy, please contact us .\nEmail: kagakusha22@gmail.com Last updated: May 14, 2026\n","permalink":"https://productivity-works.com/privacy-policy/","summary":"\u003ch2 id=\"privacy-policy\"\u003ePrivacy Policy\u003c/h2\u003e\n\u003cp\u003eProductivity Works (\u0026ldquo;this site\u0026rdquo;) takes the protection of user privacy seriously. This Privacy Policy explains how we collect, use, and safeguard your personal information.\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"1-collection-of-personal-information\"\u003e1. Collection of Personal Information\u003c/h2\u003e\n\u003cp\u003eWhen you contact us through our contact form or email, we may collect personal information such as your name and email address. This information is used solely to respond to your inquiries and provide requested information.\u003c/p\u003e\n\u003cp\u003eWe will not share your personal information with third parties without your consent, except as required by law.\u003c/p\u003e","title":"Privacy Policy"},{"content":"This page contains affiliate links.\nRetirement Savings Calculator Enter your current age, monthly contribution, and expected return to estimate your retirement nest egg at age 65.\nCurrent Age 183060 Monthly Contribution ($) $100$500$5,000 Employer Match (%) 0%50%100% Percentage of your contribution your employer matches (up to a limit) Expected Annual Return (%) 2%7.0%12% Retirement Age 556570 Estimated Retirement Savings $610,993 Years until retirement: 35 Your Contributions $210,000 Employer Match $105,000 Investment Growth $295,993 Monthly income in retirement (4% rule): $2,037/month Based on withdrawing 4% per year from your nest egg Assumptions: Monthly contributions with employer match, compounded monthly. The 4% rule estimates sustainable annual withdrawal. Actual returns vary. This does not account for inflation or taxes on withdrawals. Maximize Your Retirement Savings Get the full employer match — Not contributing enough to get the full match? You\u0026rsquo;re leaving free money on the table Max out tax-advantaged accounts — 2026 limits: $23,500 for 401(k), $7,000 for IRA ($8,000 if 50+) Choose low-cost index funds — Fees compound just like returns. A 1% fee difference can cost you hundreds of thousands over 30 years Increase contributions annually — Raise your contribution by 1% each year to painlessly build wealth 401(k) vs IRA vs Roth IRA Feature Traditional 401(k) Traditional IRA Roth IRA 2026 Contribution Limit $23,500 $7,000 $7,000 Tax on Contributions Pre-tax (deductible) Pre-tax (deductible) After-tax Tax on Withdrawals Taxed as income Taxed as income Tax-free Employer Match Yes No No Required Min. Distribution Yes (age 73) Yes (age 73) No Best For High earners now No employer plan Expect higher taxes later FAQ Q: What is the 4% rule? The 4% rule suggests withdrawing 4% of your retirement savings in the first year, then adjusting for inflation each year. Studies show this rate is sustainable for a 30-year retirement.\nQ: What return should I expect? The S\u0026amp;P 500 has returned roughly 10% annually over the last 30 years before inflation. A 7% estimate is conservative after accounting for inflation and fees.\nQ: When should I start saving for retirement? As early as possible. Starting at 25 vs 35 with the same monthly contribution can result in nearly double the final amount due to compound interest.\nRecommended Next Steps See your investment growth — Use our Compound Interest Calculator to model different scenarios Create a monthly budget — Use our Budget Planner to find more money to invest Calculate your take-home pay — Know what you\u0026rsquo;re working with. Try our Salary Calculator Pay off debt first? — Use our Loan Repayment Calculator to compare payoff vs investing Related Tools Compound Interest Calculator — See how investments grow Budget Planner — Optimize your monthly spending Salary Calculator — Calculate your take-home pay Loan Repayment Calculator — Plan your debt payoff Forex Profit Calculator — Calculate trading profits Plan your path to financial independence → FIRE Calculator Related Articles Best Investment Account for Beginners in Japan: NISA vs. iDeCo vs. Regular Brokerage 401k vs IRA Differences Explained (2026) Roth IRA vs Traditional IRA: Which Is Better? ","permalink":"https://productivity-works.com/tools/retirement-calculator/","summary":"\u003cp\u003e\u003cem\u003eThis page contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"retirement-savings-calculator\"\u003eRetirement Savings Calculator\u003c/h1\u003e\n\u003cp\u003eEnter your current age, monthly contribution, and expected return to estimate your \u003cstrong\u003eretirement nest egg\u003c/strong\u003e at age 65.\u003c/p\u003e\n\u003cdiv id=\"retire-calc\" style=\"max-width:640px;margin:0 auto;padding:24px;border:2px solid #2563eb;border-radius:12px;background:#f8fafc;\"\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eCurrent Age\u003c/label\u003e\n\u003cinput type=\"range\" id=\"age\" min=\"18\" max=\"60\" step=\"1\" value=\"30\" oninput=\"calcRetire()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e18\u003c/span\u003e\u003cspan id=\"ageVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e30\u003c/span\u003e\u003cspan\u003e60\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eMonthly Contribution ($)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"contrib\" min=\"100\" max=\"5000\" step=\"50\" value=\"500\" oninput=\"calcRetire()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e$100\u003c/span\u003e\u003cspan id=\"contribVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e$500\u003c/span\u003e\u003cspan\u003e$5,000\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eEmployer Match (%)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"match\" min=\"0\" max=\"100\" step=\"10\" value=\"50\" oninput=\"calcRetire()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e0%\u003c/span\u003e\u003cspan id=\"matchVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e50%\u003c/span\u003e\u003cspan\u003e100%\u003c/span\u003e\u003c/div\u003e\n\u003cdiv style=\"font-size:12px;color:#94a3b8;margin-top:2px;\"\u003ePercentage of your contribution your employer matches (up to a limit)\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eExpected Annual Return (%)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"rate\" min=\"2\" max=\"12\" step=\"0.5\" value=\"7\" oninput=\"calcRetire()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e2%\u003c/span\u003e\u003cspan id=\"rateVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e7.0%\u003c/span\u003e\u003cspan\u003e12%\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:24px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eRetirement Age\u003c/label\u003e\n\u003cinput type=\"range\" id=\"retireAge\" min=\"55\" max=\"70\" step=\"1\" value=\"65\" oninput=\"calcRetire()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e55\u003c/span\u003e\u003cspan id=\"retireAgeVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e65\u003c/span\u003e\u003cspan\u003e70\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#1e40af;color:white;border-radius:8px;padding:20px;text-align:center;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"font-size:14px;margin-bottom:4px;\"\u003eEstimated Retirement Savings\u003c/div\u003e\n\u003cdiv id=\"totalSavings\" style=\"font-size:36px;font-weight:bold;\"\u003e$610,993\u003c/div\u003e\n\u003cdiv style=\"font-size:13px;margin-top:4px;\"\u003eYears until retirement: \u003cspan id=\"yearsLeft\" style=\"font-weight:bold;\"\u003e35\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;margin-bottom:12px;\"\u003e\n\u003cdiv style=\"background:#e0f2fe;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eYour Contributions\u003c/div\u003e\n\u003cdiv id=\"yourContrib\" style=\"font-size:16px;font-weight:bold;color:#0369a1;\"\u003e$210,000\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#f3e8ff;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eEmployer Match\u003c/div\u003e\n\u003cdiv id=\"employerContrib\" style=\"font-size:16px;font-weight:bold;color:#7c3aed;\"\u003e$105,000\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#dcfce7;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eInvestment Growth\u003c/div\u003e\n\u003cdiv id=\"growthAmt\" style=\"font-size:16px;font-weight:bold;color:#15803d;\"\u003e$295,993\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#fef9c3;border-radius:8px;padding:16px;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"font-size:13px;color:#a16207;margin-bottom:4px;\"\u003e\u003cstrong\u003eMonthly income in retirement (4% rule):\u003c/strong\u003e\u003c/div\u003e\n\u003cdiv id=\"monthlyIncome\" style=\"font-size:22px;font-weight:bold;color:#a16207;text-align:center;\"\u003e$2,037/month\u003c/div\u003e\n\u003cdiv style=\"font-size:12px;color:#a16207;text-align:center;margin-top:4px;\"\u003eBased on withdrawing 4% per year from your nest egg\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#f1f5f9;border-radius:8px;padding:12px;font-size:13px;color:#475569;\"\u003e\n\u003cstrong\u003eAssumptions:\u003c/strong\u003e Monthly contributions with employer match, compounded monthly. The 4% rule estimates sustainable annual withdrawal. Actual returns vary. This does not account for inflation or taxes on withdrawals.\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nfunction calcRetire(){\n  var age=parseInt(document.getElementById('age').value);\n  var contrib=parseInt(document.getElementById('contrib').value);\n  var matchPct=parseInt(document.getElementById('match').value)/100;\n  var r=parseFloat(document.getElementById('rate').value)/100;\n  var retireAge=parseInt(document.getElementById('retireAge').value);\n\n  if(retireAge\u003c=age) retireAge=age+1;\n\n  var years=retireAge-age;\n  var mr=r/12;\n  var n=years*12;\n  var totalMonthly=contrib+contrib*matchPct;\n  var fv=totalMonthly*((Math.pow(1+mr,n)-1)/mr);\n  var yourTotal=contrib*n;\n  var employerTotal=Math.floor(contrib*matchPct)*n;\n  var growth=fv-yourTotal-employerTotal;\n  var monthlyIncome=Math.floor(fv*0.04/12);\n\n  document.getElementById('ageVal').textContent=age;\n  document.getElementById('contribVal').textContent='$'+contrib.toLocaleString();\n  document.getElementById('matchVal').textContent=Math.round(matchPct*100)+'%';\n  document.getElementById('rateVal').textContent=(r*100).toFixed(1)+'%';\n  document.getElementById('retireAgeVal').textContent=retireAge;\n  document.getElementById('totalSavings').textContent='$'+Math.floor(fv).toLocaleString();\n  document.getElementById('yearsLeft').textContent=years;\n  document.getElementById('yourContrib').textContent='$'+yourTotal.toLocaleString();\n  document.getElementById('employerContrib').textContent='$'+employerTotal.toLocaleString();\n  document.getElementById('growthAmt').textContent='$'+Math.floor(growth).toLocaleString();\n  document.getElementById('monthlyIncome').textContent='$'+monthlyIncome.toLocaleString()+'/month';\n}\ncalcRetire();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"maximize-your-retirement-savings\"\u003eMaximize Your Retirement Savings\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eGet the full employer match\u003c/strong\u003e — Not contributing enough to get the full match? You\u0026rsquo;re leaving free money on the table\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eMax out tax-advantaged accounts\u003c/strong\u003e — 2026 limits: $23,500 for 401(k), $7,000 for IRA ($8,000 if 50+)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eChoose low-cost index funds\u003c/strong\u003e — Fees compound just like returns. A 1% fee difference can cost you hundreds of thousands over 30 years\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eIncrease contributions annually\u003c/strong\u003e — Raise your contribution by 1% each year to painlessly build wealth\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch2 id=\"401k-vs-ira-vs-roth-ira\"\u003e401(k) vs IRA vs Roth IRA\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eFeature\u003c/th\u003e\n          \u003cth\u003eTraditional 401(k)\u003c/th\u003e\n          \u003cth\u003eTraditional IRA\u003c/th\u003e\n          \u003cth\u003eRoth IRA\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e2026 Contribution Limit\u003c/td\u003e\n          \u003ctd\u003e$23,500\u003c/td\u003e\n          \u003ctd\u003e$7,000\u003c/td\u003e\n          \u003ctd\u003e$7,000\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eTax on Contributions\u003c/td\u003e\n          \u003ctd\u003ePre-tax (deductible)\u003c/td\u003e\n          \u003ctd\u003ePre-tax (deductible)\u003c/td\u003e\n          \u003ctd\u003eAfter-tax\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eTax on Withdrawals\u003c/td\u003e\n          \u003ctd\u003eTaxed as income\u003c/td\u003e\n          \u003ctd\u003eTaxed as income\u003c/td\u003e\n          \u003ctd\u003eTax-free\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eEmployer Match\u003c/td\u003e\n          \u003ctd\u003eYes\u003c/td\u003e\n          \u003ctd\u003eNo\u003c/td\u003e\n          \u003ctd\u003eNo\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eRequired Min. Distribution\u003c/td\u003e\n          \u003ctd\u003eYes (age 73)\u003c/td\u003e\n          \u003ctd\u003eYes (age 73)\u003c/td\u003e\n          \u003ctd\u003eNo\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eBest For\u003c/td\u003e\n          \u003ctd\u003eHigh earners now\u003c/td\u003e\n          \u003ctd\u003eNo employer plan\u003c/td\u003e\n          \u003ctd\u003eExpect higher taxes later\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003ch2 id=\"faq\"\u003eFAQ\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eQ: What is the 4% rule?\u003c/strong\u003e\nThe 4% rule suggests withdrawing 4% of your retirement savings in the first year, then adjusting for inflation each year. Studies show this rate is sustainable for a 30-year retirement.\u003c/p\u003e","title":"Retirement Savings Calculator | Free 401(k) \u0026 IRA Growth Estimator"},{"content":"This article contains affiliate links.\nTake-Home Pay Calculator [2026] Enter your gross annual salary and instantly get your take-home pay, taxes, and social insurance breakdown.\nGross Annual Salary ($) $20,000$70,000$300,000 Dependents 0 (Single, no dependents) 1 dependent 2 dependents 3 dependents Annual Take-Home Pay (Estimate) $52,000 Monthly: $4,333 Federal Income Tax (Annual) $0 State Income Tax (Annual) $0 FICA / Social Insurance (Annual) $0 Take-Home Rate —% Assumptions: Single W-2 employee, standard deduction applied, average state tax ~5%, FICA at 7.65%. Actual amounts vary by filing status, state, age, and employer benefits. Take-Home Pay Reference Table by Salary Gross Salary Estimated Take-Home Take-Home Rate $30,000 ~$25,500 ~85% $50,000 ~$41,000 ~82% $70,000 ~$55,500 ~79% $90,000 ~$69,500 ~77% $120,000 ~$89,000 ~74% $150,000 ~$107,000 ~71% $200,000 ~$136,000 ~68% $300,000 ~$192,000 ~64% Estimates for single filer, no dependents, average state tax.\nHow to Increase Your Take-Home Pay The two key levers are increasing income or increasing deductions.\nMaximize 401(k) / IRA contributions — Pre-tax contributions directly reduce your taxable income Use an HSA — Health Savings Accounts offer a triple tax advantage Claim all eligible deductions — Don\u0026rsquo;t overlook student loan interest, home office, and business expenses Negotiate a raise or change jobs — Often the fastest route to a higher paycheck Frequently Asked Questions Q. Does this include bonus income? Yes — enter your total gross compensation including bonuses. The result will be your estimated annual take-home including all pay.\nQ. What about 401(k) contributions? Pre-tax 401(k) contributions reduce your taxable income. For a more precise number, subtract your annual contribution from gross salary before entering it above.\nQ. I have side income — how does that affect things? Side income (freelance, gig work, etc.) is generally taxed as self-employment income at a different rate. See the Income Tax Simulator for a freelancer-specific calculation.\nRelated Tools Income Tax Simulator — Detailed breakdown of income tax, state tax, and deductions Job Change Salary Simulator — Compare take-home pay before and after a job change FIRE Retirement Simulator — Calculate years to financial independence based on your income and expenses ","permalink":"https://productivity-works.com/tools/take-home-pay-calculator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"take-home-pay-calculator-2026\"\u003eTake-Home Pay Calculator [2026]\u003c/h1\u003e\n\u003cp\u003eEnter your gross annual salary and instantly get your \u003cstrong\u003etake-home pay\u003c/strong\u003e, \u003cstrong\u003etaxes\u003c/strong\u003e, and \u003cstrong\u003esocial insurance\u003c/strong\u003e breakdown.\u003c/p\u003e\n\u003cdiv id=\"tedori-calc\" style=\"max-width:640px;margin:0 auto;padding:24px;border:2px solid #2563eb;border-radius:12px;background:#f8fafc;\"\u003e\n\u003cdiv style=\"margin-bottom:20px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eGross Annual Salary ($)\u003c/label\u003e\n\u003cinput type=\"range\" id=\"income\" min=\"20000\" max=\"300000\" step=\"1000\" value=\"70000\" oninput=\"calcTedori()\" style=\"width:100%;\"\u003e\n\u003cdiv style=\"display:flex;justify-content:space-between;font-size:13px;color:#64748b;\"\u003e\u003cspan\u003e$20,000\u003c/span\u003e\u003cspan id=\"incomeVal\" style=\"font-weight:bold;font-size:18px;color:#2563eb;\"\u003e$70,000\u003c/span\u003e\u003cspan\u003e$300,000\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:24px;\"\u003e\n\u003clabel style=\"display:block;font-weight:bold;margin-bottom:6px;\"\u003eDependents\u003c/label\u003e\n\u003cselect id=\"dependents\" onchange=\"calcTedori()\" style=\"width:100%;padding:8px;border:1px solid #cbd5e1;border-radius:6px;font-size:16px;\"\u003e\n\u003coption value=\"0\"\u003e0 (Single, no dependents)\u003c/option\u003e\n\u003coption value=\"1\"\u003e1 dependent\u003c/option\u003e\n\u003coption value=\"2\"\u003e2 dependents\u003c/option\u003e\n\u003coption value=\"3\"\u003e3 dependents\u003c/option\u003e\n\u003c/select\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#1e40af;color:white;border-radius:8px;padding:20px;text-align:center;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"font-size:14px;margin-bottom:4px;\"\u003eAnnual Take-Home Pay (Estimate)\u003c/div\u003e\n\u003cdiv id=\"tedoriYear\" style=\"font-size:36px;font-weight:bold;\"\u003e$52,000\u003c/div\u003e\n\u003cdiv style=\"font-size:14px;margin-top:8px;\"\u003eMonthly: \u003cspan id=\"tedoriMonth\" style=\"font-weight:bold;\"\u003e$4,333\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:12px;\"\u003e\n\u003cdiv style=\"background:#fee2e2;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eFederal Income Tax (Annual)\u003c/div\u003e\n\u003cdiv id=\"incomeTax\" style=\"font-size:16px;font-weight:bold;color:#dc2626;\"\u003e$0\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#fef3c7;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eState Income Tax (Annual)\u003c/div\u003e\n\u003cdiv id=\"residentTax\" style=\"font-size:16px;font-weight:bold;color:#d97706;\"\u003e$0\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:16px;\"\u003e\n\u003cdiv style=\"background:#e0f2fe;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eFICA / Social Insurance (Annual)\u003c/div\u003e\n\u003cdiv id=\"socialIns\" style=\"font-size:16px;font-weight:bold;color:#0369a1;\"\u003e$0\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#dcfce7;border-radius:8px;padding:12px;text-align:center;\"\u003e\n\u003cdiv style=\"font-size:12px;color:#64748b;\"\u003eTake-Home Rate\u003c/div\u003e\n\u003cdiv id=\"tedoriRate\" style=\"font-size:16px;font-weight:bold;color:#15803d;\"\u003e—%\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv style=\"background:#f1f5f9;border-radius:8px;padding:12px;font-size:13px;color:#475569;\"\u003e\n\u003cstrong\u003eAssumptions:\u003c/strong\u003e Single W-2 employee, standard deduction applied, average state tax ~5%, FICA at 7.65%. Actual amounts vary by filing status, state, age, and employer benefits.\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\nfunction calcTedori(){\n  var inc=parseInt(document.getElementById('income').value);\n  var dep=parseInt(document.getElementById('dependents').value);\n  var annual=inc;\n\n  // Standard deduction (2026 approximate)\n  var standardDeduction=14600+dep*4300;\n  var taxableIncome=Math.max(0,annual-standardDeduction);\n\n  // Federal income tax (2026 single brackets, approximate)\n  var incomeTax=0;\n  if(taxableIncome\u003c=11600) incomeTax=taxableIncome*0.10;\n  else if(taxableIncome\u003c=47150) incomeTax=1160+(taxableIncome-11600)*0.12;\n  else if(taxableIncome\u003c=100525) incomeTax=5426+(taxableIncome-47150)*0.22;\n  else if(taxableIncome\u003c=191950) incomeTax=17169+(taxableIncome-100525)*0.24;\n  else if(taxableIncome\u003c=243725) incomeTax=39111+(taxableIncome-191950)*0.32;\n  else if(taxableIncome\u003c=609350) incomeTax=55679+(taxableIncome-243725)*0.35;\n  else incomeTax=183647+(taxableIncome-609350)*0.37;\n  incomeTax=Math.floor(incomeTax);\n\n  // State tax (approximate 5% average)\n  var residentTax=Math.floor(Math.max(0,taxableIncome)*0.05);\n\n  // FICA: Social Security (6.2% up to $168,600) + Medicare (1.45%)\n  var socialIns=Math.floor(Math.min(annual,168600)*0.062+annual*0.0145);\n\n  var tedori=annual-incomeTax-residentTax-socialIns;\n  var tedoriMonthly=Math.floor(tedori/12);\n  var rate=(tedori/annual*100).toFixed(1);\n\n  document.getElementById('incomeVal').textContent='$'+inc.toLocaleString();\n  document.getElementById('tedoriYear').textContent='$'+Math.max(0,tedori).toLocaleString();\n  document.getElementById('tedoriMonth').textContent='$'+Math.max(0,tedoriMonthly).toLocaleString();\n  document.getElementById('incomeTax').textContent='$'+Math.max(0,incomeTax).toLocaleString();\n  document.getElementById('residentTax').textContent='$'+Math.max(0,residentTax).toLocaleString();\n  document.getElementById('socialIns').textContent='$'+socialIns.toLocaleString();\n  document.getElementById('tedoriRate').textContent=rate+'%';\n}\ncalcTedori();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"take-home-pay-reference-table-by-salary\"\u003eTake-Home Pay Reference Table by Salary\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eGross Salary\u003c/th\u003e\n          \u003cth\u003eEstimated Take-Home\u003c/th\u003e\n          \u003cth\u003eTake-Home Rate\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e$30,000\u003c/td\u003e\n          \u003ctd\u003e~$25,500\u003c/td\u003e\n          \u003ctd\u003e~85%\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e$50,000\u003c/td\u003e\n          \u003ctd\u003e~$41,000\u003c/td\u003e\n          \u003ctd\u003e~82%\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e$70,000\u003c/td\u003e\n          \u003ctd\u003e~$55,500\u003c/td\u003e\n          \u003ctd\u003e~79%\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e$90,000\u003c/td\u003e\n          \u003ctd\u003e~$69,500\u003c/td\u003e\n          \u003ctd\u003e~77%\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e$120,000\u003c/td\u003e\n          \u003ctd\u003e~$89,000\u003c/td\u003e\n          \u003ctd\u003e~74%\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e$150,000\u003c/td\u003e\n          \u003ctd\u003e~$107,000\u003c/td\u003e\n          \u003ctd\u003e~71%\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e$200,000\u003c/td\u003e\n          \u003ctd\u003e~$136,000\u003c/td\u003e\n          \u003ctd\u003e~68%\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e$300,000\u003c/td\u003e\n          \u003ctd\u003e~$192,000\u003c/td\u003e\n          \u003ctd\u003e~64%\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e\u003cem\u003eEstimates for single filer, no dependents, average state tax.\u003c/em\u003e\u003c/p\u003e","title":"Take-Home Pay Calculator | Instantly Calculate Net Pay from Gross Salary [2026]"},{"content":"Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nBest Credit Cards 2026 — Find the Right Card for Your Spending Style Choosing a credit card in 2026 shouldn\u0026rsquo;t require a finance degree. But with thousands of cards available — each with different rewards structures, annual fees, APRs, and perks — it\u0026rsquo;s easy to pick the wrong one and leave money on the table.\nThis guide cuts through the noise. We compare the best cards in each major category, explain who each card is actually best for, and help you make a decision based on how you actually spend money.\nHow We Evaluate Credit Cards Every card in this guide is evaluated on five criteria:\nCriteria Why It Matters Rewards rate The percentage back on purchases — the core value proposition Annual fee vs. value A $95 fee is worth it if you get $300+ in value; a $0 fee card that earns 1% isn\u0026rsquo;t always better Sign-up bonus First-year value can be massive — $200-$750 in bonus rewards APR Matters if you carry a balance (ideally you shouldn\u0026rsquo;t, but reality happens) Everyday usability Mobile app quality, fraud protection, customer service, accepted everywhere Best Cash Back Cards Best Overall: Citi Double Cash Card Feature Details Cash back 2% on everything (1% when you buy + 1% when you pay) Annual fee $0 Sign-up bonus $200 after spending $1,500 in first 6 months APR 18.24%–28.24% variable Best for: People who want simplicity. No categories to track, no quarterly activations. Every purchase earns 2%.\nThe trade-off: No bonus categories means you won\u0026rsquo;t earn 3-5% in specific spending areas. If 80% of your spending is groceries and gas, a category card may beat it.\nBest for Groceries \u0026amp; Streaming: Blue Cash Preferred (Amex) Feature Details Cash back 6% at U.S. supermarkets (up to $6,000/year), 6% on streaming, 3% on transit, 1% everything else Annual fee $95 Sign-up bonus $350 after $3,000 in 6 months Best for: Families spending $300+/month on groceries. At $500/month in groceries, you earn $360/year in grocery cash back alone — easily covering the $95 fee.\nMath check: $500/month groceries × 6% = $360/year cash back − $95 fee = $265 net gain vs. a 2% flat card earning $120.\nBest No-Fee Rotating Categories: Discover it Cash Back Feature Details Cash back 5% in rotating quarterly categories (restaurants, Amazon, gas, etc.), 1% everything else Annual fee $0 Sign-up bonus Cashback Match — Discover matches all cash back earned in your first year Best for: People willing to track quarterly categories. The first-year match effectively doubles your rewards — 5% becomes 10% in bonus categories.\nBest Travel Rewards Cards Best Overall Travel Card: Chase Sapphire Preferred Feature Details Rewards 5x on travel via Chase Travel, 3x on dining \u0026amp; streaming, 2x on other travel, 1x everything else Annual fee $95 Sign-up bonus 60,000 points after $4,000 in 3 months (worth $750+ when transferred to partners) Travel perks Trip cancellation insurance, primary rental car insurance, no foreign transaction fees Best for: Frequent travelers who want flexible points. Chase Ultimate Rewards points transfer 1:1 to airlines like United, Southwest, and hotels like Hyatt — often worth 1.5-2 cents per point.\nWhy it dominates: The sign-up bonus alone is worth $750+ in travel value. The $95 fee pays for itself with a single trip\u0026rsquo;s worth of travel protections.\nBest Premium Travel: Amex Platinum Feature Details Rewards 5x on flights booked directly with airlines, 5x on prepaid hotels via Amex Travel, 1x everything else Annual fee $695 Perks Airport lounge access (Centurion + Priority Pass), $200 airline credit, $200 hotel credit, $200 Uber credit, Global Entry/TSA PreCheck credit Best for: Frequent flyers who value lounge access and travel credits. The stacked credits ($200+$200+$200+$100) offset most of the $695 fee if you use them all.\nReality check: If you fly fewer than 6 times per year and don\u0026rsquo;t use the credits, the Sapphire Preferred at $95 is the better value.\nBest No-Fee Travel: Capital One VentureOne Feature Details Rewards 1.25x miles on everything, 5x on hotels/rental cars via Capital One Travel Annual fee $0 Sign-up bonus 20,000 miles after $500 in 3 months Best for: Occasional travelers who want travel rewards without committing to an annual fee. Miles can be redeemed to erase any travel purchase.\nBest Cards for Building Credit Best Secured Card: Discover it Secured Feature Details Cash back 2% at restaurants and gas (up to $1,000/quarter), 1% everything else Annual fee $0 Deposit $200 minimum (becomes your credit limit) Credit building Reports to all 3 bureaus, automatic review for upgrade to unsecured card Best for: People with no credit history or rebuilding after a setback. Unlike most secured cards, this one actually earns cash back — plus the first-year Cashback Match.\nBest Student Card: Discover it Student Cash Back Feature Details Cash back Same as Discover it Cash Back (5% rotating + 1% base) Annual fee $0 Student perks $20 Good Grade Reward for each year GPA is 3.0+ Best for: College students building credit. Same rewards as the premium Discover card, plus GPA bonus.\nBest Balance Transfer Cards Best for Large Balances: Citi Simplicity Feature Details Intro APR 0% for 21 months on balance transfers and purchases Balance transfer fee 3% (minimum $5) Annual fee $0 Late fees None — no late fee ever, no penalty rate Best for: Anyone carrying credit card debt. Transfer a $5,000 balance from a 24% APR card and save roughly $1,800 in interest over 21 months. The 3% fee ($150) is minimal compared to the savings.\nBest Business Credit Cards Best for Small Business: Chase Ink Business Preferred Feature Details Rewards 3x on travel, shipping, internet/cable/phone, social media advertising (up to $150,000/year), 1x everything else Annual fee $95 Sign-up bonus 100,000 points after $8,000 in 3 months Best for: Freelancers and small business owners who spend on advertising, software subscriptions, and shipping. The 100K point bonus (worth $1,250+ in travel) is one of the best in any category.\nRelated: AI Tools for Freelancers to Earn More 2026 Best No-Fee Business: Chase Ink Business Unlimited Feature Details Cash back 1.5% on everything Annual fee $0 Sign-up bonus $750 after $6,000 in 3 months Best for: Business owners who want simplicity. No categories to optimize — every business expense earns 1.5%.\nQuick Comparison Table Card Best For Annual Fee Top Reward Rate Sign-Up Bonus Citi Double Cash Flat cash back $0 2% everything $200 Blue Cash Preferred Groceries $95 6% groceries $350 Discover it Rotating categories $0 5% quarterly Cashback Match Chase Sapphire Preferred Travel $95 5x travel 60K points Amex Platinum Premium travel $695 5x flights 80K points Capital One VentureOne No-fee travel $0 1.25x everything 20K miles Discover it Secured Building credit $0 2% dining/gas Cashback Match Chase Ink Preferred Business $95 3x categories 100K points Frequently Asked Questions Q: How many credit cards should I have? Two to three is the sweet spot for most people. A primary rewards card for daily spending, plus a secondary card for categories your main card doesn\u0026rsquo;t cover well. More than four gets hard to manage.\nQ: Does applying for a credit card hurt my credit score? A hard inquiry temporarily drops your score by 5-10 points. It recovers within a few months. Don\u0026rsquo;t apply for multiple cards in the same week, but one application every 6-12 months is fine.\nQ: Should I pay an annual fee? Only if the rewards and perks exceed the fee. A $95 card that gives you $300+ in value is worth it. Run the math on your actual spending before deciding.\nQ: What\u0026rsquo;s the minimum credit score needed for rewards cards? Most premium rewards cards require 670+ (good credit). No-fee cash back cards like Discover it are available with scores of 620+. Secured cards have no minimum.\nQ: Cash back or travel points — which is better? Cash back is simpler and more flexible. Travel points can be worth 50-100% more per point when transferred to airline/hotel partners, but require more effort to maximize. Choose travel points if you fly 3+ times per year.\nQ: Is it bad to carry a balance on a rewards card? Yes. Credit card interest rates (18-28%) will always exceed your rewards earnings (1-5%). Pay your full balance every month. If you\u0026rsquo;re carrying debt, focus on a 0% balance transfer card first.\nHow to Choose: Decision Framework Do you carry a balance? → Get a 0% balance transfer card first, pay it off, then get a rewards card Do you travel 3+ times per year? → Chase Sapphire Preferred Do you spend $300+/month on groceries? → Blue Cash Preferred Do you want zero complexity? → Citi Double Cash (2% on everything) Are you building credit? → Discover it Secured Are you a freelancer/business owner? → Chase Ink Business Preferred Pair Great Rewards With Smart Investing Maximizing credit card cash back is smart — but investing those rewards is even smarter. Open a Rakuten Securities account to put your cash back rewards to work in a diversified portfolio and grow your net worth beyond just rewards points.\nConclusion The best credit card is the one that matches how you actually spend. A 5% cash back card is worthless if the bonus categories don\u0026rsquo;t align with your habits. A $695 premium card is a waste if you fly twice a year.\nStart with one card that covers your biggest spending category. Use it for 6 months, track your rewards, and then decide if adding a second card would meaningfully increase your earnings.\nThe cards above represent the strongest options available in 2026, but always check the issuer\u0026rsquo;s website for the most current terms, APRs, and bonus offers before applying.\nRelated: Best Budgeting Apps 2026 Related: How to Build an Emergency Fund Fast Related Articles Best Budgeting Apps 2026: Full Comparison How to Build an Emergency Fund Fast How to Pay Off Debt Fast: Proven Strategies Best High-Yield Savings Accounts 2026 ChatGPT Tips to Save Money Related Tools Calculate loan payments and payoff timeline → Loan Calculator Plan your debt payoff strategy → Debt Payoff Calculator See your 2026 federal tax bracket and effective rate → Tax Bracket Calculator Create a monthly spending plan → Budget Planner Calculate your take-home pay → Salary Calculator See how savings grow over time → Compound Interest Calculator Calculate how long to reach any savings target → Savings Goal Calculator Related Templates Take control of your finances with our ready-made tools:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Including financial planning prompts The AI Productivity Playbook 2026 — 50+ AI workflows for smarter money management This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/best-credit-cards-2026/","summary":"\u003cp\u003e\u003cem\u003eDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"best-credit-cards-2026--find-the-right-card-for-your-spending-style\"\u003eBest Credit Cards 2026 — Find the Right Card for Your Spending Style\u003c/h1\u003e\n\u003cp\u003eChoosing a credit card in 2026 shouldn\u0026rsquo;t require a finance degree. But with thousands of cards available — each with different rewards structures, annual fees, APRs, and perks — it\u0026rsquo;s easy to pick the wrong one and leave money on the table.\u003c/p\u003e","title":"Best Credit Cards 2026: Complete Comparison Guide by Category"},{"content":"Running a small restaurant means wearing every hat in the building — chef, manager, marketer, and often your own accountant. For most independent restaurant owners, bookkeeping is the job that gets pushed to Sunday nights, causing stress, errors, and missed deductions that cost thousands every year.\nAI tools, used correctly, can change this. Not by replacing your accountant (you still need one for tax filing and compliance), but by handling the tedious daily and weekly work that has traditionally consumed your time: categorizing expenses, tracking food costs, reconciling sales data, and preparing clean records for tax season.\nThis guide walks you through exactly how to build an AI-assisted bookkeeping system for your restaurant in 2026.\nWhy Restaurant Bookkeeping Is Especially Hard Restaurants present unique bookkeeping challenges that generic small business software doesn\u0026rsquo;t fully address:\nHigh transaction volume: Dozens to hundreds of daily transactions across cash, card, and delivery platforms Complex COGS: Food and beverage costs fluctuate daily based on purchasing, waste, and menu mix Multiple revenue streams: Dine-in, delivery (Uber Eats, DoorDash, Grubhub), catering, events, retail Labor complexity: Tipped employees, varying hourly rates, split shifts, overtime calculations Cash handling: Cash transactions require meticulous daily reconciliation Perishables: Spoilage and waste need to be tracked and accounted for The average independent restaurant owner spends 8-12 hours per month on bookkeeping tasks. AI can reduce that to 2-3 hours while actually improving accuracy.\nYour AI Bookkeeping Stack for 2026 You don\u0026rsquo;t need expensive enterprise software. Here\u0026rsquo;s the practical stack that works for restaurants with under $2M in annual revenue:\nCore Stack Tool Role Monthly Cost QuickBooks Online or Xero Core accounting software $30–$80 ChatGPT Plus AI assistant for categorization, analysis, templates $20 Dext (formerly Receipt Bank) Receipt capture and OCR $30–$50 Your POS system (Square, Toast, Clover) Sales data source Varies Google Sheets or Notion Food cost tracking dashboard Free Total monthly investment: ~$80–$150\nThis stack gives you near-real-time visibility into your restaurant\u0026rsquo;s finances without a full-time bookkeeper.\nStep 1: Set Up Your Chart of Accounts for a Restaurant Before any AI can help you categorize expenses, your chart of accounts needs to be configured correctly. Generic QuickBooks setups are built for retail or service businesses — not restaurants.\nEssential Restaurant-Specific Accounts Revenue Accounts:\nDine-In Food Sales Dine-In Beverage Sales (Alcoholic / Non-Alcoholic — separate if you have a liquor license) Delivery Platform Sales (Uber Eats, DoorDash, Grubhub — separate accounts for each) Catering Revenue Gift Card Sales (liability account until redeemed) Cost of Goods Sold (COGS):\nFood Cost — Protein (meat, seafood, poultry) Food Cost — Produce Food Cost — Dairy Food Cost — Dry Goods / Pantry Beverage Cost — Alcohol Beverage Cost — Non-Alcoholic Packaging / To-Go Supplies Labor:\nKitchen Labor Front of House Labor Management Salaries Payroll Taxes Employee Benefits Operating Expenses:\nRent / Occupancy Utilities (Electric, Gas, Water — separate) Cleaning Supplies Equipment Repairs and Maintenance Credit Card Processing Fees Delivery Platform Fees (commissions from Uber Eats, etc.) Marketing and Advertising Insurance Licenses and Permits POS Software Fees Accounting / Professional Fees ChatGPT Prompt: Build Your Custom Chart of Accounts I run a [TYPE OF RESTAURANT] with [# SEATS] seats in [CITY]. We do dine-in, takeout, and delivery through [PLATFORMS]. We [DO / DO NOT] serve alcohol. Create a detailed chart of accounts for QuickBooks Online specifically designed for my restaurant. Organize it by: Revenue, Cost of Goods Sold, Labor, Occupancy, Operating Expenses, and Other. Include account numbers starting at 4000 for Revenue and include any restaurant-specific sub-accounts I should track separately. Step 2: Automate Expense Categorization with AI This is where AI saves the most time. Manual expense categorization — going through bank statements line by line — is the most tedious part of restaurant bookkeeping.\nSetting Up Automated Bank Rules in QuickBooks/Xero Both QuickBooks and Xero allow you to create \u0026ldquo;bank rules\u0026rdquo; that automatically categorize recurring transactions. This is the foundation of automation.\nHow to set up a bank rule in QuickBooks Online:\nGo to Banking \u0026gt; Bank Transactions Find a transaction you want to auto-categorize (e.g., your Sysco food delivery) Click \u0026ldquo;Create rule\u0026rdquo; next to the transaction Set conditions: \u0026ldquo;If description contains [SYSCO]\u0026rdquo; Set action: \u0026ldquo;Categorize as Food Cost — Dry Goods / Pantry\u0026rdquo; Set: \u0026ldquo;Apply to all past and future transactions matching this rule\u0026rdquo; Pro tip: Do this for your top 20 recurring vendors first. Most restaurants have 80% of their expenses concentrated in 15-25 vendors. Setting rules for those vendors means 80% of your expenses are auto-categorized without you touching them.\nUsing ChatGPT to Categorize Unknown Transactions For the remaining 20% — unfamiliar vendors, one-time purchases, or ambiguous transactions — use ChatGPT to help categorize in bulk.\nStep 1: Export your uncategorized transactions from QuickBooks as a CSV.\nStep 2: Copy the list of vendor names and amounts into ChatGPT with this prompt:\nI run a small restaurant. Below is a list of bank transactions that I need to categorize for my bookkeeping. For each transaction, suggest the most likely bookkeeping category from this list: Categories: Food Cost, Beverage Cost, Packaging Supplies, Kitchen Equipment, Cleaning Supplies, Utilities, Marketing, Software/Subscriptions, Credit Card Fees, Delivery Platform Fees, Repairs and Maintenance, Professional Services, Payroll, Insurance, Rent, Miscellaneous Transaction list: [PASTE YOUR TRANSACTION LIST] Format your response as a table: Transaction | Suggested Category | Reasoning (brief) Step 3: Review ChatGPT\u0026rsquo;s suggestions (takes 2-3 minutes), make any corrections, then import back into QuickBooks using the bulk categorization feature.\nThis process reduces a 2-hour categorization task to about 20 minutes.\nStep 3: Food Cost Tracking — Your Most Critical KPI For most restaurants, food cost is the single most important number to track. Industry benchmarks:\nFood Cost %: 28–35% of food revenue (varies by concept) Beverage Cost %: 18–24% for alcoholic, 25–30% for non-alcoholic Prime Cost (food + labor): Should be under 60-65% of revenue Building a Food Cost Tracking Spreadsheet Create a Google Sheet with these tabs:\nTab 1: Weekly Purchases\nDate Vendor Category Invoice # Amount [DATE] Sysco Protein #12345 $850 Tab 2: Sales Data (pulled from your POS weekly)\nWeek Food Sales Beverage Sales Total Revenue [DATE] $12,400 $3,100 $15,500 Tab 3: Food Cost Dashboard (auto-calculated)\nWeekly Food Cost $ Weekly Food Cost % 4-Week Rolling Average Budget vs. Actual Variance ChatGPT Prompt: Analyze Your Food Cost Trends Here is my restaurant\u0026#39;s food cost data for the past 8 weeks: [PASTE YOUR WEEKLY DATA: Week, Food Purchases $, Food Sales $, Food Cost %] My food cost target is [%]. Analyze this data and tell me: 1. Is my food cost trending up, down, or stable? 2. Which weeks had unusually high or low food costs, and what might explain them? 3. At my current trajectory, what will my monthly food cost be by end of [MONTH]? 4. What are 5 practical questions I should investigate with my kitchen team to identify cost leakage? Inventory Counting Made Smarter with AI Most restaurant owners hate doing inventory. Here\u0026rsquo;s a simplified system:\nWeekly spot count: Count your top 10 highest-cost items (usually proteins) Monthly full count: Count everything\nAfter completing your count, use this prompt:\nHere are my restaurant\u0026#39;s inventory counts: Beginning inventory value: $[AMOUNT] Purchases this period: $[AMOUNT] Ending inventory value: $[AMOUNT] Cost of Goods Sold (calculated): $[AMOUNT] My food sales this period were: $[AMOUNT] My food cost target is: [%] My actual food cost came out to: [%] I\u0026#39;m [over/under] my target by [%]. Help me create a checklist of the most likely reasons for this variance and how I would investigate each one. Step 4: QuickBooks and Xero Integration Tips for Restaurants Integrating Your POS System Your POS system is the primary source of truth for revenue. Most modern POS systems integrate directly with QuickBooks or Xero.\nSquare + QuickBooks: Use the Square for Retail/Restaurants app connector. Set up daily sales summaries to auto-import as journal entries.\nToast + QuickBooks: Toast has a native QuickBooks integration. Configure it to separate food sales, beverage sales, and tax by category.\nClover + Xero: Use the Clover connector in the Xero app marketplace.\nConfiguring the POS Integration: When setting up the sync, map your POS sales categories to your QuickBooks accounts:\nPOS \u0026ldquo;Food\u0026rdquo; → QB \u0026ldquo;Dine-In Food Sales\u0026rdquo; POS \u0026ldquo;Alcohol\u0026rdquo; → QB \u0026ldquo;Dine-In Beverage Sales — Alcohol\u0026rdquo; POS \u0026ldquo;Tips\u0026rdquo; → QB \u0026ldquo;Tips Payable\u0026rdquo; (liability account) POS \u0026ldquo;Tax Collected\u0026rdquo; → QB \u0026ldquo;Sales Tax Payable\u0026rdquo; (liability account) Handling Delivery Platform Revenue This is where most restaurant owners make bookkeeping mistakes. Delivery platforms (Uber Eats, DoorDash, Grubhub) remit net payouts after deducting their commission — but you need to record the gross revenue and the commission expense separately.\nCorrect way to record a DoorDash payout:\nAccount Debit Credit Bank Account $742 Delivery Platform Fees (Expense) $158 Delivery Food Sales (Revenue) $900 Wrong way (what most owners do): Simply recording $742 as revenue — this understates revenue and overstates your food cost percentage.\nChatGPT Prompt: Set Up Delivery Platform Journal Entries I receive weekly payouts from [UBER EATS / DOORDASH / GRUBHUB]. Each payout includes a breakdown showing: Gross Sales, Commission Fees, Promotions/Adjustments, and Net Payout. Write me step-by-step instructions for how to record each weekly payout as a journal entry in [QUICKBOOKS ONLINE / XERO] so that gross revenue and commission expenses are recorded separately. Include what accounts to use and which ones to set up if I don\u0026#39;t have them yet. Step 5: Using ChatGPT for Tax Preparation Restaurant taxes have specific deductions and requirements that many owners miss. Use AI to make sure you\u0026rsquo;re capturing everything.\nMonthly Tax Prep Checklist Prompt I run a restaurant and I\u0026#39;m preparing my records for [MONTH] tax review. Create a monthly bookkeeping checklist specific to restaurants that covers: 1. Revenue reconciliation tasks 2. Expense documentation to collect 3. Payroll records to verify 4. Sales tax records 5. Restaurant-specific deductions to review 6. Items to flag for my accountant My restaurant type: [FULL SERVICE / FAST CASUAL / BAR] My state: [STATE] I [DO / DO NOT] serve alcohol. Restaurant-Specific Tax Deductions Checklist Use ChatGPT to generate a comprehensive list, but here are the commonly missed ones:\nDeductions many restaurant owners miss:\nFICA tip credit (Form 8846): You can claim a credit for the employer portion of FICA taxes paid on tipped wages above the minimum wage. This is often worth $2,000–$15,000+ per year for a restaurant with a bar. Meals for employees working shifts: Staff meals are 50% deductible (or potentially 100% if provided for business convenience) Uniform costs: Logo\u0026rsquo;d uniforms and aprons are fully deductible Portion of your phone: If you use your personal phone for business, the business-use percentage is deductible Home office: If you do bookkeeping, scheduling, or ordering from home Continuing education: Culinary classes, food safety certifications, business courses Annual Tax Prep Prompt I\u0026#39;m preparing for my annual tax filing as a restaurant owner. Here is a summary of my year: - Total revenue: $[AMOUNT] - Total food cost: $[AMOUNT] - Total labor: $[AMOUNT] - Operating expenses: $[AMOUNT] - Net income before taxes: $[AMOUNT] - Entity type: [SOLE PROP / LLC / S-CORP / C-CORP] - State: [STATE] - Number of employees: [#] - Do I take tips? [YES / NO] Create a prioritized checklist of documents I need to gather, tax forms I likely need to file, and questions I should ask my accountant to ensure I\u0026#39;m not overpaying taxes. Step 6: Weekly Bookkeeping Routine (Under 60 Minutes) Here\u0026rsquo;s the exact weekly routine that keeps your books clean year-round:\nMonday Morning — 45-60 Minutes Week 1 and Week 3 (Monthly Tasks):\nReview and approve uncategorized transactions in QuickBooks (15 min) Pull weekly sales report from POS — verify it matches QuickBooks auto-import (10 min) Enter weekly food purchases from invoices using Dext (scan receipts all week, then batch-enter) (15 min) Update food cost spreadsheet with weekly numbers (5 min) Flag any unusual expenses for accountant review (5 min) Week 2 and Week 4 (Additional Monthly Close Tasks):\nReconcile bank accounts (10 min — most is auto-matched) Review delivery platform payouts and verify journal entries (10 min) Run P\u0026amp;L report for the month — review food cost %, labor %, and prime cost (10 min) Run ChatGPT analysis on the month\u0026rsquo;s numbers (5 min) Monthly Analysis Prompt:\nHere is my restaurant\u0026#39;s P\u0026amp;L summary for [MONTH]: Revenue: - Food Sales: $[AMOUNT] - Beverage Sales: $[AMOUNT] - Delivery Sales: $[AMOUNT] - Total Revenue: $[AMOUNT] COGS: - Food Cost: $[AMOUNT] ([%] of food revenue) - Beverage Cost: $[AMOUNT] ([%] of bev revenue) Labor: $[AMOUNT] ([%] of revenue) Prime Cost: $[AMOUNT] ([%] of revenue) Operating Expenses: $[AMOUNT] Net Income: $[AMOUNT] ([%] of revenue) Compare these to restaurant industry benchmarks and tell me: What is performing well? What are my top 3 areas of concern? Give me 3 specific action items for next month to improve profitability. Common Mistakes to Avoid Mistake 1: Recording delivery platform net payouts as gross revenue. As covered above, this distorts your food cost percentage and understates revenue.\nMistake 2: Not separating cash tips from credit card tips. Cash tips are the employee\u0026rsquo;s income and generally don\u0026rsquo;t flow through your books. Credit card tips are your liability that you pay out. Mixing these creates payroll tax errors.\nMistake 3: Skipping inventory counts. Without actual inventory counts, your food cost is estimated, not real. Even a monthly count for high-cost items dramatically improves accuracy.\nMistake 4: Expensing large equipment instead of depreciating it. Equipment over $2,500 (generally) should be depreciated, not fully expensed in one year — unless you use Section 179 expensing (which your accountant should evaluate each year).\nMistake 5: Not tracking gift card liabilities. When a customer buys a $50 gift card, you owe them $50 in future meals — it\u0026rsquo;s a liability, not revenue. Revenue is recognized when the card is redeemed.\nGetting Started: Your First 30 Days Week 1: Set up your chart of accounts (use the ChatGPT prompt above). Configure bank rules for your top 10 vendors.\nWeek 2: Set up your POS integration with QuickBooks or Xero. Create your food cost tracking spreadsheet.\nWeek 3: Set up Dext for receipt capture. Train your kitchen manager to photograph invoices immediately upon delivery.\nWeek 4: Run your first month-end analysis using the P\u0026amp;L prompt. Establish your Monday morning routine.\nMonth 2 onward: Your books are running on near-autopilot. Your weekly bookkeeping time drops to 30-45 minutes. Your accountant stops calling you in a panic before tax deadlines.\nTake the Stress Out of Restaurant Accounting Between food costs, payroll, and quarterly taxes, small restaurant finances can spiral fast. Try freee — Japan\u0026rsquo;s #1 cloud accounting to automate bookkeeping, track expenses, and get ready for tax season without the panic.\nConclusion Restaurant bookkeeping doesn\u0026rsquo;t have to be the task you dread most. With the right AI-assisted setup, you can have cleaner books, better visibility into your food costs, and a much smoother tax season — without becoming an accountant yourself.\nThe investment is about $100-150 per month in software and a few hours of setup time. The return is 6-10 hours per month of your time back, fewer costly mistakes, and the business intelligence to actually improve your profitability.\nStart with Step 1 this week. Get your chart of accounts right, and everything else builds from there.\nProductivity Works publishes weekly guides for small business owners. Subscribe to get our restaurant management toolkit.\nRelated Tools Convert between units of measurement → Unit Converter Create a business budget → Budget Planner Calculate your side hustle taxes → Side Hustle Tax Calculator Plan loan repayment → Loan Repayment Calculator Related Templates Put these techniques into practice with our ready-made templates:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Copy-paste prompts for every situation The AI Productivity Playbook 2026 — 50+ AI workflows and automation strategies This article contains affiliate links. We may earn a commission at no extra cost to you.\nYou May Also Like Track Food Costs with AI: Restaurant Owner Guide (2026) How to Automate Bookkeeping with AI for Small Business (2026 Guide) Best Accounting Software for Freelancers 2026: Full Compare ","permalink":"https://productivity-works.com/posts/ai-bookkeeping-small-restaurant-2026/","summary":"\u003cp\u003eRunning a small restaurant means wearing every hat in the building — chef, manager, marketer, and often your own accountant. For most independent restaurant owners, bookkeeping is the job that gets pushed to Sunday nights, causing stress, errors, and missed deductions that cost thousands every year.\u003c/p\u003e\n\u003cp\u003eAI tools, used correctly, can change this. Not by replacing your accountant (you still need one for tax filing and compliance), but by handling the tedious daily and weekly work that has traditionally consumed your time: categorizing expenses, tracking food costs, reconciling sales data, and preparing clean records for tax season.\u003c/p\u003e","title":"AI Bookkeeping for Small Restaurants: Complete Automation Guide (2026)"},{"content":"Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nBest Online Coding Bootcamps 2026 — A Realistic Guide for Career Changers The coding bootcamp industry has matured significantly since its early days. In 2026, the best bootcamps genuinely produce job-ready developers in 3-6 months. The worst ones charge $15,000+ and leave you with a portfolio no employer takes seriously.\nThis guide separates the two. We compare the top bootcamps based on actual outcomes — job placement rates, salary data, curriculum depth, and whether the investment makes financial sense for your specific situation.\nHow We Evaluate Bootcamps Criteria What We Look For Job placement rate Verified third-party data, not self-reported numbers Curriculum relevance Technologies that employers actually hire for in 2026 Cost vs. ROI Total cost including living expenses vs. expected salary increase Learning format Self-paced vs. cohort-based, synchronous vs. async Support quality Mentor access, career coaching, alumni network Best for Career Changers (Full-Time Programs) 1. App Academy — Best Job Placement + Deferred Tuition Feature Details Cost $17,000 upfront or deferred tuition (pay after getting a job) Duration 16 weeks (full-time) or 24 weeks (part-time) Stack JavaScript, Python, React, SQL, Express, Flask Job placement ~90% within 6 months (CIRR-verified) Average starting salary $105,000 Why it stands out: The deferred tuition option means zero financial risk. You only pay if you land a job earning $50K+. The curriculum covers both frontend and backend with modern frameworks employers actually use.\nWho should avoid it: Anyone who can\u0026rsquo;t commit full-time for 16 weeks. The pace is intense — 60-80 hour weeks are normal.\n2. Launch School — Best for Deep Understanding Feature Details Cost $199/month (no fixed end date) Duration 8-18 months (self-paced mastery-based) Stack Ruby, JavaScript, SQL, plus CS fundamentals Philosophy Mastery-based — you don\u0026rsquo;t advance until you truly understand each concept Why it stands out: Launch School graduates consistently report stronger problem-solving skills than bootcamp grads from faster programs. The mastery-based approach means no one graduates without genuine understanding. At $199/month, the total cost ($1,600-$3,600) is a fraction of traditional bootcamps.\nWho should avoid it: Anyone who needs a job in 3 months. The self-paced model rewards patience and depth over speed.\n3. Hack Reactor — Best for Experienced Professionals Feature Details Cost $17,980 Duration 12 weeks (full-time) Stack JavaScript, React, Node.js, PostgreSQL, System Design Job placement ~85% within 180 days Average starting salary $110,000+ Why it stands out: The curriculum assumes some prior coding experience and moves fast. The system design component (unusual for bootcamps) prepares graduates for mid-level interviews, not just junior roles.\nBest for Part-Time Learning (Keep Your Job) Codecademy Pro + Career Path Feature Details Cost $34.99/month or $17.49/month (annual) Duration Self-paced (career paths are 30-50 hours) Stack Multiple paths: Full-Stack, Data Science, AI, Frontend Best feature Structured career paths with projects and quizzes Best for: Self-motivated learners who want structured curriculum without bootcamp prices. The Full-Stack Engineer career path is comprehensive and costs under $500 total if completed in 12 months.\nSpringboard — Best Part-Time With Job Guarantee Feature Details Cost $16,000 (ISA available — pay after employment) Duration 6-9 months part-time (15-20 hours/week) Stack Software Engineering: JavaScript, React, Node.js, Python Job guarantee Full tuition refund if no job within 6 months of graduation Best for: Working professionals who can\u0026rsquo;t quit their job but want a structured program with accountability. The 1:1 mentorship model and job guarantee reduce risk significantly.\nThe Odin Project — Best Free Option Feature Details Cost Free (open source) Duration Self-paced (typically 6-12 months) Stack Full-Stack Ruby on Rails or Full-Stack JavaScript Community Active Discord with 100K+ members Best for: Anyone who wants a high-quality curriculum without spending a dollar. The Odin Project is consistently rated as the best free coding education available. The trade-off: no mentorship, no job guarantee, no structured career support. You need self-discipline.\nRelated: AI Tools for Freelancers to Earn More 2026 Best for Specific Career Paths Data Science: Dataquest Feature Details Cost $49/month (Premium) Stack Python, SQL, pandas, machine learning, statistics Format Browser-based coding exercises (no video lectures) Best for: Aspiring data analysts and data scientists. The hands-on, code-first approach beats passive video courses. Completion takes 6-12 months at 10 hours/week.\nAI/Machine Learning: Fast.ai Feature Details Cost Free Stack Python, PyTorch, deep learning Format Video lectures + notebooks Philosophy Top-down learning — build working AI models before understanding the math Best for: Anyone interested in AI/ML. Fast.ai\u0026rsquo;s \u0026ldquo;Practical Deep Learning for Coders\u0026rdquo; course is legendary — it\u0026rsquo;s produced thousands of practicing ML engineers. The top-down approach means you build real AI applications from week one.\nRelated: Best ChatGPT Prompts for Productivity 2026 UX/UI Design: Designlab Feature Details Cost $6,175 (UX Academy) Duration 6 months Format Project-based with 1:1 mentor sessions Job placement 93% within 6 months Best for: Career changers interested in design rather than coding. UX/UI roles pay $85K-$120K and don\u0026rsquo;t require programming skills.\nQuick Comparison Table Bootcamp Best For Cost Duration Job Guarantee? App Academy Career change (full-time) $17K or deferred 16 weeks Yes (deferred model) Launch School Deep mastery $199/mo 8-18 months No Hack Reactor Experienced learners $17,980 12 weeks Partial Springboard Part-time career change $16K or ISA 6-9 months Yes The Odin Project Budget-conscious Free 6-12 months No Codecademy Pro Self-paced supplemental $35/mo Self-paced No Dataquest Data science $49/mo 6-12 months No Fast.ai AI/ML Free Self-paced No The Math: Is a Bootcamp Worth It? Let\u0026rsquo;s run the numbers for a typical career changer:\nBefore bootcamp: $55,000/year salary in a non-tech role After bootcamp: $95,000/year as a junior developer (conservative for 2026) Bootcamp cost: $17,000 Time investment: 4 months of lost income = ~$18,000\nTotal investment: $35,000 Annual salary increase: $40,000 Payback period: ~10 months\nEven accounting for the opportunity cost of not working during the program, the ROI is strong if — and this is the key qualifier — you actually complete the program and get a development job. The ~85-90% placement rate at top bootcamps means roughly 1 in 10 graduates don\u0026rsquo;t land a dev role within 6 months.\nRed Flags: Bootcamps to Avoid Be wary of any bootcamp that:\nClaims 100% job placement — No legitimate program has 100%. Look for CIRR-verified data. Won\u0026rsquo;t share salary outcomes — If they hide average starting salaries, there\u0026rsquo;s a reason. Teaches outdated technology — jQuery-focused curricula in 2026 are a waste of money. Has no student projects — A portfolio without real projects is worthless to employers. Requires an ISA with unfavorable terms — Read the fine print. Some ISAs take 10-17% of income for 2-5 years. Frequently Asked Questions Q: Can I really become a developer in 3-6 months? You can become a junior developer capable of contributing to a team. You won\u0026rsquo;t be a senior engineer. Expect 1-2 years of on-the-job learning before you\u0026rsquo;re truly comfortable. Bootcamps give you enough skill to get hired and keep learning.\nQ: Do employers respect bootcamp graduates? Increasingly yes, especially at startups and mid-size companies. Large tech companies (FAANG) still prefer CS degrees for many roles, but bootcamp grads are hired at companies of all sizes. Your portfolio and interview performance matter more than credentials.\nQ: What if I\u0026rsquo;m over 30/35/40? Age is less of a barrier than people think. The tech industry hires based on demonstrated skill. That said, career changers over 35 should target companies that value diverse experience — your domain expertise from a previous career is an asset, not a liability.\nQ: Should I learn to code with AI (Copilot, Cursor) from the start? Yes and no. Learn the fundamentals without AI assistance first — you need to understand what the code does. Once you have solid fundamentals (2-3 months), start using AI tools. In 2026, professional developers use AI daily. Not learning to work with AI tools would be like not learning to use a search engine.\nQ: Free resources vs. paid bootcamp — when is paying worth it? Pay for a bootcamp when: (1) you need structured accountability, (2) you want career support and job placement help, or (3) you\u0026rsquo;ve tried self-learning and couldn\u0026rsquo;t stay consistent. Stick with free resources if: you\u0026rsquo;re self-disciplined, have plenty of time, and mainly need the technical skills.\nReady to Launch Your New Tech Career? Completing a bootcamp is a major achievement — your next step is landing the role that matches your new skills. Find your next career on doda to browse engineering, development, and tech roles — and connect with employers actively hiring bootcamp graduates.\nConclusion The best bootcamp depends entirely on your situation:\nCareer changers going all-in: App Academy (deferred tuition = zero risk) Working professionals: Springboard (part-time + job guarantee) Budget-conscious self-learners: The Odin Project (free, excellent curriculum) Deep learners: Launch School ($199/mo, mastery-based) AI enthusiasts: Fast.ai (free, world-class) Whatever you choose, the most important factor is completion. 40% of bootcamp enrollees don\u0026rsquo;t finish. Pick the format that matches your learning style and life constraints, not just the one with the best marketing.\nRelated: How to Create a Resume with AI Step by Step Related: How to Automate Tasks with AI Step by Step Related Tools Format and validate JSON data → JSON Formatter Test regular expressions in real-time → Regex Tester Encode and decode Base64 strings → Base64 Encoder Calculate your expected salary after bootcamp → Salary Calculator Check your tax bracket → Tax Bracket Calculator Need a markdown editor? → Markdown Preview — write and preview markdown live\nDesign with gradients → CSS Gradient Generator — create beautiful CSS gradients visually\nGenerate placeholder text for layouts → Lorem Ipsum Generator — instant filler content\nRelated Templates Level up your career change with our ready-made tools:\nAI Resume Toolkit — 25 tested ChatGPT prompts for resumes + ATS keyword guide The AI Productivity Playbook 2026 — 50+ AI workflows for career development This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/best-online-coding-bootcamps-2026/","summary":"\u003cp\u003e\u003cem\u003eDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"best-online-coding-bootcamps-2026--a-realistic-guide-for-career-changers\"\u003eBest Online Coding Bootcamps 2026 — A Realistic Guide for Career Changers\u003c/h1\u003e\n\u003cp\u003eThe coding bootcamp industry has matured significantly since its early days. In 2026, the best bootcamps genuinely produce job-ready developers in 3-6 months. The worst ones charge $15,000+ and leave you with a portfolio no employer takes seriously.\u003c/p\u003e\n\u003cp\u003eThis guide separates the two. We compare the top bootcamps based on actual outcomes — job placement rates, salary data, curriculum depth, and whether the investment makes financial sense for your specific situation.\u003c/p\u003e","title":"Best Online Coding Bootcamps 2026: Honest Comparison by Career Goal"},{"content":"Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nBest VPN Services 2026 — What Actually Matters (And What Doesn\u0026rsquo;t) The VPN market is flooded with marketing hype. Every provider claims to be the \u0026ldquo;fastest,\u0026rdquo; \u0026ldquo;most secure,\u0026rdquo; and \u0026ldquo;best for streaming.\u0026rdquo; Most comparison sites rank VPNs based on affiliate commissions, not actual quality.\nThis guide takes a different approach. We evaluate VPNs on the things that actually matter — verified security audits, real-world speed tests, transparent privacy policies, and honest trade-offs. No VPN is perfect for everyone, and we\u0026rsquo;ll tell you which one is right for your specific use case.\nWhy You Actually Need a VPN in 2026 Not everyone needs a VPN. Here\u0026rsquo;s when you genuinely do:\nSituation Why VPN Helps Public WiFi (cafes, airports, hotels) Encrypts traffic so no one on the network can intercept your data Remote work Protects sensitive business data, especially on shared networks Streaming from abroad Access your home country\u0026rsquo;s Netflix, Hulu, BBC iPlayer library while traveling Privacy from ISP Prevents your internet provider from logging and selling your browsing history Censored networks Access blocked sites in restrictive countries or corporate networks When you don\u0026rsquo;t need a VPN: Browsing on your home WiFi with HTTPS sites. Modern encryption (TLS 1.3) already protects most web traffic. A VPN adds an extra layer, but the marginal benefit on a trusted network is small.\nTop 5 VPN Services Compared VPN Monthly Price Servers Speed Simultaneous Devices Audited? NordVPN $3.59/mo (2-year) 6,400+ in 111 countries Excellent 10 Yes (multiple) ExpressVPN $6.67/mo (1-year) 3,000+ in 105 countries Best-in-class 8 Yes Surfshark $2.19/mo (2-year) 3,200+ in 100 countries Very good Unlimited Yes ProtonVPN $4.49/mo (2-year) 6,500+ in 112 countries Good 10 Yes (open source) Mullvad $5.50/mo (no discount) 700+ in 46 countries Good 5 Yes Detailed Reviews NordVPN — Best All-Around Price: $3.59/mo (2-year plan), $4.99/mo (1-year), $12.99/mo (monthly)\nWhat makes it best overall:\nThreat Protection: Built-in malware blocker, ad blocker, and tracker blocker — works even when VPN is disconnected Speed: NordLynx protocol (based on WireGuard) delivers near-native speeds Double VPN: Route traffic through two servers for extra security Independently audited: PricewaterhouseCoopers has verified their no-logs policy twice Best for: Most people. NordVPN balances speed, security, features, and price better than any competitor.\nTrade-off: The 2-year commitment is required for the best price. Monthly pricing is expensive.\nExpressVPN — Fastest VPN Available Price: $6.67/mo (1-year plan), $9.99/mo (6-month), $12.95/mo (monthly)\nWhat makes it stand out:\nLightway protocol: Custom-built protocol that\u0026rsquo;s measurably faster than WireGuard in many scenarios RAM-only servers: All servers run on volatile memory — zero data survives a reboot TrustedServer technology: Independently audited by Cure53 and KPMG Consistently unblocks streaming: Works with Netflix, Disney+, Hulu, BBC iPlayer in virtually every country Best for: Speed-sensitive users (streaming, gaming) and anyone willing to pay a premium for the best experience.\nTrade-off: Most expensive option on this list. No built-in ad blocker.\nSurfshark — Best Value (Unlimited Devices) Price: $2.19/mo (2-year plan), $3.49/mo (1-year), $15.45/mo (monthly)\nWhat makes it stand out:\nUnlimited simultaneous connections: One subscription covers your entire household CleanWeb: Built-in ad and malware blocker MultiHop: Double VPN routing At $2.19/mo, it\u0026rsquo;s the cheapest reputable VPN available Best for: Families or anyone with many devices. At unlimited connections, the per-device cost approaches zero.\nTrade-off: Smaller server network than NordVPN. Customer support response times can be slower.\nProtonVPN — Best for Privacy Purists Price: $4.49/mo (2-year), $5.99/mo (1-year), $9.99/mo (monthly). Free tier available.\nWhat makes it stand out:\nSwiss jurisdiction: Switzerland has some of the strongest privacy laws in the world Open source: All apps are open source and independently audited Secure Core: Route traffic through privacy-friendly countries (Switzerland, Iceland, Sweden) before exiting Free tier: Genuinely usable free plan with no data limit (3 countries, slower speeds) Best for: Privacy-focused users who want verifiable security. Journalists, activists, and anyone in high-risk situations.\nTrade-off: Slower than NordVPN and ExpressVPN. The free tier is limited to 3 server locations.\nMullvad — Most Anonymous VPN Price: €5/mo flat (no discounts, no long-term plans)\nWhat makes it stand out:\nNo email required: Generate a random account number to sign up Cash payment accepted: Mail cash in an envelope for complete anonymity No personal information collected: They literally don\u0026rsquo;t know who you are Simple, transparent pricing: €5/month, period. No upsells, no tricks. Best for: Maximum anonymity. If you don\u0026rsquo;t want any VPN company to have your email, payment info, or identity, Mullvad is the only real option.\nTrade-off: Smaller server network. No streaming optimization. Basic apps without extra features.\nFree VPNs: The Hidden Cost Most free VPNs are worse than no VPN at all.\nA 2024 CSIRO study found that:\n72% of free VPNs contain third-party tracking 38% contain malware 84% leak user data through DNS or WebRTC 18% don\u0026rsquo;t even encrypt traffic The exceptions:\nProtonVPN Free — Legitimate, no-logs, but limited to 3 countries and slower speeds Windscribe Free — 10GB/month limit, decent privacy policy If you can afford $2-3/month, get a paid VPN. The security difference is enormous.\nUse Case Recommendations Your Priority Best VPN Why Best overall NordVPN Speed + security + features + price Fastest speeds ExpressVPN Lightway protocol, RAM-only servers Best value / families Surfshark $2.19/mo, unlimited devices Maximum privacy Mullvad No email, cash payment, zero data Open source / activism ProtonVPN Swiss law, open source, free tier Remote work NordVPN Threat Protection blocks malware on any network Streaming abroad ExpressVPN Most reliable unblocking, fastest for video Frequently Asked Questions Q: Is using a VPN legal? In the US, UK, EU, Canada, Australia, Japan, and most countries — yes, completely legal. VPNs are restricted or banned in China, Russia, Iran, North Korea, and a few other authoritarian states. Using a VPN for illegal activities is still illegal regardless of the VPN.\nQ: Will a VPN slow down my internet? Modern VPNs reduce speed by 5-15% on average. With NordVPN or ExpressVPN on a nearby server, the difference is unnoticeable for browsing and streaming. Gaming may notice 5-20ms additional latency.\nQ: Can my employer see my traffic if I use a VPN? If you\u0026rsquo;re on a company-owned device with corporate monitoring software, a personal VPN won\u0026rsquo;t help — the monitoring happens at the device level. On a personal device on a company network, yes, a VPN hides your traffic from the network.\nQ: Do VPNs work with Netflix? NordVPN, ExpressVPN, and Surfshark all work with Netflix in most regions. Netflix actively blocks VPN IPs, so occasional server switching may be required. Dedicated streaming-optimized servers help.\nQ: Should I leave my VPN on all the time? On public WiFi — always. At home — optional, but leaving it on adds a privacy layer against ISP tracking. The speed impact of modern VPNs is minimal enough for always-on use.\nConclusion For most people: NordVPN offers the best balance of speed, security, and features at a reasonable price. Surfshark is the smart pick for budget-conscious users or families. ExpressVPN is worth the premium if speed is your top priority. ProtonVPN and Mullvad are for users who prioritize privacy above all else.\nWhatever you choose, any reputable paid VPN is dramatically better than no VPN on public networks, and infinitely better than a free VPN that sells your data.\nRelated: How to Automate Tasks with AI Step by Step Related Tools Create a monthly budget → Budget Planner Generate secure passwords instantly → Password Generator Generate SHA-256 and MD5 hashes → Hash Generator Related Templates Protect your digital life and boost productivity:\nThe AI Productivity Playbook 2026 — 50+ AI workflows including cybersecurity best practices ChatGPT Prompt Templates — 100 ready-to-use prompts for every scenario ","permalink":"https://productivity-works.com/posts/best-vpn-services-2026/","summary":"\u003cp\u003e\u003cem\u003eDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"best-vpn-services-2026--what-actually-matters-and-what-doesnt\"\u003eBest VPN Services 2026 — What Actually Matters (And What Doesn\u0026rsquo;t)\u003c/h1\u003e\n\u003cp\u003eThe VPN market is flooded with marketing hype. Every provider claims to be the \u0026ldquo;fastest,\u0026rdquo; \u0026ldquo;most secure,\u0026rdquo; and \u0026ldquo;best for streaming.\u0026rdquo; Most comparison sites rank VPNs based on affiliate commissions, not actual quality.\u003c/p\u003e\n\u003cp\u003eThis guide takes a different approach. We evaluate VPNs on the things that actually matter — verified security audits, real-world speed tests, transparent privacy policies, and honest trade-offs. No VPN is perfect for everyone, and we\u0026rsquo;ll tell you which one is right for your specific use case.\u003c/p\u003e","title":"Best VPN Services 2026: Privacy, Streaming \u0026 Remote Work"},{"content":"AI Prompt Engineering Tips for Beginners — Complete Guide [2026] You\u0026rsquo;ve been using ChatGPT or Claude for a while, and you keep getting answers that are\u0026hellip; fine. Generic. Not quite what you needed. You tweak the question, try again, get something slightly better, and wonder if you\u0026rsquo;re doing this right.\nHere\u0026rsquo;s the truth: 90% of disappointing AI responses are caused by the prompt, not the AI. Prompt engineering — the art of writing instructions that get great results from AI — is the highest-leverage skill in tech right now. And unlike coding or data science, you can get genuinely good at it in a weekend.\nHere\u0026rsquo;s what you\u0026rsquo;ll learn in this guide:\nThe core principles that separate great prompts from mediocre ones 6 proven prompt frameworks with real examples A comparison of prompt strategies by use case Step-by-step exercises to build the skill fast Advanced techniques the top 5% of AI users deploy every day No technical background needed. If you can write a clear sentence, you can master prompt engineering.\nWhy Prompt Engineering Tips for Beginners Matter in 2026 The Productivity Gap Prompt engineering used to be a niche skill for developers building AI products. In 2026, it\u0026rsquo;s a general professional skill — as important as knowing how to use a search engine effectively or structure a professional document. The gap between someone who knows how to prompt and someone who doesn\u0026rsquo;t is enormous in terms of actual output quality, time saved, and creative possibilities unlocked.\nResearch from early AI adoption studies shows that knowledge workers who receive even basic prompt training produce outputs 2–3x faster and with significantly higher quality than those who use AI without any guidance. The ceiling is even higher for people who go deeper into advanced techniques.\nHow AI Changes the Game AI language models are prediction engines. They predict the most likely continuation of your text. When you give a vague prompt, the model predicts the most \u0026ldquo;average\u0026rdquo; response — which is why you get generic answers. When you give a specific, well-structured prompt, you steer the prediction toward exactly what you need.\nPrompt engineering is essentially learning the language that AI models respond to best. It\u0026rsquo;s not a programming language — it\u0026rsquo;s natural language, used strategically.\nPrompt Frameworks Compared Framework Best For Difficulty Example Use Role + Task + Format Most everyday tasks Beginner Writing, analysis, summarization Chain-of-Thought (CoT) Complex reasoning Beginner-Intermediate Math, logic, decisions Few-Shot Consistent output style Intermediate Content creation, classification RACI/Constraint High-precision outputs Intermediate Professional documents, legal Tree of Thoughts Creative brainstorming Advanced Strategy, ideation Self-Critique Loop Quality assurance Advanced High-stakes content Step-by-Step Guide: How to Master AI Prompt Engineering Step 1: Understand the 4 Pillars of a Great Prompt Every effective prompt has four elements. You don\u0026rsquo;t always need all four, but adding each one improves output quality.\nPillar 1: Role Tell the AI who it is.\nYou are a senior marketing strategist with 15 years of experience in B2B SaaS. Pillar 2: Task State exactly what you want it to do.\nWrite a LinkedIn post announcing our new AI feature launch. Pillar 3: Context Give it what it needs to know.\nThe feature automates sales forecasting. Our audience is sales directors at companies with 50–500 employees. We\u0026#39;re launching on June 1, 2026. Pillar 4: Format Specify the output structure.\nFormat: 200 words max, conversational tone, end with a question to spark comments, no hashtags, no emojis. Full Combined Prompt:\nYou are a senior marketing strategist with 15 years of experience in B2B SaaS. Write a LinkedIn post announcing our new AI-powered sales forecasting feature. Context: The feature automatically predicts quarterly revenue using CRM data. Our audience is sales directors at companies with 50–500 employees. We\u0026#39;re launching June 1, 2026. Format: - 200 words max - Conversational but professional - Open with a pain point the audience recognizes - End with a question to encourage comments - No hashtags or emojis Step 2: Learn the Chain-of-Thought Technique Chain-of-thought prompting asks the AI to reason through a problem step by step before giving the answer. It dramatically improves accuracy on complex questions.\nWithout CoT (bad):\nWhat pricing model should I use for my SaaS product? Result: Generic advice about freemium vs. subscription.\nWith CoT (good):\nI need to choose a pricing model for my SaaS product. Think through this step by step: 1. First, describe the 4 main SaaS pricing models and when each works best. 2. Then, consider this about my product: [DESCRIBE YOUR PRODUCT + TARGET MARKET] 3. Analyze which model fits best based on my situation. 4. Give your recommendation with a brief rationale. Work through each step in order before giving your final answer. Step 3: Use Few-Shot Prompting for Consistent Style Few-shot prompting means you give AI 2–3 examples of the output you want, then ask for more in the same style. This is incredibly powerful for maintaining a consistent voice.\nYou are a copywriter. I\u0026#39;ll give you examples of our brand\u0026#39;s product descriptions, then write a new one following the same style. Example 1: [PASTE EXISTING PRODUCT DESCRIPTION 1] Example 2: [PASTE EXISTING PRODUCT DESCRIPTION 2] Now write a product description for: [NEW PRODUCT DETAILS] Same style, same length, same tone. Step 4: Add Constraints to Tighten Output Constraints are guardrails that prevent AI from going generic. Think of them as negative instructions — what NOT to do.\nConstraint examples to add to any prompt:\nConstraints: - Do NOT use these words: leverage, synergy, robust, innovative, game-changer - Do NOT use bullet points — write in paragraph form - Do NOT include a disclaimer or caveat at the end - Do NOT exceed 200 words - Start directly with the first sentence — no preamble like \u0026#34;Sure, here\u0026#39;s...\u0026#34; Step 5: Use the Self-Critique Loop for High-Stakes Content For important documents, run a two-step process: generate, then critique and improve.\nStep A: Generate\nWrite a 300-word executive summary for our Q1 investor update. [PASTE FINANCIAL DATA / KEY HIGHLIGHTS] Step B: Critique and Improve\nNow review what you just wrote and identify: 1. Any claims that are too vague or unsupported 2. Any sentences that could be misunderstood 3. Anything that sounds defensive or overly optimistic 4. The weakest sentence in the summary Then rewrite the summary incorporating your improvements. Step 6: Build Your Prompt Template Library Create a reusable library of your best prompts. Use placeholders in brackets for the parts that change.\nUniversal Template Structure:\nYou are [ROLE WITH SPECIFIC EXPERTISE]. Task: [SPECIFIC ACTION VERB] + [DELIVERABLE] Context: - Audience: [WHO WILL READ THIS] - Purpose: [WHY IT\u0026#39;S NEEDED] - Key info: [RELEVANT FACTS/DATA] - Constraints: [WHAT TO AVOID] Output format: - Structure: [PARAGRAPHS / BULLETS / TABLE / LIST] - Length: [WORD COUNT OR TIME TO READ] - Tone: [FORMAL / CASUAL / DIRECT] Pro Tips \u0026amp; Advanced Techniques Common Mistakes to Avoid Mistake 1: Writing prompts like a Google search. AI responds to instructions, not keywords. \u0026ldquo;best marketing strategies\u0026rdquo; is a search query. \u0026ldquo;List the 5 most effective B2B content marketing strategies in 2026, with one real-world example for each\u0026rdquo; is a prompt.\nMistake 2: Asking for multiple different things in one prompt. One prompt = one clear task. If you need a summary AND an action plan AND a risk assessment, either chain three separate prompts or explicitly structure the output into sections.\nMistake 3: Not iterating. The first response is feedback, not a final product. Push back: \u0026ldquo;The tone is too formal — make it sound like a conversation between colleagues.\u0026rdquo; \u0026ldquo;Add 2 more specific examples.\u0026rdquo; \u0026ldquo;Shorten the third section.\u0026rdquo;\nMistake 4: Forgetting to specify audience. AI doesn\u0026rsquo;t know if you\u0026rsquo;re writing for a 14-year-old or a PhD. Always say: \u0026ldquo;Write this for [AUDIENCE DESCRIPTION].\u0026rdquo;\nMistake 5: Accepting vague outputs. If the answer is vague, the problem is always the prompt. Ask yourself: \u0026ldquo;What specific detail did I leave out that would have made this better?\u0026rdquo;\nPower User Strategies Strategy 1: Use meta-prompting. Ask AI to write a better version of your prompt: \u0026ldquo;Here\u0026rsquo;s my prompt: [PASTE IT]. How could I rewrite it to get a better response?\u0026rdquo;\nStrategy 2: Create a system prompt for each major project. Set up a long \u0026ldquo;context dump\u0026rdquo; at the start of a session with everything the AI needs to know about your project. Then all subsequent prompts are short and focused.\nStrategy 3: Use \u0026ldquo;What else?\u0026rdquo; loops. After any brainstorm or list, ask: \u0026ldquo;What did you leave out? What\u0026rsquo;s a contrarian view? What would a skeptic say?\u0026rdquo; This surfaces ideas AI initially deprioritized.\nStrategy 4: Calibrate specificity to stakes. Low-stakes tasks (summarizing a meeting) need minimal prompting. High-stakes tasks (investor deck, job application, client proposal) deserve a fully structured prompt with constraints and a review loop.\nStrategy 5: Build domain-specific templates. Create a \u0026ldquo;legal email\u0026rdquo; template, a \u0026ldquo;technical documentation\u0026rdquo; template, a \u0026ldquo;sales pitch\u0026rdquo; template. Domain-specific templates consistently outperform generic prompts.\nRelated: Best ChatGPT Prompts for Productivity 2026 Frequently Asked Questions Q: Do I need technical knowledge to do prompt engineering? A: None. Prompt engineering is about clear thinking and clear writing — skills any professional already has. The technical side (tokens, temperature settings) matters for developers building AI products, not for everyday users.\nQ: Does prompt engineering work the same on all AI models? A: The principles are universal — role-setting, context, format, constraints — but different models have different strengths. Claude tends to respond very well to detailed context. ChatGPT tends to respond well to structured formatting. Experiment with each.\nQ: How do I know if my prompt is good? A: A good prompt produces output you can use with minimal editing. If you spend more time editing than you would have spent writing, your prompt needs more specificity.\nQ: What\u0026rsquo;s the difference between prompt engineering and fine-tuning? A: Prompt engineering is what you do as a user — writing better instructions. Fine-tuning is when developers train a model on specific data to improve its performance. Prompt engineering requires no code; fine-tuning does.\nQ: Is prompt engineering a career? A: It\u0026rsquo;s a component of many AI/ML and product roles, but the \u0026ldquo;Prompt Engineer\u0026rdquo; job title is becoming less common as the skill becomes mainstream. It\u0026rsquo;s better to think of it as a multiplier for whatever career you already have.\nQ: How long does it take to get good at prompt engineering? A: Most people see meaningful improvement within 3–5 hours of deliberate practice. Mastery comes over weeks of daily use. The fastest path: pick one task you do daily and spend a week refining the perfect prompt for it.\nQ: Are there any free resources to practice? A: Yes — the free tiers of ChatGPT, Claude, and Gemini are all you need to practice. The skills transfer across tools. Practice with real work tasks for the fastest learning curve.\nQ: What\u0026rsquo;s the single most impactful prompt tip? A: Add \u0026ldquo;Think step by step\u0026rdquo; to any complex question. This single addition improves reasoning quality on almost every AI model, for free.\nTurn AI Skills Into Career Opportunities Prompt engineering and AI fluency are becoming some of the most valued skills in the job market. Find your next career on doda — Japan\u0026rsquo;s leading job platform where tech and AI roles are growing fast.\nConclusion \u0026amp; Call to Action Prompt engineering isn\u0026rsquo;t a technical skill reserved for AI researchers. It\u0026rsquo;s a practical, learnable craft that pays off from day one. The six frameworks in this guide — Role+Task+Format, Chain-of-Thought, Few-Shot, Constraints, Self-Critique, and Templates — cover 95% of everything you\u0026rsquo;ll need as a professional.\nKey takeaways:\nA prompt has four pillars: Role, Task, Context, and Format — use all four for complex tasks Chain-of-thought (\u0026ldquo;think step by step\u0026rdquo;) dramatically improves accuracy on complex problems Few-shot examples are the fastest way to get consistent output style Constraints (what NOT to do) are as important as instructions Build a personal prompt library — it\u0026rsquo;s the highest-ROI investment in your AI workflow Ready to skip the learning curve? Our Complete ChatGPT Prompt Collection gives you 100+ professionally engineered prompts, pre-built with all four pillars and tested across real work scenarios. Copy, customize, and use immediately.\nWant a complete framework for integrating AI into your daily work? The AI Productivity Playbook includes a full prompt engineering module plus workflow templates for every major knowledge work scenario.\nRelated: Claude AI vs ChatGPT Comparison 2026 Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nRelated Tools Format and validate JSON data → JSON Formatter Test regular expressions in real-time → Regex Tester Create a monthly budget → Budget Planner Calculate your ideal freelance rate → Freelance Rate Calculator Related Templates Put these techniques into practice with our ready-made templates:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Copy-paste prompts for every situation The AI Productivity Playbook 2026 — 50+ AI workflows and automation strategies ","permalink":"https://productivity-works.com/posts/ai-prompt-engineering-tips-for-beginners-2026/","summary":"\u003ch1 id=\"ai-prompt-engineering-tips-for-beginners--complete-guide-2026\"\u003eAI Prompt Engineering Tips for Beginners — Complete Guide [2026]\u003c/h1\u003e\n\u003cp\u003eYou\u0026rsquo;ve been using ChatGPT or Claude for a while, and you keep getting answers that are\u0026hellip; fine. Generic. Not quite what you needed. You tweak the question, try again, get something slightly better, and wonder if you\u0026rsquo;re doing this right.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s the truth: 90% of disappointing AI responses are caused by the prompt, not the AI. Prompt engineering — the art of writing instructions that get great results from AI — is the highest-leverage skill in tech right now. And unlike coding or data science, you can get genuinely good at it in a weekend.\u003c/p\u003e","title":"AI Prompt Engineering Tips for Beginners 2026"},{"content":"Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nBest Job Search Sites 2026 — A Practical Guide to Finding Work That Pays Job searching in 2026 is simultaneously easier and harder than ever. Easier because there are more platforms, more remote options, and more transparency around compensation. Harder because every job posting receives hundreds of applications, AI screening filters out qualified candidates, and the best opportunities are often never publicly posted.\nThis guide covers where to actually find good jobs — not just any jobs, but positions worth applying to. We\u0026rsquo;ll explain which platforms work for which career stages, how to use each effectively, and which ones are worth your time.\nThe Job Search Landscape in 2026 Platform Type Best For Examples General job boards Maximum job volume, entry-mid level Indeed, LinkedIn, Glassdoor Company career pages Applying directly (bypasses aggregators) Company websites Niche job boards Specific industries or job types We Work Remotely, AngelList, Dice Recruiter platforms Passive job seekers, senior roles LinkedIn Recruiter, Hired Networking Hidden job market (60-70% of jobs) LinkedIn connections, industry events Best General Job Search Sites 1. LinkedIn — Best for Professional Networking + Job Search Feature Details Jobs posted 15M+ active listings globally Cost Free (Premium: $29.99/month for job seekers) Best feature \u0026ldquo;Open to Work\u0026rdquo; flag + InMail from recruiters ATS integration Most companies link LinkedIn profiles to applications Why it\u0026rsquo;s #1: LinkedIn is where recruiters live. Over 87% of recruiters use LinkedIn to find candidates. A strong LinkedIn profile is more important than a resume in many industries.\nHow to use it effectively:\nSet \u0026ldquo;Open to Work\u0026rdquo; (visible to recruiters only, not your current employer) Engage with industry content — comments and posts increase profile visibility by 5-10x Use the \u0026ldquo;Easy Apply\u0026rdquo; feature strategically (not for every job — it creates low-quality applications) Connect with hiring managers directly before applying Premium worth it? For active job seekers, yes. The ability to see who viewed your profile and send InMail to hiring managers pays for itself if it lands one interview.\n2. Indeed — Largest Job Board by Volume Feature Details Jobs posted 300M+ listings (aggregated from multiple sources) Cost Free for job seekers Best feature Salary estimates, company reviews, \u0026ldquo;Urgently Hiring\u0026rdquo; tags Resume posting Indeed Resume gets your profile in front of employers Best for: Casting a wide net. Indeed aggregates listings from company websites, other job boards, and direct postings — it has the largest single collection of job listings.\nLimitation: Quality varies widely. Many listings are duplicates or outdated. Use filters aggressively (date posted, salary range, remote/hybrid).\n3. Glassdoor — Best for Company Research Feature Details Jobs posted 2M+ active listings Cost Free Best feature Employee reviews, salary data, interview questions Unique value See what current employees say before you apply Best for: Due diligence. Before applying anywhere, check Glassdoor for salary ranges, interview process details, and honest employee reviews. A company with a 2.5-star rating is telling you something.\nBest Remote Job Boards We Work Remotely — Largest Remote-Only Board Feature Details Focus 100% remote positions Industries Tech, design, marketing, customer support, management Cost Free for job seekers Quality Higher than general boards — companies pay $299+ to post Best for: Remote-first job seekers in tech and digital roles. Because companies pay to post, the listing quality is significantly higher than free boards.\nFlexJobs — Vetted Remote \u0026amp; Flexible Jobs Feature Details Focus Remote, hybrid, and flexible schedule jobs Cost $9.95/week or $24.95/month Quality Every listing is human-screened for legitimacy Scam protection Zero scam listings (the paid model funds manual vetting) Best for: People who want guaranteed legitimate remote opportunities. The subscription fee eliminates scams, which plague free remote job boards.\nRemote.co — Curated Remote Opportunities Feature Details Focus Remote jobs with company culture profiles Cost Free Quality Curated, smaller selection but higher quality Best Niche Job Boards Industry/Niche Best Platform Why Tech/Engineering Dice, HackerNews \u0026ldquo;Who\u0026rsquo;s Hiring\u0026rdquo; Technical roles, salary transparency Startups Wellfound (AngelList Talent) Equity packages, startup culture Design Dribbble Jobs, Behance Portfolio-integrated applications Writing/Content Contently, MediaBistro Content and editorial roles Non-profit Idealist Mission-driven organizations Government USAJobs Federal positions with benefits Freelance/Contract Toptal, Upwork Pro High-quality freelance projects The Hidden Job Market: How to Access Unadvertised Positions 60-70% of jobs are never publicly posted. They\u0026rsquo;re filled through:\nInternal referrals — Ask your network: \u0026ldquo;I\u0026rsquo;m looking for [role type]. Do you know anyone hiring?\u0026rdquo; Recruiter outreach — Maintain an updated LinkedIn profile and respond to recruiter messages Informational interviews — \u0026ldquo;I\u0026rsquo;d love to learn about what your team does\u0026rdquo; conversations that lead to opportunities Company career pages — Some roles are posted on company sites weeks before hitting job boards Industry events and communities — Slack groups, conferences, meetups AI can help here too:\nUse ChatGPT to draft personalized outreach messages to hiring managers Use AI to research companies and identify potential opportunities before they\u0026rsquo;re posted Use AI to prepare for informational interviews with targeted questions Related: How to Create a Resume with AI Step by Step How to Optimize Your Job Search The Multi-Platform Strategy Don\u0026rsquo;t rely on a single platform. Here\u0026rsquo;s the optimal approach:\nPlatform Purpose Time Investment LinkedIn Networking + recruiter visibility 20 min/day Indeed Volume applications for target roles 30 min/day 1-2 niche boards Industry-specific opportunities 15 min/day Company career pages Direct applications to top-choice employers 15 min/day Application Quality vs. Quantity The math: Sending 100 generic applications produces worse results than sending 20 targeted ones.\nFor each application:\nTailor your resume to match the job description keywords (ATS optimization) Write a 3-sentence cover note (not a full letter — hiring managers don\u0026rsquo;t read them) Find the hiring manager on LinkedIn and send a brief connection request Follow up 5-7 days after applying AI-Powered Job Search in 2026 Tool Use Case ChatGPT/Claude Resume tailoring, cover letter drafting, interview prep AI resume builders ATS-optimized formatting LinkedIn AI features Job match recommendations, profile optimization suggestions AI interview prep tools Practice with AI mock interviews Related: Best ChatGPT Prompts for Productivity 2026 Frequently Asked Questions Q: How many jobs should I apply to per week? Quality over quantity. 10-15 well-targeted applications per week is more effective than 50 spray-and-pray submissions. Each application should be customized.\nQ: Should I pay for job search platforms? LinkedIn Premium ($30/month) is worth it during an active search. FlexJobs ($25/month) is worth it if you want vetted remote jobs. Everything else you need is free.\nQ: How long should a job search take? Average is 3-6 months. Senior roles take longer (4-8 months). The timeline shortens dramatically with networking and referrals.\nQ: Is it better to apply through job boards or company websites? Company websites when possible — your application goes directly into their ATS without competing with job board aggregation. Use job boards for discovery, then apply directly.\nQ: Do cover letters still matter? Less than before, but a brief, specific cover note (3-4 sentences about why this role + why this company) can differentiate you. Never send a generic cover letter — it\u0026rsquo;s worse than none.\nConclusion The best job search strategy in 2026 combines:\nLinkedIn for visibility and networking (non-negotiable) One general board (Indeed or Glassdoor) for volume One niche board matched to your industry Direct company applications for your top 5-10 target employers Networking for the hidden 60-70% of opportunities Start with LinkedIn optimization today — it\u0026rsquo;s the single highest-ROI action in any job search.\nSearching for work in Japan? doda is one of Japan\u0026rsquo;s largest job platforms, offering bilingual listings and dedicated agent support for English-speaking professionals — useful whether you\u0026rsquo;re navigating a first Japanese job search or making a mid-career pivot.\nRelated: AI Tools for Freelancers to Earn More 2026 Related Tools \u0026amp; Articles Compare salary offers → Salary Calculator Plan your budget during job transitions → Budget Calculator Convert hourly wage to salary → Hourly to Salary Calculator AI Resume Optimization for Job Search 2026 Create a Resume with AI Step by Step ChatGPT Resume Tips for Career Changers Over 50 Salary Negotiation in Japan: Switching Jobs Guide How to Start Freelancing in 2026 Related Templates Supercharge your job search with AI:\nAI Resume Toolkit — 25 tested ChatGPT prompts for resumes + ATS keyword guide The AI Productivity Playbook 2026 — Complete AI workflow for career development ","permalink":"https://productivity-works.com/posts/best-job-search-sites-2026/","summary":"\u003cp\u003e\u003cem\u003eDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"best-job-search-sites-2026--a-practical-guide-to-finding-work-that-pays\"\u003eBest Job Search Sites 2026 — A Practical Guide to Finding Work That Pays\u003c/h1\u003e\n\u003cp\u003eJob searching in 2026 is simultaneously easier and harder than ever. Easier because there are more platforms, more remote options, and more transparency around compensation. Harder because every job posting receives hundreds of applications, AI screening filters out qualified candidates, and the best opportunities are often never publicly posted.\u003c/p\u003e","title":"Best Job Search Sites 2026: Where to Actually Find Good Jobs"},{"content":"Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nBest Cell Phone Plans 2026 — Stop Overpaying for Your Phone Bill The average American spends $144/month on their cell phone plan. Most of them are overpaying by 50-70%. The three major carriers (Verizon, AT\u0026amp;T, T-Mobile) have spent billions on marketing to convince you that their premium plans are worth it. For most people, they\u0026rsquo;re not.\nIn 2026, prepaid carriers and MVNOs (Mobile Virtual Network Operators) use the exact same cell towers as Verizon, AT\u0026amp;T, and T-Mobile — at half the price or less. This guide shows you exactly where the best value is.\nHow Cell Phone Plans Actually Work Plan Type Price Range Network Priority Examples Postpaid (major carrier) $70-$100/line Own towers Highest Verizon, AT\u0026amp;T, T-Mobile Carrier prepaid $25-$50/line Same towers Medium Verizon Prepaid, AT\u0026amp;T Prepaid MVNO $15-$45/line Leased towers Lower (rarely matters) Mint, Visible, US Mobile Key insight: MVNOs use the same towers. \u0026ldquo;Lower priority\u0026rdquo; means during extreme network congestion (think major sporting events), your data might slow down temporarily. For 95%+ of the time, the experience is identical.\nBest Plans by Category Best Overall Value: Mint Mobile Feature Details Price $15/mo (5GB), $20/mo (15GB), $25/mo (20GB), $30/mo (unlimited) Network T-Mobile Contract Prepaid, purchased in 3/6/12-month blocks Perks Free international calls to 30+ countries, mobile hotspot Why it wins: $15/month for 5GB on T-Mobile\u0026rsquo;s network is the best value in wireless. The unlimited plan at $30/month is less than half of T-Mobile\u0026rsquo;s own postpaid equivalent. You pay upfront for 3-12 months, which is how they keep prices low.\nTrade-off: Must pay for multiple months upfront. No financing for phones.\nBest Unlimited Plan: Visible (by Verizon) Feature Details Price $25/mo (Visible) or $45/mo (Visible+) Network Verizon Data Truly unlimited (no caps, no throttling on Visible+) Perks Unlimited mobile hotspot, Apple Watch connectivity (Visible+) Why it wins: $25/month for unlimited data on Verizon\u0026rsquo;s network with no annual contract. Visible+ at $45/month includes priority data (same priority as Verizon postpaid customers), 50GB premium hotspot, and international calling.\nBest Family Plan: T-Mobile Essentials Feature Details Price $60/line (1 line), $30/line (4+ lines) Network T-Mobile (postpaid) Data Unlimited Perks Netflix Basic included (2+ lines), international texting Why it wins: At 4+ lines, $30/person for unlimited everything on T-Mobile\u0026rsquo;s postpaid network with Netflix included is hard to beat. Autopay saves an additional $5/line.\nBest Budget Plan: Tello Feature Details Price Starting at $5/mo (100MB + 100 min) Network T-Mobile Customization Build your own plan: choose data, minutes, and texts separately Contract Month-to-month, no commitment Why it wins: Tello lets you pay for exactly what you use. If you primarily use WiFi and barely need data, a $10/month plan with 1GB and unlimited talk/text is perfect. No other carrier offers this level of customization at these prices.\nBest for International Use: Google Fi Feature Details Price $20/mo (unlimited calls/texts) + $10/GB (flexible) or $65/mo (unlimited) Network T-Mobile + US Cellular (automatically switches) International Data works in 200+ countries at no extra charge Perks VPN included, spam call filtering Why it wins: If you travel internationally, Google Fi is unmatched. Your plan works in 200+ countries with no roaming charges. The flexible plan charges only for data used, making it ideal for WiFi-heavy users.\nSavings Calculator Scenario Major Carrier MVNO Alternative Annual Savings Single line, 5GB $70/mo (Verizon) $15/mo (Mint) $660/year Single line, unlimited $80/mo (T-Mobile) $25/mo (Visible) $660/year Family of 4, unlimited $140/mo (AT\u0026amp;T) $100/mo (Mint family) $480/year Couple, mixed use $160/mo (Verizon) $50/mo (Mint+Visible) $1,320/year A family of 4 can save $500-$1,300/year by switching. Over 5 years, that\u0026rsquo;s $2,500-$6,500 — enough for a vacation or a solid investment portfolio start.\nHow to Switch (Takes 20 Minutes) Check your phone\u0026rsquo;s compatibility — Most phones from 2020+ work with all carriers. Check IMEI at the new carrier\u0026rsquo;s website Get your account number and PIN from your current carrier (call or check your online account) Sign up with the new carrier online — Choose your plan, enter your current phone number to port Receive your SIM/eSIM — Physical SIM arrives in 2-3 days. eSIM activates in minutes Activate — Follow the setup instructions. Your number transfers automatically Your old plan cancels automatically when the number ports out No need to call your old carrier to cancel. The number port triggers automatic cancellation.\nQuick Decision Guide Your Situation Best Plan Want the cheapest possible Tello ($5-10/mo) Light data user (\u0026lt; 5GB) Mint Mobile ($15/mo) Heavy data user Visible ($25/mo unlimited) Family of 3+ T-Mobile Essentials ($30/line) International traveler Google Fi ($20/mo + data) Need Verizon network Visible ($25/mo) Need T-Mobile network Mint Mobile ($15-30/mo) Need AT\u0026amp;T network Cricket Wireless ($30-55/mo) Frequently Asked Questions Q: Will I lose my phone number? No. Number porting is a legal right in the US. Your number transfers seamlessly to the new carrier.\nQ: Are MVNOs really as good as major carriers? For 95%+ of use cases, yes. They use the exact same cell towers. The only difference is data priority during extreme congestion, which most people never notice.\nQ: Can I keep my current phone? Almost always yes, if your phone is unlocked and less than 5 years old. Phones purchased after 2020 are generally compatible with all major networks.\nQ: What\u0026rsquo;s the catch with cheap plans? No catch — MVNOs save money by eliminating retail stores, not subsidizing phones, and buying network capacity in bulk. You get the same network, minus the overhead.\nQ: What about 5G? Most MVNOs include 5G access at no extra cost. Mint, Visible, and US Mobile all offer 5G where available on their respective networks.\nConclusion Overpaying for cell phone service is one of the easiest expenses to cut. The switch takes 20 minutes, and the savings are $500-$1,300 per year for a typical household.\nStart with Mint Mobile ($15/mo) if you want the best value, or Visible ($25/mo) if you need truly unlimited data. Both use major carrier networks and deliver the same real-world experience at a fraction of the price.\nPut the savings toward your emergency fund, investments, or something more enjoyable than a phone bill.\nRelated: Best Budgeting Apps 2026 Related: How to Build an Emergency Fund Fast Related Tools Check your BMI and healthy weight range → BMI Calculator Create a monthly budget → Budget Planner Set a savings goal → Savings Goal Calculator Related Templates Save more money with AI-powered financial tools:\nChatGPT Prompt Templates — Including budget optimization prompts The AI Productivity Playbook 2026 — Automate your finances and save time ","permalink":"https://productivity-works.com/posts/best-cell-phone-plans-2026/","summary":"\u003cp\u003e\u003cem\u003eDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"best-cell-phone-plans-2026--stop-overpaying-for-your-phone-bill\"\u003eBest Cell Phone Plans 2026 — Stop Overpaying for Your Phone Bill\u003c/h1\u003e\n\u003cp\u003eThe average American spends $144/month on their cell phone plan. Most of them are overpaying by 50-70%. The three major carriers (Verizon, AT\u0026amp;T, T-Mobile) have spent billions on marketing to convince you that their premium plans are worth it. For most people, they\u0026rsquo;re not.\u003c/p\u003e","title":"Best Cell Phone Plans 2026: Save $50+/Month on Your Bill"},{"content":"This article contains affiliate links.\nYou just received a job offer in Japan. The salary looks great on paper — maybe ¥5,000,000 or ¥6,000,000 a year. But when your first payslip arrives, the number in your bank account looks noticeably smaller. Where did the rest go?\nThis is one of the most common surprises for expats and foreign workers in Japan. Japan\u0026rsquo;s payroll deduction system is thorough, with multiple layers of tax and social insurance contributions taken out before you ever see your money. Understanding exactly what gets deducted — and why — helps you budget accurately, plan your finances, and find legitimate ways to reduce your tax burden.\nThis guide walks you through every major deduction in Japan\u0026rsquo;s salary system for 2026, with a concrete worked example using a ¥5,000,000 annual salary. By the end, you will know how to estimate your actual take-home pay with reasonable accuracy.\nJapan\u0026rsquo;s Salary Deduction System Overview Japan operates a withholding tax system (源泉徴収, gensen choshu). Your employer calculates and deducts most taxes and social insurance premiums directly from your monthly salary before it is deposited into your account. You rarely need to file a separate tax return if you are a standard company employee — your employer handles it through the year-end tax adjustment (年末調整, nenmatsu chosei).\nThere are five main categories of deductions to understand.\n1. Income Tax (所得税, Shotokuzei) Income tax is a national tax levied by the Japanese government. The rates are progressive, meaning higher earnings are taxed at higher marginal rates. The tax is calculated not on your gross salary but on your taxable income, which is your gross salary minus various deductions (the employment income deduction, personal deduction, dependent deductions, social insurance premium deduction, and others).\nThe 2026 income tax brackets for employment income are:\nTaxable Income (after deductions) Tax Rate Deduction Amount Up to ¥1,950,000 5% ¥0 ¥1,950,001 – ¥3,300,000 10% ¥97,500 ¥3,300,001 – ¥6,950,000 20% ¥427,500 ¥6,950,001 – ¥9,000,000 23% ¥636,000 ¥9,000,001 – ¥18,000,000 33% ¥1,536,000 ¥18,000,001 – ¥40,000,000 40% ¥2,796,000 Over ¥40,000,000 45% ¥4,796,000 Note: A 2.1%復興特別所得税 (reconstruction special income tax) surcharge is added to the base income tax amount. This surcharge has been in place since 2013 following the Great East Japan Earthquake and continues through 2037.\n2. Residence Tax (住民税, Juuminzei) Residence tax is a local tax paid to the municipality (city or ward) and prefecture where you live. Unlike income tax, it is assessed on the previous year\u0026rsquo;s income and collected from June of the following year. This creates an important timing effect for newcomers: in your first year working in Japan, you typically pay no residence tax because you had no prior-year Japan income. From your second year onward, expect a significant deduction.\nThe standard rate is approximately 10% of taxable income, split between the municipal portion (6%) and the prefectural portion (4%). A small per-capita levy (均等割, kinto wari) of roughly ¥5,000–¥6,000 per year is also added regardless of income level.\n3. Health Insurance (健康保険, Kenko Hoken) Most company employees in Japan join their employer\u0026rsquo;s health insurance scheme, either through a society-managed health insurance association (健康保険組合, kenko hoken kumiai) or the national Japan Health Insurance Association (協会けんぽ, kyokai kenpo). Premiums are calculated as a percentage of your standardized monthly salary (標準報酬月額, hyojun hoshu getsugaku).\nThe employee share of the premium under kyokai kenpo varies by prefecture. For Tokyo in 2026, the total premium rate is approximately 9.98% of the standardized monthly salary, split roughly equally between employer and employee. The employee pays approximately 4.99%.\nA separate long-term care insurance premium (介護保険, kaigo hoken) applies to employees aged 40–64, adding roughly an additional 0.9% employee contribution.\n4. Pension Insurance (厚生年金保険, Kosei Nenkin Hoken) Employees enrolled in the corporate pension system (厚生年金) pay a fixed rate regardless of prefecture. The total premium rate is 18.3% of the standardized monthly salary, split equally between employee and employer. The employee contribution is 9.15%.\nThis is one of the largest deductions on your payslip. However, it is not money lost — it is a contribution toward your future pension entitlement. As a foreign worker, you may also be able to claim a lump-sum withdrawal (脱退一時金, dattai ichijikin) of a portion of your contributions when you leave Japan permanently, depending on your country of origin\u0026rsquo;s social security agreement with Japan.\n5. Employment Insurance (雇用保険, Koyo Hoken) Employment insurance is Japan\u0026rsquo;s unemployment benefit system. The employee contribution rate for general workers in 2026 is 0.6% of total wages. This is a relatively minor deduction but still worth accounting for.\nStep-by-Step Take-Home Pay Calculation Let\u0026rsquo;s work through a concrete example using a ¥5,000,000 annual gross salary for a single person in Tokyo with no dependents, aged under 40.\nAssumptions:\nGross annual salary: ¥5,000,000 Monthly gross salary: approximately ¥416,667 Tokyo resident, kyokai kenpo member Single, no dependents, under 40 years old Full year of employment (residence tax applied) Step 1: Social Insurance Premiums Social insurance premiums are calculated on the standardized monthly salary. For a monthly gross of approximately ¥416,667, the standardized monthly salary grade is ¥410,000.\nDeduction Rate (employee) Monthly Amount Annual Amount Health Insurance (Tokyo, kyokai kenpo) 4.99% ¥20,459 ¥245,508 Pension Insurance 9.15% ¥37,515 ¥450,180 Employment Insurance 0.6% ¥2,500 ¥30,000 Total Social Insurance ¥60,474 ¥725,688 Step 2: Employment Income Deduction (給与所得控除) Japan allows a deduction against gross employment income before calculating taxable income. For a ¥5,000,000 gross salary, the employment income deduction in 2026 is:\n¥1,440,000 (fixed deduction for incomes between ¥3,600,001 and ¥6,600,000: 10% of income + ¥1,100,000, or use the statutory table)\nFor ¥5,000,000: (¥5,000,000 × 20%) − ¥540,000 = ¥460,000\nWait — let me apply the correct 2026 formula:\nFor gross salary between ¥1,800,001 and ¥3,600,000: 30% of gross − ¥108,000 For gross salary between ¥3,600,001 and ¥6,600,000: 20% of gross + ¥54,000 For gross salary between ¥6,600,001 and ¥8,500,000: 10% of gross + ¥714,000 For gross salary over ¥8,500,000: flat ¥1,950,000 (cap) For ¥5,000,000: (¥5,000,000 × 20%) + ¥54,000 = ¥1,000,000 + ¥54,000 = ¥1,054,000\nEmployment income (給与所得) = ¥5,000,000 − ¥1,054,000 = ¥3,946,000\nStep 3: Calculate Taxable Income for Income Tax From employment income, subtract:\nSocial insurance premiums paid: ¥725,688 Basic personal deduction (基礎控除): ¥480,000 (for income under ¥24,000,000) Taxable income = ¥3,946,000 − ¥725,688 − ¥480,000 = ¥2,740,312 (rounded down to ¥2,740,000)\nStep 4: Calculate Income Tax Taxable income of ¥2,740,000 falls in the 10% bracket (¥1,950,001–¥3,300,000):\nIncome tax = (¥2,740,000 × 10%) − ¥97,500 = ¥274,000 − ¥97,500 = ¥176,500\nAdd 2.1% reconstruction surcharge: ¥176,500 × 1.021 = ¥180,207 (rounded)\nAnnual income tax: approximately ¥180,000\nStep 5: Calculate Residence Tax Residence tax is based on the previous year\u0026rsquo;s taxable income, but for our projection we use the same taxable income:\nMunicipal + prefectural rate: 10% of taxable income + per-capita levy = (¥2,740,000 × 10%) + ¥5,500 = ¥274,000 + ¥5,500 = ¥279,500\nAnnual residence tax: approximately ¥280,000\nStep 6: Total Deductions and Take-Home Pay Item Annual Amount Gross Salary ¥5,000,000 − Health Insurance ¥245,508 − Pension Insurance ¥450,180 − Employment Insurance ¥30,000 − Income Tax (approx.) ¥180,000 − Residence Tax (approx.) ¥280,000 = Estimated Take-Home Pay ¥1,814,312 \u0026hellip; Wait — let\u0026rsquo;s add this up correctly:\nTotal deductions = ¥245,508 + ¥450,180 + ¥30,000 + ¥180,000 + ¥280,000 = ¥1,185,688\nEstimated annual take-home pay = ¥5,000,000 − ¥1,185,688 = ¥3,814,312\nEstimated monthly take-home pay = approximately ¥317,859\nThat means on a ¥5,000,000 gross salary, you take home roughly 76.3% of your gross pay, or about ¥318,000 per month. The effective deduction rate is approximately 23.7%.\nFor instant, accurate results tailored to your exact salary and situation, use our Take-Home Pay Calculator .\nHow Income Tax Brackets Work in Japan Japan\u0026rsquo;s income tax system is marginal, which means only the portion of income that falls within each bracket is taxed at that bracket\u0026rsquo;s rate. A common misconception is that if your taxable income crosses into a higher bracket, all of your income is taxed at the new rate. This is not how it works.\nHere is an example. Suppose your taxable income is ¥3,500,000:\nThe first ¥1,950,000 is taxed at 5% = ¥97,500 The remaining ¥1,550,000 (¥3,500,000 − ¥1,950,000) is taxed at 10% = ¥155,000 Total income tax before surcharge = ¥97,500 + ¥155,000 = ¥252,500 Or using the bracket formula shortcut: (¥3,500,000 × 10%) − ¥97,500 = ¥350,000 − ¥97,500 = ¥252,500. The deduction amount in the formula accounts for the lower-bracket tax already applied to the portion below the threshold.\nFull 2026 Income Tax Bracket Reference Table Taxable Income Range Marginal Rate Formula (rate × income − adjustment) Up to ¥1,950,000 5% Income × 5% ¥1,950,001 – ¥3,300,000 10% Income × 10% − ¥97,500 ¥3,300,001 – ¥6,950,000 20% Income × 20% − ¥427,500 ¥6,950,001 – ¥9,000,000 23% Income × 23% − ¥636,000 ¥9,000,001 – ¥18,000,000 33% Income × 33% − ¥1,536,000 ¥18,000,001 – ¥40,000,000 40% Income × 40% − ¥2,796,000 Over ¥40,000,000 45% Income × 45% − ¥4,796,000 The effective (average) income tax rate is always lower than the marginal rate because lower portions of income are taxed at lower rates. For most mid-range salaries in Japan, effective income tax rates before residence tax tend to fall in the 5–15% range.\nWhat About Bonus Pay? Bonuses in Japan are common, with many companies paying summer (夏季賞与, kaki shoyo) and winter (冬季賞与, toki shoyo) bonuses typically in June/July and December, respectively. Understanding how bonuses are taxed prevents unpleasant surprises.\nSocial insurance on bonuses is calculated at the same pension and health insurance rates as regular salary, applied to the bonus amount directly (with a cap on the annual bonus amount used for pension calculations at ¥5,730,000 per year).\nIncome tax on bonuses uses a different withholding method. Your employer looks at the income tax withheld from the previous month\u0026rsquo;s salary, determines the applicable withholding rate from a bonus withholding table, and applies that rate to the gross bonus amount after social insurance deductions.\nThe key point: bonuses do not push you into a higher tax bracket during the payment month. Your overall annual income tax is reconciled during the year-end tax adjustment (年末調整), so if too much was withheld throughout the year, you receive a refund in December.\nPractical example: A ¥500,000 summer bonus on a ¥5,000,000 base salary might see deductions of approximately:\nPension: ¥45,750 (9.15%) Health insurance: ¥24,950 (4.99%) Employment insurance: ¥3,000 (0.6%) Income tax withholding: approximately ¥30,000–¥50,000 (varies by previous month\u0026rsquo;s salary) Leaving a net bonus of roughly ¥376,000–¥396,000 out of ¥500,000 gross.\nSide Income and Extra Tax Obligations Many workers in Japan supplement their main salary with freelance work, online businesses, rental income, investment gains, or other side income. The Japanese tax system has specific rules for this.\nThe ¥200,000 rule: If your side income (副業所得, fukugyo shotoku) totals more than ¥200,000 per year, you are required to file a final tax return (確定申告, kakutei shinkoku) with the tax office by March 15 of the following year. Your employer\u0026rsquo;s year-end adjustment only covers your employment income; side income must be declared separately.\nHow side income is taxed: Side income is added to your total income for the year and taxed at your marginal income tax rate. This means if you are already in the 20% bracket on your employment income, additional side income will also be taxed at 20% or higher. You may also owe additional residence tax, which is assessed in the following June.\nBusiness expense deductions: If your side income is from freelancing or a small business, you can deduct legitimate business expenses (equipment, software subscriptions, internet costs used for the business, etc.) to reduce your taxable side income. Keeping clean records is essential.\nCrypto and investment income: Capital gains from cryptocurrency are taxed as miscellaneous income (雑所得) at your marginal rate. Gains from stock trading in non-NISA accounts are typically taxed at a flat 20.315% (15% income tax + 2.1% surcharge + 3% residence tax equivalent) if you use a specific account type (申告分離課税).\nTo accurately track and calculate your side income taxes, use our Side Income Tax Calculator .\nWhen your side income grows to the point where you need to manage invoices, expenses, and tax filings, freee is Japan\u0026rsquo;s most popular cloud accounting software. It handles invoice creation, expense tracking, and tax return preparation in Japanese — essential if you are navigating the Japanese tax system as a freelancer or small business owner.\nTips to Maximize Your Take-Home Pay Japan offers several government-backed programs that can legitimately reduce your tax burden and increase your effective take-home pay. Here are the most impactful ones for employed workers.\niDeCo (Individual-type Defined Contribution Pension) iDeCo (イデコ) is a self-directed pension plan where your contributions are fully deductible from taxable income. For an employee already enrolled in a corporate pension plan, you can contribute up to ¥12,000 per month (¥144,000 per year). That ¥144,000 annual contribution directly reduces your taxable income, saving you income tax at your marginal rate plus residence tax.\nAt a 20% income tax rate + 10% residence tax, a ¥144,000 iDeCo contribution saves approximately ¥43,200 per year in taxes. The money grows tax-free until withdrawal at age 60 or later.\nThe trade-off is that iDeCo funds are locked until retirement age, so only contribute what you can genuinely set aside long-term.\nNISA (Nippon Individual Savings Account) NISA is Japan\u0026rsquo;s version of a tax-free investment account. Under the reformed NISA system launched in 2024 and continuing in 2026, you can invest up to ¥3,600,000 per year (¥300,000/month) with capital gains and dividends completely tax-free indefinitely. The lifetime investment limit is ¥18,000,000.\nUnlike iDeCo, NISA contributions are not deductible from taxable income — the tax benefit comes on the way out, with no tax on gains or dividends. NISA is ideal for medium-to-long-term savings goals, not just retirement.\nSee how much your investments could grow with our NISA Simulator .\nFurusato Nozei (故郷納税 — Hometown Tax Donation) Furusato nozei is a system that lets you redirect a portion of your residence tax to municipalities across Japan in exchange for local specialty goods as thank-you gifts. In 2026, the effective personal cost is just ¥2,000 regardless of how much you donate (up to your applicable limit).\nFor a ¥5,000,000 salary earner, the furusato nozei limit is approximately ¥60,000–¥70,000 per year. Donating that amount earns you local food products, crafts, or other goods worth a significant portion of the donation, while your total tax bill remains essentially the same (your residence tax is reduced by the donated amount, minus the ¥2,000 personal cost).\nYou must either use the one-stop special procedure (ワンストップ特例) or file a tax return to claim the residence tax deduction.\nLife Insurance and Medical Deductions Premiums paid for qualifying life insurance (生命保険料控除) and medical expenses over ¥100,000 (医療費控除) can be deducted from taxable income, though you typically need to file a tax return to claim medical expense deductions.\nConclusion Japan\u0026rsquo;s deduction system can feel complex at first, but once you understand the structure, it becomes predictable. The main deductions to plan around for most employed workers are:\nPension (9.15% employee share) — the largest single deduction Health insurance (~5% employee share) — varies slightly by prefecture and association Income tax (5–45% marginal, but typically 5–20% effective for mid-range incomes) Residence tax (~10% of taxable income, starting from your second year in Japan) Employment insurance (0.6%) — relatively minor For a ¥5,000,000 gross salary in Tokyo, expect to take home roughly ¥3,800,000–¥3,900,000 per year (approximately ¥316,000–¥325,000 per month), depending on your exact insurance rate, deductions, and personal situation.\nThe best way to improve your take-home pay is not to avoid paying taxes — it is to use the tools the government provides: iDeCo for tax-deferred retirement savings, NISA for tax-free investment growth, and furusato nozei to get real value from your residence tax.\nUse our Take-Home Pay Calculator to run the numbers for your specific salary and situation, and our Side Income Tax Calculator if you have freelance or investment income to account for.\nUnderstanding your payslip is the first step to managing your money confidently in Japan.\nRelated Tools Calculate percentages, discounts, and tips instantly → Percentage Calculator Calculate your take-home pay → Salary Calculator Estimate your tax bracket → Tax Bracket Calculator Create a monthly budget → Budget Planner Convert hourly wage to salary → Hourly to Salary Calculator Disclaimer: This article provides general information about Japan\u0026rsquo;s tax and social insurance system as of 2026. Individual circumstances vary. For advice on your specific tax situation, consult a qualified tax accountant (税理士, zeirishi) or the Japan National Tax Agency (国税庁) website at nta.go.jp.\nYou May Also Like How to Negotiate a Higher Salary When Switching Jobs in Japan (With Take-Home Pay Reality Check) Side Job Tax Rules in Japan: When Do You Need to File and How to Avoid Penalties? How to Use doda for Salary Negotiation in Japan: An English Guide for Career Changers (2026) ","permalink":"https://productivity-works.com/posts/take-home-pay-japan-calculator/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eYou just received a job offer in Japan. The salary looks great on paper — maybe ¥5,000,000 or ¥6,000,000 a year. But when your first payslip arrives, the number in your bank account looks noticeably smaller. Where did the rest go?\u003c/p\u003e\n\u003cp\u003eThis is one of the most common surprises for expats and foreign workers in Japan. Japan\u0026rsquo;s payroll deduction system is thorough, with multiple layers of tax and social insurance contributions taken out before you ever see your money. Understanding exactly what gets deducted — and why — helps you budget accurately, plan your finances, and find legitimate ways to reduce your tax burden.\u003c/p\u003e","title":"How to Calculate Your Take-Home Pay in Japan After Tax, Pension and Insurance (2026)"},{"content":"Food cost is where most restaurant profits go to die. The national average food cost percentage for a healthy restaurant sits between 28-35% of revenue. But it\u0026rsquo;s shockingly easy to creep to 40%, 45%, or higher — and most operators only find out when they look at their quarterly P\u0026amp;L and wonder where all the money went.\nThe challenge is that tracking food costs properly is time-intensive work. Counting inventory, reconciling invoices, calculating waste, updating recipe costs as ingredient prices change — it\u0026rsquo;s a full-time job on top of the full-time job of actually running a restaurant.\nAI tools, including ChatGPT, can\u0026rsquo;t replace a proper POS system or accounting software. But they can dramatically reduce the mental load of food cost management — helping you build systems, run calculations, write SOPs, and make better pricing decisions faster.\nThis guide covers the full picture: the math behind food costs, how AI tools fit into your workflow, waste tracking strategies, inventory management approaches, and the specific ChatGPT prompts that save time on menu pricing.\nFor a broader view of AI tools in restaurant financial management, see our AI Bookkeeping Guide for Small Restaurants .\nThe Fundamentals: Food Cost Percentage Before diving into AI tools, let\u0026rsquo;s make sure we\u0026rsquo;re working from the same baseline. Food cost percentage is the single most important metric in restaurant profitability.\nThe formula:\nFood Cost % = (Cost of Goods Sold / Total Food Revenue) × 100 Cost of Goods Sold (COGS):\nCOGS = Beginning Inventory + Purchases - Ending Inventory Example:\nBeginning inventory: $8,400 Purchases during period: $12,200 Ending inventory: $7,600 COGS = $8,400 + $12,200 - $7,600 = $13,000 If food revenue for the same period was $40,000:\nFood Cost % = ($13,000 / $40,000) × 100 = 32.5% That\u0026rsquo;s in the healthy range. But the number alone doesn\u0026rsquo;t tell the whole story. You need to know which menu items are driving high food cost, where waste is occurring, and whether your pricing reflects actual ingredient costs.\nWhere Food Costs Go Wrong: The Five Culprits Understanding your food cost percentage is step one. Understanding why it\u0026rsquo;s high is where you actually fix the problem. Here are the five most common culprits:\n1. Portion inconsistency When different cooks portion differently, your theoretical food cost (what it should be based on recipes) drifts from your actual food cost (what you actually spend). A soup that\u0026rsquo;s supposed to be 8 oz gets ladled at 10 oz half the time. Multiply that by 150 covers a night and you\u0026rsquo;ve lost a significant margin.\n2. Waste and spoilage Ordering too much, improper storage, prep waste that\u0026rsquo;s too high, and items that expire before use. The USDA estimates food service waste at 4-10% of purchased food.\n3. Theft and over-portioning A 3% shrinkage rate is often cited as a benchmark. If yours is higher, there may be theft, unauthorized consumption, or employees being overly generous with portions.\n4. Invoice errors and price drift Supplier prices change frequently. If your recipe costs were calculated at old prices and you haven\u0026rsquo;t updated them, your theoretical food cost is wrong — and your menu prices may be too.\n5. Menu pricing based on intuition, not math Many small operators price dishes based on what \u0026ldquo;feels right\u0026rdquo; or what competitors charge, rather than calculating backward from target food cost. This works until it doesn\u0026rsquo;t.\nUsing AI to Build Your Food Cost Tracking System You don\u0026rsquo;t need an enterprise software suite to track food costs properly. A spreadsheet with good formulas, combined with AI assistance for setup and analysis, gets you most of the way there.\nStep 1: Build a recipe cost calculator Ask ChatGPT to help you build the formulas and structure:\nI run a small restaurant and want to build a recipe cost calculator in Google Sheets. For each recipe, I need to track: - Ingredient name - Purchase unit (e.g., case, lb, gallon) - Purchase price per unit - Recipe unit (e.g., oz, tbsp, each) - Recipe quantity - Cost per recipe unit (calculated) - Ingredient cost for this recipe (calculated) - Total recipe cost (sum) - Number of portions per recipe - Cost per portion - Menu price - Food cost percentage (cost per portion / menu price) Please write the Google Sheets formulas I need for each calculated column, and explain how to set it up so I can easily update ingredient prices in one place and have the recipe costs update automatically. ChatGPT will walk you through building a master ingredient price list that links to individual recipe sheets — the same architecture that expensive restaurant management software uses, built for free.\nStep 2: Set up a weekly inventory count sheet Help me build a simple weekly inventory count sheet for a small restaurant in Google Sheets. I need: - A \u0026#34;par level\u0026#34; column showing my target quantity for each item - A \u0026#34;counted\u0026#34; column where I enter actual quantities weekly - An \u0026#34;on-hand value\u0026#34; column that multiplies count by unit cost - A \u0026#34;variance from par\u0026#34; column showing whether I\u0026#39;m over or under par - A \u0026#34;weekly usage\u0026#34; calculation that compares this week\u0026#39;s ending inventory to last week\u0026#39;s The sheet should flag items that are more than 20% below par so I know to reorder. My main inventory categories are: proteins, dairy, produce, dry goods, beverages, paper/packaging. Please give me the formula structure and conditional formatting rules. Step 3: Automate your COGS calculation Once you have your inventory counted weekly and your purchase invoices entered, the COGS calculation is just arithmetic. But keeping it automated saves you from doing it manually each week:\nI track weekly inventory counts and purchases in Google Sheets. Help me build a COGS calculation tab that: - Pulls beginning inventory from the previous week\u0026#39;s ending inventory automatically - Has an input area for entering weekly purchases by category - Calculates COGS by category (protein, produce, dairy, etc.) - Shows total COGS and food cost % if I enter that week\u0026#39;s food revenue - Creates a rolling 12-week chart of food cost % by category Write the formulas and explain the sheet structure. Waste Tracking with AI Assistance Waste tracking is one of the highest-ROI habits in food cost management, and one of the least practiced. Most operators know they\u0026rsquo;re wasting food; few can quantify it.\nA simple waste log — a clipboard or tablet at the prep station and line where staff record tossed items — combined with weekly analysis, can reduce waste by 15-25% in the first month just by making waste visible.\nSetting up your waste log with ChatGPT I want to set up a food waste tracking system for my restaurant. Help me: 1. Design a simple daily waste log that line cooks and prep cooks can fill out in under 2 minutes. It should capture: item name, quantity wasted, reason for waste (spoilage / wrong order / prep waste / expired / other), and estimated cost. 2. Create a weekly summary formula in Google Sheets that totals waste by category and reason, and shows what percentage of purchases each waste category represents. 3. Write a brief SOP (2 paragraphs) I can share with my team explaining why we track waste and how to fill out the log correctly. My kitchen staff are [number] people. The log should be simple enough that it gets actually used. Analyzing your waste data Once you have a few weeks of waste data, ChatGPT can help you interpret it:\nHere is my restaurant\u0026#39;s waste data for the past 4 weeks: [paste data or describe patterns] Please help me: 1. Identify the top 3 waste categories by cost 2. For each top category, suggest 2-3 specific operational changes that could reduce this waste 3. Calculate what my monthly food cost % improvement would be if I reduced total waste by 20% 4. Suggest what questions I should be asking my kitchen team about the highest-waste items AI-Powered Inventory Management For most small independent restaurants, a full restaurant management system (Toast, Square for Restaurants, Restaurant365) handles inventory. If you\u0026rsquo;re using one, you\u0026rsquo;re already ahead.\nBut if you\u0026rsquo;re managing inventory manually or with basic spreadsheets, here\u0026rsquo;s how to use ChatGPT to build smarter systems:\nBuilding reorder point calculations Reorder points tell you the minimum quantity at which you should place a new order, factoring in lead time and usage rate.\nHelp me calculate reorder points for my key ingredients. For each ingredient, I have: - Average daily usage (in units) - Supplier lead time (in days) - Safety stock I want to maintain (extra days of supply as buffer) The formula is: Reorder Point = (Average Daily Usage × Lead Time) + Safety Stock I want to build this in Google Sheets. My top 10 ingredients by cost are: [list ingredients with rough daily usage and supplier lead times]. Create the spreadsheet formulas and also suggest what safety stock level I should use for: (a) items with a single supplier, (b) items I can source from multiple suppliers, (c) perishables with less than 7-day shelf life. Seasonal demand adjustments My restaurant has significant seasonal variation. Help me think through how to adjust my par levels and ordering quantities for the following seasons: [describe your seasonal patterns — e.g., summer patio rush, slower January]. Specifically: - Which categories of ingredients should I expect to use 20%+ more of in peak season? - How should I adjust my order frequency (daily, 3x/week, weekly) for high-turnover perishables in peak season vs. slow season? - What\u0026#39;s a simple way to flag in my inventory sheet that we\u0026#39;re in \u0026#34;peak mode\u0026#34; vs. \u0026#34;slow mode\u0026#34; with different par levels for each? ChatGPT Prompts for Menu Pricing Menu pricing is where food cost tracking pays off most directly. Here are prompts for the most common pricing challenges:\nPricing a new menu item I\u0026#39;m adding a new dish to my menu and need to determine the right price. Here are the details: Dish: [name and description] Ingredient costs per portion: [list ingredients and costs, or total cost per portion] Target food cost percentage: [your target, e.g., 30%] Comparable dishes on my current menu: [name and price 2-3 similar dishes] Local market context: [rough price range for similar dishes at comparable restaurants in your area] Please: 1. Calculate the minimum price needed to hit my target food cost % 2. Suggest a final price that balances food cost target with market positioning 3. Calculate the contribution margin (selling price minus food cost) at the suggested price 4. Tell me if there are any ingredient substitutions that could reduce food cost by 5%+ without significantly affecting the dish Auditing your existing menu for profitability Here is my current menu with pricing and approximate food cost per item: [paste or describe] Please perform a simple menu engineering analysis: - Categorize each item as: Star (high profit, high popularity), Plow Horse (low profit, high popularity), Puzzle (high profit, low popularity), or Dog (low profit, low popularity) - For Plow Horses: suggest whether to raise price, reduce portion, or redesign the recipe to lower food cost - For Dogs: suggest whether to remove from menu or reposition - For Puzzles: suggest how to increase their visibility on the menu Note: I don\u0026#39;t have exact sales counts, but I can estimate rough popularity for each item as: high / medium / low. [Add estimates next to each item] Handling ingredient price increases One of my key ingredients, [ingredient], has increased in price from $[old price] to $[new price] per [unit]. This ingredient appears in [number] menu items. For each one: [List dishes with portion size of this ingredient] Please calculate: 1. The new food cost per portion for each affected dish 2. How much each dish\u0026#39;s food cost percentage increases at current menu price 3. What menu price increase (rounded to nearest $0.50) would restore each dish to my target food cost of [X]% 4. Alternative: what portion reduction (if applicable) could absorb the cost increase without a price change Also help me draft a brief message I could share with regular guests explaining a menu price adjustment, in a way that feels honest rather than defensive. Keep Your Restaurant\u0026rsquo;s Finances Under Control Food cost tracking is only half the battle — clean books make the rest manageable. Try freee — Japan\u0026rsquo;s #1 cloud accounting to automate your invoicing, expense tracking, and tax prep so you can focus on what\u0026rsquo;s actually on the menu.\nBuilding a Monthly Food Cost Review Habit The goal isn\u0026rsquo;t to obsess over food costs daily — it\u0026rsquo;s to build a monthly review rhythm that catches problems before they compound.\nA practical monthly review template:\nHelp me build a monthly food cost review checklist for a small restaurant. It should cover: 1. What numbers to pull and from where 2. How to calculate the month\u0026#39;s food cost % and compare to the previous 3 months 3. What variance thresholds should trigger investigation (e.g., \u0026gt;2% swing) 4. A set of 5 diagnostic questions to ask if food cost is higher than target 5. What to communicate to kitchen leadership about the results My restaurant has [number] seats, serves [lunch/dinner/both], and does approximately $[monthly revenue] in food sales. My target food cost is [X]%. Related Tools Convert between units of measurement → Unit Converter Calculate percentages, discounts, and tips instantly → Percentage Calculator Create a business budget → Budget Planner Plan loan repayment → Loan Repayment Calculator Related Templates Ready to take control of your restaurant\u0026rsquo;s finances with AI?\nThe AI Productivity Playbook — A complete guide to using AI tools for small business financial management, including restaurant-specific templates for food cost tracking, inventory management, cash flow monitoring, and more. Built for operators who want real results without a finance degree. This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/ai-restaurant-food-cost-tracking-2026/","summary":"\u003cp\u003eFood cost is where most restaurant profits go to die. The national average food cost percentage for a healthy restaurant sits between 28-35% of revenue. But it\u0026rsquo;s shockingly easy to creep to 40%, 45%, or higher — and most operators only find out when they look at their quarterly P\u0026amp;L and wonder where all the money went.\u003c/p\u003e\n\u003cp\u003eThe challenge is that tracking food costs properly is time-intensive work. Counting inventory, reconciling invoices, calculating waste, updating recipe costs as ingredient prices change — it\u0026rsquo;s a full-time job on top of the full-time job of actually running a restaurant.\u003c/p\u003e","title":"Track Food Costs with AI: Restaurant Owner Guide (2026)"},{"content":"Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nBest Forex Brokers 2026 — A Practical Guide for Beginners and Active Traders Forex (foreign exchange) trading is the world\u0026rsquo;s largest financial market, with over $7.5 trillion traded daily. It\u0026rsquo;s accessible, runs 24/5, and you can start with as little as $50. But choosing the wrong broker can cost you thousands in hidden fees, poor execution, or worse — losing your funds to an unregulated platform.\nThis guide compares the best forex brokers in 2026 based on what actually matters: spreads, regulation, platform quality, and minimum deposits.\nHow to Choose a Forex Broker: 5 Key Criteria Criteria Why It Matters Regulation Regulated brokers (FCA, SEC, ASIC, CySEC) protect your funds. Never trade with unregulated brokers Spreads \u0026amp; Fees The spread is your cost per trade. Lower spreads = more profit retained Trading Platform MetaTrader 4/5, cTrader, or proprietary — usability affects your performance Minimum Deposit Some brokers let you start with $1, others require $500+ Leverage US: max 50:1, EU: max 30:1, other regions: up to 500:1. Higher leverage = higher risk Best Forex Brokers 2026 — Top 5 Broker EUR/USD Spread Min. Deposit Regulation Best For IG 0.6 pips $0 FCA, ASIC, NFA Overall best, most currency pairs OANDA 1.0 pips $0 FCA, NFA, ASIC Beginners, no minimum deposit Forex.com 1.0 pips $100 FCA, NFA, CIMA US traders, education resources Interactive Brokers 0.1 pips $0 SEC, FCA, ASIC Advanced traders, lowest spreads eToro 1.0 pips $50 FCA, CySEC, ASIC Social/copy trading Spreads are typical averages and vary by market conditions.\nDetailed Broker Reviews 1. IG — Best Overall Forex Broker Feature Details EUR/USD Spread 0.6 pips (average) Min. Deposit $0 Currency Pairs 80+ (industry-leading) Platforms IG Platform, MetaTrader 4, ProRealTime Regulation FCA (UK), ASIC (Australia), NFA (US) Best for: Traders who want the widest range of currency pairs and a trusted, established broker.\nIG has been operating since 1974 and is publicly traded on the London Stock Exchange. With 80+ currency pairs, competitive spreads, and excellent charting tools, it\u0026rsquo;s the most well-rounded broker available. The proprietary platform is intuitive for beginners while offering advanced features for experienced traders.\n2. OANDA — Best for Beginners Feature Details EUR/USD Spread 1.0 pips (average) Min. Deposit $0 (no minimum) Currency Pairs 68 Platforms OANDA Trade, MetaTrader 4 Regulation FCA, NFA, ASIC, IIROC Best for: Complete beginners who want to start small with zero pressure.\nOANDA has no minimum deposit requirement and allows trading in any unit size (not just standard lots). This means you can start with $10 and trade micro amounts while you learn. Their educational resources and demo account are among the best in the industry.\n3. Forex.com — Best for US Traders Feature Details EUR/USD Spread 1.0 pips (standard), 0.2 pips (RAW pricing) Min. Deposit $100 Currency Pairs 80+ Platforms Forex.com Platform, MetaTrader 4/5, TradingView Regulation NFA, FCA, CIMA Best for: US-based traders who want a fully regulated domestic broker with strong education.\nForex.com (owned by StoneX Group) is one of the few brokers fully licensed for US forex trading. Their RAW pricing account offers spreads from 0.2 pips with a commission, making it competitive for active traders. The education center includes courses, webinars, and market analysis.\n4. Interactive Brokers — Lowest Spreads for Active Traders Feature Details EUR/USD Spread 0.1 pips (with commission) Min. Deposit $0 Currency Pairs 100+ Platforms Trader Workstation (TWS), IBKR Mobile Regulation SEC, FCA, ASIC, and 10+ other regulators Best for: Experienced, active traders who want the absolute lowest trading costs.\nInteractive Brokers offers the tightest spreads in the industry — as low as 0.1 pips on EUR/USD — but charges a small commission per trade. For high-volume traders, the total cost is still lower than any spread-only broker. TWS is powerful but has a steep learning curve; not ideal for beginners.\n5. eToro — Best for Social/Copy Trading Feature Details EUR/USD Spread 1.0 pips Min. Deposit $50 Currency Pairs 49 Platforms eToro Platform (proprietary) Regulation FCA, CySEC, ASIC Best for: Beginners who want to learn by copying successful traders.\neToro\u0026rsquo;s unique CopyTrader feature lets you automatically replicate the trades of top-performing traders. You can browse trader profiles, see their historical performance, and allocate funds to copy their strategies. It\u0026rsquo;s a powerful learning tool and passive trading option.\nForex Trading Risks — What Every Beginner Must Know Forex trading carries significant risk. Most retail traders lose money. Understand these risks before you start:\nRisk Description Leverage risk Leverage amplifies both gains AND losses. 50:1 leverage means a 2% adverse move wipes out your entire position Market risk Currency prices are driven by global events, central bank decisions, and economic data — all unpredictable Liquidity risk During major news events, spreads can widen dramatically and orders may not fill at expected prices Counterparty risk If your broker is unregulated or poorly capitalized, your funds are at risk Rules for beginners:\nStart with a demo account for at least 1 month Never risk more than 1-2% of your account on a single trade Use stop-loss orders on every trade Start with low leverage (5:1 or 10:1, not the maximum) Only trade with money you can afford to lose completely Quick Decision Guide Your Situation Best Broker Complete beginner OANDA (no minimum, micro trading) US-based trader Forex.com (NFA regulated) Want lowest costs Interactive Brokers (0.1 pip spreads) Want to copy others eToro (CopyTrader) Want most pairs IG (80+ pairs) Want MT4/MT5 Forex.com or OANDA Frequently Asked Questions Q: How much money do I need to start forex trading? Technically, as little as $10-50 with brokers like OANDA or eToro. However, $500-1,000 gives you enough margin to trade responsibly with proper position sizing.\nQ: Is forex trading profitable? It can be, but statistics show that 70-80% of retail forex traders lose money. Success requires education, discipline, risk management, and realistic expectations. It\u0026rsquo;s not a get-rich-quick scheme.\nQ: What\u0026rsquo;s the best time to trade forex? The London-New York overlap (8 AM - 12 PM EST) has the highest liquidity and tightest spreads for major pairs. Avoid trading during low-liquidity hours (late Friday, early Monday).\nQ: Should I use MetaTrader 4 or 5? MT5 is newer with more features (more timeframes, built-in economic calendar, more order types), but MT4 still has the largest community and most third-party tools. Either works fine for most traders.\nQ: Is forex trading legal? Yes, in most countries. In the US, forex brokers must be registered with the NFA and CFTC. In the EU, they need FCA or CySEC authorization. Always verify your broker\u0026rsquo;s regulatory status.\nStart Investing in Japan — Open a Rakuten Securities Account Before diving into forex, consider building your foundation with stocks and funds. Open a Rakuten Securities account — one of Japan\u0026rsquo;s most trusted online brokers with low fees, a wide range of investment products, and a beginner-friendly platform.\nConclusion Choosing the right forex broker is the foundation of successful trading. Prioritize regulation (never trade with unregulated brokers), then compare spreads and platform quality.\nFor beginners, start with OANDA (zero minimum, flexible lot sizes) or eToro (copy trading to learn). For active traders, Interactive Brokers offers the lowest all-in costs. For the best overall experience, IG leads the field.\nRemember: forex trading is high-risk. Start with a demo account, learn proper risk management, and never trade with money you can\u0026rsquo;t afford to lose. Consider building a foundation with index investing first, then exploring forex with surplus funds.\nRelated: Best Budgeting Apps 2026 This article is for informational purposes only and does not constitute financial advice. Forex trading involves substantial risk of loss. Past performance is not indicative of future results.\nRelated Tools Calculate forex trading profits → Forex Profit Calculator Create a monthly budget → Budget Planner See how reinvested profits grow → Compound Interest Calculator Related Templates Level up your financial knowledge with AI:\nAI Productivity Playbook — Automate research and analysis with AI ChatGPT Prompt Templates — Including financial analysis prompts ","permalink":"https://productivity-works.com/posts/best-forex-brokers-2026/","summary":"\u003cp\u003e\u003cem\u003eDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"best-forex-brokers-2026--a-practical-guide-for-beginners-and-active-traders\"\u003eBest Forex Brokers 2026 — A Practical Guide for Beginners and Active Traders\u003c/h1\u003e\n\u003cp\u003eForex (foreign exchange) trading is the world\u0026rsquo;s largest financial market, with over $7.5 trillion traded daily. It\u0026rsquo;s accessible, runs 24/5, and you can start with as little as $50. But choosing the wrong broker can cost you thousands in hidden fees, poor execution, or worse — losing your funds to an unregulated platform.\u003c/p\u003e","title":"Best Forex Brokers 2026: Platforms for Beginners \u0026 Traders"},{"content":"This article contains affiliate links.\nJapan offers two of the most talked-about investment paths for residents looking to grow their money: NISA (the government-backed tax-free account) and FX (foreign exchange margin trading). On the surface, both involve putting money to work in financial markets. But dig a little deeper and you will find they are almost opposites — different tax rules, different risk profiles, and very different audiences.\nIf you are new to investing in Japan — whether you are a Japanese national or an expat — understanding how these two options compare is essential before committing a single yen. This guide breaks down everything you need to know: how each product works, how they are taxed, what risks they carry, and how to decide which one (or both) belongs in your financial plan.\nWhat Is NISA? NISA stands for Nippon Individual Savings Account. It is Japan\u0026rsquo;s answer to the UK ISA or the US Roth IRA — a government-designed investment account that shelters your returns from income tax and capital gains tax entirely.\nThe current system, often called \u0026ldquo;New NISA,\u0026rdquo; was overhauled and relaunched in January 2024. It is significantly more generous than its predecessors and has two distinct components:\nTsumitate (Growth Investment) Portion\nAnnual contribution limit: 1,200,000 yen (1.2 million yen) Designed for regular, recurring purchases of eligible investment trusts Purchases must be made systematically (monthly direct debit is the most common method) Growth (Seichou) Portion\nAnnual contribution limit: 2,400,000 yen (2.4 million yen) Allows lump-sum purchases of stocks, ETFs, REITs, and investment trusts Broader product selection than the Tsumitate portion Key features of the 2024 New NISA:\nCombined annual cap: 3,600,000 yen (3.6 million yen) across both portions Lifetime contribution cap: 18,000,000 yen (18 million yen) All gains — dividends, distributions, and capital gains — are completely tax-free, with no expiration date on the tax-free status Unused allowance from one year does not roll over, but sold positions free up lifetime capacity again (a new feature introduced in 2024) Available to residents of Japan aged 18 or older, including non-Japanese nationals with a valid residence card The simplicity is a major appeal: once money is inside a NISA account, you owe zero tax on any growth. No annual declarations, no withholding, nothing.\nWhat Is FX Trading? FX stands for Foreign Exchange, and in Japan it refers specifically to leveraged margin trading on currency pairs — USD/JPY, EUR/JPY, AUD/JPY, and dozens more. Japan is one of the world\u0026rsquo;s largest retail FX markets; the industry is heavily regulated by the Financial Services Agency (FSA).\nHere is how FX margin trading works in Japan:\nLeverage: Retail traders can use leverage of up to 25x on major currency pairs. This means a 100,000-yen margin deposit controls a 2,500,000-yen position. Profit mechanisms: Traders profit from price movements (buying low, selling high or vice versa) and from swap points — the interest rate differential between the two currencies in a pair. Platforms: Major brokers such as GMO Click Securities, DMM FX, SBI FX Trade, and Monex offer competitive spreads and mobile-friendly trading interfaces. Accessibility: Accounts can be opened online with as little as a few thousand yen in margin. FX appeals to traders who want short-term, active opportunities — someone who wants to act on a Bank of Japan policy announcement or a US Non-Farm Payrolls release, for example. It is fast-moving, requires ongoing attention, and carries a real risk of losing more than you put in if positions move sharply against you.\nUnlike NISA, FX profits are not tax-free. They are taxed under a separate, defined system.\nTax Comparison Table One of the most important differences between NISA and FX is how the government taxes your profits. Here is a direct side-by-side comparison:\nCategory NISA FX Trading Tax rate on profits 0% (completely tax-free) 20.315% (15% national + 5% local + 0.315% reconstruction tax) Tax calculation method N/A — no tax Separate self-assessment (bunri kazei) Loss carryforward Not available Up to 3 years Netting with other investment losses Not applicable Can net FX losses against other \u0026ldquo;futures/derivatives\u0026rdquo; gains Annual tax filing required? No Yes, if annual profit exceeds 200,000 yen (salaried employees) Withholding at source? No No — trader files and pays directly Dividend/distribution tax 0% (inside NISA) N/A (FX swap points are treated as ordinary income under the same 20.315% rate) The 20.315% flat rate on FX is fixed regardless of how much you earn — you pay the same percentage whether your FX profit is 300,000 yen or 30,000,000 yen. This is actually favorable compared to Japan\u0026rsquo;s progressive income tax brackets (which can reach 55% for high earners), but it still represents a significant cost that NISA completely eliminates.\nThe loss carryforward rule for FX is worth noting. If you lose 500,000 yen on FX trades in one year, you can carry that loss forward and offset it against FX profits in the following three years. NISA has no equivalent mechanism — but since NISA is tax-free anyway, losses inside a NISA account do not generate a tax benefit.\nRisk Comparison Tax treatment is just one dimension. The risk profile of these two products is dramatically different.\nNISA: Low to Medium Risk, Long-Term Orientation NISA itself is not an investment — it is an account wrapper. The risk you take depends entirely on what you buy inside it. That said, the products available through the Tsumitate portion are pre-screened by the FSA to exclude high-cost or excessively speculative funds. In practice, most NISA investors hold index funds tracking the Nikkei 225, TOPIX, or global indexes like MSCI All Country World.\nKey risk characteristics of typical NISA investing:\nNo leverage: You invest only what you deposit. You cannot lose more than your invested amount. Market risk: Index funds fluctuate with the market. A global market downturn will reduce your account value temporarily. Long time horizon: Dollar-cost averaging into index funds over 20-30 years has historically smoothed out short-term volatility significantly. Liquidity: Holdings can generally be sold at any time, though selling resets your lifetime allowance rather than permanently losing it. For a beginner who contributes 50,000 yen per month to a global index fund, the NISA risk profile is manageable and well-suited to patient, hands-off investors.\nFX: High Risk, Leverage-Amplified FX trading carries substantially higher risk, primarily because of leverage.\nLeverage amplifies both gains and losses: A 4% adverse move on a 25x leveraged position wipes out the entire margin deposit. Forced liquidation (ロスカット): When your margin ratio falls below a broker\u0026rsquo;s threshold — often 50% — positions are automatically closed to prevent negative balances. 24-hour market: Currency markets trade almost continuously from Monday morning in Auckland to Friday close in New York. Significant moves can happen overnight or over a weekend. Psychological pressure: Active trading requires discipline, emotional control, and a clear strategy. Most retail FX traders in Japan lose money, according to FSA aggregate data. Swap risk: Carrying positions overnight accrues or pays swap points daily, which can erode profits on longer-term positions in high-spread pairs. The bottom line: FX can generate faster gains than NISA, but it can also generate faster losses. It is not a suitable substitute for long-term wealth building.\nWhen to Choose NISA NISA is the right choice for most people in most situations. Choose NISA if:\nYou are building long-term wealth — saving for retirement, a child\u0026rsquo;s education, or financial independence over 10, 20, or 30 years You prefer a hands-off approach — set up a monthly automatic purchase and let compounding do the work Tax efficiency is your priority — eliminating 20.315% tax on decades of compound growth makes an enormous difference to final portfolio value You are risk-averse or a complete beginner — index fund NISA investing is as simple as it gets You want certainty — unlike FX, a broad index fund will not go to zero To illustrate the tax advantage: if you invest 3 million yen over 10 years inside NISA and it doubles to 6 million yen, the 3 million yen in gains is yours, entirely. The same gain outside a tax-advantaged account would trigger a tax bill of roughly 609,450 yen (20.315% of 3 million yen). That is money that stays working for you inside NISA.\nSimulate your NISA growth with our NISA Simulator .\nWhen to Choose FX FX is not the right starting point for most beginners, but it has legitimate uses for certain investor profiles. Consider FX if:\nYou are an active trader with time to monitor markets, analyze charts, and manage positions in real time You want short-term income opportunities — capturing moves around economic data releases, central bank decisions, or technical breakouts You are hedging currency exposure — for example, you receive salary or income in USD but live and spend in Japan; FX can offset currency risk You want to trade on macro themes quickly — NISA investments are long-term commitments; FX lets you act on short-term views immediately You understand risk management — you use stop-loss orders, appropriate position sizing, and never risk more than you can afford to lose Even experienced FX traders are advised to treat it as a speculative allocation rather than a core savings strategy.\nCalculate potential FX profits with our FX Profit Calculator .\nCan You Do Both? Yes — and for many investors in Japan, combining NISA and FX is a sensible strategy. This is sometimes called a \u0026ldquo;core-satellite\u0026rdquo; approach:\nCore (80-90% of investable assets): NISA\nMonthly automatic contributions to a low-cost global index fund Completely tax-free growth compounding over decades Requires minimal ongoing attention after setup Satellite (10-20% of investable assets): FX\nActive trading with clearly defined risk limits Short-term opportunities to generate additional income Losses capped to the designated FX allocation — never touch core NISA savings The key discipline is keeping the two completely separate — mentally and practically. Never liquidate NISA holdings to cover FX margin calls. Never use potential FX profits to justify reducing NISA contributions. Treat them as independent tools serving different purposes.\nAn example allocation for a 35-year-old earning 600,000 yen per month might look like:\n50,000 yen/month into NISA Tsumitate (global index fund) Up to 300,000 yen in a dedicated FX margin account as the \u0026ldquo;satellite\u0026rdquo; pool This structure gives you the tax-free long-term foundation of NISA while leaving room for more active participation through FX if you choose.\nTax Filing Differences This is where NISA and FX diverge most sharply from an administrative standpoint.\nNISA: Zero Filing Required Profits, dividends, and distributions inside a NISA account are automatically tax-exempt. No withholding occurs, and you do not need to report NISA income on your annual tax return under any circumstances. For expats unfamiliar with Japan\u0026rsquo;s tax system, this is one of NISA\u0026rsquo;s most attractive features — complete simplicity.\nFX: Annual Tax Return (Kakutei Shinkoku) Required FX profits are subject to self-assessed separate taxation at 20.315%. Here is when you must file:\nSalaried employees: If your annual FX profit (after netting losses) exceeds 200,000 yen, you are required to file a Kakutei Shinkoku (確定申告) by March 15 of the following year. Self-employed / non-salaried: FX profits must always be reported, regardless of amount. Loss carryforward election: To carry FX losses forward into future years, you must file a tax return even if your loss means no tax is currently owed. The calculation involves:\nAdding up all FX realized gains and losses for the calendar year Subtracting any eligible losses from prior years carried forward Applying the 20.315% rate to the net gain Paying the resulting tax by the March 15 deadline (or setting up installments) For those filing in Japanese, the National Tax Agency (NTA) provides an online e-Tax portal. For English-language support and to simplify the calculation, freee simplifies FX tax filing for English speakers. The platform supports FX income reporting in a guided workflow, making it significantly more accessible for expats and non-native Japanese speakers navigating the Kakutei Shinkoku process for the first time.\nOne important note: FX profits are reported to your local tax office (zeimusho), not to your employer. Your year-end adjustment (nenmatsu chosei) done by your company covers employment income only. FX must always be handled separately.\nConclusion NISA and FX are both legitimate tools in the Japanese investor\u0026rsquo;s toolkit — but they serve fundamentally different purposes.\nNISA is the foundation. For the vast majority of people living in Japan — beginners, expats, salaried workers, or anyone with a 10+ year time horizon — NISA should be the first investment account you open. The combination of zero tax on all gains, a generous 18-million-yen lifetime cap, and access to low-cost global index funds makes it one of the most powerful personal finance tools available to residents of Japan. The administrative simplicity is a bonus: once set up, NISA runs itself.\nFX is a satellite tool. It can add short-term income opportunities and currency hedging to a portfolio, and for active traders who invest the time to learn proper risk management, it can be rewarding. But its high-risk nature, leverage, and 20.315% tax burden mean it should never replace a core long-term savings strategy.\nFor most beginners, the recommendation is clear: open a NISA account first, automate monthly contributions into a global index fund, and only consider FX after you have built a meaningful NISA balance and understand how currency markets work.\nThe tax difference alone — 0% versus 20.315% — compounds significantly over time. On a 10-million-yen portfolio growing at 7% annually over 20 years, the difference between tax-free growth (NISA) and taxed growth can amount to several million yen in your pocket. That is the power of choosing the right account for the right goal.\nStart with NISA. Explore FX when you are ready. Keep them separate, and let each do what it does best.\nRelated Tools Calculate forex trading profits → Forex Profit Calculator See how NISA investments compound tax-free → Compound Interest Calculator Estimate retirement savings → Retirement Calculator Calculate dividend income from stocks → Dividend Income Calculator Related Articles Best Investment Account in Japan: NISA vs iDeCo How Much Tax Do You Pay on FX Profits in Japan? Best FX Brokers in Japan (English Guide) iDeCo Tax Deduction Calculator for Japan Beginner Investing Guide 2026 This article is for informational purposes only and does not constitute financial advice. Tax rules are based on regulations current as of 2026. Consult a licensed tax advisor or financial planner for guidance specific to your situation.\n","permalink":"https://productivity-works.com/posts/nisa-vs-fx-japan-investing/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eJapan offers two of the most talked-about investment paths for residents looking to grow their money: NISA (the government-backed tax-free account) and FX (foreign exchange margin trading). On the surface, both involve putting money to work in financial markets. But dig a little deeper and you will find they are almost opposites — different tax rules, different risk profiles, and very different audiences.\u003c/p\u003e\n\u003cp\u003eIf you are new to investing in Japan — whether you are a Japanese national or an expat — understanding how these two options compare is essential before committing a single yen. This guide breaks down everything you need to know: how each product works, how they are taxed, what risks they carry, and how to decide which one (or both) belongs in your financial plan.\u003c/p\u003e","title":"NISA vs FX in Japan: Which Is Better for Growing Your Money Tax-Efficiently?"},{"content":"Japan has seen a surge in side work over the past several years. Ride-sharing, freelance design, online tutoring, crypto trading, rental income — the avenues for earning extra money have never been more accessible. Yet many salaried workers who bring in a little extra cash each month have only the vaguest sense of what the tax rules actually require of them.\nThat gap between earning and understanding is costly. Japan\u0026rsquo;s National Tax Agency (NTA) actively cross-references income data from various sources, and penalties for late or non-filing add up fast. This guide cuts through the complexity: when you are legally required to file, what counts as taxable side income, exactly how penalties are calculated, and practical steps to reduce your tax bill while staying fully compliant.\nThe ¥200,000 Rule — When Side Income Triggers a Tax Filing The single most important threshold every salaried worker in Japan needs to know is ¥200,000.\nUnder Japan\u0026rsquo;s Income Tax Act, a salaried employee whose only income is from one employer generally does not need to file a tax return (kakuteishinkoku / 確定申告), because their employer handles withholding. However, once you earn more than ¥200,000 in side income during a calendar year (January 1 – December 31), you are legally required to file a kakuteishinkoku with the NTA by March 15 of the following year.\nThis ¥200,000 figure is your net income from the side job — meaning revenue minus allowable business expenses — not gross revenue. If you earned ¥350,000 freelancing but had ¥180,000 in legitimate business expenses, your net side income is ¥170,000, which falls below the threshold. You would not be required to file income tax. (More on expenses in the tips section below.)\nThe Residence Tax Exception: A Critical Point Many People Miss Here is where many workers get caught off guard: the ¥200,000 exemption applies only to national income tax (所得税), not to residence tax (住民税).\nEven if your side income is below ¥200,000 and you are exempt from filing a national tax return, you are still required to report that income to your local municipal office for residence tax purposes. You do this by filing a 住民税申告 (juminzei shinkoku) at your city or ward office.\nIgnoring this obligation is one of the most common compliance mistakes among side workers in Japan. Residence tax is calculated separately and billed the following year. Underpaying it — even inadvertently — can result in additional assessments, late payment penalties, and, importantly, a residence tax bill arriving at your employer\u0026rsquo;s address that reveals your side income.\nWhat Counts as \u0026ldquo;Side Income\u0026rdquo;? The term \u0026ldquo;side income\u0026rdquo; sounds simple, but Japan\u0026rsquo;s tax code carves income into several categories, and the classification affects how it is taxed.\nMiscellaneous income (雑所得 / zatsushotoku) is the catch-all bucket for most side earnings:\nFreelance work and consulting — writing, design, programming, translation, tutoring Online sales — selling goods on Mercari, Yahoo Auctions, Amazon, or your own store (profits from selling personal possessions at a loss are generally not taxable, but running a regular resale operation is) Content creation — YouTube ad revenue, note.com subscriptions, affiliate income Cryptocurrency trading — profits from buying and selling Bitcoin, Ethereum, and other tokens FX (foreign exchange) margin trading — profits from currency speculation Real estate income (不動産所得 / fudousanshotoku) applies to rental income from apartments, parking lots, or land.\nBusiness income (事業所得 / jigyoshotoku) applies when your side activity rises to the level of a continuous, independent business. This classification gives you access to more generous deductions, including the blue return (青色申告) system described later.\nKey principle: \u0026ldquo;income\u0026rdquo; means net profit, not gross revenue.\nIf you earned ¥500,000 from freelance work but spent ¥200,000 on equipment, software subscriptions, coworking space, and transportation directly related to that work, your taxable income is ¥300,000. Accurate record-keeping of expenses is therefore not just good practice — it directly reduces your tax liability.\nHow Side Income Is Taxed in Japan Standard Side Income: Progressive Rates For most freelance and consulting income classified as miscellaneous or business income, your side earnings are added to your employment income and taxed together at Japan\u0026rsquo;s progressive income tax rates:\nTaxable Income (annual) Tax Rate Up to ¥1,950,000 5% ¥1,950,001 – ¥3,300,000 10% ¥3,300,001 – ¥6,950,000 20% ¥6,950,001 – ¥9,000,000 23% ¥9,000,001 – ¥18,000,000 33% ¥18,000,001 – ¥40,000,000 40% Over ¥40,000,000 45% A 2.1% surtax for reconstruction funding is also applied on top of the income tax amount.\nBecause your side income is stacked on top of your salary, a salaried worker earning ¥6,000,000 per year already sits in the 20% bracket. Any additional side income will be taxed at 20% (or higher, depending on how much side income pushes the total). This is the marginal rate effect — your side income is taxed at whatever bracket your total income reaches.\nResidence tax adds approximately 10% on top, bringing the effective combined rate on marginal income well above 30% for many mid-career workers.\nFX and Cryptocurrency: Separate Taxation FX trading profits and cryptocurrency gains are taxed differently:\nFX profits are subject to 申告分離課税 (separate self-assessment taxation) at a flat 20.315% (15% income tax + 5% residence tax + 0.315% reconstruction surtax). Cryptocurrency profits are classified as miscellaneous income and taxed at the progressive rates described above — they are not eligible for the flat 20.315% rate. This means heavy crypto gains can push a salaried worker into the 33% or 40% bracket. Losses from FX trading can be carried forward for three years to offset future FX gains, but crypto losses cannot be offset against other income types.\nYou can check your estimated take-home impact after all deductions with our take-home pay impact calculator , or estimate your liability directly using our Side Income Tax Calculator .\nPenalties for Not Filing Failing to file when you are required to do so is not a victimless oversight. Japan\u0026rsquo;s NTA has two primary financial penalties for non-compliance, plus a criminal provision for deliberate evasion.\n無申告加算税 (Mumoushinkoku Kasanzei) — Failure-to-File Surcharge If the NTA discovers that you had an obligation to file but did not, it will assess a failure-to-file surcharge on the unpaid tax:\n15% of the tax due, if the total unpaid tax is ¥500,000 or less 20% of the portion exceeding ¥500,000 For example: if you owed ¥800,000 in income tax and did not file, the surcharge would be calculated as (¥500,000 × 15%) + (¥300,000 × 20%) = ¥75,000 + ¥60,000 = ¥135,000 in surcharge alone.\nThis penalty is reduced to 5% if you voluntarily come forward and file before the NTA initiates an investigation. This is a significant incentive to self-correct early if you realize you missed a filing deadline.\n延滞税 (Entaizei) — Late Payment Interest On top of the failure-to-file surcharge, unpaid tax accrues late payment interest (延滞税) from the original due date until the date of actual payment:\nFor the first two months after the due date: approximately 2.4% per annum (this rate is linked to the statutory special rate and adjusts annually) After two months: approximately 8.7% per annum The exact rates are revised each year by the NTA. For 2025 filings, the NTA-published rates are 2.4% for the first two months and 8.7% thereafter. The compounding effect of the high post-two-month rate means that letting an unpaid tax balance sit for years becomes extremely expensive.\nCriminal Penalties Deliberate tax evasion is a criminal offense under Japan\u0026rsquo;s Income Tax Act and can result in imprisonment of up to ten years or fines of up to ¥5,000,000 (or both). In practice, criminal prosecution is extremely rare for individual wage earners with moderate side income. The NTA focuses its criminal enforcement on large-scale, systematic fraud. However, the legal exposure is real and worth understanding.\nThe practical takeaway: honest, timely filing is always the cheapest option. Even if you have missed past filings, voluntarily correcting before you are contacted cuts your penalty rate from 15-20% to 5%.\nHow to File Your Side Income Tax Return Filing a kakuteishinkoku is more straightforward than its reputation suggests, especially with online tools. Here is the step-by-step process.\nStep 1: Gather Your Documents Before you sit down to file, collect:\n源泉徴収票 (gensen choshuhyo) — your annual withholding statement from your employer, typically issued in January Records of all side income — invoices, payment records, bank statements, cryptocurrency exchange annual reports Records of all deductible expenses — receipts, invoices, bank/credit card statements My Number (個人番号) — your 12-digit individual identification number Bank account details for receiving any tax refund Step 2: Calculate Your Net Income and Tax Subtract your legitimate business expenses from your gross side revenue to arrive at net side income. Add this to your employment income. Apply the appropriate deductions (basic deduction, iDeCo, social insurance, etc.) to arrive at taxable income. Multiply by the applicable tax rate and subtract any tax credits.\nThis sounds complex, but tax software handles the arithmetic automatically once you input your figures.\nStep 3: Choose Your Filing Method e-Tax (online filing) is the NTA\u0026rsquo;s official electronic filing system at e-tax.nta.go.jp. It accepts filings via My Number Card (with a compatible card reader or the smartphone app) or via an ID/password issued at a tax office. e-Tax is available 24 hours a day throughout the filing period and processes refunds faster than paper filing.\nPaper filing at your local tax office is still available for those who prefer it. Tax offices also run consultation counters during the filing period where staff can assist with basic questions. Expect queues, especially in late February and early March.\nFor those navigating Japan\u0026rsquo;s tax system in a second language, freee makes Japanese tax filing manageable even for non-native speakers. The platform guides you through each step in plain language, automatically calculates deductions, and generates the completed forms required for e-Tax submission.\nFiling Period The standard filing window for the previous year\u0026rsquo;s income is:\nFebruary 16 – March 15\nFor the 2025 tax year (January 1 – December 31, 2025), the filing deadline is March 15, 2026. If March 15 falls on a weekend or public holiday, the deadline shifts to the next business day.\nStep 4: Pay Any Tax Due Tax due can be paid via:\nDirect debit from a bank account linked to e-Tax Credit card payment via the NTA\u0026rsquo;s designated payment portal Convenience store payment (using a payment slip printed from e-Tax) Bank transfer at a financial institution Can Your Employer Find Out About Your Side Job? This is one of the most common concerns for salaried workers with side income, and the answer depends on how you handle one specific step in your tax return.\nThe Residence Tax Notification Mechanism When you file a kakuteishinkoku, the NTA shares the income data with your local municipal government, which then calculates your residence tax for the coming year. By default, residence tax for salaried workers is collected via 特別徴収 (tokubestu choshu) — meaning it is deducted directly from your monthly salary by your employer.\nHere is the problem: your employer receives a residence tax billing notice each June that shows the total amount to be deducted. If this amount is significantly higher than what would be expected based on your salary alone, a HR department paying attention may infer that you have additional income.\nThe Solution: 普通徴収 (Futsu Choshu) When you file your kakuteishinkoku, look for the section that asks how you want to pay your residence tax. Select 普通徴収 (futsu choshu) — self-payment by installment. Under this method, your municipality sends the residence tax bill directly to you (at your home address), and you pay it yourself in four installments. Your employer receives a billing notice only for the portion attributable to your salary income, and the side-income portion never appears in their payroll system.\nImportant caveats:\nThis option is available on the tax return form itself, typically in a checkbox field near the residence tax section. Some municipalities handle the split less cleanly than others, and errors occasionally result in the full amount being sent to the employer anyway. If confidentiality matters, follow up with your municipal tax office to confirm the split was processed correctly. Note that choosing 普通徴収 does not reduce your tax — it only changes who receives the bill and how you pay it. Tips to Reduce Your Side Job Tax 1. Deduct Every Legitimate Business Expense This is the single highest-leverage action available to most side workers. Expenses directly and exclusively related to your side work reduce your taxable income yen-for-yen.\nCommon deductible expenses include:\nComputer, monitor, and peripherals (if used primarily for the side work) Software subscriptions and cloud services Internet service (the work-related proportion) Coworking space or a dedicated home office (proportional to the space used) Transportation to client meetings Professional reference books, courses, and certifications Business phone calls (the work-related proportion) Subcontractors or assistants you pay to help with the work Keep receipts and records for everything. A simple spreadsheet linking each expense to its business purpose is sufficient for most individuals.\n2. Switch to Blue Return (青色申告 / Aoiro Shinkoku) If your side activity qualifies as business income (事業所得) rather than miscellaneous income, you can register for the blue return system by submitting a notification to your tax office.\nThe blue return offers a ¥650,000 special deduction (for e-Tax filers who use double-entry bookkeeping) or a ¥550,000 deduction (for paper filers using double-entry). Even the simplified blue return provides a ¥100,000 deduction.\nFor someone in the 20% bracket, the ¥650,000 deduction alone saves ¥130,000 in income tax plus roughly ¥65,000 in residence tax — ¥195,000 in total annual savings. The bookkeeping requirement is real but manageable with accounting software.\nTo claim the blue return for a given tax year, you must submit the registration notification by March 15 of that year (or within two months of starting the business, whichever is later).\n3. Maximize iDeCo Contributions Contributions to iDeCo (個人型確定拠出年金) are fully deductible from your taxable income. Salaried workers with an employer pension can contribute up to ¥23,000 per month (¥276,000 per year). Contributions are deducted in full when calculating income tax and residence tax.\nFor a worker in the combined 30% bracket (20% income tax + 10% residence tax), maximizing iDeCo contributions saves approximately ¥82,800 per year in tax. The funds grow tax-deferred until retirement, and withdrawals receive favorable treatment as well.\n4. Track the Home Office Deduction Carefully If you work from home on your side job, a portion of rent, utilities, and internet can be deducted. The standard approach is to calculate what percentage of your home\u0026rsquo;s floor area is used exclusively for work, and apply that percentage to the relevant expenses. For a 60 sqm apartment where 8 sqm is used as a dedicated workspace, you could deduct roughly 13% of rent and utilities.\nThe key word is \u0026ldquo;exclusively\u0026rdquo; — the NTA expects home office deductions to reflect space that is genuinely dedicated to work, not a living room where you occasionally open a laptop.\n5. Use Accounting Software to Stay Organized Year-Round The biggest practical obstacle to accurate tax filing is disorganized records. Connecting your business bank account and credit card to accounting software means income and expense data flows in automatically throughout the year. When March arrives, you are generating your return from complete records rather than reconstructing the year from memory and scattered receipts.\nConclusion Japan\u0026rsquo;s tax rules for side workers are more manageable than they appear once you understand the key thresholds and mechanics. To summarize the essentials:\nIf your net side income exceeds ¥200,000, you must file a kakuteishinkoku by March 15. Even below ¥200,000, you likely owe residence tax and should file a juminzei shinkoku at your municipal office. Failure to file triggers a surcharge of 15-20% on unpaid tax, plus late interest of 2.4-8.7% per annum. Voluntary disclosure before NTA contact reduces the surcharge to 5%. Select 普通徴収 on your return to keep the side-income residence tax bill away from your employer. Legitimate expense deductions, the blue return deduction, and iDeCo contributions can meaningfully reduce your taxable side income. The filing process itself — while conducted in Japanese — is navigable with the right tools. freee is one option specifically designed to make Japanese tax compliance accessible, including for workers who are not native Japanese speakers. Whether you use dedicated software or work with a tax accountant, the goal is the same: accurate, on-time filing that keeps you on the right side of Japan\u0026rsquo;s tax authorities while minimizing what you legitimately owe.\nRelated Tools Calculate your ideal freelance hourly rate → Freelance Rate Calculator Estimate your side income taxes → Side Hustle Tax Calculator See your 2026 federal tax bracket and effective rate → Tax Bracket Calculator Calculate Japan take-home pay → Salary Calculator Plan your monthly budget → Budget Planner The information in this article is provided for general educational purposes and reflects the tax rules in effect as of early 2026. Tax laws change, and individual circumstances vary. For advice specific to your situation, consult a registered tax advisor (税理士) in Japan.\nYou May Also Like Freelance Tax Guide 2026: Everything You Need to Know How to File a Kakuteishinkoku for FX Income in Japan: 2026 Guide How to Start Freelancing in 2026: Complete Beginner\u0026rsquo;\u0026rsquo;s Guide ","permalink":"https://productivity-works.com/posts/side-job-tax-japan-rules/","summary":"\u003cp\u003eJapan has seen a surge in side work over the past several years. Ride-sharing, freelance design, online tutoring, crypto trading, rental income — the avenues for earning extra money have never been more accessible. Yet many salaried workers who bring in a little extra cash each month have only the vaguest sense of what the tax rules actually require of them.\u003c/p\u003e\n\u003cp\u003eThat gap between earning and understanding is costly. Japan\u0026rsquo;s National Tax Agency (NTA) actively cross-references income data from various sources, and penalties for late or non-filing add up fast. This guide cuts through the complexity: when you are legally required to file, what counts as taxable side income, exactly how penalties are calculated, and practical steps to reduce your tax bill while staying fully compliant.\u003c/p\u003e","title":"Side Job Tax Rules in Japan: When Do You Need to File and How to Avoid Penalties?"},{"content":"This article contains affiliate links.\nIf you are a salaried worker in Japan dabbling in foreign exchange trading, one question will inevitably arise when the numbers start moving in your favor: how much of that profit actually belongs to you, and how much goes to the Japanese tax authority? Understanding FX tax in Japan is not complicated once you know the rules, but getting it wrong can lead to unexpected bills, penalties, or missed deductions. This guide walks you through everything you need to know, from the headline tax rate to the filing process.\nFX Tax Rate in Japan — The 20.315% Rule The single most important number to memorize is 20.315%. That is the flat tax rate applied to realized FX profits in Japan, regardless of how large your gain is. Unlike employment income, which is subject to progressive rates that climb as high as 45% at the national level, FX profits benefit from a separate, fixed \u0026ldquo;申告分離課税\u0026rdquo; (separate declaration tax) system.\nThe 20.315% is not one tax — it is a combination of three:\nComponent Rate National income tax (所得税) 15.000% Reconstruction special income tax (復興特別所得税) 0.315% Residence tax (住民税) 5.000% Total 20.315% National income tax at 15% is the core levy collected by the national government on gains from financial instruments classified under \u0026ldquo;先物取引に係る雑所得\u0026rdquo; (miscellaneous income related to futures transactions). FX trades executed through Japanese brokers are legally treated as futures contracts under the Financial Instruments and Exchange Act, which is why this favorable flat rate applies rather than the ordinary progressive rate on miscellaneous income.\nReconstruction special income tax at 0.315% is a surcharge introduced after the 2011 Great East Japan Earthquake to fund reconstruction. It is calculated as 2.1% of the national income tax amount (2.1% × 15% = 0.315%). This surcharge applies to all income taxes and currently runs through the 2037 tax year.\nResidence tax at 5% is collected by your municipality rather than the national government. It is based on your previous year\u0026rsquo;s income and is typically withheld from your monthly salary starting in June after you file your return. The 5% rate on FX gains is uniform nationwide.\nOne practical implication of this structure: if you are trying to estimate your after-tax FX profit quickly, simply multiply your gain by 0.79685. That is how much you keep.\nWhen Do Salaried Workers Need to File? Japan has a specific rule that gives salary workers a partial reprieve from filing paperwork, but it comes with an important asterisk.\nThe ¥200,000 threshold: If your total \u0026ldquo;other income\u0026rdquo; — which includes FX profits, side job revenue, rental income, and similar items — is ¥200,000 or less in a calendar year, you are not required to file a national income tax return (確定申告, kakuteishinkoku). This exemption applies only to people whose salary income comes from a single employer that withholds year-end tax adjustments (年末調整).\nThe asterisk — residence tax still applies: Even if you fall under the ¥200,000 threshold and skip the national tax return, you are still legally required to report that income to your municipality for residence tax purposes. You do this by filing a \u0026ldquo;住民税申告\u0026rdquo; (residence tax declaration) directly with your local city or ward office by March 15. Many people overlook this step and later receive an unexpected bill or notice from their municipality.\nWhen you must file regardless:\nYour FX profit exceeds ¥200,000 in the calendar year You have income from multiple employers You have other reasons requiring a kakuteishinkoku (medical expense deductions, home loan deductions, etc.) You want to carry forward FX losses to future years (explained below) The filing deadline for the kakuteishinkoku is March 15 of the following year, covering income earned in the previous calendar year (January 1 to December 31).\nHow to Calculate Your FX Tax The calculation itself is straightforward. Your taxable FX income is:\nRealized FX profit − Deductible losses and costs = Net taxable income\nDeductible costs include trading commissions paid to your broker and, in some cases, costs directly attributable to your trading activities (such as software subscriptions used exclusively for FX analysis). Unrealized gains on open positions are not taxable — only profits locked in by closing a trade count.\nOnce you have your net taxable income, apply 20.315%.\nExample 1: ¥300,000 profit Item Amount Realized profit ¥300,000 Trading commissions ¥3,000 Net taxable income ¥297,000 Tax at 20.315% ¥60,335 After-tax profit ¥239,665 At this level you are also above the ¥200,000 threshold, so a kakuteishinkoku is required.\nExample 2: ¥500,000 profit Item Amount Realized profit ¥500,000 Trading commissions ¥5,000 Net taxable income ¥495,000 Tax at 20.315% ¥100,559 After-tax profit ¥394,441 Notice that even though your gross profit doubled compared to Example 1, the effective percentage going to tax remains exactly 20.315%. This is the advantage of the flat-rate separate taxation system.\nExample 3: ¥1,000,000 profit Item Amount Realized profit ¥1,000,000 Trading commissions ¥8,000 Net taxable income ¥992,000 Tax at 20.315% ¥201,524 After-tax profit ¥790,476 For a salaried worker whose employment income is already taxed at a combined marginal rate of 30–40% or higher, the 20.315% rate on FX gains is significantly more favorable. A trader in the 33% national income tax bracket, for example, saves roughly 13 percentage points by having gains taxed under the separate declaration system rather than as ordinary miscellaneous income.\nUse our FX Profit Calculator for instant results without doing the math manually.\nWhat About FX Losses? Losses are not simply written off and forgotten. Japan\u0026rsquo;s tax code gives FX traders a meaningful tool: the 3-year loss carryforward (損失の繰越控除).\nIf your FX trading results in a net loss in a given year, you can carry that loss forward and offset it against FX profits in the following three years. This can substantially reduce your tax bill in profitable years that follow a losing streak.\nHow it works in practice:\nYear 1: Net FX loss of ¥400,000 → file a return, record the loss Year 2: Net FX profit of ¥250,000 → offset against ¥250,000 of the carryforward loss, taxable income = ¥0, remaining carryforward = ¥150,000 Year 3: Net FX profit of ¥300,000 → offset against remaining ¥150,000, taxable income = ¥150,000, tax = ¥30,472 Without the carryforward, Year 3 tax would have been ¥60,945 on the full ¥300,000. The carryforward saved approximately ¥30,473.\nCritical requirement: you must file every year. The carryforward is only available if you file a kakuteishinkoku in the year the loss occurred and in every subsequent year you want to maintain the carryforward. If you skip a year\u0026rsquo;s filing — even a year in which you had no FX activity — the carryforward is extinguished. This is one of the most costly mistakes FX traders make.\nThe loss carryforward applies specifically to losses classified under the futures-related miscellaneous income category. It cannot be used to offset ordinary salary income or other categories of income.\nFX vs Stock Trading Tax — Similar but Not Interchangeable Both FX profits and listed stock trading profits are taxed at the same headline rate of 20.315%, which can make them seem equivalent from a tax perspective. They share the rate but not the category, and that distinction matters.\nFX profits are classified as \u0026ldquo;先物取引に係る雑所得\u0026rdquo; — miscellaneous income related to futures transactions.\nListed stock profits (from shares, ETFs, investment trusts) are classified as \u0026ldquo;株式等に係る譲渡所得\u0026rdquo; — transfer income related to shares and similar instruments.\nBecause they fall into different categories, FX losses cannot be offset against stock gains, and stock losses cannot be offset against FX gains. Each is ring-fenced within its own category.\nFeature FX (OTC/Margin) Listed Stocks / ETFs Tax rate 20.315% 20.315% Tax category Futures miscellaneous income Transfer income (shares) Loss carryforward 3 years 3 years Cross-offset with each other Not permitted Not permitted NISA exemption applies No Yes (within NISA account) Loss offset with dividends Not permitted Permitted (with special election) One frequently misunderstood point: stock investors can elect to offset stock trading losses against dividend income within the same tax category. FX traders have no equivalent option — FX losses can only be offset against future FX gains.\nAnother important distinction is NISA (少額投資非課税制度), Japan\u0026rsquo;s tax-exempt investment account. NISA applies exclusively to stocks, investment trusts, and ETFs. FX margin trading is explicitly excluded, so there is no tax shelter equivalent available for FX profits.\nHow to File Your FX Tax Return For most salaried workers, filing a kakuteishinkoku for FX income is a one-time learning curve that becomes routine in subsequent years. Here is the process:\nStep 1: Gather your documents\nYour FX broker is required to issue an annual trading statement (年間取引報告書) by late January for the previous year\u0026rsquo;s activity. This document summarizes total realized profit and loss, commissions paid, and other relevant figures. Download it from your broker\u0026rsquo;s online platform.\nStep 2: Choose your filing method\nThe National Tax Agency (NTA) offers e-Tax, its online filing system, which is the most efficient option. You can access it at the NTA\u0026rsquo;s website using a My Number Card and a compatible card reader or smartphone. e-Tax pre-fills some fields and reduces manual errors.\nAlternatively, you can download paper forms and mail them, or visit a tax office in person. However, e-Tax is strongly recommended for its speed and the immediate confirmation it provides.\nStep 3: Complete the correct forms\nFX income is reported on the kakuteishinkoku B form (確定申告書B). Within that return, you need to attach:\nSchedule 3 (第三表): \u0026ldquo;Separate taxation income declaration table\u0026rdquo; — this is where you declare the FX profit amount and calculate the 20.315% tax Attachment form for futures-related miscellaneous income (先物取引に係る雑所得用の付表): This attachment provides the detailed breakdown of trades and is required when claiming a loss carryforward If you are carrying forward a loss from a prior year, you will also reference the prior year\u0026rsquo;s filed return to confirm the amount.\nStep 4: Report residence tax preference\nOn the kakuteishinkoku, there is a section where you can elect how your residence tax on separately-taxed income is assessed. In most cases, the default \u0026ldquo;普通徴収\u0026rdquo; (ordinary collection) option keeps your FX income details separate from the notice sent to your employer, preserving privacy about your trading activity.\nStep 5: Pay any tax owed\nTax due can be paid via e-Tax bank transfer, convenience store payment slip, credit card (through the NTA payment portal), or at a bank. The deadline for both filing and payment is March 15.\nfreee guides you through Japanese tax filing step by step, including FX income reporting, in a way that makes the process manageable even if your Japanese is limited. The software asks guided questions and automatically routes your answers to the correct form fields.\nCommon Mistakes to Avoid 1. Forgetting that residence tax still applies below ¥200,000\nThe ¥200,000 exemption only waives the requirement to file a national income tax return. Your municipality still expects a residence tax declaration. Skipping this is a compliance error and can result in a notice or, in rare cases, a penalty.\n2. Not filing loss years\nAs explained above, the 3-year loss carryforward is voided if you fail to file in the loss year or any subsequent year. A loss that feels insignificant — say, ¥50,000 — could offset a sizeable future gain if preserved through consistent filing. The cost of filing is small; the cost of losing the carryforward can be substantial.\n3. Missing the annual trading statement\nSome brokers issue statements only through their platform rather than mailing them. If you changed brokers during the year, collect statements from all brokers you used. Every realized trade is taxable regardless of which platform executed it.\n4. Treating swap income as non-taxable\nSwap interest (スワップポイント) earned on carry trade positions is also taxable as FX miscellaneous income. It appears separately on your annual trading statement but must be included in your total.\n5. Mixing personal and trading expenses\nA portion of expenses like internet bills or trading software subscriptions may be deductible if they are genuinely and proportionally used for trading. However, claiming excessive or personal expenses as deductions invites scrutiny. Keep clean records with receipts and document the business purpose.\n6. Assuming your employer handles it\nYear-end tax adjustment (年末調整) conducted by your employer covers only your employment income. It does not touch FX profits under any circumstances. You are solely responsible for declaring and paying tax on FX gains.\n7. Confusing the calendar year with fiscal year\nJapan\u0026rsquo;s personal income tax year runs January 1 to December 31, not April to March (which is the government fiscal year). All trades closed between January 1 and December 31 of a given year are reported together in the kakuteishinkoku filed the following January–March.\nConclusion FX trading as a salaried worker in Japan carries a clear and manageable tax obligation. The 20.315% flat rate is favorable compared to the progressive income tax rates applied to employment income, but it requires active compliance: filing an annual return, reporting to your municipality even in low-profit years, and rigorously maintaining loss carryforward records.\nThe key numbers to keep in mind:\n20.315% on all net realized FX profits ¥200,000 threshold below which the national return is not required — but the residence tax declaration still is 3 years of loss carryforward, contingent on filing every year March 15 annual deadline for filing and payment Getting these basics right puts you in full compliance and positions you to take full advantage of the deductions and carryforwards available under Japanese tax law.\nCheck your take-home pay after FX taxes to understand the full picture of what you earn and keep each year.\nRelated Articles NISA vs FX in Japan: Which Is Better for Growing Your Money? FX Loss Carryforward in Japan: How to File Best FX Brokers in Japan (English Guide) FX Spread Comparison Japan 2026 Kakuteishinkoku FX Income Guide Related Tools Calculate forex profits → Forex Profit Calculator Estimate your tax bracket → Tax Bracket Calculator Calculate your take-home pay → Salary Calculator This article is for informational purposes only and does not constitute tax advice. Tax rules can change. Consult a licensed tax accountant (税理士) for advice specific to your situation.\n","permalink":"https://productivity-works.com/posts/fx-tax-japan-salary-worker/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eIf you are a salaried worker in Japan dabbling in foreign exchange trading, one question will inevitably arise when the numbers start moving in your favor: how much of that profit actually belongs to you, and how much goes to the Japanese tax authority? Understanding FX tax in Japan is not complicated once you know the rules, but getting it wrong can lead to unexpected bills, penalties, or missed deductions. This guide walks you through everything you need to know, from the headline tax rate to the filing process.\u003c/p\u003e","title":"How Much Tax Do You Pay on FX Profits in Japan? A Salary Worker's Guide"},{"content":"This article contains affiliate links.\nIf you trade foreign exchange (FX) in Japan, you have likely encountered the word kakuteishinkoku (確定申告) — Japan\u0026rsquo;s annual income tax return. Filing it correctly for FX income is not as straightforward as it sounds. FX profits are taxed under a special category, reported on a separate schedule, and subject to a flat rate that differs from ordinary income tax. Miss a step or use the wrong form, and you risk penalties, overpaying, or losing the right to carry forward losses.\nThis guide walks you through everything you need to know about filing a kakuteishinkoku for FX income in Japan in 2026: whether you need to file, what documents to gather, how FX profits are classified under Japanese tax law, and how to complete the process via e-Tax from start to finish.\nDo You Need to File a Kakuteishinkoku for FX Income? Whether you are legally required to file depends primarily on your employment status and the size of your FX profits. Here is how the rules break down:\nSalaried Employees (Salary Income) If you are a company employee whose taxes are handled through year-end adjustment (年末調整), you are still required to file a kakuteishinkoku if your FX profit for the year exceeds ¥200,000. This threshold applies to the combined total of all miscellaneous income and side income outside your main salary.\nIf your FX profit is ¥200,000 or below, you are technically exempt from filing an income tax return with the National Tax Agency (NTA). However — and this is an important caveat — you are not exempt from paying residence tax (住民税). Your municipality collects residence tax based on total income, including FX income. This means even if your FX gains fall under the ¥200,000 threshold, you may still need to file a separate residence tax declaration with your local ward or city office, depending on local rules. Check with your municipality directly.\nSelf-Employed / Freelancers / Business Owners If you are self-employed or run your own business, you are required to file a kakuteishinkoku every year regardless of the amount of your FX income. FX profits (or losses) must be reported in addition to your regular business income.\nAnyone with FX Losses Even if your FX trading resulted in a net loss for the year, filing a kakuteishinkoku is strongly advisable. Japan\u0026rsquo;s tax law allows you to carry forward FX losses for up to three years, offsetting future gains. You forfeit this benefit entirely if you fail to file.\nNon-Residents and Visa Status Tax residency in Japan is determined by domicile, not citizenship or visa type. If you lived in Japan for more than 183 days in the tax year, you are generally treated as a resident taxpayer and are subject to Japanese tax on worldwide income, including FX income from overseas brokers.\nKey Dates for the 2026 Filing Season The kakuteishinkoku filing season for income earned in the 2025 calendar year (Reiwa 7) runs as follows:\nEvent Date Filing period opens February 16, 2026 Filing deadline March 15, 2026 Tax payment deadline March 15, 2026 Deadline for tax payment by bank transfer (furikomi) April 20, 2026 (approximate — confirm with NTA) Late filing (期限後申告) is possible but triggers a late-filing penalty (無申告加算税) of 5–15% of the tax owed, plus delinquency interest (延滞税) that accrues daily. It is always better to file late than not at all — the penalty is lower than the cost of non-filing discovered during an audit.\nIf you are due a refund (還付申告), you can file as early as January 1, and there is no strict deadline — refund filings are accepted year-round. However, refund claims expire after five years, so do not leave money on the table.\nDocuments You Need Before You Start Gather the following before you begin. Missing any of these will stall your filing.\nChecklist Annual trading statement (年間取引報告書) — Issued by your FX broker, typically in January for the previous year. This is the core document. It shows total profit/loss, number of contracts, and any withholding tax. Download it from your broker\u0026rsquo;s member portal. Withholding tax certificate (支払調書 or 特定口座年間取引報告書) — If your broker withheld tax on your behalf (unusual for FX, but relevant for some account types), you will need this slip. My Number card (マイナンバーカード) — Required for e-Tax login and identity verification. If you do not have the card itself, you can use your My Number notification slip plus a photo ID, but the card is strongly recommended for a smooth e-Tax experience. Schedule 3 (第三表: 分離課税用) — The separate taxation schedule appended to Form B. This is where FX income is entered. It is not a physical document you gather — you select it during the filing process — but knowing it exists will save you confusion. Attachment for futures transactions (先物取引に係る雑所得等の計算明細書) — A supporting worksheet that details your FX profit/loss calculation. Available on the NTA website or auto-generated in e-Tax. Bank account information — For receiving refunds or making payments via direct debit. Previous year\u0026rsquo;s return (if applicable) — Needed if you are carrying forward losses from prior years. How FX Income Is Classified Under Japanese Tax Law This is where many filers go wrong. FX income in Japan is not classified as ordinary miscellaneous income (雑所得) in the standard sense. It falls under a specific sub-category:\n先物取引に係る雑所得等 (Miscellaneous income from futures transactions)\nThis classification is established under Article 41-14 of the Special Taxation Measures Law (租税特別措置法). Domestic FX trading (行為差金決済取引) conducted through licensed Japanese brokers is explicitly included.\nTax Rate: Separate Taxation at 20.315% Because FX income is classified as futures-related miscellaneous income, it is subject to 申告分離課税 (separate self-assessed taxation), not the progressive income tax brackets that apply to salary income. The flat rate is:\nTax component Rate National income tax 15% Special reconstruction surtax 0.315% Residence tax 5% Total 20.315% This rate applies regardless of how much you earn. Whether your FX profit is ¥300,000 or ¥30,000,000, the tax rate is the same 20.315%. This is both good news (high earners avoid top bracket rates of up to 55%) and important to understand (you cannot offset FX income against salary income to reduce your bracket).\nLoss Carryforward Net losses on FX can be carried forward for up to three years and offset against future FX profits, reducing your tax in profitable years. This is only available if you file every year, including loss years.\nStep-by-Step Filing via e-Tax The easiest and most reliable way to file is through e-Tax, the NTA\u0026rsquo;s online filing system at https://www.e-tax.nta.go.jp/ . Here is how to complete the process from scratch.\nStep 1: Get Your Annual Trading Statement from Your Broker Log in to your FX broker\u0026rsquo;s member portal and download the 年間取引報告書 for the previous calendar year. Most major brokers (GMO Click, DMM FX, SBI FX Trade, Saxo Bank Japan, etc.) make this available by mid-January.\nKey figures you will need from this document:\nTotal realized profit/loss (決済損益) Swap interest received/paid (スワップポイント) — this is also taxable and must be included Any fees charged by the broker that offset your profit Note: Unrealized (open) positions at year-end are not included in that year\u0026rsquo;s taxable income. Only closed trades count.\nStep 2: Log In to e-Tax with Your My Number Card Navigate to https://www.e-tax.nta.go.jp/ and select 確定申告書等作成コーナー (Tax Return Preparation Corner). You will be prompted to authenticate:\nWith My Number card: Use a card reader or the smartphone NFC authentication app (マイナポータルアプリ). This is the most seamless option. With ID/password: If you obtained an e-Tax ID and password from your local tax office, you can use those instead. For first-time filers, allow extra time to set up your authentication method before the filing deadline.\nStep 3: Select Kakuteishinkoku-sho B and Schedule 3 In the filing preparation interface, select:\n確定申告書B (Form B) — This is the general-purpose form used for all income types other than employment-only filers. 第三表 (Schedule 3: 分離課税用) — The separate taxation schedule. Selecting this will add the FX income section to your return. If you are a salaried employee also reporting salary income, you will enter your salary details on Form B as well. The two streams (salary income and FX separate income) coexist on the same return.\nStep 4: Enter FX Income in the Futures Transactions Section In Schedule 3, locate the section labeled 先物取引に係る雑所得等. You will enter:\nTotal income amount (収入金額): Gross receipts from FX trading (total profit before expenses) Necessary expenses (必要経費): Deductible costs directly related to FX trading (see the expenses section below) Net income (所得金額): Income minus expenses Carried-forward loss from prior years (前年分からの繰越損失額): If applicable The system will automatically calculate the 20.315% tax on your net FX income.\nAlso fill in the 先物取引に係る雑所得等の計算明細書 (calculation detail worksheet). This is an attachment to your return that provides a breakdown of trades. For most retail traders, the annual trading statement from your broker maps directly onto this form.\nStep 5: Review and Submit Before submitting:\nDouble-check that your FX income figures match your broker\u0026rsquo;s annual statement exactly Confirm your bank account details for refunds or direct debit payment Review the final tax calculation Once satisfied, click submit (送信). e-Tax will generate a confirmation number. Save this — it is your proof of filing.\nIf you owe tax, you can pay via:\nDirect debit (口座振替) — set up in advance Pay-easy (ペイジー) — via internet banking Credit card (クレジットカード納付) — a convenience fee applies Convenience store payment — using a QR code or payment slip Using Accounting Software to Simplify the Process If you have multiple income streams — freelance income, rental income, or investment income alongside FX — manually preparing Schedule 3 and the supporting worksheets can become time-consuming. freee can generate your filing documents and submit directly to e-Tax, consolidating all your income sources into a single, submission-ready return. It supports Schedule 3 and the futures transactions attachment, which makes it particularly useful for FX traders who also have business or freelance income.\nCommon Mistakes When Filing FX Income Mistake 1: Filing FX Income as Ordinary Miscellaneous Income This is the most consequential error. Some filers — or even some accountants unfamiliar with FX — enter FX profits in the standard miscellaneous income (雑所得) section of Form B instead of in Schedule 3 under separate taxation. This is incorrect. Regular miscellaneous income is subject to progressive tax rates (up to 45% nationally, plus residence tax), meaning you could pay far more than the correct 20.315% flat rate. The NTA may also flag the inconsistency.\nAlways use 第三表 (Schedule 3) and the 先物取引に係る雑所得等 classification.\nMistake 2: Forgetting to Claim Deductible Expenses Many traders are unaware that legitimate expenses directly related to FX trading can be deducted from gross profit, reducing taxable income. These are not large amounts for most retail traders, but they are real deductions you are entitled to. See the expenses section below.\nMistake 3: Not Filing Loss Years If your FX trading resulted in a net loss, you might think there is nothing to file. However, not filing means you cannot carry that loss forward to offset future profits. Over a three-year period, a loss year left unfiled could cost you tens of thousands of yen in unnecessary tax on subsequent profitable years.\nMistake 4: Ignoring Swap Income Swap points (スワップポイント) — the interest-like income earned from holding certain FX positions overnight — are taxable FX income and must be included in your total. Your broker\u0026rsquo;s annual trading statement should include this figure, but double-check that you are reading the statement correctly.\nMistake 5: Using the Wrong Year\u0026rsquo;s Figures The kakuteishinkoku filed in early 2026 covers income earned between January 1 and December 31, 2025. Confusion sometimes arises because you are filing in 2026 for 2025 income. Make sure your broker statement covers the correct calendar year.\nMistake 6: Missing the Deadline Without Filing for Extension Unlike some other countries, Japan does not have a general filing extension system for individuals. If you cannot file by March 15, you should still file as soon as possible. A late-filed return (期限後申告) incurs penalties, but those penalties are lower than what results from an NTA-initiated assessment (更正処分) discovered during a later audit.\nDeductible Expenses for FX Trading Japanese tax law allows you to deduct necessary expenses (必要経費) that are directly and clearly related to your FX trading activity. The standard for deductibility is that the expense must be incurred in order to generate the FX income — it cannot be a personal expense that merely overlaps with trading.\nCommonly accepted deductible expenses include:\nExpense category Notes Internet connection fees Proportional to trading use. If you use the same line for personal and trading purposes, only the trading-use proportion is deductible. Keep records. VPS (Virtual Private Server) costs If you run automated trading systems (EA) or need reliable server connectivity for your FX platform, VPS fees are deductible. Books and publications Technical analysis books, FX strategy guides, and tax/accounting books directly related to your trading activity. Seminars and courses Fees for FX trading seminars, online courses, or workshops. Must be directly related to improving trading skill, not general investment education. Trading platform fees Any subscription fees charged by your broker for a premium trading platform or data feed. Accounting software fees Software used specifically to track and report FX income. Home office (partial) If you trade from a dedicated workspace at home, a proportional share of rent may be deductible. This is harder to substantiate and more closely scrutinized — keep detailed records. What is not deductible:\nLosses from other investment types (stock losses cannot offset FX income) General living expenses, even if you \u0026ldquo;think about FX\u0026rdquo; while using them Travel to overseas markets or exchanges (rarely accepted without very specific business purpose) The NTA requires documentation. Keep receipts, invoices, and bank statements for all claimed expenses. In practice, the amounts involved for a retail FX trader are usually modest, but the deductions are legitimate and worth claiming.\nConclusion Filing a kakuteishinkoku for FX income in Japan requires attention to classification, form selection, and timing. The critical points to remember are: FX income falls under 先物取引に係る雑所得等 on Schedule 3, is taxed at a flat 20.315%, and must be reported whether you made money or lost it — because losses are only valuable if you file them. The e-Tax system makes the process manageable once you have your broker\u0026rsquo;s annual trading statement in hand.\nStart collecting your documents in January, authenticate your My Number card for e-Tax early, and give yourself time before the March 15 deadline to work through the Schedule 3 worksheet. If you have multiple income streams, accounting tools can save significant time.\nReady to estimate your tax before you file? Calculate your FX tax with our FX Profit Calculator .\nHave other income sources alongside FX? Understand your full tax picture with our Side Income Tax Calculator .\nRelated Tools Calculate forex profits → Forex Profit Calculator Estimate your tax bracket → Tax Bracket Calculator This article is for general informational purposes. Tax rules change annually — always confirm the current rules with the NTA website (nta.go.jp ) or a qualified tax professional (税理士) before filing.\nYou May Also Like How Much Tax Do You Pay on FX Profits in Japan? A Salary Worker\u0026rsquo;s Guide FX Trading and Japan Tax Filing: Can You Carry Forward Losses for 3 Years? Side Job Tax Rules in Japan: When Do You Need to File and How to Avoid Penalties? ","permalink":"https://productivity-works.com/posts/kakuteishinkoku-fx-income-guide/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eIf you trade foreign exchange (FX) in Japan, you have likely encountered the word \u003cstrong\u003ekakuteishinkoku\u003c/strong\u003e (確定申告) — Japan\u0026rsquo;s annual income tax return. Filing it correctly for FX income is not as straightforward as it sounds. FX profits are taxed under a special category, reported on a separate schedule, and subject to a flat rate that differs from ordinary income tax. Miss a step or use the wrong form, and you risk penalties, overpaying, or losing the right to carry forward losses.\u003c/p\u003e","title":"How to File a Kakuteishinkoku for FX Income in Japan: 2026 Guide"},{"content":"This article contains affiliate links.\nThe job market in 2026 has a strange duality. AI-powered Applicant Tracking Systems (ATS) screen out the majority of resumes before a human sees them. Meanwhile, AI tools available to job seekers can create resumes and cover letters that are more targeted than anything a human could produce manually.\nThe result: job seekers who understand how to use AI tools are dramatically outperforming those who don\u0026rsquo;t. Not because AI writes better than humans — it doesn\u0026rsquo;t — but because it enables faster iteration, better keyword matching, and more thorough preparation than any individual can sustain manually.\nThis guide covers how to use AI across every stage of your job search — from resume optimization through offer negotiation.\nHow the Modern Hiring Process Actually Works Understanding the system you\u0026rsquo;re navigating is the first step.\nStage 1: ATS screening. Most companies with more than 20 employees use an ATS (Applicant Tracking System) to filter applications before a human reviews them. Common systems include Workday, Greenhouse, Lever, and Taleo. These systems parse your resume, extract information, and score it against job requirements — primarily based on keyword matching.\nA resume that is perfect for humans but poorly optimized for ATS may never be seen by a person.\nStage 2: Recruiter screen. If you clear the ATS, a recruiter typically spends 6–10 seconds on the first pass. Their job is to quickly identify candidates worth a phone call. They\u0026rsquo;re looking for clear indicators of relevance: job titles, company names, measurable results, and education.\nStage 3: Hiring manager review. More thorough — but still skimming. A hiring manager might spend 2 minutes on a resume they find interesting.\nStage 4: Interviews. Phone screen, technical/skills assessment, panel interview, final interview. Each stage selects further.\nAI tools can improve your performance at stages 1, 2, 3, and 4. Here\u0026rsquo;s how.\nPart 1: Resume Optimization with AI Step 1: Start with Your Master Resume Before using AI, build a complete master resume that contains everything:\nEvery job, every title, every date All quantified achievements (with numbers, percentages, dollar amounts) All skills, certifications, tools, languages All education and relevant coursework This master resume is never sent to anyone — it\u0026rsquo;s your source document. AI will help you tailor versions from it for each application.\nStep 2: Extract Keywords from Job Postings The most important thing your resume can do is match the language of the job posting. Recruiters and ATS systems look for the exact terms used in the job description.\nPrompt to use:\n\u0026ldquo;Here is a job description for a [Job Title] role at [Company]. Extract the 20 most important keywords and phrases — skills, tools, qualifications, and experience — ranked by how frequently or prominently they appear. Format as a list.\u0026rdquo;\nThen: identify which of those keywords appear in your resume and which are missing. For keywords that accurately describe your experience but aren\u0026rsquo;t in your current resume, add them using your actual experience.\nTools that automate this: Jobscan, Resumeworded, and Teal all compare your resume against job postings and provide an ATS optimization score. (Try Jobscan at jobscan.co )\nStep 3: Rewrite Bullet Points for Impact The difference between a weak and strong resume bullet point is specificity and measurable outcomes.\nWeak: \u0026ldquo;Managed social media accounts and created content\u0026rdquo;\nStrong: \u0026ldquo;Grew Instagram following from 4,200 to 31,000 over 18 months; increased engagement rate from 1.8% to 6.4% through a short-form video strategy\u0026rdquo;\nPrompt to transform weak bullets:\n\u0026ldquo;Rewrite the following resume bullet point to be more specific, results-oriented, and include measurable outcomes. If the original lacks specific numbers, keep [X] placeholders where I can insert real data. Original: [your bullet point]\u0026rdquo;\nThe formula for strong bullets: Action verb + what you did + how you did it + the result (quantified)\nRun every bullet point through this process. Even if you can only quantify 40% of them, the upgrade to those is significant.\nStep 4: Tailor Your Summary Section The resume summary (3–4 sentences at the top) is your pitch. For each application, it should specifically match the role and company.\nPrompt:\n\u0026ldquo;Write a 3-sentence professional resume summary for a candidate applying for a [Job Title] role at [Company]. The candidate has [X years] of experience in [relevant field], with specific expertise in [your top 3 skills]. The job description emphasizes [key requirements from the posting]. Write in first-person without the pronoun \u0026lsquo;I\u0026rsquo;. Avoid clichés like \u0026lsquo;results-driven\u0026rsquo; or \u0026lsquo;passionate\u0026rsquo;.\u0026rdquo;\nStep 5: Optimize Formatting for ATS ATS systems parse text and can choke on complex formatting. For applications going through an ATS:\nUse a single-column layout (not the fancy two-column templates) Use standard section headers: Work Experience, Education, Skills (not creative alternatives) No tables, text boxes, or headers/footers — ATS systems often can\u0026rsquo;t read these Use standard fonts (Arial, Calibri, Georgia) Save as .docx or PDF depending on what the application system specifies Use AI to check your resume for these issues: paste your resume text and ask if it contains any formatting elements that might cause ATS parsing problems.\nPart 2: Cover Letters That Don\u0026rsquo;t Get Ignored Most cover letters are ignored because they repeat what\u0026rsquo;s already in the resume and lead with \u0026ldquo;I am excited to apply for the position of\u0026hellip;\u0026rdquo;\nA cover letter worth reading does three things:\nOpens with a specific hook (something you know about the company\u0026rsquo;s current situation) Connects your specific experience to their specific problem Ends with a clear, confident close Prompt for a strong cover letter:\n\u0026ldquo;Write a cover letter for [Your Name] applying for [Job Title] at [Company]. Key context about the company: [something specific — a recent product launch, their stated mission, a challenge they\u0026rsquo;re facing]. My most relevant experience for this role: [2–3 specific achievements]. The job posting emphasizes [key requirements]. Write a cover letter that opens with a specific hook rather than a generic opener. Keep it to 3 short paragraphs. Confident but not arrogant tone.\u0026rdquo;\nThen edit it to ensure it sounds like you, not like AI. The draft is the starting point, not the submission.\nPart 3: Interview Preparation with AI Generate Custom Interview Questions Every role and company will ask predictably structured questions — but the specific topics vary. AI can generate a role-specific interview prep list in minutes.\nPrompt:\n\u0026ldquo;I have an interview for a [Job Title] role at [Company type/industry]. The role involves [key responsibilities from the posting]. Generate 20 likely interview questions covering: behavioral questions, technical/skills questions, situational questions, and culture/values questions. For each, briefly note what the interviewer is likely assessing.\u0026rdquo;\nPractice answering these out loud, not just mentally. Speaking an answer and thinking an answer are very different experiences.\nStructure Your Answers with STAR For behavioral questions (\u0026ldquo;Tell me about a time when\u0026hellip;\u0026rdquo;), the STAR framework produces the clearest answers:\nSituation: Set the context briefly Task: What you were responsible for Action: Specifically what you did (use \u0026ldquo;I,\u0026rdquo; not \u0026ldquo;we\u0026rdquo;) Result: The quantified outcome AI prompt to refine a STAR answer:\n\u0026ldquo;Here is my draft answer to the interview question \u0026lsquo;[question]\u0026rsquo;: [your answer]. Rewrite it using the STAR framework. Make it concise (under 2 minutes when spoken), emphasize my specific actions, and end with a clear, quantified result. Flag any places where I\u0026rsquo;ve said \u0026lsquo;we\u0026rsquo; when I should specify my individual contribution.\u0026rdquo;\nResearch the Company Thoroughly Prompt:\n\u0026ldquo;I have an interview at [Company]. Summarize what I should know about: their core business model, recent news or developments, their main competitors, stated company culture and values, and likely challenges in their industry. Format this as a brief interview prep document.\u0026rdquo;\nFollow up by checking their LinkedIn, Glassdoor reviews, and recent press coverage yourself — AI knowledge has a cutoff date and won\u0026rsquo;t know about very recent developments.\nPrepare Smart Questions to Ask The questions you ask at the end of an interview signal your thinking level and preparation. Avoid questions whose answers are on the company website.\nStrong questions generated by AI:\n\u0026ldquo;Generate 10 thoughtful questions I could ask at the end of an interview for a [Job Title] role. These should demonstrate strategic thinking, genuine curiosity about the team and role, and interest in the company\u0026rsquo;s direction. Avoid questions about salary or generic questions a candidate who didn\u0026rsquo;t research the company could ask.\u0026rdquo;\nUse 3–4 from the list, personalizing them with anything specific you learned during the interview.\nPart 4: Negotiating Your Offer with AI Getting an offer is the setup for the most financially significant conversation in your job search. The difference between accepting the initial offer and negotiating successfully is typically $5,000–$20,000 in base salary — and that difference compounds over your career.\nResearch Your Market Value Prompt:\n\u0026ldquo;What is the market salary range for a [Job Title] with [X years] of experience in [City/Remote] in [industry]? Consider data from multiple sources. What factors would push toward the higher end of the range?\u0026rdquo;\nSupplement AI research with:\nLinkedIn Salary Glassdoor salary data Levels.fyi (for tech roles) Robert Half or Michael Page salary guides (UK) Craft Your Negotiation Response Prompt:\n\u0026ldquo;I received a job offer for [Job Title] with a base salary of $[X]. Based on market data, I believe the appropriate range is $[Y]–$[Z]. I have [specific leverage point — competing offer, strong experience, specialized skill]. Write a professional email that expresses genuine enthusiasm for the role, presents a counter of $[specific number], briefly justifies it, and keeps the tone collaborative rather than adversarial. Under 150 words.\u0026rdquo;\nThen practice saying this out loud. If the negotiation happens over the phone, you\u0026rsquo;ll want the words to flow naturally.\nThe Complete AI Job Search Toolkit Task Best Tool Resume keyword optimization Jobscan, Teal, or ChatGPT Resume bullet point rewriting ChatGPT, Claude ATS formatting check Resumeworded, Jobscan Cover letter drafts ChatGPT, Claude Interview question generation ChatGPT STAR answer refinement Claude (better at conversational writing) Company research ChatGPT + Perplexity for current events Salary research ChatGPT + LinkedIn Salary + Glassdoor Negotiation email ChatGPT, Claude [Try Teal free at tealhq.com] [Try Jobscan at jobscan.co]\nWhat AI Can\u0026rsquo;t Do for Your Job Search Build relationships. Referrals account for 30–50% of hires at many companies. AI cannot attend networking events, send a thoughtful LinkedIn message, or have a coffee with someone who will later pass on your name. Invest in relationships even when you\u0026rsquo;re not actively looking.\nMake up experience. AI can help you articulate your experience more clearly and compellingly. It cannot create experience you don\u0026rsquo;t have. Don\u0026rsquo;t allow AI-assisted resume writing to drift into misrepresentation — it always surfaces in an interview.\nReplace genuine enthusiasm. A hiring manager who spends time with you in an interview will know if you\u0026rsquo;re genuinely excited about the role or just need a job. AI can prepare you, but the energy and authenticity are yours to bring.\nYour Next Three Actions Paste your current resume into ChatGPT and ask it to identify the three weakest bullet points and suggest stronger alternatives.\nFind a job posting that interests you. Run it through Jobscan or use the keyword extraction prompt above. Note the gaps between your resume and the posting.\nGenerate 20 interview questions for your target role. Answer three of them out loud, record yourself, and watch it back once.\nTargeting the Japanese job market? doda offers bilingual job listings and agent support for English-speaking professionals — a useful complement to the AI-powered application strategy in this guide.\nThe gap between where your resume is and where it needs to be is probably smaller than you think — and with AI assistance, closing that gap takes hours, not weeks.\nRelated Tools Calculate your take-home pay for any offer → Salary Calculator Check your federal tax bracket → Tax Bracket Calculator Related Templates Ace your next career move with these resources:\nJob Interview Prep Guide: AI-Powered — 50 questions, STAR method templates, salary negotiation scripts ChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Resume and interview prep prompts included You May Also Like How to Create a Resume with AI (Step-by-Step Guide for 2026) ChatGPT Resume Tips for Career Changers Over 50 (2026) Best Job Search Sites 2026: Where to Actually Find Good Jobs ","permalink":"https://productivity-works.com/posts/ai-resume-optimization-job-search-2026/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eThe job market in 2026 has a strange duality. AI-powered Applicant Tracking Systems (ATS) screen out the majority of resumes before a human sees them. Meanwhile, AI tools available to job seekers can create resumes and cover letters that are more targeted than anything a human could produce manually.\u003c/p\u003e\n\u003cp\u003eThe result: job seekers who understand how to use AI tools are dramatically outperforming those who don\u0026rsquo;t. Not because AI writes better than humans — it doesn\u0026rsquo;t — but because it enables faster iteration, better keyword matching, and more thorough preparation than any individual can sustain manually.\u003c/p\u003e","title":"How to Use AI to Land Your Dream Job (Resume + Interview)"},{"content":"Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nBest High-Yield Savings Accounts 2026 — Stop Earning 0.01% on Your Money The average savings account at a traditional bank pays 0.01-0.05% APY. On $10,000, that\u0026rsquo;s $1-5 per year in interest. Meanwhile, online banks and credit unions are paying 4.00-5.00% APY — that\u0026rsquo;s $400-500 per year on the same $10,000.\nThe only difference? Where you keep your money. Same FDIC insurance, same safety, dramatically different returns.\nWhy Online Banks Pay More Traditional Bank Online Bank Typical APY 0.01-0.05% 4.00-5.00% Interest on $10,000/year $1-5 $400-500 FDIC Insured Yes Yes Physical Branches Yes No (app-based) Monthly Fees $5-12 (often waivable) $0 Why the difference? Online banks don\u0026rsquo;t pay for branches, tellers, or prime retail locations. They pass those savings to customers as higher interest rates.\nBest High-Yield Savings Accounts 2026 Bank APY Min. Deposit FDIC Insured Best Feature Wealthfront 4.50% $0 Yes Automated savings tools Marcus by Goldman Sachs 4.40% $0 Yes No fees, Goldman Sachs backing Ally Bank 4.25% $0 Yes Best overall banking experience Capital One 360 4.10% $0 Yes Checking + savings integration Discover Online Savings 4.20% $0 Yes Cashback debit card + savings APYs as of 2026. Rates are variable and may change.\nDetailed Reviews 1. Wealthfront Cash Account — Highest APY Feature Details APY 4.50% Minimum Deposit $0 Monthly Fee $0 FDIC Insurance Up to $8 million (through partner banks) Withdrawal Unlimited transfers Best for: Maximizing interest earnings with extended FDIC coverage.\nWealthfront\u0026rsquo;s cash account offers one of the highest APYs available, plus FDIC insurance up to $8 million through their partner bank network (far beyond the standard $250,000). Automated savings features help you set aside money without thinking about it.\n2. Marcus by Goldman Sachs — No-Frills High Yield Feature Details APY 4.40% Minimum Deposit $0 Monthly Fee $0 FDIC Insurance $250,000 Notable No minimum balance, no fees whatsoever Best for: People who want a simple, reliable savings account with no gimmicks.\nMarcus keeps it straightforward — high APY, no fees, no minimum balance. Backed by Goldman Sachs, one of the world\u0026rsquo;s largest financial institutions. The trade-off: no checking account or debit card (savings-only).\n3. Ally Bank — Best Overall Banking Experience Feature Details APY 4.25% Minimum Deposit $0 Monthly Fee $0 FDIC Insurance $250,000 Notable Checking + savings + CDs + investing all in one Best for: People who want to replace their traditional bank entirely.\nAlly offers the complete banking package — checking, savings, CDs, and investing — all with competitive rates and no fees. Their \u0026ldquo;buckets\u0026rdquo; feature lets you organize savings goals within one account. The mobile app is consistently rated among the best in banking.\n4. Capital One 360 Performance Savings Feature Details APY 4.10% Minimum Deposit $0 Monthly Fee $0 FDIC Insurance $250,000 Notable Physical branches (cafes) in select cities Best for: People who want high yield but also access to physical locations.\nCapital One is unique among high-yield banks: they have physical \u0026ldquo;Capital One Cafes\u0026rdquo; in major cities where you can bank, work, and grab coffee. If the idea of a purely online bank makes you nervous, Capital One offers the best of both worlds.\n5. Discover Online Savings Feature Details APY 4.20% Minimum Deposit $0 Monthly Fee $0 FDIC Insurance $250,000 Notable 1% cashback on debit card purchases (checking account) Best for: People who want high-yield savings plus a cashback checking account.\nDiscover pairs their competitive savings rate with a checking account that earns 1% cashback on debit card purchases (up to $3,000/month). US-based customer service available 24/7.\nHow Much Can You Earn? Savings Amount Traditional Bank (0.05%) High-Yield (4.50%) Difference $5,000 $2.50/year $225/year +$222.50 $10,000 $5/year $450/year +$445 $25,000 $12.50/year $1,125/year +$1,112.50 $50,000 $25/year $2,250/year +$2,225 $50,000 in a high-yield account earns $2,225/year in interest — essentially free money for moving your savings online.\nHow to Switch (15 Minutes) Open the new account online — Usually takes 5-10 minutes with ID verification Link your current bank — Add your existing bank account for transfers Transfer your savings — ACH transfer takes 1-3 business days Set up direct deposit (optional) — Route part of your paycheck directly Keep your old account open — Maintain a small balance for any linked services No need to close your old account immediately. Transfer gradually and keep a small buffer.\nQuick Decision Guide Your Priority Best Choice Highest APY Wealthfront (4.50%) Simplicity, no fees Marcus by Goldman Sachs Complete bank replacement Ally Bank Want physical branches too Capital One 360 Cashback on spending + savings Discover Frequently Asked Questions Q: Are online savings accounts safe? Yes. All accounts listed here are FDIC-insured up to $250,000 (Wealthfront up to $8 million). Your money is protected by the same federal insurance as any traditional bank.\nQ: Can I withdraw my money anytime? Yes. High-yield savings accounts have no lock-up period. You can transfer money out anytime, though ACH transfers take 1-3 business days.\nQ: Will the interest rate go down? Possibly. High-yield savings rates are variable and tied to the Federal Reserve\u0026rsquo;s rate. If the Fed cuts rates, savings APYs typically decrease. However, online banks consistently offer rates 10-100x higher than traditional banks regardless of the rate environment.\nQ: Should I put my emergency fund in a high-yield savings account? Absolutely — it\u0026rsquo;s the ideal place. Your emergency fund stays liquid (accessible anytime) while earning meaningful interest. Don\u0026rsquo;t lock emergency funds in CDs or investments.\nQ: How many savings accounts should I have? One to three. One primary high-yield account for your emergency fund, and optionally one or two more for specific savings goals (vacation, down payment, etc.).\nConclusion Moving your savings from a traditional bank to a high-yield online account is one of the easiest financial wins available. It takes 15 minutes, costs nothing, and earns you hundreds or thousands of dollars more per year.\nStart with Wealthfront (4.50% APY) for maximum returns, or Ally Bank for the best overall banking experience. Both are FDIC-insured and fee-free.\nThe money you earn in interest can fund your investments, build your emergency fund faster, or simply give you more financial breathing room.\nOnce your savings are working harder, consider putting some to work in the market. Rakuten Securities offers NISA accounts and low-cost index funds for residents in Japan — a logical next step once your emergency fund is in place and earning a competitive rate.\nRelated: Best Budgeting Apps 2026 Related: How to Build an Emergency Fund Fast Related Tools See how your savings grow with compound interest → Compound Interest Calculator Create a monthly savings plan → Budget Calculator Calculate how long to reach any savings target → Savings Goal Calculator Calculate your mortgage payment → Mortgage Calculator See how inflation affects your money → Inflation Calculator This article is for informational purposes only and does not constitute financial advice. APYs are variable and subject to change. Check each bank\u0026rsquo;s website for current rates.\nRelated Templates Grow your savings with AI-powered financial planning:\nChatGPT Prompt Templates — Budget optimization and savings goal prompts The AI Productivity Playbook 2026 — Automate your financial planning ","permalink":"https://productivity-works.com/posts/best-high-yield-savings-accounts-2026/","summary":"\u003cp\u003e\u003cem\u003eDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"best-high-yield-savings-accounts-2026--stop-earning-001-on-your-money\"\u003eBest High-Yield Savings Accounts 2026 — Stop Earning 0.01% on Your Money\u003c/h1\u003e\n\u003cp\u003eThe average savings account at a traditional bank pays 0.01-0.05% APY. On $10,000, that\u0026rsquo;s \u003cstrong\u003e$1-5 per year\u003c/strong\u003e in interest. Meanwhile, online banks and credit unions are paying \u003cstrong\u003e4.00-5.00% APY\u003c/strong\u003e — that\u0026rsquo;s \u003cstrong\u003e$400-500 per year\u003c/strong\u003e on the same $10,000.\u003c/p\u003e\n\u003cp\u003eThe only difference? Where you keep your money. Same FDIC insurance, same safety, dramatically different returns.\u003c/p\u003e","title":"Best High-Yield Savings Accounts 2026: Where to Earn 4-5% APY"},{"content":"This article contains affiliate links.\nIf you are trading USD/JPY in Japan, you already know that the yen pair is one of the most liquid currency pairs in the world. What you might not have fully accounted for is how much the spread — that small gap between the buy price and the sell price — is quietly eating into your returns every single time you enter a trade.\nMost traders spend hours researching entry signals and risk management strategies. Far fewer sit down and calculate what their annual spread cost actually looks like. This article does that work for you. We will walk through what spreads are, why they matter far more than most traders realize, how Japanese brokers generally compare in 2026, and what you can do to keep your total trading cost as low as possible.\nWhat Is a Spread in FX Trading? When you open a forex position, you are quoted two prices simultaneously: the bid price (the price at which you can sell) and the ask price (the price at which you can buy). The difference between these two prices is the spread.\nFor example, if USD/JPY is quoted as:\nBid: 155.498 Ask: 155.500 The spread is 0.2 pips (or 0.002 yen per unit).\nSpreads are typically measured in pips, where one pip for USD/JPY equals 0.01 yen (the second decimal place). Some brokers also quote in fractional pips (pipettes), going to the third decimal place.\nThe spread is not a commission you pay separately — it is built directly into the price. The moment you open a position, you are already at a small loss equal to the spread. To break even, the market must move in your favor by at least the spread amount. This is why spread size is one of the most fundamental factors in evaluating a broker\u0026rsquo;s actual cost to trade.\nThere are two main types of spreads you will encounter in Japan:\nFixed spreads (原則固定スプレッド): The quoted spread stays the same regardless of market conditions, at least under normal trading hours. Some brokers market their USD/JPY spread as \u0026ldquo;fixed\u0026rdquo; at 0.2 pips during standard sessions. Variable spreads (変動スプレッド): The spread widens and narrows based on market liquidity and volatility. During deep liquid hours, variable spreads can be extremely tight, sometimes lower than fixed alternatives. During volatile events, they can spike dramatically. Why Spreads Matter More Than You Think A spread of 0.1 pips sounds trivially small. In raw yen terms, on a standard lot of 100,000 USD/JPY, 0.1 pip equals just 10 yen. So what is the problem?\nThe problem is volume and repetition.\nLet\u0026rsquo;s run the numbers with a realistic scenario for an active retail trader:\nVariable Value Spread difference between two brokers 0.1 pip Lot size per trade 10 lots (1,000,000 USD notional) Cost per trade at 0.1 pip difference 100 yen Number of trades per year 250 Annual cost difference ¥25,000 Now consider a more active trader running 20 lots per trade at 500 trades per year. A 0.1 pip spread difference becomes ¥100,000 per year — just from that fractional pip.\nScale up to institutional or semi-professional volumes and the math becomes impossible to ignore. This is why professional traders obsess over execution costs and why spread is often the first comparison point when evaluating any broker.\nThere is a second dimension beyond the raw spread number: effective spread. The effective spread includes slippage — the difference between the price you intended to execute at and the price you actually got. A broker quoting a tight 0.2 pip spread but consistently delivering 0.5 pips of slippage during fast markets is effectively more expensive than a broker quoting 0.4 pips with no slippage. We will return to this point in the broker comparison section.\nMajor Japanese FX Brokers: Spread Comparison Overview Japan has one of the most competitive retail FX markets in the world. The country\u0026rsquo;s Financial Services Agency (FSA) regulates all domestic brokers, and the combination of high retail participation and fierce competition has driven USD/JPY spreads to some of the tightest levels globally.\nGeneral market observations for USD/JPY in 2026:\nThe benchmark for competitive Japanese retail brokers during standard Tokyo and London/New York overlap hours is approximately 0.2 pips for USD/JPY. Some brokers advertise spreads as low as 0.0 pips on USD/JPY but add a per-lot commission instead (an ECN-style model). When you factor in the commission, the all-in cost is often comparable to a 0.2–0.3 pip spread model. Mid-tier brokers in Japan typically quote USD/JPY spreads in the 0.3–0.5 pip range during normal conditions. During major news events (such as US CPI releases, Federal Reserve rate decisions, or Bank of Japan policy announcements), even \u0026ldquo;fixed\u0026rdquo; spreads at many brokers will temporarily widen. Always read the fine print on how a broker defines \u0026ldquo;fixed.\u0026rdquo; Important disclaimer: Spreads vary by market conditions, time of day, and broker policy and are subject to change at any time. The observations above are general market-level characterizations, not guarantees of any specific broker\u0026rsquo;s current offering. Always check the broker\u0026rsquo;s official website for their current spread schedule before opening an account.\nBeyond the spread number: what else should you evaluate?\nWhen comparing Japanese FX brokers, the raw spread figure is only the starting point. Consider:\nExecution speed and fill quality. A broker with a slightly wider quoted spread but superior execution technology may deliver a better effective spread in practice. Look for brokers that publish execution statistics or offer demo accounts with realistic fills.\nSlippage policy. Does the broker allow positive slippage (where you get filled at a better price than quoted)? Some brokers only pass through negative slippage, which systematically disadvantages traders in fast markets.\nPlatform quality. Consistent access to accurate quotes, reliable charting, and stable order execution affects your ability to get in and out at the prices you target. Platform downtime during high-volatility events is a real cost even if it does not appear on a spread schedule.\nSwap rates. If you hold positions overnight, the swap point (金利スワップ) can be a significant cost or benefit depending on the direction of your trade and the interest rate differential between USD and JPY.\nCustomer support and fund safety. All FSA-regulated brokers in Japan must segregate client funds and participate in investor protection schemes up to ¥10 million per customer. Verify FSA registration before opening any account.\nFixed vs Variable Spreads: Which Is Better? The debate between fixed (原則固定) and variable (変動) spreads is one of the most common questions from traders new to the Japanese market.\nFixed spreads — advantages:\nPredictable cost per trade; easier to calculate P\u0026amp;L No sudden widening during ordinary market hours Simpler to build trading strategies around known costs Fixed spreads — disadvantages:\n\u0026ldquo;Fixed\u0026rdquo; usually means fixed only during standard hours (typically 6:00–27:00 Japan time or similar). Spreads may widen around 5:00–7:00 AM JST (the New Zealand/Sydney open) and during major news events The fixed rate is often set slightly wider than the tightest available variable spread during peak liquidity You are paying a slight premium for predictability Variable spreads — advantages:\nDuring the most liquid hours (Tokyo-London overlap, London-New York overlap), variable spreads can be extremely tight ECN/STP brokers with variable models often offer better execution transparency Suitable for scalpers and algorithmic traders who time their entries to high-liquidity windows Variable spreads — disadvantages:\nCosts are harder to predict and model in advance Can spike significantly during news events, making stop-losses harder to manage Requires more active monitoring of market conditions Practical recommendation: For swing traders and position traders who hold for hours or days, a fixed-spread broker with a reliable 0.2 pip USD/JPY quote during standard hours is often the most convenient choice. For scalpers and high-frequency traders who execute many trades per day, a variable or ECN-style broker where you can access sub-0.2 pip spreads during peak liquidity windows may reduce overall cost — provided you time your entries carefully.\nOther Costs to Consider Beyond the Spread Focusing exclusively on the spread is a mistake. A complete picture of your trading cost includes:\nSwap Points (スワップポイント) When you hold an FX position overnight, your broker applies a swap point — an adjustment reflecting the interest rate differential between the two currencies in the pair. For USD/JPY in 2026, with US rates elevated relative to Japan, holding a long USD/JPY position typically earns a positive swap, while a short position incurs a negative swap.\nSwap rates vary significantly across brokers and can shift as central bank policies change. If you regularly carry positions overnight, compare swap schedules just as carefully as spreads.\nDeposit and Withdrawal Fees Most major Japanese FX brokers waive withdrawal fees for domestic bank transfers, but some charge fees for wire transfers to foreign accounts or for same-day withdrawal processing. Confirm the fee structure before depositing, especially if you intend to move funds frequently.\nPlatform and Data Fees Retail FX in Japan is generally commission-free at the platform level — you pay through the spread. However, some brokers charge for premium charting packages, automated trading (EA) hosting, or advanced data feeds. If you rely on third-party tools like MetaTrader 4/5 or proprietary platforms, check whether there are any subscription costs embedded in the offering.\nInactivity Fees Some brokers impose inactivity fees if your account has no trades for an extended period (often six to twelve months). If you trade seasonally or take extended breaks, this is worth checking.\nHow to Calculate Your Total Trading Cost Once you have the key variables, calculating your annual trading cost is straightforward.\nBasic formula:\nTotal Cost = (Spread in pips × Pip Value per Lot × Number of Lots × Number of Trades) + Commissions + Net Swap Costs For USD/JPY, the pip value for a standard lot (100,000 units) is approximately ¥1,000 per pip (this varies slightly with the exchange rate).\nExample calculation:\nInput Value Spread 0.2 pips Pip value (1 standard lot) ¥1,000 Lot size per trade 3 lots Number of trades per year 200 Commissions ¥0 (spread-only model) Swap (net, assume negligible for short holds) ¥0 Spread cost = 0.2 × ¥1,000 × 3 × 200 = ¥120,000 per year\nNow run the same calculation with a broker charging 0.3 pips:\nSpread cost = 0.3 × ¥1,000 × 3 × 200 = ¥180,000 per year\nThe difference is ¥60,000 annually — just from a 0.1 pip spread difference.\nUse our FX Profit Calculator to model your specific trading volume, spread, and lot size, and see exactly how spreads affect your bottom line across different scenarios.\nTips for Minimizing Trading Costs Beyond broker selection, you have direct control over several factors that affect your effective trading cost.\n1. Trade During High-Liquidity Hours USD/JPY is most liquid — and spreads are typically at their tightest — during the overlap of major financial center sessions:\nTokyo session: 9:00–15:00 JST — solid liquidity for yen pairs London-New York overlap: 21:00–01:00 JST — globally the highest liquidity window for most major pairs, including USD/JPY Avoid trading in the early hours of the Tokyo morning (roughly 6:00–8:00 AM JST) when liquidity is at its daily low and spreads tend to widen even at brokers with nominally fixed quotes.\n2. Avoid Major News Events (Unless That Is Your Strategy) The minutes surrounding major economic releases — US Non-Farm Payrolls, CPI, FOMC decisions, Bank of Japan rate announcements — are characterized by extremely wide spreads and erratic fills. Unless you are specifically trading the news release, either close positions before the announcement or place your entries and exits well away from the release window.\nIf news trading is your approach, make sure you are using a broker whose platform and execution infrastructure is designed for it, with explicit policies on slippage during volatile events.\n3. Use Limit Orders Where Possible A limit order allows you to specify the exact price at which you want to enter or exit. Unlike a market order — which executes at whatever the current ask or bid is — a limit order prevents you from getting a worse price than intended. This is especially valuable in fast-moving markets where market orders can incur additional slippage on top of the quoted spread.\nNote that limit orders carry the risk of non-execution if the market does not reach your price, so they require more precise entry planning.\n4. Be Aware of Weekend Gaps Holding positions over the weekend exposes you to gap risk — the market can open significantly higher or lower than Friday\u0026rsquo;s close, with spreads widening at the Sunday open. If you are a swing trader who holds through weekends, factor this potential cost (and risk) into your position sizing.\n5. Negotiate or Choose the Right Account Tier Some Japanese FX brokers offer reduced spreads or improved swap terms for higher-volume traders or for clients maintaining a minimum account balance. If you are trading at scale, it is worth contacting the broker directly or checking whether a premium account tier is available and cost-effective for your volume.\nTax on FX Profits in Japan FX trading profits in Japan are subject to 申告分離課税 (separate self-assessment taxation) at a flat rate of 20.315% (20% income tax + 0.315% special reconstruction income tax).\nKey points:\nThe 20.315% flat rate applies regardless of your total income level — FX gains are not added to your regular employment income for tax calculation purposes. If your total FX profits for the year exceed ¥200,000, you are generally required to file a final tax return (確定申告) by March 15 of the following year. FX losses can be carried forward for up to three years and offset against future FX gains, provided you file a tax return even in loss years. Trading expenses — including platform subscriptions, data fees, and relevant educational costs — may be deductible. Keep records throughout the year. Managing trading records across hundreds of transactions annually is genuinely time-consuming. freee helps you track trading income and expenses, organize your records, and simplify the tax filing process — particularly useful if you are also managing business income or freelance earnings alongside your FX activity.\nConclusion In Japan\u0026rsquo;s highly competitive retail FX market, the headline spread for USD/JPY has been compressed to very tight levels — often 0.2 pips or less at the top-tier brokers during standard market hours. That compression is good news for traders, but it also means the differences between brokers are small in absolute pip terms, and yet those small differences compound into meaningful real costs over a year of active trading.\nThe key takeaways from this comparison:\nEven a 0.1 pip spread difference matters at meaningful trade volumes. Run your own numbers using the formula above or our calculator. Look beyond the quoted spread to evaluate execution quality, slippage policy, and swap rates for a true picture of total cost. Fixed vs variable spreads each have a place depending on your trading style and whether you can time trades to high-liquidity windows. Tax compliance matters. FX profits above ¥200,000 require a confirmed申告, and proper record-keeping throughout the year makes filing far less painful. Before committing to any broker, use a demo account to test real execution quality — not just the quoted spread — under the specific market conditions you trade in. Spreads are important, but they are one input into a complete cost picture. The lowest quoted spread is not always the lowest actual cost.\nRelated Tools Calculate forex profits → Forex Profit Calculator Estimate your tax bracket → Tax Bracket Calculator Spreads and trading conditions mentioned in this article are general market observations as of early 2026 and are subject to change. Always verify current rates and conditions directly with any broker\u0026rsquo;s official website before opening an account or making trading decisions. This article is for informational purposes only and does not constitute financial advice.\nYou May Also Like Best FX Brokers for English Speakers in Japan 2026: What to Look For Best Forex Brokers 2026: Platforms for Beginners \u0026amp; Traders How to Open a DMM FX Account in Japan: Complete English Guide for Beginners (2026) ","permalink":"https://productivity-works.com/posts/fx-spread-comparison-japan-2026/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eIf you are trading USD/JPY in Japan, you already know that the yen pair is one of the most liquid currency pairs in the world. What you might not have fully accounted for is how much the spread — that small gap between the buy price and the sell price — is quietly eating into your returns every single time you enter a trade.\u003c/p\u003e\n\u003cp\u003eMost traders spend hours researching entry signals and risk management strategies. Far fewer sit down and calculate what their annual spread cost actually looks like. This article does that work for you. We will walk through what spreads are, why they matter far more than most traders realize, how Japanese brokers generally compare in 2026, and what you can do to keep your total trading cost as low as possible.\u003c/p\u003e","title":"FX Spread Comparison Japan 2026: Finding the Lowest Cost Broker for USD/JPY"},{"content":"This article contains affiliate links.\nLosing money on FX is painful. You watched the market move against you, cut your position, and ended up with a net loss for the year. It stings — but before you write off that loss entirely, Japan\u0026rsquo;s tax code offers a genuinely useful silver lining: you can carry forward FX losses for up to three years and use them to offset future profits.\nMost traders in Japan either don\u0026rsquo;t know this rule exists, or they know about it but never bother filing because \u0026ldquo;there\u0026rsquo;s nothing to pay.\u0026rdquo; That\u0026rsquo;s a costly mistake. Skipping even one year\u0026rsquo;s filing can permanently erase your carryforward rights — and with it, potentially tens or hundreds of thousands of yen in future tax savings.\nThis guide covers everything you need to know: what the rule is, how it works, how much you can actually save, the exact requirements, and a step-by-step walkthrough of how to file.\nWhat Is Loss Carryforward for FX? In Japan, profits from FX margin trading (外国為替証拠金取引, or FX) are classified as miscellaneous income from futures transactions (先物取引に係る雑所得等). This category is subject to separate self-assessed taxation (申告分離課税) at a flat rate of 20.315% (15% national income tax + 2.1% surtax + 3% local tax).\nBecause FX is taxed as a separate category — not bundled into your ordinary income — it comes with a special rule: losses from futures transactions can be carried forward for up to three years (損失の繰越控除, son-shitsu no kurikoshi kōjo).\nHere\u0026rsquo;s how it works in plain terms:\nIf you post a net FX loss in Year 1, you can record that loss on your tax return. In Year 2 and Year 3, if you make profits from FX (or other eligible instruments), the carried-forward loss is deducted first before your taxable income is calculated. The carryforward window is exactly three years from the year the loss was incurred. After that, any remaining unclaimed loss expires. This isn\u0026rsquo;t a loophole or a gray area — it is explicitly provided for in Japan\u0026rsquo;s Income Tax Act (所得税法 第41条の15). It\u0026rsquo;s a legitimate, fully above-board mechanism that the tax office expects traders to use.\nThe key insight: a loss year is not a wasted year, tax-wise. As long as you file correctly, that loss has real monetary value that you can cash in over the next three years.\nHow Much Can You Save? A Concrete Example Let\u0026rsquo;s run through a realistic scenario to show exactly how much this carryforward is worth.\nThe setup:\nYear FX Result Cumulative Carryforward Year 1 -¥500,000 (loss) -¥500,000 Year 2 +¥300,000 (profit) -¥200,000 remaining Year 3 +¥400,000 (profit) -¥0 (fully used) Without carryforward:\nYear 2 tax: ¥300,000 × 20.315% = ¥60,945 Year 3 tax: ¥400,000 × 20.315% = ¥81,260 Total tax paid: ¥142,205 With carryforward:\nYear 2: ¥300,000 profit − ¥300,000 carried-forward loss = ¥0 taxable. Tax = ¥0. Year 3: ¥400,000 profit − ¥200,000 remaining loss = ¥200,000 taxable. Tax = ¥200,000 × 20.315% = ¥40,630 Total tax paid: ¥40,630 Total savings: ¥142,205 − ¥40,630 = ¥101,575\nThat\u0026rsquo;s over ¥100,000 saved — simply by filing a tax return in a year when you had no obligation to pay anything. The filing itself takes maybe a few hours (especially if you use software). The return on that time investment is exceptional.\nNote: The 20.315% rate used above is the standard rate for separate taxation on futures income. The exact yen figures are illustrative and based on a flat-rate calculation; your actual tax may vary slightly depending on your situation and the applicable local tax rate in your municipality.\nThree Requirements for Loss Carryforward The carryforward benefit does not apply automatically. You must actively meet three strict conditions. Miss any one of them, and you lose the benefit — with no way to recover it after the fact.\n1. You Must File a Tax Return Every Year — Even With No Income or No Tax Due This is the rule that trips up the most people. In a loss year, you have no FX profits, so you owe no FX tax. Many traders — quite reasonably — assume there\u0026rsquo;s no need to file. This assumption is wrong and costly.\nTo establish the carryforward, you must file a tax return (確定申告, kakuteishinkoku) in the year the loss occurred, even if your total FX result is negative and even if you have no other filing obligation. The loss is not registered with the tax office unless you explicitly report it.\nSimilarly, in the years following the loss, you must continue to file every year for as long as the carryforward remains active, even if in a given year your profits are smaller than the carryforward balance and your net tax is still zero.\nThe rule: file every year without exception for the full duration of the carryforward period.\n2. The Loss Must Be from \u0026ldquo;Miscellaneous Income from Futures Transactions\u0026rdquo; The carryforward rule applies specifically to losses from instruments classified as futures transactions (先物取引) under Japanese tax law. FX margin trading qualifies. So do CFDs (差金決済取引) and commodity futures.\nHowever, this category does not include ordinary income, capital gains, dividends, or other types of miscellaneous income. The rules for each income type in Japan are strict and separate — losses in one bucket generally cannot offset gains in another.\nCrucially: stock trading losses and crypto losses cannot be offset against FX gains, and FX losses cannot offset stock or crypto gains. Each is taxed under different rules.\nMore on what can and cannot be offset is covered in the next section.\n3. You Cannot Skip a Year The carryforward is not a static record that waits patiently for you. It requires continuous annual filing from the year the loss was incurred through the year you fully exhaust it (or the end of the three-year window, whichever comes first).\nIf you file in Year 1 (loss year) and Year 2, but forget to file in Year 3 — even if you had a profit in Year 3 — the carryforward is broken. The remaining balance from Year 1 cannot be claimed in Year 4 or later. It simply disappears.\nSet a calendar reminder. Treat your annual tax filing as a non-negotiable task, not an optional one.\nWhat Can Be Offset? FX, CFD, and Futures — But Not Stocks, NISA, or Crypto Understanding what qualifies for the carryforward (and what can be offset against what) prevents surprises at filing time.\nEligible for carryforward and offset (futures transactions income):\nFX margin trading (外国為替証拠金取引) CFD trading (差金決済取引) on indices, commodities, or currency pairs Domestic commodity futures (商品先物取引) Osaka Exchange financial futures (金融先物取引) Gains and losses across all of these instruments can be netted against each other within the same category. If you lost ¥300,000 on FX but made ¥100,000 on index CFDs, your net loss for the year is ¥200,000 — and that ¥200,000 is what you carry forward.\nNot eligible — separate tax treatment applies:\nInstrument Tax Treatment Can offset FX? Listed stocks (上場株式) Separate taxation (申告分離) No Stock mutual funds Separate taxation (申告分離) No NISA accounts Tax-exempt No Cryptocurrency (暗号資産) Miscellaneous income (総合課税) No Unlisted stocks Separate taxation No Crypto deserves special mention. Cryptocurrency gains in Japan are classified as ordinary miscellaneous income (雑所得, general category), taxed at progressive rates up to 55%. This is a completely different bucket from FX. Crypto losses cannot offset FX gains, and FX losses cannot offset crypto gains. They are filed and taxed entirely separately.\nThe bottom line: the carryforward system is a closed loop within the futures transactions category. Gains and losses can only be netted within that same category.\nStep-by-Step Filing Guide Filing for the FX loss carryforward requires a few specific forms and selections. Here is the complete process.\nStep 1: Obtain Your Annual Trading Statement At the end of each calendar year (or in early January for the prior year), your FX broker will issue an annual transaction report (年間取引報告書). This document shows your total realized gains and losses for the year. Download it from your broker\u0026rsquo;s online portal — most Japanese FX brokers provide this in PDF format by late January.\nIf you traded with multiple brokers, collect statements from all of them. You will net the results across all accounts.\nStep 2: Calculate Your Net Gain or Loss Add up the total gains and losses across all your FX accounts. If the result is negative (a net loss), that is the figure you will report as your carryforward loss.\nIf you also traded CFDs or other eligible futures instruments, include those results in the calculation as well, since they belong to the same tax category.\nStep 3: Complete the Kakuteishinkoku — Required Forms The standard tax return form (確定申告書 B form, or the newer unified form introduced from FY2023 onward) must be supplemented with two additional documents:\nSchedule 3 (第三表 — 分離課税用): This is the separate taxation schedule. FX income (or loss) is reported here, not on the main income summary page. Look for the section labeled 先物取引に係る雑所得等. Attachment: Statement of Futures Transactions Income (先物取引に係る雑所得等の金額の計算明細書): This worksheet details the calculation of your FX gains/losses, the amount being carried forward, and any prior-year carryforward amounts being applied this year. In the Schedule 3, select the appropriate checkbox for 申告分離課税 (separate taxation). Do not report FX income on the main miscellaneous income line used for freelance or other ordinary income — that would incorrectly subject it to progressive rates and eliminate your carryforward rights.\nStep 4: Report the Carryforward Amount In the loss year: enter your net loss on the carryforward attachment form. The tax office will record this as your carryforward balance.\nIn subsequent years (when you have profits): on the same attachment form, enter the prior year\u0026rsquo;s carryforward amount and subtract it from your current-year gain. The difference (if positive) is your taxable FX income for that year. If the carryforward fully absorbs the profit, your taxable FX income is zero.\nStep 5: Submit the Return File by March 15 of the year following the tax year. For example, for calendar year 2025 (January–December 2025), the deadline is March 15, 2026.\nYou can file:\nOnline via e-Tax (the NTA\u0026rsquo;s own system, available in Japanese) In person at your local tax office (税務署) Via tax filing software For traders who are non-Japanese speakers or who have multiple income sources to reconcile, freee makes filing in Japan manageable for English speakers. It guides you through the relevant schedules, handles the separate taxation classification, and supports carryforward entry — without requiring you to interpret Japanese tax forms from scratch.\nStep 6: Keep Your Records Retain your annual trading statements and a copy of each filed tax return for at least seven years (the statute of limitations for tax assessments in Japan). If the NTA ever queries your carryforward claim, you will need to produce the original documentation.\nFAQ What if I forget to file one year? This is the most consequential mistake traders make. If you miss a year\u0026rsquo;s filing during the carryforward period, the chain is broken. The remaining unclaimed loss from prior years cannot be revived.\nThere is one partial remedy: if you realize your mistake quickly — within the same tax filing season — you may be able to file a late return (期限後申告). A late return still counts, though you may incur a small late-filing penalty (無申告加算税) if you owe tax. If you owe zero tax (because the carryforward absorbs your profit), the penalty is typically minimal or zero. However, this is a situation-specific matter; consult the NTA or a tax professional.\nIf the deadline for the missed year has completely passed, the carryforward for that year\u0026rsquo;s balance is generally forfeited. There is no mechanism to retroactively reestablish it.\nCan I amend a past return to add a carryforward I originally omitted? If you filed a tax return in the loss year but forgot to include the carryforward loss on the attachment form, you may be able to file an amended return (更正の請求, kōsei no seikyū) within five years of the original filing deadline. This requests the NTA to correct your return to properly record the loss.\nHowever, if you did not file at all in the loss year, amending is not an option — there is no original return to amend. A late return might still be possible depending on timing, but once the statute of limitations has passed, the loss is gone.\nIf you are in this situation, consult a Japanese tax accountant (税理士) promptly to understand your options before more time passes.\nDo I need to file if I only traded FX and had no other income? Yes, if you had a net FX loss and want to preserve the carryforward, you must file. There is no income threshold that creates an exception — the filing is required specifically to register the loss, regardless of whether any tax is owed.\nWhat about FX trading through a foreign broker? The same rules apply. If you trade FX through an overseas broker (e.g., Interactive Brokers, OANDA Global, or other offshore platforms), the gains and losses are still classified as futures transactions income in Japan and are subject to the same 20.315% rate and the same carryforward rules. You simply use the same forms and report the amounts from your overseas account statements.\nNote that overseas brokers do not withhold Japanese tax or submit reports to the NTA on your behalf — you are entirely responsible for self-reporting.\nWhat if my loss is larger than my total profits over three years? If your Year 1 loss is so large that even three years of profits don\u0026rsquo;t fully absorb it, any remaining balance expires at the end of the three-year window. You cannot carry it forward to Year 4 or beyond. The three-year cap is absolute.\nThis is a reason to actively track your carryforward balance and ensure you are claiming it in full each year as profits allow.\nConclusion Japan\u0026rsquo;s three-year FX loss carryforward is one of the most underused tax benefits available to retail traders. It doesn\u0026rsquo;t require any complex planning — just consistent, timely filing every year during the carryforward period. The paperwork is straightforward once you know which forms to use, and the potential savings are significant even for traders with modest trading volumes.\nThe core rules to remember:\nFile every year — even in a zero-profit year. Use Schedule 3 and the futures transactions attachment form. Do not skip a single year during the carryforward period. Only FX, CFD, and futures instruments are eligible — not stocks, NISA, or crypto. If you want to get a clear picture of your numbers before you start filing, use our tools:\nCalculate your realized gains and losses with our FX Profit Calculator Understand your total tax liability across all side income with our Side Income Tax Calculator Getting the filing right in a loss year costs you nothing but time — and it can save you over ¥100,000 in future years.\nRelated Tools Calculate forex profits → Forex Profit Calculator Estimate your tax bracket → Tax Bracket Calculator You May Also Like How to File a Kakuteishinkoku for FX Income in Japan: 2026 Guide How Much Tax Do You Pay on FX Profits in Japan? A Salary Worker\u0026rsquo;s Guide NISA vs FX in Japan: Which Is Better for Growing Your Money Tax-Efficiently? ","permalink":"https://productivity-works.com/posts/fx-loss-carryforward-japan-tax/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eLosing money on FX is painful. You watched the market move against you, cut your position, and ended up with a net loss for the year. It stings — but before you write off that loss entirely, Japan\u0026rsquo;s tax code offers a genuinely useful silver lining: \u003cstrong\u003eyou can carry forward FX losses for up to three years and use them to offset future profits\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eMost traders in Japan either don\u0026rsquo;t know this rule exists, or they know about it but never bother filing because \u0026ldquo;there\u0026rsquo;s nothing to pay.\u0026rdquo; That\u0026rsquo;s a costly mistake. Skipping even one year\u0026rsquo;s filing can permanently erase your carryforward rights — and with it, potentially tens or hundreds of thousands of yen in future tax savings.\u003c/p\u003e","title":"FX Trading and Japan Tax Filing: Can You Carry Forward Losses for 3 Years?"},{"content":"AI Tools for Freelancers to Earn More — Complete Guide [2026] Freelancing is brutal on your time. You\u0026rsquo;re simultaneously the service provider, the salesperson, the accountant, the project manager, and the customer support team. Every hour you spend on admin, proposals, invoicing, and client communication is an hour you\u0026rsquo;re not billing or building your skills.\nAI has become the great equalizer for freelancers. The right AI stack lets you punch well above your weight — producing the output of a small agency while operating as a solo professional. In 2026, the freelancers earning $150–$250/hour aren\u0026rsquo;t necessarily more talented than those earning $50/hour. They\u0026rsquo;re working smarter with AI.\nHere\u0026rsquo;s what you\u0026rsquo;ll learn:\nThe 10 highest-ROI AI tools for freelancers in 2026 How to use AI to win more proposals, charge more, and deliver faster Specific prompt templates for client communication, proposals, and delivery A comparison of tools by freelance use case Strategies to increase your effective hourly rate by 2–3x with AI Whether you\u0026rsquo;re a copywriter, designer, developer, consultant, or virtual assistant — this guide has specific tactics for you.\nWhy AI Tools for Freelancers Matter in 2026 The Productivity Gap The average freelancer spends 40–50% of their working time on non-billable activities: pitching, invoicing, client communication, revisions, research, and admin. That\u0026rsquo;s a devastating ratio. If you work 40 hours a week and half of it is non-billable, your effective income is based on 20 hours of real output.\nAI can absorb most of that non-billable work. Proposals in 10 minutes instead of 3 hours. Client emails in 30 seconds instead of 20 minutes. Research that took half a day done in 20 minutes. The math is simple: if AI saves you 10 non-billable hours per week and you bill at $100/hour, that\u0026rsquo;s $1,000/week in recaptured capacity — or more likely, the ability to take on 25–30% more clients with the same schedule.\nHow AI Changes the Game Beyond saving time, AI helps freelancers compete on quality. A one-person content shop can now produce the strategic output of a four-person agency. A solo developer can prototype and document at the speed of a small team. AI doesn\u0026rsquo;t just make you faster — it makes you look bigger, more professional, and more capable than the solo operation you actually are.\nBest AI Tools for Freelancers Compared Tool Category Key Benefit Price ChatGPT Plus Writing, research, proposals Most versatile AI assistant $20/month Claude Pro Writing, long-form content Best for nuanced client writing $20/month Notion AI Project management, notes Embedded AI in your workspace $16/month Jasper Marketing copy Fast marketing content $39/month Grammarly Pro Writing quality Professional tone \u0026amp; grammar $12/month Canva Pro + Magic AI Design Non-designer visual content $13/month Otter.ai Meeting transcription Auto-transcribes client calls $10/month Loom Video communication Async client updates $12.50/month Descript Video/podcast editing AI-powered video editing $12/month HoneyBook / Dubsado + AI Business management CRM, invoicing, contracts $16–$20/month Try It Free Best entry stack for a solo freelancer: ChatGPT Plus ($20) + Grammarly Pro ($12) + Canva Pro ($13) = $45/month. This stack can easily save 8–12 hours per week, making it one of the highest-ROI investments you can make.\nStep-by-Step Guide: How Freelancers Can Use AI to Earn More Step 1: Win More Proposals with AI Proposals are where most freelancers leave money on the table. They\u0026rsquo;re either too generic (no differentiation), too long (no one reads them), or written in a way that doesn\u0026rsquo;t speak to the client\u0026rsquo;s specific pain.\nDiscovery Call Summary Prompt (use after every sales call):\nI just finished a discovery call with a potential client. Here are my rough notes: [PASTE YOUR NOTES] Please: 1. Summarize the client\u0026#39;s top 3 pain points as they described them 2. Identify what success looks like to them (their desired outcome) 3. Flag any risks or red flags I should address in the proposal 4. Suggest the 3 most important points I should emphasize in my proposal Winning Proposal Prompt:\nWrite a project proposal for the following: - Client: [CLIENT NAME/TYPE] - Their pain: [SPECIFIC PROBLEM THEY DESCRIBED] - My solution: [WHAT YOU\u0026#39;LL DELIVER] - Timeline: [X weeks/months] - Investment: $[AMOUNT] - My unique angle: [WHY YOU\u0026#39;RE THE BEST CHOICE] - Tone: Confident, client-focused, avoid jargon Structure: Executive summary \u0026gt; Understanding of the problem \u0026gt; Proposed solution \u0026gt; Deliverables \u0026gt; Timeline \u0026gt; Investment \u0026gt; Why work with me \u0026gt; Next step Length: 400–600 words. Persuasive but not pushy. Step 2: Price Yourself Higher with AI-Powered Research Most freelancers undercharge because they don\u0026rsquo;t know what the market pays. AI can research rates for you.\nRate Research Prompt:\nI\u0026#39;m a [FREELANCE SPECIALTY] with [X years] of experience, specializing in [NICHE/INDUSTRY]. My clients are typically [COMPANY SIZE/TYPE]. Research and give me: 1. Current market rates for my specialty (hourly and project-based) for different experience levels (junior, mid, senior) 2. What factors command the highest rates in my field? 3. How should I position and justify a rate of $[YOUR TARGET RATE]? 4. What should be on my portfolio/profile to command premium rates? Value Proposition Sharpener:\nHere is my current freelance bio/pitch: [PASTE YOUR BIO] Rewrite it to: 1. Lead with the outcome/value I deliver, not my job title 2. Include a specific, credible result (I\u0026#39;ll add the real numbers) 3. Speak directly to my ideal client\u0026#39;s biggest fear or frustration 4. End with a clear call to action 5. Keep it under 100 words Step 3: Deliver Faster Without Cutting Quality Speed is the freelancer\u0026rsquo;s greatest financial lever. If you can deliver the same quality in half the time, you either earn twice as much or win twice as many clients.\nContent/Copy Brief Prompt (for writers):\nI\u0026#39;ve been hired to write [CONTENT TYPE] about [TOPIC] for [CLIENT TYPE]. Target audience: [AUDIENCE DESCRIPTION] Key message: [WHAT THE CLIENT WANTS TO COMMUNICATE] Tone: [PROFESSIONAL/CASUAL/URGENT/ETC.] Word count: [TARGET] First, give me: 1. A content outline with H2 and H3 headings 2. The strongest hook sentence for the introduction 3. 5 supporting facts or statistics I should include (I\u0026#39;ll verify them) 4. 3 strong CTA options for the conclusion Then wait for my approval before writing the full draft. Client Report Prompt (for consultants/agencies):\nGenerate a monthly client report based on these results: [PASTE YOUR METRICS/RESULTS] Format as a professional client report: 1. Executive Summary (3 sentences, plain language) 2. What We Did This Month (bullet list of activities) 3. Results \u0026amp; Metrics (table format with previous month comparison) 4. What\u0026#39;s Working (top 2 wins with brief explanation) 5. Opportunities for Next Month (2–3 recommendations) 6. Next Steps Tone: Confident and client-friendly. No jargon. Step 4: Handle Client Communication Efficiently Client communication is a massive time drain. AI can draft, refine, and manage it.\nScope Creep Response Prompt:\nA client is asking me to add significant work that\u0026#39;s outside our original agreement. The original project was: [DESCRIBE PROJECT] They\u0026#39;re now asking for: [DESCRIBE ADDITIONAL REQUEST] Our relationship is: [GOOD / STRAINED / NEW] Write a professional, friendly email that: 1. Acknowledges their request positively 2. Clarifies that it\u0026#39;s outside the original scope 3. Offers to add it as a change order at [YOUR RATE] 4. Keeps the relationship warm and collaborative No more than 150 words. Difficult Client Feedback Response:\nMy client sent this feedback: [PASTE CLIENT FEEDBACK] They seem [FRUSTRATED / CONFUSED / UNHAPPY]. The actual situation is: [YOUR PERSPECTIVE ON WHAT HAPPENED] Write a response that: 1. Acknowledges their concern without being defensive 2. Explains the situation clearly 3. Proposes a specific solution 4. Reaffirms my commitment to their success Tone: Professional, empathetic, solution-focused. Under 200 words. Step 5: Build Passive Income with AI AI enables freelancers to productize their knowledge — creating templates, guides, and courses that earn income while you sleep.\nProductized Service Description Prompt:\nI want to create a productized service package based on my expertise in [FIELD]. My ideal client is [DESCRIPTION]. The main result I deliver is [OUTCOME]. Design a productized service offer: 1. Package name (outcome-focused, not service-focused) 2. What\u0026#39;s included (5–7 specific deliverables) 3. What\u0026#39;s NOT included (set expectations clearly) 4. Timeline from purchase to delivery 5. Price rationale for $[YOUR TARGET PRICE] 6. Sales page headline and 3 bullet points Pro Tips \u0026amp; Advanced Techniques Common Mistakes to Avoid Mistake 1: Using AI to race to the bottom. Some freelancers use AI to produce more content at lower rates. This is a trap. Use AI to produce better quality faster — and charge more, not less.\nMistake 2: Sending AI-generated proposals without personalization. AI proposals that aren\u0026rsquo;t tailored to the specific client and their specific words from the discovery call are immediately obvious and ineffective. Always personalize.\nMistake 3: Ignoring AI for the intake process. Most freelancers have no system for client onboarding, briefing, and expectation-setting. AI can write your intake questionnaires, onboarding emails, and project kick-off documents in an afternoon.\nMistake 4: Not using AI to upsell. After delivery, AI can help you identify and pitch natural add-on services. Ask: \u0026ldquo;Given I just completed [PROJECT] for a [CLIENT TYPE], what are 5 logical follow-on services I could pitch them?\u0026rdquo;\nMistake 5: Treating AI as a shortcut rather than a collaborator. The best freelancers use AI as a thinking partner — brainstorming with it, stress-testing their ideas, getting alternative perspectives. This is where AI creates the most value.\nPower User Strategies Strategy 1: Create a client-specific AI context. At the start of any AI session for a client project, paste a one-paragraph brief about the client, their brand voice, and the project goals. Every output will be pre-calibrated.\nStrategy 2: Use AI for your own marketing. Generate your LinkedIn content, email newsletter, portfolio case studies, and pitch emails with AI. Most freelancers neglect their own marketing because it\u0026rsquo;s \u0026ldquo;not billable.\u0026rdquo; AI makes it fast enough to actually do.\nStrategy 3: Build a client onboarding kit with AI. Create a complete onboarding package (welcome email, questionnaire, project brief template, terms and conditions) — AI can write all of it in one afternoon.\nStrategy 4: Use AI for competitive intelligence. Regularly prompt AI to analyze competitors in your niche: what they offer, how they price, where they\u0026rsquo;re weak. Use this to sharpen your positioning.\nStrategy 5: Create a Gumroad product from your best work. Your AI-powered proposal template, your client communication scripts, your content brief system — these are all products freelancers would pay $19–$49 for. AI helps you package and sell your expertise at scale.\nRelated: Best AI Image Generators Free 2026 Frequently Asked Questions Q: Will AI replace freelancers? A: Not the skilled ones. AI replaces the commodity work — generic content, basic translations, simple data entry. It amplifies the specialized work — strategy, nuanced writing, creative direction, client relationships. Freelancers who use AI well are pulling further ahead of those who don\u0026rsquo;t.\nQ: How much can AI realistically increase my freelance income? A: Real estimates from freelancers who\u0026rsquo;ve adopted AI workflows range from 20–200% income increase, depending on their specialty. The highest gains come from: faster proposal writing, faster delivery of billable work, and productizing knowledge into digital products.\nQ: Which AI tool should I start with if I\u0026rsquo;m on a tight budget? A: Start with ChatGPT Plus at $20/month. It\u0026rsquo;s the most versatile tool and covers proposals, writing, research, client communication, and more. Add Grammarly Pro ($12/month) as your second tool.\nQ: Is it ethical to use AI in my freelance work without telling clients? A: Context matters. Using AI to help you draft, research, or iterate faster is broadly acceptable — just as using spellcheck or Google is. If clients are specifically paying for human-only creative work and you\u0026rsquo;ve represented it as such, disclosure is appropriate. Most clients care about the quality and outcome, not whether you used AI to draft.\nQ: Can AI help me transition from hourly to project-based pricing? A: Yes. Ask AI to help you design a project-based pricing structure, create a scope-of-work template, and build a FAQ that addresses client concerns about fixed pricing. AI can also help you calculate project rates based on your hourly rate and estimated time.\nQ: What freelance niches benefit most from AI? A: Writing and content (enormous), marketing and copywriting, web development (code generation), graphic design (AI image tools), consulting (research and reporting), and virtual assistance (automation). Nearly every knowledge-based freelance niche benefits significantly.\nSimplify Your Freelance Finances More clients means more invoices, more expenses, and more tax complexity. Try freee — Japan\u0026rsquo;s #1 cloud accounting to automate invoicing, track deductions, and handle your books so you can spend more time earning.\nConclusion \u0026amp; Call to Action AI is the most powerful productivity multiplier available to freelancers in 2026. The tools are affordable, the learning curve is low, and the ROI is immediate. The freelancers winning right now are the ones who\u0026rsquo;ve built an AI workflow into every part of their practice — from prospecting to delivery to invoicing.\nKey takeaways:\nThe $45/month AI stack (ChatGPT Plus + Grammarly + Canva Pro) can save 8–12 hours per week Use AI to win better proposals, not just faster ones — quality and personalization still matter AI-powered delivery speed lets you raise rates or take on more clients Productize your expertise with AI to create passive income streams The freelancers earning premium rates in 2026 are the ones using AI most strategically Ready to build your full AI-powered freelance workflow? Our Complete ChatGPT Prompt Collection includes a dedicated freelancer section with prompts for proposals, client communication, content delivery, and self-marketing.\nFor a complete system — from landing clients to delivering work to building passive income — the AI Productivity Playbook is your roadmap.\nRelated: How to Automate Tasks with AI Step by Step Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nRelated Tools Count words and characters in your text → Word Counter Calculate your ideal freelance hourly rate → Freelance Rate Calculator Create and download professional client invoices → Invoice Generator Generate a QR code for your portfolio or payment link → QR Code Generator Estimate taxes on freelance income → Side Hustle Tax Calculator Calculate your take-home pay → Salary Calculator Create a monthly budget → Budget Planner Related Templates Start your freelance or side hustle journey with these resources:\nSide Hustle Starter Kit 2026 — 15 proven ideas with step-by-step guides ChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Boost your productivity instantly ","permalink":"https://productivity-works.com/posts/ai-tools-for-freelancers-to-earn-more-2026/","summary":"\u003ch1 id=\"ai-tools-for-freelancers-to-earn-more--complete-guide-2026\"\u003eAI Tools for Freelancers to Earn More — Complete Guide [2026]\u003c/h1\u003e\n\u003cp\u003eFreelancing is brutal on your time. You\u0026rsquo;re simultaneously the service provider, the salesperson, the accountant, the project manager, and the customer support team. Every hour you spend on admin, proposals, invoicing, and client communication is an hour you\u0026rsquo;re not billing or building your skills.\u003c/p\u003e\n\u003cp\u003eAI has become the great equalizer for freelancers. The right AI stack lets you punch well above your weight — producing the output of a small agency while operating as a solo professional. In 2026, the freelancers earning $150–$250/hour aren\u0026rsquo;t necessarily more talented than those earning $50/hour. They\u0026rsquo;re working smarter with AI.\u003c/p\u003e","title":"AI Tools for Freelancers to Earn More 2026"},{"content":"This article contains affiliate links.\nJapan is home to one of the most active retail foreign exchange markets in the world. With over 50 brokers registered with the Financial Services Agency (FSA), the country\u0026rsquo;s FX industry is among the most competitive and tightly regulated anywhere. For English-speaking residents — whether you are a long-term expat, a foreign professional on a work visa, or a new arrival still getting your bearings — this abundance of choice is both an opportunity and a challenge.\nMost Japanese brokers operate entirely in Japanese. Their websites, customer support lines, apps, and even account opening forms default to Japanese-only interfaces. For someone who does not read kanji or speak business-level Japanese, navigating this landscape can feel daunting. Yet the underlying products — USD/JPY, EUR/USD, and dozens of other currency pairs — are universally understood, and the regulatory framework is among the safest in the world.\nThis guide walks you through everything you need to know before opening an FX account in Japan as a non-Japanese speaker: how the regulatory environment works, the seven factors that matter most when comparing brokers, what documents you will need, how the leverage rules differ from other countries, and what tax obligations await you once you start trading.\nJapan\u0026rsquo;s FX Regulation: Why It Matters The Financial Services Agency (FSA) is Japan\u0026rsquo;s primary financial regulator, equivalent in function to the FCA in the United Kingdom or the ASIC in Australia. All retail FX brokers offering services to Japan-based customers must be registered with the FSA as a Financial Instruments Business Operator under the Financial Instruments and Exchange Act (FIEA).\nThis registration requirement is not a formality. Brokers must maintain strict capital adequacy ratios, segregate client funds from company operating funds, submit to regular audits, and comply with anti-money laundering (AML) rules. In practice, this means that if a broker fails, your deposited funds are protected and held separately from the broker\u0026rsquo;s own assets. The Japan Investor Protection Fund (JIPF) also provides a degree of insurance coverage for eligible accounts, although the exact terms vary by broker type.\nKey FSA protections for retail traders include:\nClient money segregation: your funds are held in a separate trust account, not mixed with broker capital Negative balance protection: retail clients cannot lose more than their deposited margin Mandatory disclosure of risk warnings and fees Regular financial reporting obligations for registered brokers One important caveat: some international brokers accept Japanese residents without FSA registration by relying on \u0026ldquo;offshore\u0026rdquo; regulatory loopholes. Trading with an unregistered broker is legal for the individual but carries substantially higher risk — you lose the client money protections described above. If a broker is not on the FSA\u0026rsquo;s public registry, it falls outside the Japanese regulatory framework entirely.\nYou can verify a broker\u0026rsquo;s FSA registration status at the FSA\u0026rsquo;s official website: https://www.fsa.go.jp/ 7 Key Factors When Choosing an FX Broker in Japan Not all brokers are created equal, and the \u0026ldquo;best\u0026rdquo; broker for you depends heavily on your individual situation — your experience level, how actively you plan to trade, which currency pairs you focus on, and how comfortable you are operating in Japanese. Here are the seven factors that matter most.\n1. English Platform and Customer Support This is the single most important factor for non-Japanese speakers. Ask these questions before opening an account:\nIs the trading platform (web, desktop, or mobile) available in English? Can you open the account with an English-language application form? Is there English-language customer support, and if so, during what hours? Are the fee schedules, margin requirement tables, and legal disclosures translated into English? The reality is that most major Japanese brokers — including the market leaders by trading volume — operate primarily in Japanese. A small but growing number offer English interfaces or at minimum English-language support via email. Some international brokers with FSA registration deliberately target English-speaking expats in Japan and offer fully bilingual experiences.\nIf you are comfortable using a Japanese-language platform with occasional help from machine translation tools, your options expand considerably. But if you need end-to-end English support, your shortlist will be shorter and you should verify English availability before submitting any documents.\n2. USD/JPY Spread The USD/JPY spread — the difference between the buy and sell price on the most traded currency pair in Japan — is one of the most useful single numbers for comparing broker cost structures. For active traders, a difference of 0.1 pips on USD/JPY can translate to significant costs over hundreds of trades per month.\nAmong major registered brokers in Japan, USD/JPY spreads for standard accounts typically range from 0.2 pips to 1.0 pip during normal market hours. Some brokers advertise spreads as low as 0.0 pips but charge a per-trade commission on top, which you need to factor into the total cost.\nThings to check:\nIs the advertised spread a fixed spread or a variable (floating) spread? How does the spread widen during major news releases (non-farm payrolls, Bank of Japan rate decisions)? Is the advertised spread available for standard lot sizes or only for specific account tiers? Spreads on less liquid pairs — AUD/JPY, EUR/GBP, GBP/JPY — tend to be wider and vary more between brokers, so if you trade beyond the majors, compare those spreads specifically rather than relying on USD/JPY as a proxy.\n3. Minimum Lot Size Japanese retail FX accounts typically operate in units of 10,000 currency units (one \u0026ldquo;mini lot\u0026rdquo; or \u0026ldquo;1 lot\u0026rdquo; in Japanese convention). Some brokers allow trading in increments as small as 1,000 units, which is sometimes called a \u0026ldquo;micro lot.\u0026rdquo;\nFor beginning traders or those who want to manage risk carefully with a small capital base, access to smaller lot sizes is important. If a broker\u0026rsquo;s minimum is 10,000 units and you are trading USD/JPY at 150.00, each lot represents roughly ¥1,500,000 in notional value. Even at 25:1 leverage, you would need ¥60,000 in margin per standard lot. Micro lots allow you to scale down to one-tenth of that.\nWhen evaluating a broker, confirm:\nWhat is the minimum trade size in currency units? Can you scale positions in granular increments, or only in whole-lot steps? Does the minimum lot size vary by currency pair? 4. Mobile App Quality For most retail traders in Japan, the mobile app is the primary trading interface. Commuter trading on the train, monitoring positions during a lunch break, or receiving price alerts on weekends — these use cases make app quality critical.\nCriteria to evaluate:\nIs the app available in English (iOS and Android)? Does it support charting with technical indicators? Can you set conditional orders (stop-loss, take-profit, OCO) directly from the app? Are push notifications available for price alerts and margin warnings? How does the app perform on low-bandwidth connections? User reviews on the App Store and Google Play for the Japanese version of each broker\u0026rsquo;s app can give you a rough sense of stability and usability, even if you cannot read the text reviews. Filter by one-star reviews to identify recurring complaints about crashes or failed order executions.\n5. Account Opening Requirements for Foreigners This deserves its own section below, but at the comparison stage, the key question is: does the broker explicitly accept non-Japanese nationals as customers? Not all registered brokers do, and some that nominally accept foreigners have application processes that are entirely in Japanese with no English-language alternative.\nThe primary friction points for foreign residents tend to be:\nMy Number (Individual Number) card submission and verification Proof of Japanese residence card (Zairyu Card) Japanese bank account requirement for deposits and withdrawals Verification of income or employment status Some brokers have streamlined their processes for foreign nationals and offer document upload workflows that work with English-language names and non-Japanese document formats.\n6. Deposit and Withdrawal Options Almost all registered Japanese FX brokers require Japanese yen deposits and withdrawals via a Japanese domestic bank account. This means that before you can fund an FX account, you need an active Japanese bank account — typically at one of the major megabanks (MUFG, SMBC, Mizuho), Japan Post Bank, or one of the growing number of internet banks (Sony Bank, Rakuten Bank, SBI Shinsei Bank) that tend to have better English-language support.\nKey things to check:\nIs same-day or next-day funding available? Are there deposit or withdrawal fees? Is there a minimum or maximum deposit amount? Can you withdraw back to any Japanese bank account, or only the one you registered with? Credit card deposits and overseas bank transfers are generally not supported by Japanese brokers due to AML regulations. If you are arriving in Japan from overseas and have not yet opened a domestic bank account, opening an FX account will need to wait until your banking setup is complete.\n7. Educational Resources For newer traders, the quality of educational content can meaningfully affect outcomes. Many Japanese brokers invest heavily in educational materials — webinars, trading guides, market analysis — but these are predominantly in Japanese.\nIf English-language education is important to you, check whether the broker:\nOffers English-language market commentary or economic calendars Provides introductory guides to FX mechanics in English Has a demo account you can use to practice before funding Runs English-language webinars or video tutorials Alternatively, some traders use the educational resources of one broker while trading with another — using an English-friendly platform for learning while conducting actual trades through a broker with better spreads or a more convenient app.\nAccount Opening Requirements for Non-Japanese Residents Opening an FX account in Japan as a foreign national requires a specific set of documents. While requirements vary slightly between brokers, the following are standard across the industry.\nRequired Documents 1. Residence Card (Zairyu Card) Your residence card is the primary identity document for foreigners in Japan. Most brokers will ask you to upload a photo or scan of both the front and back. Your card must be valid and show your current registered address.\n2. My Number (Individual Number) Japan\u0026rsquo;s national identification number system assigns a 12-digit Individual Number to all residents, including foreign nationals. You will need to provide your My Number either on your residence card (if it includes the number) or via your My Number notification letter or My Number card. This is a legal requirement for financial account opening under Japan\u0026rsquo;s AML framework.\n3. Japanese Bank Account As noted above, virtually all Japanese FX brokers require a domestic bank account for fund transfers. The account must be in your own name. Joint accounts are not typically accepted.\n4. Income and Employment Information Most broker applications will ask you to self-report your annual income, occupation, and purpose of trading. This is standard under FSA regulations and is not a credit check — but you should answer honestly, as misrepresentation on a financial account application is a compliance violation.\nEnglish-Language Applications A growing number of brokers now offer account opening in English, either through an English-language version of their standard online application or through a dedicated expat-focused sign-up flow. Before starting the application process, check whether an English option is available — starting the process in Japanese and switching mid-way is rarely possible.\nSome international brokers registered with the FSA specifically market to the English-speaking resident community in Japan and have customer support teams with strong English capability. These brokers are often worth a closer look if navigating Japanese-language interfaces is a significant obstacle.\nUnderstanding Japanese FX Leverage Rules Japan\u0026rsquo;s leverage rules for retail FX traders are among the strictest in the developed world, and they are often a surprise to traders arriving from countries with more permissive regimes.\nThe current retail leverage cap is 25:1, which has been in effect since August 2011. This was a deliberate regulatory decision by the FSA following the 2008 financial crisis, introduced in two phases: a 50:1 cap in August 2009, followed by a further reduction to 25:1 in August 2011.\nWhat this means in practice:\nTo hold a 10,000-unit USD/JPY position at 150.00 (notional value approximately ¥1,500,000), you need at least ¥60,000 in available margin (1/25 of the notional value) Leverage cannot exceed this ratio regardless of how experienced you are or how much capital you have on account — the 25:1 limit applies to all retail accounts Some brokers set their default leverage lower than 25:1 and require you to explicitly request the maximum Margin calls and forced liquidation: If your account equity falls below the maintenance margin requirement (which varies by broker but is typically 50% of the required margin), the broker may issue a margin call. If equity continues to fall below the forced liquidation threshold (often 50% of initial margin), positions will be automatically closed. Each broker sets these thresholds independently — review them carefully before trading.\nProfessional accounts: Unlike in the EU or UK, Japan does not have a formal \u0026ldquo;professional client\u0026rdquo; classification that allows higher leverage for experienced traders. The 25:1 cap is universal for retail FX, regardless of account size or trading history.\nTrading Platforms Available in Japan The trading platform landscape in Japan is somewhat different from the global retail FX market. Here is what you need to know.\nMetaTrader 4 (MT4) and MetaTrader 5 (MT5) MT4 and MT5 are globally dominant platforms for retail FX and are well-known for their English-language interfaces, extensive library of technical indicators, and support for algorithmic trading via Expert Advisors (EAs). However, MT4/MT5 availability among Japanese FSA-registered brokers is less widespread than in Europe or Australia. This is partly because Japan\u0026rsquo;s domestic platform ecosystem is mature and partly because some FSA compliance requirements around client money reporting are easier to implement on proprietary platforms.\nThat said, several Japanese brokers and international brokers with FSA registration do offer MT4 or MT5. If you have an existing MT4 workflow — charts, EAs, custom indicators — finding a Japan-registered broker that supports MT4 is worth prioritizing.\nProprietary Platforms Most of Japan\u0026rsquo;s large domestic brokers operate their own proprietary platforms, both web-based and mobile. These are often highly optimized for Japanese trading styles (heavy use of technical analysis, high-frequency manual trading), with fast order execution and sophisticated one-click trading interfaces. The downside for English speakers is that these platforms are almost exclusively in Japanese.\nSome brokers offer both a proprietary platform and MT4/MT5, allowing you to choose based on your preference.\nWeb-Based and App-Only Brokers A growing segment of the market consists of mobile-first brokers with no dedicated desktop platform. These can be convenient for casual or part-time traders but may lack features needed by more active traders (multi-chart layouts, full order type support, deep historical data).\nTax Obligations for FX Traders in Japan FX trading profits are taxable in Japan, and the tax treatment is relatively straightforward compared to some countries — but you need to understand the rules before you start trading.\nSeparate Declaration Taxation (申告分離課税) Profits from FX trading conducted through registered Japanese brokers are subject to separate declaration taxation (申告分離課税 — Shinkoku Bunri Kazei) at a flat rate of 20.315% (comprising 15% national income tax, 0.315% reconstruction special tax, and 5% local inhabitant tax). This rate applies regardless of your total income level — it is not added to your ordinary income and taxed at a marginal rate.\nThis is generally favorable for high earners, since ordinary income in Japan can be taxed at marginal rates of up to 55% (45% national + 10% local) for high brackets.\nThe ¥200,000 Threshold You are required to file a final tax return (Kakuteishinkoku) if your FX profits exceed ¥200,000 in a calendar year AND you have a salary paid by a Japanese employer (from which withholding tax is deducted). If you are self-employed or have no Japanese employer, the standard ¥380,000 basic deduction applies instead, and you are required to file regardless of income level.\nThis threshold applies to net profits — that is, profits after deducting losses from the same year.\nLoss Carryforward (損失の繰越控除) One of the most valuable features of Japan\u0026rsquo;s FX tax regime is the three-year loss carryforward provision. If your FX trading results in a net loss in a given year, you can carry that loss forward and offset it against FX profits in the following three tax years. To take advantage of this, you must file a tax return in the year the loss was incurred, even if no tax is owed.\nThis makes it particularly important to file in loss years — many traders skip filing because they owe nothing, inadvertently forfeiting their right to the carryforward.\nCalculating Your After-Tax Returns Use our FX Profit Calculator to estimate your after-tax returns on individual trades and across a full year. The calculator accounts for the 20.315% rate and can help you model the impact of the loss carryforward across multiple years.\nManaging FX Tax Records Tracking FX trades for tax purposes requires maintaining detailed records of every transaction — entry price, exit price, trade size, fees, and realized profit or loss in yen terms. For active traders, this quickly becomes complex.\nfreee is the most popular cloud accounting software for managing FX tax obligations in Japan. It can import transaction data from many major brokers, automatically calculate realized gains and losses in yen, and generate the figures you need for your Kakuteishinkoku filing. freee also supports the loss carryforward calculation across multiple years, which is particularly useful if you have both profitable and loss years in your trading history.\nA Note on Overseas Brokers If you trade with an overseas broker that is not registered with the FSA, the same 20.315% tax rate still applies to your profits — but the classification may be different. Profits from overseas unregistered brokers are sometimes treated as \u0026ldquo;miscellaneous income\u0026rdquo; (Zatsu Shotoku) rather than separate declaration income, which can result in a higher effective tax rate depending on your total income. This is another reason to use FSA-registered brokers.\nConclusion Japan\u0026rsquo;s FX market offers a sophisticated, highly regulated trading environment with competitive spreads, deep liquidity in JPY pairs, and strong investor protections. For English-speaking residents, the main challenges are finding a broker with adequate English support, navigating the document requirements, and getting comfortable with the 25:1 leverage cap that differs from many other markets.\nThe checklist for choosing a broker comes down to these essentials: FSA registration (non-negotiable), English-language interface availability, competitive USD/JPY spread, mobile app quality, and clear documentation requirements for foreign nationals. Beyond these, your choice will depend on personal priorities — some traders value tight spreads above all else and are willing to use Japanese-only interfaces; others prioritize the ability to reach an English-speaking support agent when something goes wrong.\nBefore you start trading, make sure your capital allocation is realistic. Trading with money you can afford to lose is a cliche, but in Japan\u0026rsquo;s tightly leveraged environment it is especially relevant — the 25:1 cap means you cannot use leverage to amplify small capital into large position sizes the way traders in some other jurisdictions can.\nCheck your take-home pay to plan your trading capital — understanding your net monthly cash flow after tax, social insurance, and other deductions gives you a clear picture of how much you can realistically allocate to an FX account without affecting your day-to-day financial stability.\nWith the right broker, the right expectations, and a solid understanding of Japan\u0026rsquo;s regulatory and tax environment, FX trading can be a rewarding part of a broader personal finance strategy for English-speaking residents in Japan.\nRelated Tools Calculate forex profits → Forex Profit Calculator Estimate your tax bracket → Tax Bracket Calculator This article is for informational purposes only and does not constitute financial advice. FX trading involves the risk of loss. Please consult a qualified financial advisor before making investment decisions.\nYou May Also Like Best Forex Brokers 2026: Platforms for Beginners \u0026amp; Traders How to Open a DMM FX Account in Japan: Complete English Guide for Beginners (2026) FX Spread Comparison Japan 2026: Finding the Lowest Cost Broker for USD/JPY ","permalink":"https://productivity-works.com/posts/best-fx-brokers-japan-english/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eJapan is home to one of the most active retail foreign exchange markets in the world. With over 50 brokers registered with the Financial Services Agency (FSA), the country\u0026rsquo;s FX industry is among the most competitive and tightly regulated anywhere. For English-speaking residents — whether you are a long-term expat, a foreign professional on a work visa, or a new arrival still getting your bearings — this abundance of choice is both an opportunity and a challenge.\u003c/p\u003e","title":"Best FX Brokers for English Speakers in Japan 2026: What to Look For"},{"content":"Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nBest Internet Service Providers 2026 — Find the Right Plan for Your Needs Internet service is one of those expenses most people never shop around for — and they\u0026rsquo;re often overpaying by $20-50/month as a result. In 2026, fiber internet is expanding rapidly, 5G home internet is a viable alternative, and competition is finally driving prices down in many markets.\nThis guide helps you find the best ISP based on your needs: speed, price, reliability, and availability.\nTypes of Internet Service Type Speed Reliability Price Best For Fiber 300-8,000 Mbps Excellent $30-80/mo Best overall, where available Cable 100-1,200 Mbps Good $30-100/mo Most widely available 5G Home 100-1,000 Mbps Good $25-60/mo No contract, easy setup DSL 10-100 Mbps Fair $20-50/mo Rural areas (limited) Satellite 25-200 Mbps Variable $50-120/mo Remote/rural only option Best ISPs by Category Best Overall: AT\u0026amp;T Fiber Feature Details Speed 300 Mbps - 5 Gbps Price $55-180/mo Contract None Equipment WiFi gateway included Availability 25+ million locations Why it wins: AT\u0026amp;T Fiber offers symmetric upload/download speeds (rare and valuable for video calls and uploads), no data caps, no contracts, and competitive pricing. Their 1 Gbps plan at $80/month is one of the best values in fiber.\nBest Budget: T-Mobile 5G Home Internet Feature Details Speed 72-245 Mbps (typical) Price $50/mo (with phone line) or $60/mo standalone Contract None Equipment 5G gateway included Setup Self-install, 15 minutes Why it wins: No contract, no installation appointment, no hidden fees. T-Mobile sends you a gateway, you plug it in, and you\u0026rsquo;re online. At $50/month for unlimited data with no strings attached, it\u0026rsquo;s the simplest and cheapest option for most households.\nTrade-off: Speeds vary by location and tower congestion. Not ideal for competitive gaming or households with 5+ heavy users.\nBest for Speed: Google Fiber Feature Details Speed 1 Gbps - 8 Gbps Price $70-150/mo Contract None Equipment WiFi 6E router included Availability Limited (20+ cities) Why it wins: Google Fiber consistently delivers the fastest real-world speeds with the highest customer satisfaction ratings in the industry. Their 2 Gbps plan at $100/month is exceptional value. No data caps, no contracts.\nLimitation: Only available in select cities (Austin, Nashville, Kansas City, Salt Lake City, etc.).\nBest Cable: Xfinity (Comcast) Feature Details Speed 75-2,000 Mbps Price $20-80/mo (first year) Contract 1-2 year options (or month-to-month at higher price) Equipment Gateway rental $14/mo or use your own Availability 40+ states Why it wins: Xfinity has the widest availability of any ISP. Their introductory pricing is competitive, and speeds up to 2 Gbps are available in many markets. If fiber isn\u0026rsquo;t available in your area, Xfinity is likely your best wired option.\nWatch out for: Price increases after promotional period, equipment rental fees, and data caps (1.2 TB in most markets).\nBest for Rural: Starlink Feature Details Speed 25-200 Mbps Price $120/mo + $599 equipment Contract None Setup Self-install dish Availability Almost anywhere in the US Why it wins: For rural and remote areas with no cable or fiber options, Starlink is transformative. It delivers 50-200 Mbps where the only alternative might be 5 Mbps DSL or nothing at all.\nTrade-off: Expensive ($120/month + $599 upfront for equipment), higher latency than wired connections, and speeds can vary with weather and congestion.\nHow Much Speed Do You Need? Usage Recommended Speed Email, browsing, social media 25-50 Mbps HD streaming (1-2 devices) 50-100 Mbps 4K streaming + video calls 100-200 Mbps Remote work + family streaming 200-500 Mbps Gaming + streaming + large household 500 Mbps - 1 Gbps Content creation, large uploads 1 Gbps+ (fiber preferred) Rule of thumb: 25-50 Mbps per person in your household. A family of 4 needs 100-200 Mbps minimum.\nMoney-Saving Tips Negotiate your current rate — Call your ISP and ask for a loyalty discount. Many will reduce your bill by $10-20/month to keep you Buy your own router — Skip the $10-15/month equipment rental fee. A good router costs $80-150 and pays for itself in 6-12 months Bundle strategically — Phone + internet bundles sometimes save money, but check the math carefully Check for new competitors — Fiber and 5G are expanding rapidly. A new ISP in your area may offer aggressive introductory pricing Remove unnecessary speed tiers — If you\u0026rsquo;re paying for 1 Gbps but only need 200 Mbps, downgrade and save $20-40/month Quick Decision Guide Your Situation Best ISP Fiber available AT\u0026amp;T Fiber or Google Fiber Want cheapest option T-Mobile 5G Home ($50/mo) Need fastest speeds Google Fiber (up to 8 Gbps) Only cable available Xfinity Rural/remote area Starlink No contract, easy setup T-Mobile 5G Home Frequently Asked Questions Q: How do I check what\u0026rsquo;s available at my address? Use BroadbandNow.com or InMyArea.com — enter your address to see all available ISPs, speeds, and prices.\nQ: Is 5G home internet as good as fiber? Not quite. Fiber is more consistent and typically faster, especially for upload speeds. But 5G home internet is good enough for most households and much easier to set up.\nQ: Should I rent or buy my router? Buy. A $100-150 WiFi 6 router outperforms most ISP-provided equipment and pays for itself in under a year of saved rental fees.\nQ: Do I need a 1 Gbps plan? Most households don\u0026rsquo;t. 200-500 Mbps handles 4K streaming, video calls, and gaming for a family of 4 without issues. Only upgrade to gigabit if you regularly transfer large files or have 6+ devices streaming simultaneously.\nQ: How can I test my actual internet speed? Use Speedtest.net or Fast.com. Test at different times of day — speeds often drop during peak evening hours (7-10 PM) on cable connections.\nConclusion The best internet service depends on what\u0026rsquo;s available at your address. Check availability first, then prioritize:\nFiber (AT\u0026amp;T Fiber or Google Fiber) if available — best speed, reliability, and value 5G Home Internet (T-Mobile) for the easiest setup and lowest price Cable (Xfinity) as a reliable fallback with wide availability Starlink for rural areas with no other options Whatever you choose, buy your own router and negotiate your rate annually. These two actions alone can save $200-400/year.\nRelated: Best Cell Phone Plans 2026 Related: Best Budgeting Apps 2026 This article is for informational purposes only. Pricing and availability vary by location. Check each ISP\u0026rsquo;s website for current offers in your area.\nRelated Tools Create a monthly budget → Budget Planner Set a savings goal → Savings Goal Calculator Generate secure passwords instantly → Password Generator Related Templates Optimize your household expenses with AI:\nChatGPT Prompt Templates — Budget optimization and bill negotiation scripts The AI Productivity Playbook 2026 — Automate expense tracking and savings ","permalink":"https://productivity-works.com/posts/best-internet-service-providers-2026/","summary":"\u003cp\u003e\u003cem\u003eDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"best-internet-service-providers-2026--find-the-right-plan-for-your-needs\"\u003eBest Internet Service Providers 2026 — Find the Right Plan for Your Needs\u003c/h1\u003e\n\u003cp\u003eInternet service is one of those expenses most people never shop around for — and they\u0026rsquo;re often overpaying by $20-50/month as a result. In 2026, fiber internet is expanding rapidly, 5G home internet is a viable alternative, and competition is finally driving prices down in many markets.\u003c/p\u003e","title":"Best Internet Service Providers 2026: Speed \u0026 Price Compared"},{"content":"This article contains affiliate links.\nGoing freelance in Japan is more achievable than most expats assume — but it does require navigating a specific set of administrative steps. Whether you are a designer, developer, consultant, English teacher, or online content creator, the process of becoming legally recognized as a self-employed individual follows the same path: register as a kojin jigyonushi (個人事業主), elect the blue return system, and set up proper bookkeeping.\nThis guide walks you through each step in plain English, from filing your opening notification at the tax office to using freee — Japan\u0026rsquo;s leading cloud accounting software — to handle your annual tax return. By the end, you will understand exactly what paperwork is needed, what tax obligations apply, and how to stay compliant without hiring an accountant from day one.\nWhat Is a Kojin Jigyonushi? A kojin jigyonushi (個人事業主) is a sole proprietor — an individual who runs a business independently, without incorporating. It is the simplest and most common business structure for freelancers and self-employed people in Japan.\nUnlike forming a kabushiki kaisha (KK, a joint-stock company) or a godo kaisha (GK, a limited liability company), registering as a kojin jigyonushi involves almost no setup cost and minimal ongoing compliance burden. There is no minimum capital requirement, no company registration fee, and no mandatory auditing.\nKey characteristics:\nLegal identity: You and the business are legally the same entity. Business profits are taxed as your personal income. Liability: You bear unlimited personal liability for business debts (unlike a corporation). Tax filing: You file an annual kakuteishinkoku (確定申告, income tax return) each February–March covering the previous calendar year. Best suited for: Freelancers, consultants, creators, and anyone earning self-employment income below the consumption tax threshold (currently ¥10 million in annual revenue). If your annual revenue is likely to stay below ¥10 million for the foreseeable future and you do not need the liability shield of a corporation, starting as a kojin jigyonushi is the practical choice.\nStep 1 — File Your Opening Notification (開業届) The kaigyou todoke (開業届, Business Opening Notification) is a one-page form submitted to your local tax office (税務署, zeimusho). It officially informs the government that you have started a business.\nKey facts Item Detail Form name 個人事業の開業・廃業等届出書 Where to submit The tax office (zeimusho) with jurisdiction over your home address Deadline Within 1 month of starting your business activity Cost Free Required ID Your My Number (individual number) card or notification slip How to find your local tax office Go to the National Tax Agency website (nta.go.jp) and use the tax office locator tool (税務署の所在地などを調べる). Enter your address to find the correct office.\nCan I file online? Yes. The National Tax Agency\u0026rsquo;s e-Tax system allows you to submit the opening notification electronically. You will need a My Number card and a compatible IC card reader, or you can use the smartphone app authentication option introduced in recent years.\nWhat business type should I list? On the form, you will be asked to describe your jigyo no shurui (事業の種類, type of business). Be reasonably specific — for example, \u0026ldquo;Web design and development,\u0026rdquo; \u0026ldquo;English language instruction,\u0026rdquo; or \u0026ldquo;Freelance translation services.\u0026rdquo; Vague descriptions like \u0026ldquo;consulting\u0026rdquo; are acceptable but more specific descriptions help if you ever need to explain your activities to a bank or client.\nWhat happens after filing? The tax office stamps your copy and retains the original. You will not receive a formal \u0026ldquo;approval\u0026rdquo; — the notification is simply an administrative record. However, this stamped copy is often requested by banks when opening a business bank account, by co-working spaces, and occasionally by clients who need to verify your business status.\nStep 2 — Apply for Blue Return (青色申告承認申請書) Immediately after (or at the same time as) filing your opening notification, submit the ao-iro shinkoku shonin shinsei-sho (青色申告承認申請書, Application for Approval of Blue Return).\nThis is one of the most financially significant decisions you will make as a freelancer in Japan.\nWhat is the blue return? Japan\u0026rsquo;s tax system offers two types of income tax filing for sole proprietors:\nWhite return (白色申告, shiro-iro shinkoku): Simple cash-basis bookkeeping. No special deduction. Blue return (青色申告): More rigorous bookkeeping in exchange for significant tax deductions and privileges. Blue return benefits 1. Special deduction of up to ¥650,000\nIf you use double-entry bookkeeping AND file electronically via e-Tax, you are entitled to a ¥650,000 deduction from your business income before income tax is calculated. This deduction alone can save a freelancer earning ¥4 million in net profit approximately ¥97,500–¥130,000 in income tax, depending on their tax bracket.\nIf you file on paper or use simplified single-entry bookkeeping, the deduction is reduced to ¥550,000 (double-entry, paper) or ¥100,000 (single-entry).\n2. Three-year loss carryforward\nIf your business runs at a net loss in a given year, you can carry that loss forward and offset it against profits in the following three years. This is particularly valuable in your first year when startup costs may exceed revenue.\n3. Depreciation of assets (special provisions)\nBlue return filers can use accelerated depreciation methods and claim certain small asset write-offs (up to ¥300,000 per item) that white return filers cannot access.\n4. Spouse and family employee deductions\nIf your spouse works in your business, you may be able to deduct their salary as a business expense — something not available under the white return system.\nDeadline for the application Situation Deadline Filed opening notification on or before January 16 of the first year March 15 of the same year Filed opening notification after January 16 Within 2 months of the opening notification date Missing this deadline means you must wait until the following year to elect blue return status. Submit this form at the same time as your opening notification to be safe.\nStep 3 — Set Up freee for Bookkeeping Once registered, you need an ongoing system for recording income, expenses, and invoices. This is where freee (フリー) comes in.\nWhy freee? freee is Japan\u0026rsquo;s most widely used cloud accounting software for small businesses and sole proprietors. Its core advantage over traditional Japanese accounting software is its question-based setup interface: instead of presenting you with a blank chart of accounts and expecting you to know accounting terminology, freee asks plain-language questions to configure your books correctly from the start.\nKey reasons freelancers in Japan choose freee:\nNo accounting knowledge required. The guided setup and transaction categorization suggestions mean you can start recording expenses and income on day one without understanding debit/credit entries. ~3,200 bank and credit card connections. freee can automatically import transactions from most Japanese banks, credit cards, and payment services — including PayPay, LINE Pay, Rakuten, and major regional banks. This eliminates manual data entry for the majority of your financial activity. Blue return compliance built in. freee automatically generates the double-entry bookkeeping records required for the ¥650,000 blue return deduction and produces the required balance sheet (貸借対照表) and income statement (損益計算書). Direct e-Tax filing. freee connects directly to the National Tax Agency\u0026rsquo;s e-Tax system, allowing you to submit your kakuteishinkoku without printing a single page. Invoicing and expense management. freee includes invoicing tools compliant with Japan\u0026rsquo;s Qualified Invoice System (インボイス制度, applicable if you are a registered consumption taxpayer), plus receipt scanning via the smartphone app. Japanese and English support. While the interface is primarily in Japanese, freee offers English-language customer support resources and the UI is clear enough that intermediate Japanese readers can navigate it with minimal friction. Start your free trial of freee The free trial gives you full access to the paid features for a limited period. After that, the starter plan for sole proprietors costs approximately ¥1,980/month (prices subject to change), which is tax-deductible as a business expense.\nGetting started with freee Create an account at freee.co.jp using your email address. Run the initial setup wizard. freee asks about your business type, whether you are filing blue or white return, your fiscal year start (January 1 for most sole proprietors), and your registered business name. Connect your bank accounts. Navigate to \u0026ldquo;口座\u0026rdquo; (accounts) and add your business bank account. freee will import your recent transaction history automatically. Categorize imported transactions. For each imported transaction, freee suggests a category (e.g., \u0026ldquo;消耗品費\u0026rdquo; for office supplies, \u0026ldquo;通信費\u0026rdquo; for phone/internet). Review and confirm or correct the suggestions. Over time, freee learns your patterns and the suggestions improve. Set up recurring income. If you have regular clients, create invoice templates in freee and mark them as paid when payment arrives. This keeps your accounts receivable current. Understanding Japanese Freelance Taxes Before your first tax filing, it helps to understand the taxes you will owe as a kojin jigyonushi.\nIncome tax (所得税, shotokuzei) Income tax in Japan is progressive, ranging from 5% to 45% depending on your taxable income:\nTaxable income (¥) Rate Up to 1,950,000 5% 1,950,001 – 3,300,000 10% 3,300,001 – 6,950,000 20% 6,950,001 – 9,000,000 23% 9,000,001 – 18,000,000 33% 18,000,001 – 40,000,000 40% Over 40,000,000 45% A 2.1% surtax (復興特別所得税) is added to all income tax amounts to fund post-earthquake reconstruction. This applies through 2037.\nTaxable income is your gross business revenue minus deductible business expenses, minus the blue return special deduction, minus personal deductions (basic deduction of ¥480,000, social insurance premiums, etc.).\nResidence tax (住民税, juminzei) A flat 10% of your prior year\u0026rsquo;s income is collected by your municipality. Residence tax is assessed in June of the following year based on your confirmed income tax return. You will receive a payment notice and can pay in four installments or as a lump sum.\nNote: In your first year of freelancing, your residence tax bill from the previous year (when you were an employee) may still arrive — this is normal. The residence tax bill based on your first full year of freelance income will arrive the following June.\nConsumption tax (消費税, shohizei) If your taxable sales in a given fiscal year exceed ¥10 million, you become a consumption taxpayer in the year two years later and must collect 10% consumption tax from clients (with a reduced 8% rate on food) and remit it to the government.\nMost freelancers starting out do not need to worry about this threshold immediately. However, if you register as a Qualified Invoice Issuer (適格請求書発行事業者) under the invoice system — sometimes necessary when your clients are corporations that want to claim input tax credits — you become a consumption taxpayer regardless of your revenue.\nSocial insurance and pension As a kojin jigyonushi, you are no longer covered by your employer\u0026rsquo;s shakai hoken (social insurance). You must enroll in:\nKokumin kenko hoken (国民健康保険, National Health Insurance): premiums vary by municipality and are income-based, typically 8–12% of the prior year\u0026rsquo;s income up to a ceiling. Kokumin nenkin (国民年金, National Pension): a flat monthly premium of approximately ¥16,980 (2026 rate; adjusted annually). Both are deductible from your taxable income, which reduces your income tax and residence tax burden.\nEstimate your freelance taxes with our Side Income Tax Calculator to see how different income levels and deductions affect your take-home pay.\nFiling Your First Tax Return With freee The kakuteishinkoku (確定申告) filing period runs from February 16 to March 15 each year, covering income earned in the prior calendar year (January 1 – December 31).\nHere is a condensed walkthrough of the process in freee:\n1. Reconcile your accounts (January) Before filing opens, use January to review the previous year\u0026rsquo;s transactions in freee. Confirm that all income has been recorded, all deductible expenses are categorized, and any end-of-year adjustments (like depreciation of equipment) have been entered.\n2. Run the profit and loss statement In freee, navigate to \u0026ldquo;レポート\u0026rdquo; (Reports) and open the \u0026ldquo;損益計算書\u0026rdquo; (Income Statement) for the full calendar year. This shows your gross revenue, total expenses by category, and net profit. This is your starting point for the tax return.\n3. Start the tax return wizard freee has a dedicated \u0026ldquo;確定申告\u0026rdquo; section in the menu. Click \u0026ldquo;申告書を作成する\u0026rdquo; (Create Tax Return). The wizard walks you through each section:\nBusiness income: Pre-filled from your freee books. Other income: Enter any salary income (if you kept a part-time job), dividend income, or rental income. Deductions: The wizard prompts you for social insurance premiums paid, life insurance premiums, medical expenses over ¥100,000, earthquake insurance, and the blue return deduction (applied automatically if your books meet the requirement). Tax credits: Input dependent information, disability status, and other applicable credits. 4. Review the calculated tax amount freee displays your estimated income tax, shows any withholding tax already deducted by clients (源泉徴収, genzen choshu), and calculates whether you owe additional tax or are due a refund.\n5. File via e-Tax Connect freee to e-Tax using your My Number card (via a card reader or smartphone NFC). freee transmits your return directly to the National Tax Agency. You receive an electronic confirmation receipt immediately.\n6. Pay any remaining tax (or receive your refund) If you owe tax, you can pay via bank transfer, convenience store payment slip, or credit card through the e-Tax payment portal. Refunds are deposited directly to the bank account you specified on the return, typically within 3–6 weeks of filing.\nCommon Questions Will freelancing affect my visa status? This is a critical point for non-Japanese residents. The rules depend on your visa type:\nEngineer/Specialist in Humanities/International Services (技術・人文知識・国際業務): This visa permits work only for the sponsoring employer in the listed activities. Taking on freelance clients in the same field may or may not be permitted without a status change — consult the Immigration Services Agency or an immigration lawyer before proceeding. Highly Skilled Professional (高度専門職): Generally permits a broader range of work activities, but review your specific visa conditions. Business Manager (経営・管理): Designed for business owners, but the requirements (office space, minimum capital or employees) are more stringent than simply filing a kojin jigyonushi notification. Permanent Resident or Spouse of Japanese National: No work restrictions. You can freelance freely. Filing an opening notification as a kojin jigyonushi does not automatically change your visa status, but working outside your visa\u0026rsquo;s permitted scope is a serious violation. Always confirm with an immigration professional before starting freelance activities.\nCan I keep my salaried job while freelancing? Yes. Many people in Japan operate as kojin jigyonushi alongside full-time employment. Your employer\u0026rsquo;s HR department does not need to be notified in most cases — though some employment contracts prohibit side work, so check yours.\nFor tax purposes, if your freelance side income exceeds ¥200,000 in a calendar year, you are required to file a kakuteishinkoku (income tax return) to declare it. The process is the same as described above; freee handles both your employment income (from your gensen choshu hyo, the annual withholding statement your employer provides) and your business income in a single return.\nDo I need to hire an accountant? For most freelancers starting out, the answer is no — especially if you use freee. The software handles the bookkeeping complexity, generates compliant financial statements, and guides you through the tax return. The question-based interface was specifically designed to make accounting accessible to non-specialists.\nThat said, consider consulting a zeirishi (税理士, certified tax accountant) if:\nYour annual revenue exceeds approximately ¥5–10 million. You have complex income from multiple sources (real estate, investments, overseas income). You are considering incorporating as a KK or GK. You receive a tax audit notice from the NTA. Many tax accountants in Japan now offer subscription-style services starting around ¥15,000–¥30,000 per month that include quarterly reviews and year-end filing preparation.\nWhat records do I need to keep? Under blue return rules, you are required to retain all supporting documents — receipts, invoices, bank statements, contracts — for 7 years. freee\u0026rsquo;s receipt scanning feature (using the smartphone camera) creates a digital archive that satisfies the electronic bookkeeping law (電子帳簿保存法, Denshi Chobohzon-ho) requirements introduced in 2024, so you do not need to keep paper originals for most documents.\nConclusion Registering as a kojin jigyonushi in Japan is a straightforward process once you know the steps: file your opening notification at the local tax office within one month of starting, submit your blue return application at the same time, and set up freee to handle the bookkeeping from day one. The ¥650,000 blue return deduction alone makes the modest effort of double-entry bookkeeping financially worthwhile for almost any freelancer.\nThe combination of proper registration and good accounting software removes the two biggest sources of stress for new freelancers in Japan: legal uncertainty and tax filing. With freee handling the numbers automatically from your connected bank accounts, your monthly bookkeeping takes minutes rather than hours, and your annual tax return becomes a guided wizard rather than a paper ordeal.\nManage your household budget alongside business finances with our Budget Planner — keeping personal and business spending visible in one place makes it easier to plan quarterly tax payments and manage cash flow through slower months.\nReady to get your books in order? Get started with freee today and take the administrative complexity out of going freelance in Japan.\nRelated Tools Calculate your ideal freelance hourly rate → Freelance Rate Calculator Estimate your freelance tax burden → Side Hustle Tax Calculator Calculate your take-home pay → Salary Calculator See how savings compound over time → Compound Interest Calculator You May Also Like freee Cloud Accounting Tutorial: The Complete English Guide for Freelancers in Japan (2026) Freelance Tax Guide 2026: Everything You Need to Know Setting Up a Freelance Business in Japan: Domain, Registration, and Accounting in One Weekend ","permalink":"https://productivity-works.com/posts/freelance-japan-freee-tax-filing/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eGoing freelance in Japan is more achievable than most expats assume — but it does require navigating a specific set of administrative steps. Whether you are a designer, developer, consultant, English teacher, or online content creator, the process of becoming legally recognized as a self-employed individual follows the same path: register as a \u003cstrong\u003ekojin jigyonushi\u003c/strong\u003e (個人事業主), elect the blue return system, and set up proper bookkeeping.\u003c/p\u003e","title":"Freelance in Japan: How to Register as Kojin Jigyonushi and File Taxes With freee"},{"content":"※本記事にはアフィリエイト広告が含まれています。\n401k vs IRA Differences Explained — Complete Guide [2026] If you\u0026rsquo;ve ever stared at your benefits enrollment form wondering whether to contribute to your 401k, your IRA, or somehow both — you\u0026rsquo;re not alone. Most Americans know these accounts exist, but far fewer understand how they actually differ, which one to prioritize, or how to use them together strategically.\nHere\u0026rsquo;s the bottom line up front: both 401k plans and IRAs are tax-advantaged retirement accounts, but they have very different rules, limits, and advantages. Understanding these differences could be worth tens of thousands of dollars over your career.\nIn this guide, you\u0026rsquo;ll learn:\nThe key differences between 401k plans and IRAs (including Roth versions of each) 2026 contribution limits for every account type Why the employer match changes everything Which account to prioritize based on your income and situation The optimal order of contributions for maximizing tax advantages Common myths and mistakes about both account types Let\u0026rsquo;s decode these once and for all.\nWhat Are 401k and IRA Accounts? Definition \u0026amp; How It Works 401k Plan A 401k is an employer-sponsored retirement savings plan. Your employer sets it up, and you contribute money directly from your paycheck before (or after, for Roth 401k) taxes. Many employers also match a portion of your contributions — free money that significantly boosts your retirement savings.\nKey features:\nOffered by your employer — you can only access it through your job Contributions come out of your paycheck automatically (pre-tax or Roth/after-tax) Investment options are limited to what your employer\u0026rsquo;s plan offers Much higher contribution limits than IRAs Many employers offer a \u0026ldquo;match\u0026rdquo; (free money!) on contributions IRA (Individual Retirement Account) An IRA is a retirement account you open yourself, independently of your employer, at a bank, brokerage, or financial institution. You control where it\u0026rsquo;s held and what you invest in.\nKey features:\nYou open it yourself — not tied to your employer Complete investment flexibility (stocks, ETFs, mutual funds, bonds, REITs, etc.) Lower contribution limits than a 401k Available to anyone with earned income (Roth IRA has income limits) Traditional IRA or Roth IRA depending on when you want the tax break Pros and Cons 401k — Pros:\nMuch higher contribution limits ($23,500/year in 2026) Employer match can be worth thousands of dollars per year Automatic payroll deductions make saving effortless Available regardless of income level Some plans offer both Traditional and Roth 401k options 401k — Cons:\nLimited investment choices (often 10–30 mutual funds, sometimes expensive ones) Fees can be higher than self-directed IRAs Less control — you\u0026rsquo;re at the mercy of your employer\u0026rsquo;s plan design Tied to your job (though you can roll over when you leave) Required Minimum Distributions (RMDs) starting at age 73 IRA — Pros:\nFull investment flexibility — any stock, ETF, fund available at your broker Lower fees (especially at Fidelity or Vanguard with no-fee index funds) More control over your retirement strategy Roth IRA has no RMDs during your lifetime Roth IRA contributions can be withdrawn penalty-free at any time IRA — Cons:\nMuch lower contribution limits ($7,000/year in 2026) No employer match Roth IRA has income limits that may exclude high earners Requires self-discipline to set up and fund consistently Related: Roth IRA vs Traditional IRA: Which Is Better? 401(k) vs IRA — Key Differences at a Glance 401(k) Set up by Your Employer 2026 Contribution Limit $23,500 / year Employer Match Yes — free money! Investment Choices Limited to plan menu IRA Set up by You (any brokerage) 2026 Contribution Limit $7,000 / year Employer Match No Investment Choices Nearly unlimited VS 401k vs IRA: Complete Comparison Table 2026 Feature Traditional 401k Roth 401k Traditional IRA Roth IRA Who sets it up Employer Employer You You 2026 Contribution Limit $23,500 $23,500 $7,000 $7,000 Catch-up (age 50+) +$7,500 +$7,500 +$1,000 +$1,000 Catch-up (age 60–63) +$11,250 +$11,250 N/A N/A Tax on contributions Pre-tax (reduces taxable income) After-tax Pre-tax (may be deductible) After-tax Tax on growth Tax-deferred Tax-free Tax-deferred Tax-free Tax on withdrawals Taxed as ordinary income Tax-free (qualified) Taxed as ordinary income Tax-free (qualified) Employer match Yes (many employers) Yes (same match as trad.) No No Income limits None None None to contribute; deductibility limits apply Phase-out at $150K (single) Investment choices Limited to plan options Limited to plan options Nearly unlimited Nearly unlimited Required Min. Distributions Yes — age 73 Yes — age 73 (unlike Roth IRA) Yes — age 73 No (during lifetime) Early withdrawal penalty 10% + taxes (before 59½) 10% on earnings (before 59½) 10% + taxes (before 59½) 10% on earnings only Compare retirement accounts at top brokerages How to Choose: Key Factors What to Look For The Employer Match Rule This is the single most important factor in 401k vs IRA decisions: if your employer offers a match, always contribute at least enough to get the full match before putting money anywhere else.\nWhy? An employer match is an instant 50%–100% return on your money. If your employer matches 50 cents for every dollar you contribute up to 6% of your salary, and you earn $60,000, you get up to $1,800 in free employer contributions per year. No investment can guarantee those returns.\nThe 401k Fee Problem Many 401k plans, especially at smaller employers, offer expensive mutual funds with high expense ratios (sometimes 0.5%–1.5% or more). If your 401k options are expensive, the strategy is:\nContribute enough to get the full employer match Then maximize your IRA (where you can choose cheap index funds) Then return to your 401k if you have more to save Income and Tax Bracket Considerations\nYour Situation Recommended Priority Employer offers match 401k to full match → Roth IRA → remaining 401k No employer match, income under $100K Roth IRA first → then 401k No employer match, income $100K–$150K Split between Roth IRA and Traditional 401k High income, income over $165K (single) Traditional 401k → Backdoor Roth IRA → remaining 401k Self-employed SEP-IRA or Solo 401k (much higher limits) Common Mistakes to Avoid Mistake 1: Not contributing enough to get the full employer match Leaving employer match on the table is the financial equivalent of turning down a raise. Even if your 401k has expensive fund options, contribute at least enough to capture the full match.\nMistake 2: Confusing contribution limits The $7,000 IRA limit and the $23,500 401k limit are separate. You can (and should) maximize both if your income allows. Many people think using an IRA reduces their 401k limit — it doesn\u0026rsquo;t.\nMistake 3: Ignoring a Roth 401k option Many employers now offer a Roth 401k option alongside the traditional 401k. This gives you the high contribution limits of a 401k with the tax-free growth of a Roth. For younger workers, this can be an excellent choice.\nMistake 4: Forgetting to roll over old 401k accounts When you leave a job, your 401k doesn\u0026rsquo;t disappear — but it can become forgotten and stuck in expensive funds. Roll your old 401k into an IRA or your new employer\u0026rsquo;s 401k to maintain control and potentially lower fees.\nMistake 5: Not updating beneficiaries Both 401k plans and IRAs pass outside of probate — they go directly to named beneficiaries. After life changes (marriage, divorce, children), update your beneficiary designations on all accounts.\nStep-by-Step Guide: Optimizing Your Retirement Accounts Step 1: Find out your employer\u0026rsquo;s 401k match formula Log into your HR system or ask your HR department. Common formats:\n\u0026ldquo;We match 50% of contributions up to 6% of salary\u0026rdquo; = contribute 6% to get the full 3% match \u0026ldquo;We match 100% of contributions up to 4% of salary\u0026rdquo; = contribute 4% to get the full 4% match \u0026ldquo;No employer match\u0026rdquo; = contribute minimum, prioritize IRA first Step 2: Calculate your ideal contribution rate Work backward from your retirement goal. A simplified rule: save 15% of your gross income for retirement (including employer match). If you earn $70,000, aim for $10,500/year across all retirement accounts.\nStep 3: Open an IRA if you don\u0026rsquo;t have one After capturing your full 401k match, open a Roth IRA (most under-40 workers) or Traditional IRA at Fidelity, Schwab, or Vanguard. Set up automatic monthly contributions: $583/month hits the $7,000 annual limit.\nStep 4: Choose your investments\nIn your 401k: Choose the lowest-cost index funds available. Look for S\u0026amp;P 500 index funds or total market index funds with expense ratios under 0.20%. If your plan has a Vanguard Institutional Index Fund or similar, that\u0026rsquo;s usually the best choice. In your IRA: You have full freedom. Choose a simple three-fund portfolio or a target-date index fund. Step 5: Set your asset allocation A commonly cited rule of thumb for stock/bond allocation: subtract your age from 110 to get your stock percentage. At age 30, that\u0026rsquo;s 80% stocks / 20% bonds. Adjust based on your risk tolerance.\nStep 6: Enable automatic rebalancing Many 401k plans offer annual auto-rebalancing. Turn it on. For your IRA, rebalance manually once per year or choose a target-date fund that rebalances automatically.\nStep 7: Plan for rollovers When you change jobs, roll your old 401k into:\nYour new employer\u0026rsquo;s 401k (if it has better investment options), OR A Traditional IRA at your preferred brokerage (for maximum investment flexibility) Never take a cash distribution — you\u0026rsquo;ll owe income taxes + 10% early withdrawal penalty.\nFrequently Asked Questions Q: Can I contribute to both a 401k and an IRA in the same year? A: Yes, absolutely. The contribution limits are separate and completely independent. Maxing both in 2026 means contributing $23,500 to your 401k and $7,000 to your IRA — a total of $30,500 in tax-advantaged retirement savings.\nQ: What happens to my 401k if I lose my job? A: Your 401k balance is yours regardless of employment status. Options: leave it with your former employer (if the plan allows), roll it over to an IRA, roll it to your new employer\u0026rsquo;s 401k, or (not recommended) cash it out and pay taxes + penalties.\nQ: Can I withdraw from my 401k early? A: Early withdrawals (before age 59½) from a Traditional 401k trigger income taxes plus a 10% penalty, with some exceptions (disability, certain medical expenses, first-time home purchase for IRAs, etc.). For Roth 401k, you can withdraw contributions penalty-free but earnings are still subject to the 10% penalty.\nQ: Does having a 401k affect my IRA tax deduction? A: For Roth IRA: no — Roth contributions are never deductible, so your 401k status doesn\u0026rsquo;t matter. For Traditional IRA: yes — if you (or your spouse) have a workplace retirement plan, your ability to deduct Traditional IRA contributions phases out at moderate income levels.\nQ: What\u0026rsquo;s the best 401k investment option if my plan has poor choices? A: Look for the fund with the lowest expense ratio that tracks a broad market index (S\u0026amp;P 500 or total market). Even an S\u0026amp;P 500 index fund with a 0.15% expense ratio is acceptable. Avoid actively managed funds with expense ratios above 0.50% if any index alternative exists.\nQ: What is a 401k rollover, and should I do it? A: A rollover is moving money from a 401k to an IRA (or another 401k) without triggering taxes. Direct rollovers (institution to institution) are tax-free. You should strongly consider rolling over if your old 401k has high fees or limited fund choices. Rolling into an IRA gives you full investment flexibility with access to low-cost index funds.\nQ: At what income does the Roth IRA phase out in 2026? A: For 2026: single filers begin to phase out at $150,000 and are fully phased out at $165,000. Married filing jointly: phase-out begins at $236,000 and ends at $246,000. Above these limits, use the backdoor Roth strategy.\nOpen a Tax-Advantaged Account Today Whether you\u0026rsquo;re maximizing your 401k match or opening your first IRA, the sooner you start the more compound growth works in your favor. Open a Rakuten Securities account to start investing in low-cost index funds inside a tax-advantaged account today.\nConclusion: Use Both Accounts Strategically The 401k vs. IRA debate is a false choice for most workers — the optimal answer is to use both, in the right order. Here\u0026rsquo;s the simple framework to remember:\nContribute to 401k up to the full employer match (never leave free money on the table) Max out your Roth IRA ($7,000 in 2026) — especially if you\u0026rsquo;re under 40 Return to your 401k and contribute more, up to the $23,500 limit Taxable brokerage account for anything beyond that Follow this order consistently over your career, invest in low-cost index funds, and the math takes care of the rest. The exact split between 401k and IRA matters far less than simply saving consistently in tax-advantaged accounts.\nStart optimizing your retirement accounts today:\nOpen a Roth IRA at Fidelity — $0 minimum Compare IRA options at Charles Schwab Get your 401k portfolio reviewed by Blooom Related: Best Index Funds for Beginners 2026 Disclaimer: This article is for informational purposes only and does not constitute financial advice. Investment decisions should be made based on your individual circumstances. Please consult a qualified financial advisor before making investment decisions. Information is current as of the publication date — verify details on official websites.\nDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nRelated Tools \u0026amp; Articles Calculate your exact age instantly → Age Calculator Calculate percentages, discounts, and tips instantly → Percentage Calculator Plan your retirement contributions → Retirement Savings Calculator See your 2026 federal tax bracket and effective rate → Tax Bracket Calculator See how your investments grow over time → Compound Interest Calculator Estimate dividend income from your portfolio → Dividend Income Calculator Calculate how long to reach any savings target → Savings Goal Calculator Track your total net worth → Net Worth Calculator Plan your path to financial independence → FIRE Calculator Roth IRA vs Traditional IRA: Which Is Better? ETF vs Mutual Fund: Which Should I Choose? How to Start Investing with $100 in 2026 Best Index Funds for Beginners 2026 Passive Income Ideas That Actually Work 2026 This article contains affiliate links. We may earn a commission at no extra cost to you.\nRelated Templates Take control of your finances with these tools:\nSmart Budget Tracker (Excel Template) — Track income, expenses, and savings goals Side Hustle Starter Kit 2026 — Build additional income streams ","permalink":"https://productivity-works.com/posts/401k-vs-ira-differences-explained/","summary":"\u003cp\u003e※本記事にはアフィリエイト広告が含まれています。\u003c/p\u003e\n\u003ch1 id=\"401k-vs-ira-differences-explained--complete-guide-2026\"\u003e401k vs IRA Differences Explained — Complete Guide [2026]\u003c/h1\u003e\n\u003cp\u003eIf you\u0026rsquo;ve ever stared at your benefits enrollment form wondering whether to contribute to your 401k, your IRA, or somehow both — you\u0026rsquo;re not alone. Most Americans know these accounts exist, but far fewer understand how they actually differ, which one to prioritize, or how to use them together strategically.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s the bottom line up front: both 401k plans and IRAs are tax-advantaged retirement accounts, but they have very different rules, limits, and advantages. Understanding these differences could be worth tens of thousands of dollars over your career.\u003c/p\u003e","title":"401k vs IRA Differences Explained (2026)"},{"content":"This article contains affiliate links.\nFreelancing in Japan comes with real financial freedom — but it also comes with a set of tax obligations that can catch newcomers completely off guard. Unlike a salaried employee whose employer handles withholding tax and year-end adjustments, freelancers and solopreneurs in Japan are responsible for filing their own annual tax return (確定申告, kakutei shinkoku) every year by the March 15 deadline.\nThat responsibility extends to bookkeeping. If you want to claim the powerful ¥650,000 blue-return deduction (青色申告特別控除, aoiro shinkoku tokubetsu kōjo) — one of the most valuable tax breaks available to individual business owners in Japan — you must maintain accurate double-entry books throughout the year. Doing that manually in a spreadsheet is tedious, error-prone, and increasingly unnecessary.\nThe good news: Japan\u0026rsquo;s accounting software market for individuals and small businesses is mature, competitive, and packed with options. The three dominant players are freee, Money Forward Cloud, and Yayoi. Each takes a different approach, serves a different type of user, and comes at a different price point.\nThis guide breaks down all three in detail so you can choose the one that fits your situation — and stop dreading tax season.\nWhy Japanese Freelancers Need Accounting Software Before comparing products, it helps to understand exactly what you\u0026rsquo;re up against as a freelancer in Japan.\nThe Blue Return Deduction Is Worth ¥650,000 — But It Has Requirements Japan\u0026rsquo;s tax code offers two categories of business filing for individual operators: white return (白色申告) and blue return (青色申告). The blue return requires pre-approval from the tax office, but it unlocks a deduction of up to ¥650,000 from your business income — which can translate to tens of thousands of yen in actual tax savings depending on your bracket.\nTo claim the full ¥650,000 deduction, you must:\nBe approved for blue return status (file Form 09 with your local tax office) Maintain double-entry bookkeeping records throughout the year File your return electronically via e-Tax (電子申告) This is not optional accounting — it is a legal requirement if you want the deduction. Accounting software handles all three conditions automatically.\nAnnual Tax Filing Is Mandatory for All Freelancers Even if you opt for the simpler white return, you are still required to file a tax return if your annual freelance income exceeds ¥480,000 (after business expense deductions). Most working freelancers will clear this threshold easily. Failure to file results in penalties, and late filing forfeits deductions.\nAccounting software generates the required forms — including Schedule B (第二表) and the profit/loss statement (収支内訳書 or 青色申告決算書) — automatically from your bookkeeping data, and submits them directly to the NTA via e-Tax.\nThe Invoice System (インボイス制度) Adds Another Layer Since October 2023, Japan\u0026rsquo;s qualified invoice system (適格請求書等保存方式) requires consumption tax (消費税) records to be kept in a specific format. All three accounting tools covered here support invoice system compliance, but the quality of implementation and ease of use varies.\nfreee — Best for Beginners and Non-Japanese Speakers freee (フリー株式会社) is widely regarded as the most beginner-friendly accounting platform in Japan. It was built from the ground up with the assumption that most users have zero accounting background — and that assumption shapes every design decision in the product.\nHow freee Works Instead of presenting you with a traditional journal entry screen, freee walks you through transactions using a question-based interface: \u0026ldquo;Did you receive money or pay money?\u0026rdquo; \u0026ldquo;Was this for business or personal?\u0026rdquo; \u0026ldquo;What category does this fall under?\u0026rdquo; The system infers the accounting entries behind the scenes.\nBank accounts, credit cards, and e-money services connect directly to freee, which automatically imports transactions and suggests categorizations using AI. You review, correct if needed, and confirm. Most users can process a month\u0026rsquo;s worth of transactions in under 30 minutes once the initial setup is complete.\nPricing (Tax Excluded — Check Official Site for Latest) Plan Monthly Price (approx.) Starter ¥1,180/month Standard ¥2,380/month The Starter plan covers basic bookkeeping and tax filing needs for most freelancers. The Standard plan adds features like invoice management, payroll (if you have employees or pay yourself a salary), and more advanced reporting. Both plans include a 30-day free trial.\nNote: Prices are listed without consumption tax. Always verify current pricing on the freee official website as rates may change.\nKey Features Intuitive question-based input: No accounting knowledge required to get started Bank and financial institution connections: Approximately 3,200 financial institutions supported (one of the broadest networks in Japan) Direct e-Tax filing: Submit your blue return directly from freee without exporting files Invoice system compliance: Full support for the qualified invoice (適格請求書) format Receipt OCR: Scan paper receipts with the mobile app; freee extracts the amount and date automatically Mobile app: Full-featured iOS and Android apps for on-the-go expense logging English support: freee offers partial English-language interface elements and has English-language help documentation, making it the most accessible option for non-Japanese speakers Limitations Higher price point: freee is the most expensive of the three options, particularly at the Standard tier Over-simplification for advanced users: Experienced bookkeepers may find the abstraction layer frustrating when they want to input entries directly Our Verdict on freee For English-speaking freelancers new to Japan\u0026rsquo;s tax system, freee minimizes the learning curve as much as any product can. The question-based flow, broad bank connectivity, and mobile receipt scanning remove most of the friction from daily bookkeeping — leaving you to focus on your actual work.\nTry freee free for 30 days Money Forward Cloud — Best for Experienced Bookkeepers Money Forward Cloud Kakuteishinkoku (マネーフォワード クラウド確定申告) takes a more traditional approach to bookkeeping. Where freee abstracts the accounting away, Money Forward gives you direct access to journal entries and financial statements in a format that experienced bookkeepers will recognize immediately.\nHow Money Forward Works Money Forward presents a standard journal entry interface. You record transactions by entering debit and credit accounts directly, or use the simplified input view for common transaction types. The platform uses AI to auto-categorize imported bank transactions — and its categorization accuracy is generally considered very strong, with the system learning from your corrections over time.\nLike freee, Money Forward connects to financial institutions and imports transactions automatically. It also supports e-Tax filing and the full suite of tax forms required for blue return filing.\nPricing (Tax Excluded — Check Official Site for Latest) Plan Monthly Price (approx.) Personal Mini ¥900/month Personal ¥1,280/month The Personal Mini plan covers basic blue return filing. The Personal plan adds features like receipt OCR, multiple bank account connections, and additional reporting tools. Money Forward is generally priced lower than freee for comparable functionality.\nNote: Money Forward periodically revises its plan structure and pricing. Confirm current plans on the official Money Forward Cloud website.\nKey Features Traditional journal entry interface: Directly accessible for users with accounting experience Strong AI auto-categorization: One of the best in class for automatically matching imported transactions to expense categories Bank and financial institution connections: Approximately 2,500 institutions supported Direct e-Tax filing: Blue return submission directly from the platform Invoice system compliance: Full support for qualified invoice format Competitive pricing: Lower monthly cost than freee for equivalent functionality Integration with Money Forward ME: Personal finance app that many Japan residents already use for household budgeting Limitations Steeper learning curve: The journal entry interface is intuitive to accountants but can be daunting for users with no bookkeeping background Limited English support: The interface is almost entirely in Japanese, and English help documentation is minimal compared to freee Mobile app is less complete: The mobile experience is adequate but not as polished as freee\u0026rsquo;s for receipt capture and on-the-go use Our Verdict on Money Forward Money Forward is the smart choice for freelancers who already understand double-entry bookkeeping, have some comfort reading Japanese, and want to keep monthly costs down. The AI categorization is excellent, and the lower price point is meaningful over a full year.\nYayoi (弥生) — Best Budget Option Yayoi (弥生株式会社) has been in the Japanese accounting software business longer than either of its competitors — the company was founded in 1978 and has been developing accounting software since the early 1980s. This history shows in the product: Yayoi is deeply integrated with Japan\u0026rsquo;s tax filing infrastructure, has the most comprehensive phone support network, and at the right plan level, is the cheapest long-term option available.\nHow Yayoi Works Yayoi\u0026rsquo;s cloud product, Yayoi Blue Return Online (やよいの青色申告 オンライン), supports both simplified and double-entry bookkeeping. Input is done through a transaction register interface — slightly more traditional than freee but less technically demanding than Money Forward\u0026rsquo;s full journal entry view.\nYayoi also offers desktop software for users who prefer not to use a browser-based tool, though the cloud version is recommended for e-Tax integration and accessibility.\nPricing (Tax Excluded — Check Official Site for Latest) Plan Annual Price (approx.) First year Free (セルフプラン) セルフプラン (from year 2) ¥9,680/year (~¥807/month equivalent) ベーシックプラン ¥13,800/year (~¥1,150/month equivalent) トータルプラン ¥24,200/year (~¥2,017/month equivalent) The セルフプラン (Self Plan) is genuinely free for the first year — including blue return filing — making Yayoi the most accessible entry point for someone who wants to try cloud accounting at no cost. From year two, the annual pricing makes it the cheapest sustained option of the three.\nThe higher-tier plans add phone support (安心電話サポート) and additional consulting features. The base Self Plan relies on online help and community forums.\nNote: Yayoi\u0026rsquo;s promotional pricing and plan structure change periodically. Check the official Yayoi website for current offers.\nKey Features Free first year: The only major accounting platform that offers a fully functional free tier for an extended period Lowest long-term cost: Annual billing at the Self Plan is the cheapest of the three platforms over a multi-year horizon Longest track record: Decades of experience with Japan\u0026rsquo;s tax filing requirements; deep NTA integration Comprehensive support options: Phone and chat support available on higher tiers; extensive online documentation e-Tax filing: Direct submission to the NTA supported across plans Invoice system compliance: Full support for qualified invoices Limitations Least modern UI: The interface feels noticeably older than freee or Money Forward; less visually polished Limited English support: Almost entirely Japanese; not suitable as a primary tool for users who cannot read Japanese No mobile receipt scanning: Unlike freee and Money Forward, Yayoi\u0026rsquo;s mobile experience is limited and does not include OCR-based receipt capture Fewer bank connections: The number of supported financial institutions is lower than both competitors Learning curve for non-accountants: The interface does not offer the kind of guided question-based flow that freee provides Our Verdict on Yayoi Yayoi makes the most financial sense for cost-conscious freelancers who have been in Japan long enough to navigate Japanese-language interfaces, do not need mobile receipt capture, and want the reassurance of a company with a decades-long track record in Japanese tax compliance.\nFeature Comparison Table Feature freee Money Forward Cloud Yayoi Blue Return Online Monthly cost (approx.) ¥1,180–¥2,380 ¥900–¥1,280 ¥807–¥2,017 (annual billing) Free trial 30 days 1 month 1 year (Self Plan) Ease of use (beginners) Excellent Moderate Moderate Bank connections ~3,200 ~2,500 Fewer e-Tax filing Yes Yes Yes Mobile app Full-featured Moderate Limited English support Partial Minimal Minimal Receipt OCR Yes Yes (Personal plan+) No Invoice system support Yes Yes Yes Blue return (65万円 deduction) Yes Yes Yes Established since 2012 2012 1978 All pricing excludes consumption tax. Check each provider\u0026rsquo;s official website for the most current plan details.\nWhich One Should You Choose? The right accounting software depends on your specific situation. Here is a practical decision guide:\nYou are new to freelancing in Japan and have no accounting background. Choose freee. The question-based interface removes the need to understand debit/credit entries, the mobile app makes daily expense logging easy, and partial English support lowers the language barrier. The higher monthly cost is worth paying for the reduced time investment and lower risk of filing errors.\nYou are cost-conscious and comfortable reading Japanese. Choose Yayoi. The first year is free, and from year two the annual cost is the lowest of the three. If you do not need mobile receipt scanning and can navigate a Japanese-language interface, Yayoi delivers everything you need to file a compliant blue return at minimum cost.\nYou have accounting experience and want direct control over your books. Choose Money Forward Cloud. The journal entry interface gives you the control you are used to, the AI categorization is excellent, and the pricing is lower than freee for comparable functionality. The trade-off is a nearly all-Japanese interface.\nYou are a non-Japanese speaker who needs ongoing English support. Choose freee without hesitation. It is the only platform among the three that has made meaningful investment in English-language resources and partial UI localization.\nYou are already using Money Forward ME for personal budgeting. Consider Money Forward Cloud. The shared ecosystem and ability to separate business and personal finances within the same product family is a genuine convenience.\nHow to File Your Tax Return With freee For freelancers choosing freee, here is a brief overview of the annual tax filing process:\nStep 1: Connect your bank accounts and credit cards. freee imports transactions automatically throughout the year. Do this as early as possible — retroactive import is possible, but staying current throughout the year is far easier.\nStep 2: Categorize your transactions. freee\u0026rsquo;s AI suggests categories for each imported transaction. Review suggestions regularly (monthly is ideal) and correct any misclassifications. Common business expense categories include 通信費 (communications), 消耗品費 (consumables), 外注費 (outsourcing), and 旅費交通費 (travel).\nStep 3: Register fixed assets and depreciation. If you purchased equipment worth ¥100,000 or more for business use, freee manages the depreciation schedule automatically.\nStep 4: Run the year-end checklist. freee provides a guided checklist for closing the fiscal year, including reconciliation checks and reminder to record any business-use portion of home rent or utilities.\nStep 5: Generate and submit your tax return. freee produces the required tax forms — the 青色申告決算書 (Blue Return Settlement Sheet) and the 確定申告書 (Tax Return) — and submits them directly via e-Tax. You will need a My Number Card and a card reader, or the My Number Card smartphone app, for digital authentication.\nPro tip: Before you start, it helps to know roughly what you owe. Calculate your estimated taxes first with our Side Income Tax Calculator to avoid any year-end surprises.\nThe entire filing process through freee typically takes two to four hours for a straightforward freelance operation — far less than manual filing, and far less stressful.\nConclusion Japan\u0026rsquo;s three leading accounting software platforms for freelancers — freee, Money Forward Cloud, and Yayoi — are all capable of handling everything you need: double-entry bookkeeping, blue return documentation, invoice system compliance, and direct e-Tax filing.\nThe choice between them comes down to three factors: your Japanese language proficiency, your accounting background, and how much you want to spend.\nFor the majority of English-speaking freelancers and solopreneurs in Japan, freee is the strongest all-around choice. Its guided interface removes the steep learning curve of Japanese accounting conventions, its broad bank connectivity automates most of the daily work, and its partial English support makes it accessible to users who are still building their Japanese reading skills. The higher monthly cost is a reasonable trade-off for the time saved and the reduced likelihood of filing errors that result in penalties or missed deductions.\nIf you are ready to get your bookkeeping under control and stop dreading every March tax deadline, there is no better time to start.\nStart your free trial of freee Related Tools Calculate your ideal freelance hourly rate → Freelance Rate Calculator Disclaimer: Tax rules and software pricing change regularly. This article reflects information available as of May 2026. Always verify current pricing on each provider\u0026rsquo;s official website and consult a qualified tax accountant (税理士) for advice specific to your situation.\nYou May Also Like Best Accounting Software for Freelancers 2026: Full Compare Freelance in Japan: How to Register as Kojin Jigyonushi and File Taxes With freee freee Cloud Accounting Tutorial: The Complete English Guide for Freelancers in Japan (2026) ","permalink":"https://productivity-works.com/posts/accounting-software-japan-freelancer/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eFreelancing in Japan comes with real financial freedom — but it also comes with a set of tax obligations that can catch newcomers completely off guard. Unlike a salaried employee whose employer handles withholding tax and year-end adjustments, freelancers and solopreneurs in Japan are responsible for filing their own annual tax return (確定申告, \u003cem\u003ekakutei shinkoku\u003c/em\u003e) every year by the March 15 deadline.\u003c/p\u003e\n\u003cp\u003eThat responsibility extends to bookkeeping. If you want to claim the powerful ¥650,000 blue-return deduction (青色申告特別控除, \u003cem\u003eaoiro shinkoku tokubetsu kōjo\u003c/em\u003e) — one of the most valuable tax breaks available to individual business owners in Japan — you must maintain accurate double-entry books throughout the year. Doing that manually in a spreadsheet is tedious, error-prone, and increasingly unnecessary.\u003c/p\u003e","title":"Best Accounting Software for Freelancers in Japan 2026: freee vs Money Forward vs Yayoi"},{"content":"Real estate has always been a relationship business. The agents who thrive are the ones who can be everywhere at once — following up with leads promptly, writing compelling listing descriptions, staying in front of past clients, analyzing neighborhood comps, and still having enough energy left to show houses. In 2025 and into 2026, AI tools are making that kind of scale genuinely possible for solo agents and small teams.\nThis is not a list of generic \u0026ldquo;AI writing tools.\u0026rdquo; This is a guide specifically for licensed real estate agents and brokers — the tools that handle the parts of your job that eat time without requiring your unique expertise, so you can spend more hours on the irreplaceable human parts.\nHow Real Estate Agents Are Actually Using AI in 2026 Before the tool list, let\u0026rsquo;s be specific about what AI is actually good at in a real estate context:\nWriting listing descriptions from bullet points (photos, features, address) in seconds Drafting client emails and follow-up sequences that feel personal at scale Generating social media content from listings without starting from scratch Summarizing lengthy documents (disclosures, inspection reports, HOA documents) Answering common client questions via chatbot so you are \u0026ldquo;available\u0026rdquo; 24/7 Analyzing market data and generating narrative summaries from raw comps Transcribing and summarizing showing notes, voicemails, and client calls Drafting offers and contract language (always reviewed by an attorney — AI drafts, you verify) AI does not (yet) replace your local market knowledge, your negotiation instincts, your ability to read a buyer\u0026rsquo;s emotional state in a showing, or your professional relationships. The agents winning with AI are augmenting those irreplaceable skills, not trying to automate them.\nCategory 1: Listing Description and Marketing Copy AI ChatGPT / GPT-4o Best for: Listing descriptions, neighborhood narratives, email drafts, social captions\nChatGPT remains the most versatile writing tool for agents because it can be customized so precisely. The key is having a solid prompt library rather than winging it each time.\nA high-performing listing description prompt:\nWrite a compelling MLS listing description for this property: Address: [address] Property type: [SFH / condo / townhome / multi-family] Bedrooms/Bathrooms: [X bed / X bath] Square footage: [X sq ft] Lot size: [if relevant] Key features: [list 5-8 bullet points — hardwood floors, updated kitchen, pool, etc.] Neighborhood highlights: [walkability, schools, nearby parks, commute] Target buyer: [families, first-time buyers, investors, downsizers] Tone: [warm and inviting / luxury / investment-focused] MLS character limit: 500 characters (or \u0026#34;no limit\u0026#34;) Rules: No price. No address in the description. No first-person. Start with an evocative opening line, not \u0026#34;Welcome to...\u0026#34; The \u0026ldquo;No \u0026lsquo;Welcome to\u0026hellip;\u0026rsquo;\u0026rdquo; rule alone immediately differentiates AI-written listings from generic ones.\nPricing: Free (GPT-4o mini) or $20/month (ChatGPT Plus with GPT-4o)\nListing Copy AI / Addressable Purpose-built tools for real estate listing copy. These are more expensive but offer real-estate-specific templates, MLS formatting, and faster output for high-volume agents.\nPricing: $49-99/month depending on tier\nCanva AI (Magic Write + Design) Canva\u0026rsquo;s AI features now allow agents to input a listing description and generate social media graphics, just listed postcards, and email headers simultaneously. For agents managing their own marketing, this collapses design time from an hour to ten minutes per listing.\nBest for: Just Listed / Just Sold social posts, open house flyers, email graphics\nCategory 2: Lead Generation and Nurturing AI Follow Up Boss AI + FUB Assist Follow Up Boss remains one of the most popular real estate CRMs, and their AI features are genuinely useful for agents:\nAI-generated follow-up suggestions based on lead behavior and timeline Email drafts for specific lead stages (new inquiry, post-showing, gone-silent) Lead scoring that prioritizes which leads to call first Smart drip campaign generation based on buyer/seller intent signals For agents running their lead nurture entirely through FUB, the AI layer meaningfully reduces the cognitive load of \u0026ldquo;what do I say to this lead today?\u0026rdquo;\nPricing: $69-$499/month (CRM + AI features)\nSierra Interactive Sierra combines an IDX website, CRM, and AI texting/calling features. Their AI can send the first text response to a new web lead in under 60 seconds — a critical metric since lead response time within the first 5 minutes dramatically increases contact rates.\nBest for: High-volume lead generation teams, agents running paid search campaigns\nStructurely (AI ISA) Structurely provides an AI inside sales assistant (ISA) that handles text conversations with new leads, qualifies them, answers common questions, and schedules appointments — then hands off to the agent. For agents who buy leads but struggle with response consistency, this is genuinely transformative.\nPricing: Custom, typically $400-$600/month for solo agents\nCategory 3: Market Analysis and Research AI ChatGPT + MLS Data (Manual Workflow) The most accessible market analysis workflow in 2026: export a CMA (comparable market analysis) from your MLS, paste the raw data into ChatGPT, and ask it to:\nWrite a narrative summary of market conditions Identify pricing trends in plain English Draft a market update email to send to your database Generate talking points for a listing presentation This turns a table of numbers into a compelling story that clients actually understand.\nPerplexity AI (Pro) For research tasks — understanding neighborhood development plans, school rating changes, new employer announcements affecting local demand, zoning changes — Perplexity\u0026rsquo;s real-time web search capability makes it faster than manual Googling and better than standard ChatGPT (which has a knowledge cutoff).\nBest for: Neighborhood research, market news summaries, competitive market positioning\nPricing: Free tier available; Pro at $20/month\nHouseCanary / Quantarium These are professional-grade AI property valuation tools used by institutional investors, lenders, and sophisticated agents. They provide AVM (Automated Valuation Model) data, rental yield estimates, neighborhood trend forecasts, and risk scores.\nBest for: Investor clients, luxury market agents, agents doing heavy CMA work\nPricing: Custom enterprise pricing\nCategory 4: Client Communication AI Otter.ai / Fireflies.ai After every client call or showing debrief, dictate your notes and let Otter or Fireflies transcribe and summarize them. Features include:\nAuto-transcription of calls and voice memos AI summary with key action items extracted Search across all transcripts — find every client who mentioned \u0026ldquo;good school district\u0026rdquo; in one search Integration with CRMs like Follow Up Boss and HubSpot For agents doing 10+ client interactions per day, these tools prevent the constant \u0026ldquo;what did they say about the garage?\u0026rdquo; memory problem.\nPricing: Free tier (Otter); Fireflies Pro at $18/month\nZapier + ChatGPT (Automation Workflow) This combination allows sophisticated automation:\nNew lead comes in from Zillow → Zapier triggers ChatGPT to draft a personalized first-contact email based on the lead\u0026rsquo;s search criteria → email sent via your Gmail in under 2 minutes Showing scheduled → Zapier triggers a pre-showing preparation email to the buyer with neighborhood info generated by ChatGPT Offer accepted → Zapier generates and sends a \u0026ldquo;congratulations, here\u0026rsquo;s what happens next\u0026rdquo; email sequence This kind of automation previously required a full ISA or transaction coordinator. Now a solo agent can run it with $50/month in tools.\nPricing: Zapier Pro at $49/month + ChatGPT API (typically $5-30/month for most agents)\nCategory 5: Document and Contract AI Skyline AI / Docusign Maestro Document handling is one of the highest time-cost areas in real estate. AI tools in this space include:\nAuto-population of forms from MLS data and client profiles Clause risk flagging in contracts (non-attorney advisory only) Plain-English summaries of disclosure documents for buyer clients Deadline extraction — pulling key dates from contracts into your calendar automatically Important caveat: AI document tools in real estate assist with workflow but do not constitute legal advice. Always have a licensed real estate attorney review any contract language the AI flags.\nChatGPT for Disclosure Summaries A highly practical use case: a buyer receives a 40-page seller disclosure packet. Paste the text into ChatGPT and ask:\n\u0026ldquo;Summarize this seller disclosure document in plain English for a first-time homebuyer. Identify any items that are flagged as \u0026lsquo;yes/present\u0026rsquo; under defects, and list them clearly. Note any items I should ask a home inspector to specifically check.\u0026rdquo;\nThis does not replace a buyer\u0026rsquo;s agent explaining the document — it prepares you for that conversation and ensures nothing is missed.\nCategory 6: Social Media and Content Marketing AI Buffer + AI Assistant Buffer\u0026rsquo;s AI tools help agents maintain consistent social media posting without constant manual effort:\nGenerate a month of Instagram captions from a list of your recent listings, market stats, and client milestones Schedule posts across Instagram, Facebook, LinkedIn, and X Analyze which post types perform best in your specific market Best for: Agents building a personal brand on social media\nDescript (Video + AI) For agents doing video tours, market update videos, or YouTube content, Descript allows:\nAI-powered transcription and editing (edit video by editing the transcript) Filler word removal with one click AI voiceover and caption generation Short-form clip generation from long-form video Video converts exceptionally well in real estate. Descript removes the editing bottleneck that keeps most agents from creating it consistently.\nPricing: Free tier; Pro at $24/month\nBuilding Your Real Estate AI Stack You do not need all of these tools. Here is a practical stack by agent type:\nSolo Agent, $3-8M Annual Volume Tool Purpose Monthly Cost ChatGPT Plus Listing copy, emails, market summaries $20 Otter.ai Pro Call transcription and notes $17 Canva Pro Marketing graphics $15 Zapier Starter Basic automations $20 Total ~$72/month Mid-Volume Agent, $8-20M Annual Volume Add to the above:\nFollow Up Boss ($69-$99/month) for CRM + AI follow-up Perplexity Pro ($20/month) for market research Descript Pro ($24/month) for video Total addition: ~$113-$143/month High-Volume Team, $20M+ Add:\nStructurely AI ISA (~$400-$600/month) for lead qualification HouseCanary for valuation data Sierra Interactive for lead gen + AI outreach Dedicated transaction coordinator software with AI features The ROI calculation is straightforward: at a $9,000 average commission (buyer side, $300K home at 3%), a single additional closed transaction per quarter from faster follow-up or better-converting listings pays for an entire year of AI tools.\nThe AI-Powered Listing Launch Workflow Here is how a listing launch looks when AI is integrated throughout:\nDay -7 (Listing Agreement Signed):\nInput property data into ChatGPT prompt → generate 3 listing description options in 3 minutes Use Canva AI to create just-listed social media templates with property photos Generate neighborhood narrative for the listing presentation Day -3 (Photos Received):\nFinalize listing description based on actual photos Generate 2-week social media posting calendar for the listing Draft email announcement to your database Day 0 (Listed):\nSchedule all social posts via Buffer Send database email (drafted by AI, personalized by you) Activate any automated lead-response sequences in your CRM Showing Period:\nOtter.ai transcribes and summarizes every showing feedback call ChatGPT synthesizes feedback into a price adjustment recommendation document for the seller Under Contract:\nAI summarizes inspection report for the buyer Automated timeline emails keep buyer and seller updated Deadline extraction ensures nothing slips This workflow compresses what used to take a full-time marketing assistant into 2-3 hours of an agent\u0026rsquo;s time.\nKey Risks and How to Manage Them 1. AI hallucination in market data. Never publish AI-generated market statistics without verifying against your MLS. AI can generate plausible-sounding but incorrect numbers.\n2. Fair Housing Act compliance. AI-generated listing descriptions and marketing copy must be reviewed for compliance. Certain descriptive language (related to demographics, families, religion, national origin) can create Fair Housing violations even when unintentional. Always review AI copy through a Fair Housing lens.\n3. Client data privacy. Do not paste client names, addresses, financial details, or contract specifics into public AI tools (ChatGPT, etc.) unless you have reviewed the tool\u0026rsquo;s data privacy policy. Use anonymized versions where possible, or use enterprise tools with appropriate data agreements.\n4. Over-automation of relationship. Real estate is a trust business. Buyers and sellers can tell when communication is fully automated. Use AI to draft; add your personal voice before sending anything important.\nGetting Started This Week If you are new to AI tools, here is a simple three-day start:\nDay 1: Sign up for ChatGPT Plus. Use the listing description prompt from this article on your next listing. Compare the output to your usual process. Day 2: Install Otter.ai on your phone. Record your next client call or debrief. Review the transcript and summary. Day 3: Use ChatGPT to draft the follow-up emails for your current active leads. Customize, send, measure. For deeper implementation, our AI Productivity Playbook on Payhip includes a dedicated Real Estate Agent module with 50+ industry-specific prompts, automation workflow templates, and a 30-day implementation plan for agents going from zero AI usage to a fully AI-augmented practice.\nReal Estate Careers Are Evolving — Stay Ahead Agents who combine market expertise with AI productivity tools are building stronger books of business. Find your next career on doda — browse real estate and business development roles on Japan\u0026rsquo;s leading job platform.\nFinal Thoughts The real estate agents who will dominate the next five years are not the ones who resist AI — they are the ones who adopt it early enough to build a competitive advantage before it becomes standard practice. In 2026, AI fluency is still a differentiator. In 2028, it will be table stakes.\nYour clients are already interacting with AI in every other part of their lives. Meeting them with faster responses, better-written communications, and more insightful market analysis — delivered with the warmth only a human agent can provide — is not a compromise. It is what exceptional service looks like in 2026.\nInterested in the full AI toolkit for real estate? Our AI Productivity Playbook includes real-estate-specific prompts, workflows, and implementation guides. Available on Payhip.\nRelated Tools Estimate your mortgage payments → Mortgage Calculator Calculate your ideal freelance rate → Freelance Rate Calculator Related Templates Supercharge your real estate business with these AI tools:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Property listings, client emails, market analysis The AI Productivity Playbook 2026 — 50+ AI workflows for business professionals This article contains affiliate links. We may earn a commission at no extra cost to you.\nYou May Also Like ChatGPT Prompts for Real Estate Agents: 30 Templates (2026) How to Write Property Listing Descriptions with ChatGPT (10 Templates) Real Estate Investment in Japan for Salaried Workers: How It Compares to NISA and iDeCo ","permalink":"https://productivity-works.com/posts/ai-tools-real-estate-agents-2026/","summary":"\u003cp\u003eReal estate has always been a relationship business. The agents who thrive are the ones who can be everywhere at once — following up with leads promptly, writing compelling listing descriptions, staying in front of past clients, analyzing neighborhood comps, and still having enough energy left to show houses. In 2025 and into 2026, AI tools are making that kind of scale genuinely possible for solo agents and small teams.\u003c/p\u003e","title":"Best AI Tools for Real Estate Agents 2026: Complete Guide"},{"content":"Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nBest Life Insurance 2026 — A No-Nonsense Guide to Protecting Your Family Life insurance is one of those things everyone knows they need but few actually understand. The industry thrives on complexity, but the core decision is simple: if someone depends on your income, you need life insurance. Period.\nThis guide strips away the jargon and helps you find the right policy at the best price.\nTypes of Life Insurance (Only 2 Matter) Type What It Does Cost Duration Best For Term Life Pays out if you die within the term (10-30 years) $20-50/mo for $500K 10, 20, or 30 years 95% of people Whole Life Pays out whenever you die + builds cash value $200-500/mo for $500K Lifetime High net worth estate planning The simple answer: Get term life insurance. It costs 5-10x less than whole life and covers you during the years when your family most needs protection (while kids are young, mortgage is outstanding, etc.).\nWhole life insurance is a financial product for wealthy individuals doing estate planning — not a general recommendation.\nBest Life Insurance Companies 2026 Company Best For Coverage Monthly Cost (30yr, $500K) No-Exam Option Haven Life Online simplicity $100K-3M ~$25-35 Yes (up to $1M) Bestow No-exam policies $50K-1.5M ~$20-30 Yes (all policies) Ladder Flexible coverage $100K-8M ~$25-40 Yes (up to $3M) State Farm Local agent support $25K-10M+ ~$30-45 Limited Northwestern Mutual Whole life / high net worth $50K-unlimited ~$200+ (whole life) No Rates shown for healthy 30-year-old, 30-year term. Your rate depends on age, health, and coverage amount.\nDetailed Reviews 1. Haven Life — Best Overall Online Experience Feature Details Coverage $100,000 - $3,000,000 Term Options 10, 15, 20, 25, 30 years No-Exam Up to $1,000,000 (ages 20-55) Backed By MassMutual (A++ rating) Application 100% online, decision in minutes Best for: Most people looking for straightforward term life insurance.\nHaven Life combines the financial strength of MassMutual (one of the oldest and highest-rated insurers) with a modern, entirely online experience. Apply in 20 minutes, get a decision in minutes to days, and manage your policy through their app.\n2. Bestow — Best No-Exam Option Feature Details Coverage $50,000 - $1,500,000 Term Options 10, 15, 20, 25, 30 years No-Exam All policies — no medical exam required Application 5-minute online application Best for: People who want coverage immediately without a medical exam.\nBestow uses data-driven underwriting to offer policies without requiring a medical exam — ever. Apply in 5 minutes, get approved in minutes. Coverage starts immediately. The trade-off: slightly higher rates than exam-based policies, and maximum coverage capped at $1.5M.\n3. Ladder — Most Flexible Coverage Feature Details Coverage $100,000 - $8,000,000 Term Options 10, 15, 20, 25, 30 years No-Exam Up to $3,000,000 Unique Feature Adjust coverage up or down anytime Best for: People whose coverage needs may change over time.\nLadder\u0026rsquo;s standout feature is \u0026ldquo;laddering\u0026rdquo; — you can increase or decrease your coverage amount at any time without reapplying. Had a baby? Increase coverage. Paid off your mortgage? Decrease it. This flexibility is unique in the industry.\n4. State Farm — Best for Local Agent Support Feature Details Coverage $25,000 - $10,000,000+ Term Options 10, 20, 30 years No-Exam Limited availability Network 19,000+ agents nationwide Best for: People who want a local agent to guide them through the process.\nIf you prefer face-to-face advice from a local agent, State Farm\u0026rsquo;s network of 19,000+ agents is unmatched. They also offer discounts when bundling with auto or home insurance.\n5. Northwestern Mutual — Best for Whole Life / High Net Worth Feature Details Coverage Unlimited Types Term, whole life, universal life Financial Strength A++ (AM Best), top-rated for 30+ consecutive years Approach Dedicated financial advisor Best for: High-net-worth individuals who need whole life insurance for estate planning.\nIf you\u0026rsquo;re in the small percentage of people who actually benefit from whole life insurance (estate tax planning, high net worth), Northwestern Mutual is the gold standard. Their financial advisors provide comprehensive planning beyond just insurance.\nHow Much Coverage Do You Need? Simple formula: 10-12x your annual income.\nAnnual Income Recommended Coverage $50,000 $500,000 - $600,000 $75,000 $750,000 - $900,000 $100,000 $1,000,000 - $1,200,000 $150,000 $1,500,000 - $1,800,000 Adjust for:\nOutstanding mortgage balance (+) Number of children and years until independence (+) Spouse\u0026rsquo;s income (-) Existing savings and investments (-) College fund needs (+) Quick Decision Guide Your Situation Best Choice Want the easiest process Haven Life (online, MassMutual-backed) No medical exam Bestow (all policies no-exam) Coverage needs may change Ladder (adjustable coverage) Want a local agent State Farm High net worth / estate planning Northwestern Mutual Frequently Asked Questions Q: How much does life insurance cost? A healthy 30-year-old can get $500,000 of 30-year term coverage for $25-40/month. Costs increase with age, health conditions, and coverage amount. The younger you buy, the cheaper it is.\nQ: Do I need life insurance if I\u0026rsquo;m single with no dependents? Generally no. Life insurance is for protecting people who depend on your income. If no one depends on you financially, skip it (or get a small policy to cover funeral costs and debts).\nQ: Is employer-provided life insurance enough? Usually not. Most employers provide 1-2x your salary, but you need 10-12x. Plus, employer coverage ends when you leave the job. Get your own policy as a supplement.\nQ: When should I buy life insurance? As soon as someone depends on your income — typically when you get married, buy a home, or have children. The younger and healthier you are, the lower your rates will be locked in.\nQ: Can I have multiple life insurance policies? Yes. Many people have employer coverage plus a personal policy. Some \u0026ldquo;ladder\u0026rdquo; multiple term policies (e.g., a 30-year and a 20-year) to match their decreasing needs over time.\nProtect Your Family, Then Grow Your Wealth Life insurance covers the downside. Investing builds the upside. Open a Rakuten Securities account to start building the investment portfolio that makes your family financially secure — alongside the life insurance that protects them.\nConclusion Life insurance doesn\u0026rsquo;t have to be complicated:\nGet term life insurance (not whole life) — it covers 95% of situations at 1/10th the cost Get 10-12x your income in coverage Choose a 20-30 year term to cover your working years Apply online with Haven Life or Bestow for the fastest, simplest process Buy it now — rates only go up as you age The best time to buy life insurance was yesterday. The second-best time is today.\nRelated: How to Build an Emergency Fund Fast Related: Best Budgeting Apps 2026 This article is for informational purposes only and does not constitute insurance advice. Coverage needs vary by individual. Consult a licensed insurance professional for personalized recommendations.\nRelated Tools Calculate your exact age instantly → Age Calculator Check your BMI and healthy weight range → BMI Calculator Calculate your take-home pay → Salary Calculator How much emergency fund do you need? → Emergency Fund Calculator Create a monthly budget → Budget Planner Calculate your mortgage payment → Mortgage Calculator Related Templates Protect your family\u0026rsquo;s finances with better planning:\nChatGPT Prompt Templates — Financial planning and insurance comparison prompts The AI Productivity Playbook 2026 — Automate your financial research This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/best-life-insurance-2026/","summary":"\u003cp\u003e\u003cem\u003eDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"best-life-insurance-2026--a-no-nonsense-guide-to-protecting-your-family\"\u003eBest Life Insurance 2026 — A No-Nonsense Guide to Protecting Your Family\u003c/h1\u003e\n\u003cp\u003eLife insurance is one of those things everyone knows they need but few actually understand. The industry thrives on complexity, but the core decision is simple: \u003cstrong\u003eif someone depends on your income, you need life insurance.\u003c/strong\u003e Period.\u003c/p\u003e\n\u003cp\u003eThis guide strips away the jargon and helps you find the right policy at the best price.\u003c/p\u003e","title":"Best Life Insurance 2026: Compare Policies, Rates \u0026 Coverage"},{"content":"Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nBest Web Hosting 2026 — The Only Guide You Need Choosing web hosting shouldn\u0026rsquo;t be complicated, but the industry makes it so — introductory pricing that triples on renewal, confusing feature lists, and aggressive upselling. This guide cuts through the noise.\nWhether you\u0026rsquo;re starting a blog, launching a business site, or building an online store, here are the hosts that actually deliver on their promises.\nTypes of Web Hosting Type Best For Price Range Technical Skill Shared Hosting Beginners, small blogs $2-10/mo None Managed WordPress WordPress sites wanting speed + support $15-50/mo None VPS Growing sites needing more resources $20-80/mo Moderate Cloud Hosting Traffic spikes, scalable applications $10-100+/mo Moderate-High Dedicated Server Large, high-traffic sites $80-300+/mo High For most people: Shared hosting or managed WordPress hosting is all you need.\nBest Web Hosting Providers 2026 Host Starting Price Best For Free Domain WordPress Cloudways $14/mo Performance No Managed SiteGround $2.99/mo WordPress + support Yes (1 year) Managed Hostinger $2.99/mo Budget Yes (1 year) 1-click install Bluehost $2.95/mo WordPress beginners Yes (1 year) Official WP recommended A2 Hosting $2.99/mo Speed + developer tools No Turbo servers Prices shown are introductory rates. Renewal rates are typically 2-3x higher.\nDetailed Reviews 1. Cloudways — Best Performance Feature Details Price $14/mo (DigitalOcean), $16/mo (Vultr) Server Stack Nginx, Memcached, Varnish, Redis Managed Yes — updates, security, backups handled Scalability Upgrade server resources with a click Support 24/7 live chat and ticket support Best for: Anyone who wants top-tier performance without managing a server.\nCloudways sits between shared hosting and VPS — you get dedicated cloud server resources (no noisy neighbors) with a managed interface that handles the technical complexity. Choose from DigitalOcean, Vultr, AWS, or Google Cloud as your infrastructure provider.\nTrade-off: No cPanel, no email hosting (use a third-party service), and the $14/month minimum is higher than shared hosting.\n2. SiteGround — Best WordPress Support Feature Details Price $2.99/mo (StartUp), $4.99/mo (GrowBig), $7.99/mo (GoGeek) Server Stack Google Cloud, custom caching WordPress Managed updates, staging, migration tools Support 24/7 phone, chat, ticket — consistently highest-rated Free SSL, CDN, daily backups, email Best for: WordPress users who want excellent support and don\u0026rsquo;t mind paying more at renewal.\nSiteGround has the best customer support in the hosting industry — that\u0026rsquo;s not marketing, it\u0026rsquo;s consistently reflected in every independent review. Their WordPress-specific tools (auto-updates, staging environments, migration plugin) make managing WordPress painless.\nWatch out for: Renewal prices are $14.99-39.99/month — significantly higher than intro rates.\n3. Hostinger — Best Budget Option Feature Details Price $2.99/mo (Premium), $3.99/mo (Business) Storage 100-200GB SSD WordPress 1-click install, AI website builder Free Domain (1 year), SSL, email, weekly backups Support 24/7 live chat Best for: Budget-conscious beginners starting their first website.\nHostinger offers the most features per dollar in the hosting market. Their hPanel interface is clean and beginner-friendly, and the AI-powered website builder can generate a basic site in minutes. Reliable enough for small-to-medium sites.\n4. Bluehost — WordPress.org Official Recommendation Feature Details Price $2.95/mo (Basic), $5.45/mo (Choice Plus) Storage 10-100GB SSD WordPress Pre-installed, official WordPress.org recommended Free Domain (1 year), SSL, CDN Support 24/7 phone and chat Best for: Complete WordPress beginners who want the easiest possible setup.\nBluehost is one of only three hosts officially recommended by WordPress.org. WordPress comes pre-installed, and their onboarding wizard walks you through every step. The Choice Plus plan includes domain privacy and automated backups.\n5. A2 Hosting — Fastest Shared Hosting Feature Details Price $2.99/mo (Startup), $5.99/mo (Turbo Boost) Server Stack LiteSpeed (Turbo), NVMe storage Speed Up to 20x faster (Turbo servers) Free SSL, site migration, SSD storage Developer SSH, staging, Git integration Best for: Developers and speed-focused users who want shared hosting performance.\nA2\u0026rsquo;s Turbo servers (LiteSpeed + NVMe storage) deliver the fastest page load times in the shared hosting category. Developer-friendly features include SSH access, staging environments, and Git integration.\nQuick Decision Guide Your Situation Best Host First website, tight budget Hostinger ($2.99/mo) WordPress beginner Bluehost ($2.95/mo) WordPress + best support SiteGround ($2.99/mo) Want best performance Cloudways ($14/mo) Developer / speed-focused A2 Hosting Turbo ($5.99/mo) Frequently Asked Questions Q: What\u0026rsquo;s the real cost of web hosting? Introductory prices ($3-5/month) typically double or triple on renewal. Budget for $8-15/month after the first year. Add $10-15/year for a domain name.\nQ: Do I need managed WordPress hosting? Not necessarily. Shared hosting with 1-click WordPress install works fine for most sites. Managed WordPress (Cloudways, SiteGround) adds automatic updates, better caching, and expert support — worth it as your site grows.\nQ: Can I switch hosting providers later? Yes. Most hosts offer free migration tools or services. It\u0026rsquo;s easier than you might think, but it\u0026rsquo;s always better to choose well from the start.\nQ: How much traffic can shared hosting handle? Typically 10,000-50,000 monthly visitors before you need to upgrade. For most new sites, this is more than enough for the first 1-2 years.\nQ: Should I get the cheapest plan? Get the mid-tier plan. The cheapest plans often have tight resource limits that cause slowdowns. The $3-6/month range offers the best value.\nConclusion For most people starting a website in 2026:\nBudget pick: Hostinger ($2.99/month) — most features per dollar WordPress pick: Bluehost ($2.95/month) — easiest WordPress setup Performance pick: Cloudways ($14/month) — best speed and reliability Support pick: SiteGround ($2.99/month) — best customer service Start with shared hosting, upgrade when your traffic demands it. Your hosting choice matters far less than your content — pick one, start publishing, and adjust later.\nBefore you can launch, you need a domain. Onamae.com is Japan\u0026rsquo;s largest domain registrar — competitively priced, easy to set up, and a natural pairing with any of the hosting providers above.\nRelated: Best AI Tools for Small Business 2026 Prices and features are subject to change. Check each host\u0026rsquo;s website for current offers.\nRelated Tools Pick colors and convert between formats → Color Picker Create a monthly budget → Budget Planner Calculate your ideal freelance rate → Freelance Rate Calculator Generate secure passwords instantly → Password Generator Generate SHA-256 and MD5 hashes → Hash Generator Encode and decode Base64 strings → Base64 Encoder Design with gradients → CSS Gradient Generator — create beautiful CSS gradients visually\nGenerate placeholder text for your site → Lorem Ipsum Generator — instant filler content\nNeed a markdown editor? → Markdown Preview — write and preview markdown live\nRelated Templates Build your online presence with AI:\nChatGPT Prompt Templates — Content creation and SEO optimization prompts The AI Productivity Playbook 2026 — Automate your content workflow ","permalink":"https://productivity-works.com/posts/best-web-hosting-2026/","summary":"\u003cp\u003e\u003cem\u003eDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"best-web-hosting-2026--the-only-guide-you-need\"\u003eBest Web Hosting 2026 — The Only Guide You Need\u003c/h1\u003e\n\u003cp\u003eChoosing web hosting shouldn\u0026rsquo;t be complicated, but the industry makes it so — introductory pricing that triples on renewal, confusing feature lists, and aggressive upselling. This guide cuts through the noise.\u003c/p\u003e\n\u003cp\u003eWhether you\u0026rsquo;re starting a blog, launching a business site, or building an online store, here are the hosts that actually deliver on their promises.\u003c/p\u003e","title":"Best Web Hosting 2026: Compared for Speed, Price \u0026 WordPress"},{"content":"Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nBest Accounting Software for Freelancers 2026 — Track Money, Not Spreadsheets If you\u0026rsquo;re freelancing without accounting software, you\u0026rsquo;re either spending hours on spreadsheets or leaving tax deductions on the table — probably both. The right tool automates expense tracking, invoicing, and tax preparation for less than the cost of a single business lunch per month.\nHere\u0026rsquo;s what actually works for freelancers in 2026.\nWhat Freelancers Need from Accounting Software Feature Why It Matters Expense tracking Automatic categorization of business expenses for tax deductions Invoicing Professional invoices with online payment options Bank sync Automatic import of transactions from bank/credit cards Tax preparation Estimated quarterly taxes + annual tax filing support Receipt capture Snap photos of receipts for expense documentation Mileage tracking Automatic tracking for vehicle deductions Best Accounting Software Compared Software Price Invoicing Expense Tracking Tax Support Best For FreshBooks $19/mo Unlimited Automatic Quarterly estimates Service-based freelancers QuickBooks Self-Employed $15/mo Basic Automatic TurboTax integration Tax-focused freelancers Wave Free Unlimited Manual + import Basic Budget freelancers Xero $15/mo Unlimited Automatic Accountant access Growing businesses Bonsai $21/mo Unlimited Automatic Tax estimates Creative freelancers Detailed Reviews 1. FreshBooks — Best for Service-Based Freelancers Feature Details Price Lite $19/mo (5 clients), Plus $33/mo (50 clients), Premium $60/mo (unlimited) Invoicing Customizable, recurring, auto-reminders, online payments Expense Tracking Bank sync, receipt capture, automatic categorization Tax Quarterly tax estimates, profit/loss reports Time Tracking Built-in timer, billable hours to invoices Best for: Consultants, designers, writers, and other service providers who bill by the hour or project.\nFreshBooks was built specifically for service freelancers. The time tracking → invoice workflow is seamless: track hours, convert them to an invoice with one click, and get paid online. Client management features (proposals, contracts, payments) make it an all-in-one business tool.\n2. QuickBooks Self-Employed — Best for Tax Season Feature Details Price $15/mo (Self-Employed), $25/mo (with TurboTax) Invoicing Basic invoicing with online payments Expense Tracking Automatic bank sync, swipe-to-categorize Tax Quarterly estimates, direct TurboTax export Mileage Automatic GPS mileage tracking Best for: Freelancers who want the easiest possible tax filing.\nThe killer feature is TurboTax integration — your entire year\u0026rsquo;s financial data exports directly into TurboTax at tax time, making filing nearly automatic. The mobile app\u0026rsquo;s swipe-to-categorize interface makes daily expense tracking painless.\n3. Wave — Best Free Option Feature Details Price Free (accounting, invoicing) — payments processing 2.9% + 60¢ Invoicing Unlimited, customizable, online payments Expense Tracking Bank import, manual entry, receipt scanning Tax Basic reports (P\u0026amp;L, balance sheet) Limitation No time tracking, limited automation Best for: New freelancers or those with simple financial needs and tight budgets.\nWave is genuinely free for core accounting and invoicing — not a trial, not freemium, but actually free. They make money on payment processing and payroll. The trade-off: less automation, no time tracking, and fewer integrations than paid options.\n4. Xero — Best for Growing into a Business Feature Details Price Starter $15/mo, Standard $42/mo, Premium $78/mo Invoicing Unlimited, multi-currency, recurring Expense Tracking Automatic bank feeds, receipt capture Tax Accountant access, comprehensive reporting Scalability Inventory, payroll, multi-currency, project tracking Best for: Freelancers who are growing into a small business with employees or complex finances.\nXero scales from solo freelancer to small business without switching platforms. Multi-currency support is excellent for international freelancers. The accountant access feature lets your CPA log in directly.\n5. Bonsai — Best All-in-One for Creatives Feature Details Price Starter $21/mo, Professional $39/mo, Business $66/mo Invoicing Branded invoices, automatic reminders Expense Tracking Bank sync, tax categorization Extras Contracts, proposals, time tracking, tax filing Tax Estimated quarterly taxes, Schedule C preparation Best for: Creative freelancers who want contracts, proposals, invoicing, and accounting in one tool.\nBonsai combines accounting with client management — contracts (legally vetted templates), proposals, time tracking, and invoicing all in one platform. The tax preparation feature estimates quarterly payments and helps file annually.\nQuick Decision Guide Your Situation Best Choice Service freelancer (consulting, writing, design) FreshBooks Want easiest tax filing QuickBooks Self-Employed Tight budget / just starting Wave (free) Growing into a business Xero Creative freelancer wanting all-in-one Bonsai Frequently Asked Questions Q: Can I use a spreadsheet instead of accounting software? You can, but you\u0026rsquo;ll spend 5-10x more time on bookkeeping and likely miss tax deductions. The average freelancer saves $1,000+ per year in found deductions by using proper accounting software.\nQ: When should I hire an accountant? When your annual revenue exceeds $75,000-100,000, or when you have complex tax situations (S-corp, multiple income streams, international income). Below that, accounting software is sufficient.\nQ: What tax deductions can freelancers claim? Home office, internet, phone, equipment, software subscriptions, professional development, travel, meals (50%), health insurance, retirement contributions, and more. Accounting software helps you track all of these automatically.\nQ: How often should I update my books? Weekly at minimum. With bank sync, it takes 5-10 minutes per week to review and categorize transactions. Don\u0026rsquo;t wait until tax season — that\u0026rsquo;s how deductions get missed.\nConclusion The best accounting software depends on your freelancing style:\nService freelancers: FreshBooks ($19/month) for time tracking + invoicing Tax-focused: QuickBooks Self-Employed ($15/month) for TurboTax integration Budget: Wave (free) for core accounting and invoicing Growing business: Xero ($15/month) for scalability Whatever you choose, the return on investment is immediate — less time on bookkeeping, more deductions found, and zero stress at tax time.\nFreelancing or running a small business in Japan? freee is purpose-built for self-employed individuals and sole proprietors — it handles invoicing, expense tracking, and kakuteishinkoku (annual tax filing) in one guided workflow, even if Japanese isn\u0026rsquo;t your first language.\nRelated: AI Tools for Freelancers to Earn More Related: Freelance Tax Guide 2026 This article is for informational purposes only and does not constitute tax or financial advice. Consult a tax professional for advice specific to your situation.\nRelated Tools Calculate your ideal freelance hourly rate → Freelance Rate Calculator Create and download professional invoices for free → Invoice Generator Calculate your freelance tax obligations → Side Hustle Tax Calculator Estimate your take-home pay → Salary Calculator Related Templates Streamline your freelance finances:\nChatGPT Prompt Templates — Invoice and expense tracking prompts The AI Productivity Playbook 2026 — Automate your freelance business workflows ","permalink":"https://productivity-works.com/posts/best-accounting-software-freelancers-2026/","summary":"\u003cp\u003e\u003cem\u003eDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"best-accounting-software-for-freelancers-2026--track-money-not-spreadsheets\"\u003eBest Accounting Software for Freelancers 2026 — Track Money, Not Spreadsheets\u003c/h1\u003e\n\u003cp\u003eIf you\u0026rsquo;re freelancing without accounting software, you\u0026rsquo;re either spending hours on spreadsheets or leaving tax deductions on the table — probably both. The right tool automates expense tracking, invoicing, and tax preparation for less than the cost of a single business lunch per month.\u003c/p\u003e","title":"Best Accounting Software for Freelancers 2026: Full Compare"},{"content":"Teaching has always demanded more than anyone can give. You are simultaneously a curriculum designer, a feedback engine, a student motivator, an administrative record-keeper, a communications director for thirty families, and a lifelong learner who is supposed to stay current with your subject and pedagogy — all within a contract that is somehow supposed to fit into forty hours per week but regularly overflows into evenings and weekends.\nAI tools do not replace any of the irreplaceable parts of teaching. They cannot build the relationship that makes a struggling student feel safe enough to ask for help. They cannot read the room when a planned lesson is not landing and pivot in real time. But they can handle an enormous amount of the preparatory, mechanical, and administrative work that currently consumes hours that could be spent on those irreplaceable parts.\nThis guide covers the AI tools that are genuinely useful in classrooms and education settings in 2026 — specifically for teachers, not administrators or ed-tech buyers. We focus on tools that work in real classroom conditions, produce educationally sound outputs, and respect the professional judgment of educators.\nThe Honest State of AI in Education (2026) Before the tool list, a few important realities:\nWhat AI is genuinely good at for teachers:\nDrafting lesson plans, rubrics, and assessments (that you then refine) Generating differentiated versions of content at different reading levels Creating practice problems, discussion questions, and exit tickets in large quantities Summarizing documents, research papers, and professional development materials Drafting parent communication emails Providing a first pass at written feedback on student work Translation and multilingual support for ELL students and their families What AI is not good at (yet):\nUnderstanding the specific dynamics of your classroom and students Replacing pedagogical judgment about when and how to teach something Detecting student confusion, engagement, or emotional state Providing the relational warmth that motivates students Generating assessments that are automatically free of cultural bias or age-inappropriate content (always review) The ethical dimension: AI in education requires ongoing thoughtfulness about student data privacy (never input student-identifiable information into public AI tools), academic integrity (distinguish between AI supporting your work vs. enabling student shortcuts), and equity (not all students have equal AI access at home, which affects AI-dependent assignments).\nThese concerns do not mean avoiding AI — they mean using it thoughtfully.\nCategory 1: Lesson Planning AI MagicSchool AI MagicSchool has emerged as the most educator-specific AI platform in 2026. Built by teachers for teachers, it includes 60+ tools designed around actual classroom workflows:\nLesson Plan Generator — Input grade level, subject, standard/objective, and available time; receive a complete lesson plan with introduction, main activity, assessment, and differentiation suggestions Differentiation Helper — Input any lesson plan; receive versions adapted for advanced learners, struggling learners, and ELL students Text Leveler — Paste any text; receive versions at multiple Lexile/grade levels for the same content Discussion Question Generator — Higher-order thinking questions (Bloom\u0026rsquo;s Taxonomy levels) for any topic Exit Ticket Generator — Quick formative assessment questions aligned to lesson objectives IEP Goal Generator — Draft IEP goal language (always reviewed and customized by the case manager) Rubric Generator — Standards-aligned rubrics for any assignment type MagicSchool is also FERPA-compliant and specifically designed to protect student privacy.\nPricing: Free tier (generous); Pro at $99/year for individuals; school/district licenses available\nChatGPT for Lesson Planning For teachers who prefer a more flexible tool, ChatGPT (particularly with the GPT-4o model) produces excellent lesson plans when given detailed prompts.\nHigh-performing lesson plan prompt:\nYou are an experienced [grade level] [subject] teacher. Create a complete lesson plan for the following: Grade: [grade level] Subject: [subject] Topic/Objective: [specific topic] Learning objective (what students will be able to do): [SWBAT statement] Duration: [45/60/90 minutes] Class profile: [e.g., 28 students, mixed ability, 4 ELL students, 2 students with IEPs for reading] Available materials: [list what you have] Standards alignment: [if relevant — state standard code] Include: 1. Hook/anticipatory set (5 min) 2. Direct instruction with key questions to check understanding 3. Guided practice activity 4. Independent or group practice 5. Closure/exit assessment 6. Differentiation suggestions for above/below grade level 7. Potential misconceptions to watch for This level of detail produces lesson plans that are genuinely usable — not generic outlines.\nKhanmigo (Khan Academy\u0026rsquo;s AI Tutor) Khanmigo is designed primarily as a student tutor, but it has teacher-facing features that are genuinely useful:\nLesson plan creation aligned to Khan Academy\u0026rsquo;s curriculum Class discussion facilitation prompts and Socratic questioning guides Writing feedback that coaches students rather than rewrites for them Particularly strong for math and science teachers who already use Khan Academy as a classroom resource.\nPricing: Free for teachers in many regions through Khan Academy for Educators\nCategory 2: Assessment and Grading AI This is the category where teachers report the largest time savings — and where the greatest care is required.\nFormative Assessment Tools Eduaide.Ai\nEduaide generates assessments in multiple formats from a single topic input:\nMultiple choice (with distractors based on common misconceptions) Short answer True/False with explanation requirement Matching Fill-in-the-blank Performance tasks For a unit test, a teacher can generate a 40-question question bank in 5 minutes and select the best 20 questions, rather than spending 40 minutes writing questions from scratch.\nQuizizz with AI Question Generation\nQuizizz now includes AI question generation from text, URLs, or PDFs. Paste a chapter from your textbook and receive a quiz aligned to its content. The AI also adapts question difficulty based on student performance data.\nSummative Assessment Support Turnitin\u0026rsquo;s AI Writing Detection + Feedback\nTurnitin remains a staple in middle school, high school, and higher education. Their 2026 version includes:\nAI-written content detection (though this remains imperfect and should not be used as sole evidence of academic dishonesty) QuickMarks — reusable comment banks that attach to specific student errors PeerMark — structured peer review facilitation AI-generated feedback suggestions that the teacher approves/modifies before students see them Important caveat: Turnitin\u0026rsquo;s AI detection generates false positives. Never cite an AI detection score alone as evidence of dishonesty. Use it as a flag for conversation, not a verdict.\nWritten Feedback Assistance The most time-consuming part of grading essays and written work is providing meaningful, specific feedback. AI can draft initial feedback that the teacher then reviews and personalizes.\nThe feedback workflow:\nStudent submits written work Teacher reads the work (cannot be skipped — you need to understand it to evaluate the AI\u0026rsquo;s feedback) Teacher pastes the work into ChatGPT or MagicSchool with a feedback prompt: I am a [grade level] [subject] teacher. A student submitted this [essay/lab report/short answer]. The assignment asked them to [assignment description]. The rubric priorities are: [list 3-5 criteria]. Please provide constructive feedback following these rules: - Start with one specific strength in the work - Identify the single most important area for improvement - Provide a specific suggestion for revision, not just a general comment - Use language appropriate for a [grade level] student - Do NOT rewrite any part of the student\u0026#39;s work for them - Limit feedback to 100-150 words [Paste student work here] Teacher reviews the AI feedback, adjusts for accuracy and appropriateness, adds their personal observations, and delivers it This workflow reduces the time per student from 8-12 minutes to 3-5 minutes for written feedback — meaningful savings across a class of 30.\nCategory 3: Differentiated Instruction AI Differentiation is pedagogically critical and logistically brutal. Creating three versions of every activity, worksheet, and text — for above-grade, on-grade, and below-grade learners — is often aspirational rather than actual practice, simply because of time.\nAI changes this calculation.\nText Differentiation Newsela and ReadWorks have long offered multi-level reading materials, and their AI-powered 2026 versions allow teachers to paste any text and receive instant versions at multiple Lexile levels. Combine this with ChatGPT\u0026rsquo;s ability to adjust reading level on demand.\nPrompt for text level adjustment:\nRewrite the following text at a [Grade 5 / Grade 8 / Grade 11] reading level. Preserve all key information and vocabulary essential to the topic. Define technical terms inline (in parentheses) when appropriate for the lower reading level. Do not simplify concepts — only adjust sentence structure and vocabulary complexity. [Paste original text] This produces a genuinely lower or higher readability version while maintaining conceptual integrity — critical for differentiation that does not water down learning for struggling students.\nScaffolded Activity Generation For students who need additional support, AI generates scaffolds quickly:\nGraphic organizers pre-populated with sentence starters Vocabulary list with definitions for a specific text or unit Step-by-step procedure guides for complex tasks Anchor charts for key concepts Chunked versions of complex tasks with checkpoints Prompt template:\nCreate a graphic organizer scaffold for [grade level] students who struggle with [skill: e.g., comparing and contrasting, supporting claims with evidence, solving multi-step problems]. The activity they are working on is [describe task]. Include sentence starters and visual structure. Keep the text simple but not condescending. Extension Activities for Advanced Learners Equally important and equally under-resourced: teachers rarely have time to generate meaningful enrichment for students who complete work early or need additional challenge.\nI have advanced learners who have mastered [standard/objective]. They need an extension activity that goes deeper — not just more problems, but genuinely higher-order thinking. Topic: [topic]. Grade: [grade level]. Available time: [15-30 minutes]. Format preference: [discussion, written, research, creative, problem-solving]. Generate an extension task that requires synthesis, evaluation, or creation. Include 2-3 guiding questions. Category 4: Parent Communication AI Teacher-parent communication is important, time-consuming, and often anxiety-producing. Drafting communications that are professional, warm, informative, and culturally sensitive requires real care. AI handles the drafting; teachers add the relational specificity.\nRoutine Communication Templates Class newsletter / weekly update:\nWrite a weekly classroom newsletter for a [grade level] [subject] class. This week we covered [topics]. Upcoming: [homework, tests, field trips, project deadlines]. Tone: warm and professional. Include one specific positive observation about the class. Length: 150-200 words. Avoid jargon. Translation for multilingual families:\nChatGPT and DeepL both handle translation well for parent communications. Always flag to families that automated translation is provided and invite them to reach out if anything is unclear.\nChallenging Communications Concern email about student behavior or performance:\nHelp me draft an email to the parent of a student who is [specific concern: e.g., consistently missing homework, disrupting class, showing signs of academic struggle]. Key points to convey: 1. I care about their child\u0026#39;s success 2. Specific observable behavior (not diagnosis or judgment): [describe specifically] 3. Impact on learning: [describe] 4. My proposed next step: [describe] 5. Request for conversation / collaboration Tone: direct but warm, non-accusatory, partnership-oriented. This is not a disciplinary email — it is a concern and collaboration email. The AI draft gives you a professional starting point. Always add specific, accurate details about the student that only you know.\nCategory 5: Administrative and Professional Efficiency AI Otter.ai for Meeting Notes Professional development sessions, IEP meetings, department meetings, and parent conferences generate information you need to remember and act on. Otter.ai transcribes and summarizes these in real time — no more frantic note-taking.\nFor IEP meetings in particular, having a transcription (with parent consent) ensures that commitments made are accurately documented.\nCanva AI for Classroom Materials Canva\u0026rsquo;s AI features allow teachers to create:\nProfessional-looking bulletin board materials Student-facing anchor charts and reference guides Certificates and recognition materials Parent information handouts Presentation slides from an outline For teachers who are not designers, Canva AI collapses the gap between \u0026ldquo;I know what I want it to look like\u0026rdquo; and actually having it.\nChatGPT for Professional Development Prep Before a workshop, conference session, or professional learning community (PLC) meeting:\nI am presenting a 45-minute PD session to [K-5 / middle school / high school] teachers on the topic of [topic]. The audience is [describe: mix of experience levels, subject areas, etc.]. The desired outcome is that participants leave with [specific takeaway or skill]. Help me create: 1. A session agenda with timing 2. An opening engagement activity (5 minutes) 3. Three discussion questions for collaborative reflection 4. A key resource list (3-5 items) 5. A closing reflection prompt for participants Curipod (AI Lesson Slides) Curipod generates interactive lesson slides from a topic input — including polls, word clouds, drawing activities, and reflection prompts built in. For teachers moving away from passive lecture slides, this is a strong tool that takes a topic and produces an interactive 20-minute lesson structure in about 3 minutes.\nAI Tools by Subject Area English Language Arts Diffit — Text adaptation at multiple reading levels ChatGPT — Writing prompts, mentor text examples, feedback scaffolds Writable — AI-assisted writing feedback aligned to standards Mathematics Khanmigo — Socratic tutoring for math concepts Desmos AI — Interactive graphing with guided exploration prompts Photomath — Step-by-step problem solving (know your students are using this; address it pedagogically) Science ChatGPT — Lab procedure generation, hypothesis scaffolds, science reading at appropriate levels Phet Simulations + AI Questions — Virtual labs with AI-generated analysis questions Social Studies / History ChatGPT — Primary source analysis scaffolds, multiple perspective exercises, Socratic seminar preparation Perplexity AI — Current events research with source citations World Languages Duolingo for Schools — AI-adaptive practice with teacher dashboard ChatGPT — Conversation practice scripts, cultural context explanations, grammar correction with explanations Addressing Student AI Use This guide would be incomplete without acknowledging that students use AI too — and that how teachers respond to this reality shapes academic culture significantly.\nProductive approaches for 2026:\nTeach students to use AI as a first-draft and brainstorming tool, not a submission tool Build in in-class writing components that cannot be AI-completed from home Focus assessments on process, reflection, and verbal explanation alongside product Design assignments that require personal experience, local context, or specific class discussions — things AI cannot replicate Have explicit class conversations about when AI use is appropriate, when it is not, and why Our ChatGPT Prompt Templates pack on Payhip includes an educator-specific section with 30+ classroom-ready AI prompts for teachers, including lesson planning, assessment design, differentiation, and parent communication templates — pre-tested and ready to use.\nTeaching Is a Career Worth Building — doda Can Help Whether you\u0026rsquo;re looking to move into educational technology, curriculum design, or a new school environment, find your next career on doda — Japan\u0026rsquo;s leading job platform with opportunities across education and beyond.\nBuilding Your Personal AI Workflow as a Teacher Start small. Pick one category from this guide — the one that currently takes the most of your time — and spend one week integrating an AI tool into that specific workflow.\nRecommended starting points by pain point:\nSpend hours on lesson planning → Start with MagicSchool Dreading essay grading → Try the ChatGPT feedback workflow Struggling to differentiate → Use text leveling prompts Drowning in parent emails → Build a prompt template library for communications After one week, evaluate: Did the AI help? Is the output quality acceptable after your review? Is it saving time? Adjust and expand.\nThe goal is not to use every AI tool. The goal is to reclaim enough time that you can be more fully present in the interactions that require you specifically — the conversations, the encouragement, the insight that sees a struggling student not just as a data point but as a person.\nThat is what great teaching has always been. AI can give you more time to do it.\nWant ready-to-use AI prompt templates for your classroom? Our ChatGPT Prompt Templates pack includes 30+ educator-specific prompts for lesson planning, assessments, differentiation, and parent communications. Available on Payhip.\nRelated Tools Create a monthly budget → Budget Planner Set a savings goal → Savings Goal Calculator Related Templates Put these techniques into practice with our ready-made templates:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Copy-paste prompts for every situation The AI Productivity Playbook 2026 — 50+ AI workflows and automation strategies This article contains affiliate links. We may earn a commission at no extra cost to you.\nYou May Also Like How to Use ChatGPT for Studying: The Complete Student Guide 2026 Best Language Learning Apps 2026: Actually Become Conversational Best AI Tools for Small Business 2026: The Complete Roundup ","permalink":"https://productivity-works.com/posts/ai-tools-teachers-educators-2026/","summary":"\u003cp\u003eTeaching has always demanded more than anyone can give. You are simultaneously a curriculum designer, a feedback engine, a student motivator, an administrative record-keeper, a communications director for thirty families, and a lifelong learner who is supposed to stay current with your subject and pedagogy — all within a contract that is somehow supposed to fit into forty hours per week but regularly overflows into evenings and weekends.\u003c/p\u003e\n\u003cp\u003eAI tools do not replace any of the irreplaceable parts of teaching. They cannot build the relationship that makes a struggling student feel safe enough to ask for help. They cannot read the room when a planned lesson is not landing and pivot in real time. But they can handle an enormous amount of the preparatory, mechanical, and administrative work that currently consumes hours that could be spent on those irreplaceable parts.\u003c/p\u003e","title":"Best AI Tools for Teachers \u0026 Educators 2026: Complete Guide"},{"content":"Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nBest Language Learning Apps 2026 — What Actually Gets You to Conversational Language learning apps generated $15 billion in 2025. Most of that money was spent by people who never became conversational in their target language. The problem isn\u0026rsquo;t motivation — it\u0026rsquo;s using the wrong tool for the wrong stage of learning.\nThis guide matches you with the right tool based on where you are in your language journey and where you want to go.\nThe Language Learning Stack No single app does everything well. Here\u0026rsquo;s the combination that works:\nStage What You Need Best Tool Beginner (0-3 months) Vocabulary + basic grammar Duolingo or Babbel Elementary (3-6 months) Structured lessons + listening Pimsleur or Busuu Intermediate (6-12 months) Speaking practice with humans italki or Preply Advanced (12+ months) Immersion + conversation AI tutors + native content Best Language Learning Apps Compared App Price Best For Languages Approach Duolingo Free / $7.99/mo Beginners, gamified learning 40+ Gamification + AI Babbel $7.45-14.95/mo Structured grammar lessons 14 Conversation-focused Pimsleur $14.95-20.95/mo Listening + pronunciation 50+ Audio-based italki $5-30/lesson Speaking with real tutors 150+ 1-on-1 tutoring Busuu Free / $6.66/mo Community feedback 14 AI + native feedback Detailed Reviews 1. Duolingo — Best for Getting Started Feature Details Price Free (ads) / Super: $7.99/mo / Max: $13.99/mo Languages 40+ Daily Time 5-20 minutes Best Feature Gamification keeps you consistent AI Features Duolingo Max includes AI conversation practice Best for: Absolute beginners who need to build a daily habit.\nDuolingo\u0026rsquo;s gamification (streaks, XP, leaderboards) solves the hardest problem in language learning: consistency. The free tier is genuinely useful. Duolingo Max ($13.99/month) adds AI-powered conversation practice and mistake explanations — a significant upgrade.\nLimitation: Won\u0026rsquo;t get you to conversational fluency alone. Use it as a foundation, then add speaking practice.\n2. Babbel — Best Structured Curriculum Feature Details Price $7.45/mo (annual) or $14.95/mo (monthly) Languages 14 Daily Time 15-20 minutes Best Feature Grammar explanations + real-world conversations Guarantee 20-day money-back guarantee Best for: Learners who want structured, classroom-style lessons focused on practical conversation.\nWhere Duolingo gamifies, Babbel educates. Lessons are built around real-world conversations you\u0026rsquo;ll actually have — ordering food, asking directions, workplace small talk. Grammar is explained clearly (not just pattern-matched like Duolingo). Made by a team of linguists.\n3. Pimsleur — Best for Listening \u0026amp; Speaking Feature Details Price $14.95/mo (1 language) or $20.95/mo (all languages) Languages 50+ Daily Time 30 minutes Best Feature Audio-only, science-backed spaced repetition Method Listen → Repeat → Recall Best for: Commuters, people who learn by listening, pronunciation perfectionists.\nPimsleur is entirely audio-based — perfect for learning during commutes, walks, or workouts. The method focuses on listening comprehension and pronunciation through spaced repetition. After 30 days, you\u0026rsquo;ll surprise yourself with how much you can understand and say.\n4. italki — Best for Speaking Practice Feature Details Price $5-30/lesson (tutor sets price) Languages 150+ Lesson Length 30-60 minutes Best Feature Affordable 1-on-1 lessons with native speakers Tutors Professional teachers + community tutors Best for: Anyone who wants to actually speak the language with real people.\nitalki connects you with native-speaking tutors for 1-on-1 video lessons. Community tutors start at $5-10/hour (great for conversation practice), while professional teachers charge $15-30/hour (better for structured learning). This is the fastest path to conversational fluency.\n5. Busuu — Best for Community Learning Feature Details Price Free (limited) / Premium $6.66/mo / Premium Plus $8.33/mo Languages 14 Best Feature Writing exercises corrected by native speakers AI Personalized study plan, AI-powered grammar review Certificate Official McGraw-Hill certificates Best for: Learners who benefit from community feedback and structured study plans.\nBusuu\u0026rsquo;s unique feature is community corrections — native speakers review your writing and speaking exercises, giving you real human feedback. The AI study planner adapts to your goals and available time. McGraw-Hill certificates add credibility for professional contexts.\nThe AI Factor in 2026 AI has transformed language learning:\nAI Tool How to Use It ChatGPT / Claude Conversation practice, grammar explanations, translation Duolingo Max AI roleplay conversations within structured curriculum Speak (app) AI-powered speaking practice with pronunciation feedback Elsa Speak AI pronunciation coaching with accent analysis Pro tip: Use ChatGPT or Claude as a free conversation partner. Ask it to play a role (waiter, taxi driver, colleague) and practice real scenarios. It corrects your grammar and explains mistakes — like having a patient tutor available 24/7.\nQuick Decision Guide Your Goal Best Tool Monthly Cost Build a daily habit Duolingo (free tier) $0 Learn grammar properly Babbel $7.45 Learn by listening Pimsleur $14.95 Start speaking ASAP italki $20-60 (for 4 lessons) Get community feedback Busuu $6.66 Practice with AI ChatGPT + Speak $20 + $8.33 Frequently Asked Questions Q: Can I become fluent with just an app? Not completely. Apps are great for vocabulary, grammar, and listening, but you need human conversation practice to become truly conversational. Use apps for 3-6 months, then add italki or a language exchange partner.\nQ: How long does it take to become conversational? With 30-60 minutes of daily practice: 3-6 months for \u0026ldquo;tourist conversational\u0026rdquo; (basic needs), 6-12 months for comfortable conversation, 1-2 years for professional fluency. Consistency matters more than intensity.\nQ: Is Duolingo actually effective? For building vocabulary and daily habit, yes. For conversational fluency alone, no. Use Duolingo as part of a stack — it\u0026rsquo;s excellent at what it does, but it\u0026rsquo;s not everything you need.\nQ: Which language is easiest for English speakers? Spanish, followed by Portuguese, Italian, French, and Dutch. These share vocabulary and grammar patterns with English. The FSI estimates 600-750 hours for these vs. 2,200 hours for Japanese, Chinese, or Arabic.\nLanguage Skills Give Your Career a Global Edge Bilingual and multilingual professionals command higher salaries and access more opportunities. Find your next career on doda — Japan\u0026rsquo;s top job platform with thousands of roles that value international communication skills.\nConclusion The best language learning approach in 2026 combines:\nAn app for daily vocabulary and grammar (Duolingo or Babbel) Speaking practice with real humans (italki) or AI (ChatGPT) Immersion content in your target language (podcasts, YouTube, Netflix) Start with Duolingo (free) to build the habit, add Babbel ($7.45/month) for grammar depth, and once you have a foundation, invest in italki lessons ($5-15/session) for real conversation practice.\nThe key is starting today and practicing daily — even 15 minutes counts.\nRelated: Best ChatGPT Prompts for Productivity This article is for informational purposes only. Prices and features are subject to change.\nRelated Tools Convert between units of measurement → Unit Converter Planning to work abroad? Calculate your take-home pay → Salary Calculator Related Templates Accelerate your learning with AI:\nChatGPT Prompt Templates — Language learning prompts and conversation practice scripts The AI Productivity Playbook 2026 — Use AI to learn any skill faster This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/best-online-language-learning-apps-2026/","summary":"\u003cp\u003e\u003cem\u003eDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"best-language-learning-apps-2026--what-actually-gets-you-to-conversational\"\u003eBest Language Learning Apps 2026 — What Actually Gets You to Conversational\u003c/h1\u003e\n\u003cp\u003eLanguage learning apps generated $15 billion in 2025. Most of that money was spent by people who never became conversational in their target language. The problem isn\u0026rsquo;t motivation — it\u0026rsquo;s using the wrong tool for the wrong stage of learning.\u003c/p\u003e","title":"Best Language Learning Apps 2026: Actually Become Conversational"},{"content":"Veterinary professionals are among the most time-pressured workers in healthcare. Between seeing patients, managing staff, handling emergency calls, and keeping up with medicine, there\u0026rsquo;s rarely enough time for the communication and administrative work that keeps a practice thriving.\nAI tools — particularly large language models like ChatGPT — can\u0026rsquo;t examine a patient or make clinical decisions. But they can handle a significant portion of the writing, organizing, and communicating that currently falls on already-stretched veterinary teams.\nThis guide covers practical, clinic-ready applications of AI across five key areas: client communication, appointment management, pet care education content, social media, and inventory and operations.\nThe State of AI in Veterinary Practice in 2026 Veterinary medicine is in the early stages of AI adoption. While human healthcare has seen AI deployed for diagnostic imaging, drug discovery, and clinical decision support, veterinary practice is still largely at the \u0026ldquo;how do AI tools help with the communication side?\u0026rdquo; stage — and that\u0026rsquo;s precisely where the time savings are most immediate.\nThe average veterinary practice manager estimates that 25-35% of staff time goes to:\nAnswering routine client inquiries (by phone, email, and message) Writing discharge instructions and post-procedure care guides Creating appointment reminders and follow-up communications Managing social media and educational content Handling inventory orders and tracking Most of this work involves writing and organizing — exactly what AI does well.\nWhat AI can do for your clinic:\nDraft client communications in seconds Generate pet care guides and discharge instructions Create social media content calendars Help manage and analyze inventory data Train new reception staff with scripted responses What AI cannot do:\nMake clinical diagnoses Replace the judgment of licensed veterinarians and technicians Handle legal or compliance decisions without professional review Guarantee accuracy of medical information (always have a DVM review clinical content) With those guardrails in mind, let\u0026rsquo;s get practical.\nPart 1: Client Communication Templates Great client communication builds trust, drives compliance, and reduces no-shows and calls back to the clinic for basic questions. Here\u0026rsquo;s how to use AI to create your full communication library.\nSetting Up Your Communication Template Library The goal is to build a set of templates for every routine communication scenario in your practice. Once built (using AI to draft, your team to review and customize), these templates save 2-4 hours per staff member per week.\nChatGPT Prompt 1: New Client Welcome Message Write a warm, professional welcome message to send to new clients before their first visit to our veterinary clinic. The message should: - Welcome them and their pet by name (e.g., \u0026#34;Hi Sarah, we\u0026#39;re looking forward to meeting Coco\\!\u0026#34;) - Confirm their appointment details - Explain what to bring (records, medications, pet in carrier if cat/small animal) - Set expectations for arrival (where to park, where to check in) - Reassure first-time visitors that we\u0026#39;re friendly and gentle - Give our contact information for questions - Sign off warmly with our clinic name Clinic name: Sunshine Veterinary Clinic Clinic address: 123 Main Street Phone: (555) 234-7890 Appointment reminder: Thursday, March 15 at 2:00 PM Staff contact: Dr. Emily Hartman Clinic personality/vibe: Friendly and relaxed, Fear Free Certified ChatGPT Prompt 2: Appointment Reminder (SMS/Short Version) Write 3 versions of a brief appointment reminder text message for our veterinary clinic. Keep each under 160 characters (SMS limit). Include appointment date, time, clinic name, and a call-to-action to confirm or reschedule. Versions needed: 1. Friendly and casual 2. Professional and straightforward 3. With a light touch of humor appropriate for a pet clinic Use these example details: client Sarah, pet Coco, your clinic name and phone number ChatGPT Prompt 3: Post-Appointment Follow-Up (Wellness Visit) Write a follow-up email to send 24 hours after a routine wellness appointment. The message should: - Thank them for coming in - Briefly recap what was done (e.g., \u0026#34;annual wellness exam and vaccinations\u0026#34;) - Remind them of any at-home care instructions discussed - Let them know when to schedule the next visit - Invite them to call if they have any concerns - Be warm and personal, not form-letter feeling Fill in your client name, pet name, species, procedures performed, vet name, next visit timeframe, clinic name, and phone number ChatGPT Prompt 4: Post-Surgery/Procedure Care Reminder Write a post-surgery follow-up email for a pet who just had a procedure at our clinic. The message should: - Express that we\u0026#39;re thinking of the pet and hope they\u0026#39;re recovering well - List the most important post-operative care reminders (list specific post-op care steps for the procedure) - List warning signs that should prompt an immediate call to us - Provide clear instructions for follow-up appointment scheduling - Be reassuring without being overly casual about surgery Fill in your client name, pet name, procedure details, post-op instructions, follow-up date, emergency contact, and clinic name ChatGPT Prompt 5: Overdue Vaccination Reminder Write an email reminder for a client whose pet\u0026#39;s vaccinations are overdue. The message should: - Be warm, not guilt-inducing or scolding - Briefly explain why staying current on vaccinations matters (1-2 sentences, simple language) - Make it easy to schedule (include CTA) - Mention if there\u0026#39;s a reminder call or text option - Keep it under 150 words Fill in your client name, pet name, overdue vaccines, last visit date, booking link or phone, and clinic name ChatGPT Prompt 6: Condolence Message After Pet Loss Write a heartfelt condolence message to send to a client after the loss of their pet. The tone should be: - Genuinely compassionate, not clinical - Personal (we remember their pet) - Brief — grief is not the time for long messages - Offer support without pressure Include a hand-signed feel (even if it\u0026#39;s sent digitally). Mention that we\u0026#39;ll keep the pet\u0026#39;s records should they ever wish to welcome another pet into their family. Fill in your client name, pet name, species, vet name, and clinic name Keep it under 100 words. This should feel handwritten. ChatGPT Prompt 7: Difficult Conversation Follow-Up (Terminal Diagnosis) Write a follow-up email to send after a client receives a serious or terminal diagnosis for their pet. The message should: - Acknowledge that this is difficult news - Briefly summarize the next steps or options discussed (fill in the specific diagnosis and options) - Offer resources (palliative care, quality of life scales, grief support) - Reassure them that we are their partner through this - Invite them to call anytime with questions - Be compassionate, clear, and free of jargon Fill in your client name, pet name, diagnosis, next steps discussed, vet name, and clinic phone Part 2: Pet Care Education Content Educated clients are better clients. When pet owners understand why preventive care matters, they\u0026rsquo;re more compliant with treatment plans, more likely to return, and more likely to refer friends.\nAI is excellent at generating first drafts of pet care guides that your veterinary team can then review and customize for accuracy.\nImportant: Always have a licensed DVM review any clinical content before publishing or distributing to clients.\nChatGPT Prompt 8: Seasonal Pet Care Guide Write a pet care guide for spring for our veterinary clinic\u0026#39;s newsletter and website. Include: - Top spring-specific health risks for dogs and cats (and add rabbits and birds if applicable) - Preventive steps clients can take at home - Signs that warrant a vet visit - Clinic reminder (seasonal wellness appointments, parasite prevention, etc.) Keep it practical and in plain English — no medical jargon. Target length: 400-500 words. Clinic location/climate: Pacific Northwest — mild, wet climate Species your clinic sees most: dogs, cats, and small mammals ChatGPT Prompt 9: New Pet Owner Welcome Guide (Dog) Write a comprehensive new dog owner guide for clients who just adopted or purchased a puppy or young dog. This will be given as a PDF handout at new patient appointments. Include sections on: 1. First vet visit: what to expect and when to schedule 2. Vaccination schedule overview (general — we\u0026#39;ll add specifics) 3. Parasite prevention (fleas, ticks, heartworm, intestinal worms) 4. Nutrition basics: what to feed and what to avoid 5. Socialization and training: why it matters for health 6. Common puppy health issues to watch for 7. When to call the vet vs. wait and see 8. Spay/neuter: timing and why we recommend it Write in a warm, encouraging tone for excited new pet owners. Use clear headers. Target length: 600-800 words. Clinic name: Sunshine Veterinary Clinic ChatGPT Prompt 10: Discharge Instructions (Post-Neuter — Male Dog) Write clear, client-friendly discharge instructions for a male dog after a routine neuter surgery. The instructions should: - Use simple, non-clinical language - Cover: activity restriction, diet the night after surgery, incision care, e-collar use, what\u0026#39;s normal vs. concerning - Include a list of specific warning signs to call us immediately - Specify when to schedule the follow-up appointment - Be organized with clear headers and bullet points - Fit on one page (approximately 350-400 words) Include fields for: pet name, follow-up date, [EMERGENCY PHONE], Sunshine Veterinary Clinic Note: This draft must be reviewed by a licensed DVM before clinic use. ChatGPT Prompt 11: FAQ Page for Clinic Website Create a list of 20 frequently asked questions with clear, concise answers for a veterinary clinic\u0026#39;s website FAQ page. Focus on questions that new clients or first-time pet owners commonly ask. Include questions about: - Appointment scheduling and what to bring - How to know if something is an emergency - Vaccination timing and requirements - What preventive care looks like at different life stages - Cost and payment (use general guidance, not specific prices) - How the clinic handles multi-pet households - Prescription and medication refills - Dental care for pets Keep each answer under 75 words. Write in a warm, authoritative voice that builds trust. Part 3: Appointment Management Systems ChatGPT Prompt 12: No-Show Recovery Message Write a message to send to a client who missed their appointment without canceling. The tone should be: - Completely non-judgmental (life happens) - Concerned for the pet\u0026#39;s health, not frustrated - Focused on getting them rescheduled easily - Brief and easy to act on Include options: reply to this message, call us, or book online. Include fields for: client name, pet name, missed appointment date, [BOOKING LINK OR PHONE], Sunshine Veterinary Clinic ChatGPT Prompt 13: Appointment Confirmation With Prep Instructions Write an appointment confirmation message that also includes specific preparation instructions for the visit type. Create versions for: 1. Annual wellness exam (dog) 2. Annual wellness exam (cat) 3. Dental cleaning (requires fasting) 4. Surgical procedure (requires fasting and no water after midnight) 5. Sick pet visit (no special prep, but what to observe and bring) 6. Senior wellness workup (bloodwork — fasting required) For each version: - Confirm appointment details - List the specific prep instructions clearly - State what to bring (prior records, current medications, fecal sample if applicable) - Include emergency contact if pet\u0026#39;s condition changes before appointment Keep each under 200 words. Building an Automated Response Library for Reception Train your AI tool (or create a script/lookup sheet) with standard answers to your 10 most common phone/email inquiries:\nCreate a reception script reference card covering the following 10 most common client inquiries at a veterinary clinic. For each inquiry, provide: - The question (as clients typically phrase it) - A warm, helpful scripted response - When to escalate to a veterinarian or technician - Any relevant follow-up question to ask the client Common inquiries to cover: 1. \u0026#34;My pet is vomiting — should I bring them in?\u0026#34; 2. \u0026#34;How much does a [procedure] cost?\u0026#34; 3. \u0026#34;My pet needs a prescription refill\u0026#34; 4. \u0026#34;How do I know if my pet is in pain?\u0026#34; 5. \u0026#34;Is this an emergency?\u0026#34; 6. \u0026#34;Can I get a copy of my pet\u0026#39;s records?\u0026#34; 7. \u0026#34;My pet ate [something] — is that dangerous?\u0026#34; 8. \u0026#34;When does my pet need their next rabies shot?\u0026#34; 9. \u0026#34;Do I need an appointment for a nail trim?\u0026#34; 10. \u0026#34;My pet is limping but acting normal — should I worry?\u0026#34; Clinic name: Sunshine Veterinary Clinic After-hours emergency contact: [NUMBER OR EMERGENCY CLINIC NAME] Part 4: Social Media Content for Veterinary Clinics Veterinary clinics have a natural advantage on social media: everyone loves animals. The challenge is consistency and content variety. AI makes this manageable.\nChatGPT Prompt 14: Monthly Social Media Content Calendar Create a 30-day social media content calendar for a veterinary clinic. Each day should have: - The content type/topic - The platform it\u0026#39;s best for (Facebook, Instagram, TikTok) - A brief caption (2-4 sentences) - Relevant hashtags Include a variety of content types: - Educational pet health tips - \u0026#34;Did you know?\u0026#34; facts - Pet photos with captions (add your own patient photos) - Staff spotlights - Before/after wellness stories - Seasonal health reminders - Client appreciation posts - Behind-the-scenes clinic content - Pet-of-the-month feature - Community involvement/charity content Month: [MONTH] Season: spring Any upcoming events: [AWARENESS MONTHS, HOLIDAYS, CLINIC EVENTS] Clinic name: Sunshine Veterinary Clinic Clinic social handles: [@HANDLE] Clinic personality: [WARM AND HUMOROUS / PROFESSIONAL / FEAR FREE FOCUSED / ETC.] ChatGPT Prompt 15: Educational Instagram Carousel Write the text for a 6-slide Instagram carousel about [PET HEALTH TOPIC]. Each slide should have: - A bold headline (under 10 words) - 2-3 sentences of accessible, accurate information - Slide 6 should be a call to action Topic suggestions: [DENTAL HEALTH IN DOGS / SIGNS OF PAIN IN CATS / TICK PREVENTION / SENIOR PET CARE / HOW TO CHOOSE A VET / UNDERSTANDING PET INSURANCE] Chosen topic: [YOUR TOPIC] Target audience: [DOG OWNERS / CAT OWNERS / ALL PET OWNERS] Clinic name: Sunshine Veterinary Clinic CTA: [BOOK AN APPOINTMENT / LEARN MORE ON OUR WEBSITE / CALL US] Note: Have a DVM review before posting if the content includes clinical guidance. ChatGPT Prompt 16: \u0026ldquo;Meet the Team\u0026rdquo; Staff Spotlight Write a warm, engaging social media post introducing a staff member. Make it personal and fun — show the human side of the clinic team. Staff member details: - Name: [NAME] - Role: [VETERINARIAN / TECHNICIAN / RECEPTIONIST / PRACTICE MANAGER] - How long with the clinic: [TIME] - Species specialty or favorite patient type: [DESCRIBE] - Something fun or unexpected about them: [FACT] - Their pets (if any): [NAMES AND SPECIES] - Why they love veterinary work: [BRIEF QUOTE OR SUMMARY] Post length: 100-150 words Include a closing line that connects back to the clinic\u0026#39;s commitment to their pets. ChatGPT Prompt 17: Google Business Review Request Message Write 3 variations of a friendly text or email asking satisfied clients to leave a Google review for our clinic. Each version should: - Feel personal, not automated - Make leaving a review sound easy - Include your Google review link - Be under 100 words each - Not offer incentives (which violates Google\u0026#39;s policies) Version tones: 1. Warm and conversational 2. Brief and casual (for text) 3. Slightly more formal (for email) Clinic name: Sunshine Veterinary Clinic Google review link: [URL] Part 5: Inventory and Operations Management Using ChatGPT for Inventory Analysis While AI can\u0026rsquo;t manage your inventory system directly, it can help you analyze data and build better tracking systems.\nChatGPT Prompt 18: Inventory Reorder Point Calculator Help me calculate reorder points for the following veterinary supply items. For each item, tell me: - The reorder point (units to trigger a new order) - Suggested order quantity - Whether I should consider a safety stock level Here is my usage data for the past 3 months: Item 1: [ITEM NAME] - Units used per month: [AVG] - Lead time from supplier: [# DAYS] - Storage capacity: [MAX UNITS] - Current stock: [#] [Repeat for each item] My goals: minimize stockouts while avoiding expired or overstocked items. ChatGPT Prompt 19: Supplier Negotiation Email Write a professional email to our pharmaceutical/supply vendor requesting a volume discount or better payment terms. I want to negotiate without damaging the relationship. Vendor name: [NAME] Contact: [NAME] Our relationship: [HOW LONG WE\u0026#39;VE BEEN A CUSTOMER] Current annual spend with them: $[AMOUNT] What I\u0026#39;m asking for: [% DISCOUNT / NET 30 TERMS / FREE SHIPPING] My leverage: [VOLUME, LOYALTY, COMPETITIVE PRICING FROM OTHERS] Tone: professional, collaborative, not confrontational ChatGPT Prompt 20: New Staff Onboarding Checklist Create a comprehensive onboarding checklist for a new veterinary receptionist. Organize by their first day, first week, and first month. Include: - Administrative tasks (paperwork, system access, software logins) - Training modules to complete - Shadowing schedule - Key phone scripts to memorize - Practice management software training - Client communication protocols - Emergency and escalation procedures - Where to find resources and who to ask for help Format as a checklist with checkboxes. This will be printed and used as an actual onboarding document. Software our clinic uses: [PRACTICE MANAGEMENT SOFTWARE NAME] Number of DVMs: [#] Number of technicians: [#] Part 6: Building Your AI Workflow Step 1: Audit Your Communication Bottlenecks Before deploying AI tools, spend 30 minutes with your team identifying:\nWhich written tasks take the most time each week? Which communication types do clients ask about most? Where do errors or inconsistencies in communication cause problems? What content do you wish you had but never have time to create? Prioritize the top 5 answers. Build templates for those first.\nStep 2: Create Your Template Library Use a shared document (Google Drive, Notion, or your practice management system\u0026rsquo;s notes section) to store approved templates. Organize by category:\nAppointment Reminders Post-Visit Follow-Up Post-Procedure Client Education Social Media Emergency Protocols Step 3: Establish a Review Protocol All AI-generated clinical content (anything that advises clients on pet health) must be reviewed by a DVM before use. Create a simple approval workflow:\nAI drafts content Designated DVM reviews for accuracy Front desk or practice manager reviews for tone Approved version is added to the template library Step 4: Train Your Team Your reception team should know:\nWhat the templates are and where to find them How to personalize templates for individual clients When to use a template vs. write from scratch When NOT to use AI (sensitive conversations, complex clinical questions, legal matters) Step 5: Measure the Impact After 60 days, measure:\nHas call volume for routine questions decreased? Has client response rate to reminders improved? Have no-shows decreased? Has staff time spent on written communications changed? Recommended AI Tools for Veterinary Clinics in 2026 For general writing and communication:\nChatGPT Plus ($20/month) — Best general-purpose AI writing assistant Claude — Strong alternative, excellent for longer documents For veterinary-specific applications:\nVetPawer — AI-assisted SOAP note drafting Instinct Science — Clinical decision support for emergency and specialty Digitail — Practice management with built-in AI communication tools For social media:\nBuffer or Hootsuite — Schedule AI-drafted content across platforms Canva — Design social posts using AI image tools For inventory management:\nVetter Software — Inventory tracking with reorder alerts Covetrus Pulse — Integrated ordering and practice management Streamline Your Clinic\u0026rsquo;s Accounting Too As your practice grows, so does financial complexity. Try freee — Japan\u0026rsquo;s #1 cloud accounting to handle invoicing, expense tracking, and payroll so you can focus on your patients, not your paperwork.\nConclusion Veterinary medicine is built on relationships — between clinicians and patients, and between practices and the families who love their animals. AI tools, used thoughtfully, make those relationships stronger by ensuring no client falls through the cracks, no communication is forgotten, and no educational opportunity is missed.\nThe clinics that will differentiate themselves over the next five years aren\u0026rsquo;t necessarily the ones with the most advanced diagnostic equipment — they\u0026rsquo;re the ones whose clients feel known, communicated with, and cared for between visits.\nStart with your appointment reminders. Build your template library one scenario at a time. Have your DVMs review anything clinical. And watch how much easier it becomes to communicate with the care and consistency your clients deserve.\nProductivity Works covers AI tools for healthcare professionals, small business owners, and specialized service providers. Subscribe for weekly guides.\nRelated Tools Create a monthly budget → Budget Planner Calculate your ideal freelance rate → Freelance Rate Calculator Related Templates Streamline your veterinary practice with these AI tools:\nThe AI Productivity Playbook 2026 — 50+ AI workflows for business operations ChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Client communication and marketing prompts This article contains affiliate links. We may earn a commission at no extra cost to you.\nYou May Also Like 15 AI-Generated Client Communication Templates for Veterinary Clinics Best AI Tools for Small Business 2026: The Complete Roundup Best AI Tools for Small Business Owners 2026: Complete Guide ","permalink":"https://productivity-works.com/posts/ai-tools-veterinary-clinic-2026/","summary":"\u003cp\u003eVeterinary professionals are among the most time-pressured workers in healthcare. Between seeing patients, managing staff, handling emergency calls, and keeping up with medicine, there\u0026rsquo;s rarely enough time for the communication and administrative work that keeps a practice thriving.\u003c/p\u003e\n\u003cp\u003eAI tools — particularly large language models like ChatGPT — can\u0026rsquo;t examine a patient or make clinical decisions. But they can handle a significant portion of the writing, organizing, and communicating that currently falls on already-stretched veterinary teams.\u003c/p\u003e","title":"AI Tools for Veterinary Clinics: Streamline Operations (2026)"},{"content":"Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nBest Password Managers 2026 — Stop Reusing Passwords The average person has 100+ online accounts. If you\u0026rsquo;re using the same password for more than one of them, you\u0026rsquo;re one data breach away from losing everything — email, banking, social media, all of it.\nA password manager generates unique, strong passwords for every account and remembers them for you. It costs $3-5/month and is the single most impactful security upgrade you can make.\nWhy You Need a Password Manager Problem Without Password Manager With Password Manager Remembering passwords Reuse same 2-3 passwords Unique password per account Password strength \u0026ldquo;Password123!\u0026rdquo; \u0026ldquo;x7$kM9#pL2@nQ8\u0026amp;vR4\u0026rdquo; Data breach impact All accounts compromised Only one account affected Filling login forms Type manually Auto-fill in 1 click Sharing passwords Text/email (insecure) Encrypted sharing Best Password Managers Compared Manager Price Free Tier Platforms Best Feature 1Password $2.99/mo No All Watchtower security alerts Bitwarden Free / $1/mo Yes (full-featured) All Best free option, open source Dashlane $3.33/mo Limited All Built-in VPN NordPass $1.49/mo Yes (1 device) All Breach scanner, NordVPN integration Apple Passwords Free Yes Apple only Built into iOS/macOS Detailed Reviews 1. 1Password — Best Overall Feature Details Price Individual $2.99/mo, Family $4.99/mo (5 users) Platforms Windows, Mac, Linux, iOS, Android, browsers Security AES-256, zero-knowledge, Secret Key + master password Best Feature Watchtower (alerts for weak/reused/breached passwords) Travel Mode Hide sensitive vaults when crossing borders Best for: Most people who want the best overall experience.\n1Password has the most polished interface, the strongest security model (unique Secret Key per device), and Watchtower — a real-time dashboard showing which passwords are weak, reused, or found in breaches. The Family plan at $4.99/month for 5 users is exceptional value.\n2. Bitwarden — Best Free Option Feature Details Price Free (unlimited passwords, 2 devices) / Premium $1/mo Platforms All platforms + self-hosting option Security AES-256, open source (audited code) Best Feature Open source transparency, self-hosting Free Tier Unlimited passwords, unlimited devices (sync across 2) Best for: Security-conscious users, budget users, and anyone who values open source.\nBitwarden\u0026rsquo;s free tier is genuinely usable — unlimited passwords with sync across devices. The $1/month Premium adds 2FA authentication, file attachments, and advanced reports. Being open source means the code is publicly audited, providing maximum transparency.\n3. Dashlane — Best for Extra Features Feature Details Price Premium $3.33/mo, Family $4.99/mo Platforms All platforms Security AES-256, zero-knowledge, patented security architecture Best Feature Built-in VPN + dark web monitoring Unique Automatic password changer for supported sites Best for: People who want a password manager plus VPN and dark web monitoring in one subscription.\nDashlane bundles a VPN and dark web monitoring with password management. The automatic password changer can update passwords on supported sites without you logging in manually. If you\u0026rsquo;d otherwise pay for a separate VPN, Dashlane\u0026rsquo;s bundle pricing makes sense.\n4. NordPass — Best Budget Premium Feature Details Price Free (1 device) / Premium $1.49/mo / Family $2.79/mo Platforms All platforms Security XChaCha20 encryption (newer than AES-256) Best Feature Data breach scanner, passkey support Integration Pairs with NordVPN and NordLocker Best for: NordVPN users and budget-conscious users wanting premium features.\nNordPass uses XChaCha20 encryption (considered more future-proof than AES-256) and offers premium features at the lowest price point. The Data Breach Scanner checks if your credentials have appeared in known breaches. Excellent passkey support for the passwordless future.\n5. Apple Passwords (iCloud Keychain) — Best Free for Apple Users Feature Details Price Free (included with Apple devices) Platforms iOS, macOS, Safari (limited Windows support) Security AES-256, end-to-end encrypted via iCloud Best Feature Zero setup, built into every Apple device Limitation Limited to Apple ecosystem Best for: Apple-only users who want zero friction.\nIf you use exclusively Apple devices, the built-in Passwords app (upgraded in iOS 18) is genuinely good — password generation, auto-fill, passkey support, and breach detection, all for free with no setup required. The limitation: poor cross-platform support makes it impractical if you use Windows or Android.\nQuick Decision Guide Your Situation Best Choice Cost Want the best experience 1Password $2.99/mo Want free + open source Bitwarden Free Want VPN bundled Dashlane $3.33/mo Want cheapest premium NordPass $1.49/mo All Apple devices Apple Passwords Free Family (5 people) 1Password Family $4.99/mo How to Switch to a Password Manager Choose a manager from the list above Install the browser extension and mobile app Import existing passwords from Chrome/Safari (all managers support this) Set a strong master password — the ONE password you\u0026rsquo;ll remember (use a passphrase like \u0026ldquo;correct-horse-battery-staple\u0026rdquo;) Enable 2FA on the password manager itself Gradually update weak and reused passwords as you log into sites Don\u0026rsquo;t try to change all passwords at once. Update them naturally over 2-4 weeks as you use different services.\nFrequently Asked Questions Q: What if the password manager gets hacked? Reputable managers use zero-knowledge architecture — they can\u0026rsquo;t see your passwords even if breached. Your vault is encrypted with your master password, which only you know. A breach would expose encrypted data that\u0026rsquo;s practically impossible to decrypt.\nQ: What if I forget my master password? Most managers have account recovery options (emergency contacts, recovery keys). Write down your master password and recovery key and store them in a physical safe. This is the ONE password you need to remember.\nQ: Are browser built-in password managers (Chrome, Safari) enough? They\u0026rsquo;re better than nothing but lack features like secure sharing, breach monitoring, cross-browser support, and advanced 2FA. Dedicated managers are significantly more secure and functional.\nQ: Should I use passkeys instead of passwords? Yes, when available. Passkeys are more secure than passwords. All recommended managers support passkeys. Use passkeys where offered, passwords (via manager) everywhere else.\nQ: Is it safe to store banking passwords in a password manager? Yes — it\u0026rsquo;s far safer than reusing passwords or writing them on sticky notes. Password managers with AES-256 encryption and zero-knowledge architecture are trusted by millions of users and security professionals.\nConclusion A password manager is the single best investment in your digital security. For $0-5/month, you eliminate password reuse, protect against breaches, and save time on every login.\nStart with 1Password ($2.99/month) for the best experience, or Bitwarden (free) if budget is a concern. Either one will dramatically improve your security posture.\nThe best time to start using a password manager was years ago. The second best time is today.\nRelated: Best VPN Services 2026 This article is for informational purposes only. Security needs vary by individual. All recommended products have been independently evaluated.\nRelated Tools Create a monthly budget → Budget Planner Generate secure passwords instantly → Password Generator Generate SHA-256 and MD5 hashes → Hash Generator Related Templates Secure your digital life with AI:\nChatGPT Prompt Templates — Security audit and privacy check prompts The AI Productivity Playbook 2026 — Automate your digital security workflow ","permalink":"https://productivity-works.com/posts/best-password-managers-2026/","summary":"\u003cp\u003e\u003cem\u003eDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"best-password-managers-2026--stop-reusing-passwords\"\u003eBest Password Managers 2026 — Stop Reusing Passwords\u003c/h1\u003e\n\u003cp\u003eThe average person has 100+ online accounts. If you\u0026rsquo;re using the same password for more than one of them, you\u0026rsquo;re one data breach away from losing everything — email, banking, social media, all of it.\u003c/p\u003e\n\u003cp\u003eA password manager generates unique, strong passwords for every account and remembers them for you. It costs $3-5/month and is the single most impactful security upgrade you can make.\u003c/p\u003e","title":"Best Password Managers 2026: Secure Every Account for $3/Month"},{"content":"Client communication is one of the highest-leverage activities in any veterinary practice. A well-timed reminder reduces no-shows. A thoughtful post-visit message builds loyalty. A clear care instruction sheet reduces callback volume. A compassionate condolence note can turn a grieving client into a lifelong advocate.\nYet in most practices, client communication is cobbled together — different staff writing messages in different styles, no-show reminders sent too late or not at all, discharge instructions printed from a generic template that nobody reads because it reads like a legal document.\nAI tools change this. With a good prompt and five minutes of setup, you can build a library of polished, consistent, genuinely helpful client communications that your entire team can use. This article gives you 15 templates across the most important communication categories in veterinary practice.\nFor a broader look at how AI tools can transform your clinic\u0026rsquo;s operations, see our Complete Guide to AI Tools for Veterinary Clinics .\nHow to Use These Templates Each template below is either a ready-to-use message you can adapt directly, or a ChatGPT prompt that generates the message for you. For the AI prompts, paste them into ChatGPT (GPT-4o recommended) and fill in the bracketed fields.\nA few principles for all veterinary client communication:\nUse the pet\u0026rsquo;s name. Every message should address the pet by name. \u0026ldquo;Your dog\u0026rsquo;s appointment\u0026rdquo; feels generic. \u0026ldquo;[Dog\u0026rsquo;s name]\u0026rsquo;s appointment\u0026rdquo; feels personal. Be warm, not clinical. Most pet owners are emotionally invested in their animals. Clinical, transactional language creates distance. Be specific and actionable. Vague instructions (\u0026ldquo;monitor your pet\u0026rdquo;) cause anxiety. Specific guidance (\u0026ldquo;monitor for vomiting, lethargy, or refusal to eat for 24 hours — call us if any of these occur\u0026rdquo;) gives clients confidence. Keep it short. Busy pet owners don\u0026rsquo;t read long messages. Get to the point, then provide detail for those who want it. Section 1: Appointment Reminders Template 1: Standard Appointment Reminder (Email) Here is a ready-to-use example — customize the highlighted details for your practice:\nSubject: Reminder: Max\u0026#39;s appointment at Sunshine Veterinary Clinic — Tuesday, June 10 Hi Sarah, This is a friendly reminder that Max has an appointment scheduled at Sunshine Veterinary Clinic: Date: Tuesday, June 10, 2026 Time: 10:30 AM Reason for visit: Annual wellness exam \u0026amp; vaccinations Location: 4821 Maple Avenue, Suite 101 A few things to help us give Max the best care: - Please arrive 5-10 minutes early to complete any paperwork - Bring Max\u0026#39;s vaccination records if you have them on hand - No special prep needed for this visit — just bring Max! If you need to reschedule, please call us at (555) 234-7890 or reply to this email. We ask for at least 24 hours\u0026#39; notice when possible. We look forward to seeing Max soon! Dr. Emily Hartman \u0026amp; the Sunshine Vet Team Sunshine Veterinary Clinic | (555) 234-7890 | sunshinevet.com (Customize pet name, client name, clinic details, date, time, and prep instructions for your practice.)\nTemplate 2: SMS Appointment Reminder For text messages, brevity is everything. 160 characters or under is ideal.\nReminder: Max\u0026#39;s appt at Sunshine Vet is Tuesday at 10:30 AM. Reply CONFIRM or call (555) 234-7890 to reschedule. We look forward to seeing you! For clinics with automated SMS systems, you can create slightly longer variants:\nHi Sarah! Max\u0026#39;s appointment is coming up: Tuesday, June 10 at 10:30 AM at Sunshine Veterinary Clinic. No special prep needed. Questions? Call (555) 234-7890. See you soon! (Customize pet name, client first name, clinic name, date, time, phone number, and any prep note.)\nTemplate 3: AI-Generated Reminder for Overdue Preventive Care Use this prompt when a patient is overdue for a wellness exam or vaccine:\nWrite a friendly, non-judgmental reminder message for a pet owner whose dog named Max is overdue for their annual wellness exam. The message should: - Be warm and conversational, not guilt-inducing - Explain briefly why this care matters for Max\u0026#39;s health - Make it easy to book (include a simple call to action) - Be appropriate for email — 3-4 short paragraphs max Tone: caring and professional. The owner is a good pet owner who just got busy, not a neglectful one. Clinic name: [name] Phone/booking link: [details] Section 2: Post-Visit Follow-Ups Template 4: Standard Post-Visit Thank You Here is a ready-to-use example — customize the highlighted details for your practice:\nSubject: Thanks for visiting Sunshine Veterinary Clinic with Luna Hi Michael, Thank you for bringing Luna in today! It was a pleasure caring for her. We\u0026#39;re so glad to hear Luna\u0026#39;s weight is back on track — that\u0026#39;s a real testament to how consistently you\u0026#39;ve been following her diet plan. As a reminder, here\u0026#39;s what we discussed at today\u0026#39;s appointment: - Continue the current low-calorie diet — we\u0026#39;ll reweigh Luna at her next visit in 30 days - Luna is due for her Bordetella booster in 3 months - Keep an eye on the small skin tag on her left shoulder — call us if it changes in size or color If you have any questions or notice anything unusual in the next few days, please don\u0026#39;t hesitate to call us at (555) 234-7890 or send us a message through our client portal at sunshinevet.com. Take care, Dr. Emily Hartman Sunshine Veterinary Clinic (Customize client name, pet name, visit details, action items, phone number, and clinic name.)\nTemplate 5: Post-Surgery Follow-Up (AI-Generated) Write a post-surgery follow-up email for a pet owner whose dog named Cooper just had a routine dental cleaning with two extractions. Include: - A warm opening acknowledging that surgery is stressful for pet owners - What to expect in the first 24-48 hours (specific to the procedure type) - Clear watch-for signs that should prompt a call to the clinic - Activity restriction guidance (specific to the procedure) - Wound care instructions (if applicable) - When the follow-up appointment should happen - An invitation to call with any questions, including a reassurance that there are no \u0026#34;silly questions\u0026#34; Tone: warm, clear, and reassuring — like a knowledgeable friend explaining things after a medical procedure, not a clinical discharge form. Clinic name: [name], Phone: [number] Template 6: Chronic Condition Check-In For patients with ongoing conditions (diabetes, Cushing\u0026rsquo;s, heart disease, osteoarthritis), a periodic check-in message builds trust and catches problems early.\nWrite a 3-month check-in message for a pet owner managing a cat named Whiskers with early-stage kidney disease. The message should: - Acknowledge the owner\u0026#39;s ongoing commitment to managing Whiskers\u0026#39;s condition - Ask 3-4 specific check-in questions relevant to the condition (e.g., for diabetes: appetite changes, water intake, energy level, any hypoglycemic episodes) - Remind them of their next scheduled recheck date - Offer a brief window for a phone consult if they have concerns - Be short enough to actually get read — 2-3 paragraphs Clinic name: [name], Vet name: [name], Phone: [number] Section 3: Pet Care Instructions Template 7: Post-Vaccination Care Instructions Write a brief, clear post-vaccination care instruction sheet for pet owners. The vaccinations given were: [list vaccines given — e.g., DHPP, Rabies, Bordetella] Include: - Normal reactions to expect (and for how long) - Reactions that are NOT normal and require an immediate call or emergency visit - Activity guidance for the rest of the day - When they can socialize with other animals (if relevant — e.g., after Bordetella) - Any return dates for booster vaccinations administered today Format: short bullets under clear headings. Avoid medical jargon. The goal is that a pet owner reads this in 60 seconds and knows exactly what to watch for. Template 8: New Puppy/Kitten Care Guide (Customizable) Create a new [puppy/kitten] care guide for a client who just adopted a [breed/species] approximately [age] weeks/months old. Sections to include: 1. Feeding (what, how much, how often) 2. Vaccination schedule (based on current age — first visit was today) 3. Parasite prevention (heartworm, fleas, ticks) 4. Socialization and behavior (key windows for puppies/kittens, what to prioritize) 5. Spay/neuter timing recommendation 6. What to watch for health-wise in the first few weeks 7. When to call us Tone: encouraging and warm — adopting a new pet should feel exciting. Acknowledge that it can be overwhelming and reassure them that we\u0026#39;re here to help. Clinic: [name], Phone: [number], Portal/website: [URL if applicable] Template 9: Dental Care Instructions (Post-Cleaning) Write a post-dental cleaning care sheet for a dog/cat owner. The procedure: routine dental cleaning under anesthesia [with/without] extractions. [If extractions: specify how many and approximate location — e.g., \u0026#34;two lower premolars\u0026#34;] Include: - Diet for the next 24-48 hours (soft food vs. normal) - Activity restrictions - Medication instructions [list any medications prescribed] - What the mouth/gums may look like for the first few days (reassure about normal healing) - Warning signs: what to watch for and when to call - Ongoing home dental care recommendations - When to schedule the next dental cleaning Keep it parent-friendly: friendly tone, simple language, bullet-heavy format for easy scanning. Clinic: [name], Phone: [number] Template 10: Weight Management Program Instructions Write a customized weight management instruction sheet for a pet owner whose Labrador named Buddy is starting a weight loss program. Details: - Current weight: [X lbs] - Target weight: [X lbs] - Recommended daily caloric intake: [X calories] - Recommended food: [name/type] - Feeding schedule: [meals per day, portion sizes] - Exercise recommendations: [X minutes/day, activity type appropriate for condition] - Recheck schedule: every [X weeks] Tone: motivating and positive — frame this as an investment in Buddy\u0026#39;s quality and length of life, not a punishment. Acknowledge that pets love to eat and this will require commitment from the whole household. Include a brief \u0026#34;tips for success\u0026#34; section with 4-5 practical suggestions. Section 4: Vaccination and Preventive Care Reminders Template 11: Annual Vaccine Reminder Here is a ready-to-use example — customize the highlighted details for your practice:\nSubject: Time to schedule Bella\u0026#39;s annual vaccines Hi Jennifer, It\u0026#39;s that time of year for Bella! Her annual vaccines are due this month, and we want to make sure she stays fully protected. Staying current on vaccines is one of the most important things you can do to keep Bella healthy. This year\u0026#39;s appointment will include: - DHPP booster - Rabies booster - Annual wellness exam - Heartworm test (required before we refill her prevention) Scheduling is easy — call us at (555) 234-7890, book online at sunshinevet.com/appointments, or simply reply to this email. We\u0026#39;d love to see Bella — it\u0026#39;s been a year! Dr. Emily Hartman \u0026amp; the Sunshine Vet Team Sunshine Veterinary Clinic (Customize pet name, client name, vaccine types, clinic phone, booking URL, and clinic name.)\nTemplate 12: Heartworm Prevention Reminder (Seasonal) Write a spring heartworm awareness message for pet owners who are due for their annual heartworm test. Include: - A brief (2-sentence) explanation of heartworm disease and transmission - Why annual testing matters even for pets on year-round prevention - A clear call to action to schedule the heartworm test + refill prevention - A seasonal hook (spring mosquito season is starting) - Tone: friendly and informative, not alarmist This is for an email and social media post version. Keep the email under 200 words and the social post under 100 words. Clinic name: [name], Phone/booking: [details] Section 5: Condolence and Difficult Situation Messages Template 13: Condolence Message (Loss of Pet) This is one of the most important communications a veterinary clinic sends. Done well, it creates a lasting bond with the client. Done poorly or not at all, it leaves a void at the moment a client is most vulnerable.\nWrite a sincere condolence message for a pet owner who recently lost their Golden Retriever named Charlie. Charlie passed away peacefully at our clinic after a long battle with cancer. We had the honor of caring for Charlie for the past 12 years. The message should: - Open with the pet\u0026#39;s name and a specific acknowledgment (not generic sympathy) - Acknowledge the depth of the human-animal bond without being dismissive or minimizing (\u0026#34;they were just a pet\u0026#34; energy must be completely absent) - Share one brief, warm reflection if you have a personal memory of the pet - Let them know the team is thinking of them - Mention your clinic\u0026#39;s loss resources if applicable (e.g., pet loss support group, Rainbow Bridge card, paw print keepsake) - Close gently, leaving the door open without pressure Tone: genuinely compassionate. This message should feel like it was written by someone who cared, not generated by a form. Length: 3-4 short paragraphs. Handwritten card format preferred, but email also works. From: [Vet name and clinic name] Template 14: Euthanasia Appointment Confirmation and Preparation Write a compassionate message to a pet owner confirming a euthanasia appointment for their cat named Oliver. This message must: - Acknowledge this is an incredibly difficult time without being maudlin - Confirm the appointment details clearly (date, time, location, what to expect for timing/logistics) - Explain what to expect during the appointment process in gentle, plain language — so they\u0026#39;re not caught off-guard - Answer common questions preemptively: can family members be present, how long will it take, what happens afterward (if aftercare has been arranged) - Remind them it\u0026#39;s okay to feel however they feel - Provide contact information for any last-minute questions Tone: deeply empathetic, clear, and calm. This person is already in grief. Every sentence should reduce anxiety, not add to it. Appointment details: [date/time/location] Clinic: [name], Phone: [number] Template 15: AI-Generated Response to a Negative Review About a Pet Outcome Negative reviews involving pet outcomes are among the most challenging to respond to. A defensive or dismissive response can escalate; a thoughtful response can demonstrate your practice\u0026rsquo;s character.\nHelp me write a professional and empathetic public response to this negative review: [Paste the review text here] The context (for your understanding — do not include confidential details in the response): [Brief background on the situation, e.g., \u0026#34;The patient came in in critical condition and despite our team\u0026#39;s best efforts, the pet passed away. The family was extremely upset and we understand why.\u0026#34;] The response should: - Open by acknowledging the person\u0026#39;s grief and pain, not by defending the clinic - Express genuine condolence for the loss of their pet - Invite them to contact us privately to discuss their concerns further (include phone number) - NOT make medical admissions or discuss case specifics in the public response - Be brief (4-6 sentences) — this is a public response, not a full explanation - Close with a reaffirmation of the clinic\u0026#39;s commitment to excellent care Tone: human, accountable, and warm. A grieving pet owner wrote this. Match that energy with compassion first. Manage Your Clinic\u0026rsquo;s Finances as Smoothly as Your Communications Great client communication keeps clients coming back — clean accounting keeps the lights on. Try freee — Japan\u0026rsquo;s #1 cloud accounting to automate your billing, track expenses, and simplify tax season for your veterinary practice.\nBuilding a Communication Template Library for Your Clinic These 15 templates are a starting point. The real goal is building a library your entire team can access — so communications are consistent regardless of who\u0026rsquo;s sending them.\nA practical setup:\nSave all templates in a shared Google Doc or Notion database organized by category Create a naming convention (e.g., \u0026ldquo;APPT-001: Appointment Reminder Email — Standard\u0026rdquo;) Train front desk and client service staff on which template to use in which situation Review and update templates quarterly, or whenever you notice the same question coming in repeatedly (that\u0026rsquo;s a sign your instructions aren\u0026rsquo;t clear enough) ChatGPT can help you expand this library on demand: \u0026ldquo;We keep getting calls asking about [X] after procedures. Write a template message that addresses this preemptively.\u0026rdquo;\nRelated Tools Create a monthly budget → Budget Planner Calculate your ideal freelance rate → Freelance Rate Calculator Related Templates Ready to build a complete AI-powered communication system for your clinic?\nThe AI Productivity Playbook — A practical guide to using AI tools across your veterinary practice: client communication, staff training, documentation, social media, and more. Includes customizable templates and step-by-step instructions for clinic teams of any size. This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/ai-veterinary-client-communication-templates/","summary":"\u003cp\u003eClient communication is one of the highest-leverage activities in any veterinary practice. A well-timed reminder reduces no-shows. A thoughtful post-visit message builds loyalty. A clear care instruction sheet reduces callback volume. A compassionate condolence note can turn a grieving client into a lifelong advocate.\u003c/p\u003e\n\u003cp\u003eYet in most practices, client communication is cobbled together — different staff writing messages in different styles, no-show reminders sent too late or not at all, discharge instructions printed from a generic template that nobody reads because it reads like a legal document.\u003c/p\u003e","title":"15 AI-Generated Client Communication Templates for Veterinary Clinics"},{"content":" AI Writing Tools Comparison 2026: Which One Is Actually Worth It? AI writing tools have multiplied faster than anyone can track. In 2024 there were dozens. In 2026, there are hundreds. And the quality gap between the best and worst has never been larger.\nThis comparison cuts through the marketing noise. We tested 12 of the most popular AI writing tools across real use cases — blog posts, marketing copy, email sequences, social media, and technical writing — and scored them honestly.\nWhether you\u0026rsquo;re a freelancer, content marketer, small business owner, or someone who just wants to write better emails, this guide has your answer.\nWhat We Tested and How Tools tested: ChatGPT Plus, Claude Pro, Jasper, Copy.ai, Writesonic, Rytr, Notion AI, Grammarly AI, Surfer AI, Anyword, Writer.com, and Canva Magic Write\nTest tasks for each tool:\nWrite a 1,500-word SEO blog post from a brief Write 5 email subject line variations Write a product description (100 words) Rewrite a paragraph in a specific brand voice Generate 10 social media captions Write a cold outreach email Scoring criteria:\nOutput quality (40%) Ease of use (20%) Value for price (20%) Specialized features (20%) The Quick Rankings Tool Score Best For Price Claude Pro 94/100 Long-form, nuanced writing $20/month ChatGPT Plus 92/100 Versatile, all-purpose $20/month Jasper 85/100 Marketing teams, templates $49/month Surfer AI 83/100 SEO-optimized content $59/month Copy.ai 81/100 Sales copy, email sequences $36/month Writesonic 79/100 Volume content production $12-16/month Notion AI 78/100 In-workflow writing assistance $10/user/month add-on Anyword 77/100 Performance-scored copy $39/month Writer.com 76/100 Brand voice consistency (teams) $18/user/month Grammarly AI 73/100 Editing and improvement $12-30/month Rytr 68/100 Budget-conscious beginners $7.50/month Canva Magic Write 62/100 Quick social/design copy Included with Canva Detailed Reviews 1. Claude Pro — 94/100 Price: $20/month Best for: Long-form writing, professional content, research-based articles\nClaude (by Anthropic) produces the most natural-sounding writing of any AI tool we tested. Where ChatGPT occasionally slips into generic structure and filler phrases, Claude maintains voice consistency and handles nuanced briefs more accurately.\nWhat Claude does best:\nFollowing complex, multi-part writing briefs Maintaining brand voice across long documents Writing that requires analytical reasoning (reports, case studies, white papers) Avoiding clichés and generic phrasing Handling sensitive topics with appropriate nuance Claude in action — blog post test: Given a brief to write about \u0026ldquo;sustainable investing for millennials\u0026rdquo; in a \u0026ldquo;confident but approachable tone, no jargon, target audience has $0 investing experience,\u0026rdquo; Claude produced an article that needed minimal editing. The analogies were fresh, the structure was logical, and the voice stayed consistent through 1,500 words.\nLimitations:\nNo image generation No built-in SEO integration Smaller ecosystem of templates than dedicated tools like Jasper Ideal user: Freelance writers, professional content creators, businesses that publish thought leadership content.\nScore breakdown: Quality 96 | Ease of use 88 | Value 92 | Features 78 = 94/100\n2. ChatGPT Plus — 92/100 Price: $20/month Best for: Versatile all-purpose writing, creative content, iterative editing\nChatGPT remains the most versatile AI writing tool. Its combination of strong language generation, image creation (DALL-E 3), web browsing, and the GPT Store ecosystem makes it the best single subscription for writers who do varied work.\nWhat ChatGPT does best:\nCreative writing and ideation Writing and image integration in a single workflow Iterating quickly through multiple drafts Custom GPTs for specific brand voices or content formats Writing with access to current web information ChatGPT in action — social media test: For 10 social media captions for a fitness brand, ChatGPT generated strong options across different tones (motivational, educational, conversational) quickly. The best 3-4 of the 10 were nearly ready to post.\nLimitations:\nCan be verbose and pad content with filler Generic structure sometimes needs deliberate prompting to break No built-in SEO metrics or scoring Ideal user: Content marketers, social media managers, entrepreneurs who want one tool for everything.\nScore breakdown: Quality 91 | Ease of use 95 | Value 92 | Features 90 = 92/100\n3. Jasper — 85/100 Price: $49/month (Creator), $125/month (Teams) Best for: Marketing teams with high content volume and brand consistency needs\nJasper is purpose-built for marketing content. It includes over 50 templates (blog outlines, Facebook ads, product descriptions, email campaigns), brand voice training, and campaign workflows. For teams producing 30+ pieces of content per month, Jasper\u0026rsquo;s structure creates real efficiency.\nWhat Jasper does best:\nTemplate-driven content production Brand voice training across a team Campaign workflows (brief → outline → draft in sequence) Integrations with SurferSEO, Grammarly, and Copyscape Team collaboration features Jasper in action — email sequence test: Given a product brief and target audience, Jasper generated a 5-email welcome sequence with logical progression, distinct angles per email, and on-brand voice. The output needed one editing pass but was structurally solid.\nLimitations:\nExpensive for solo creators (Claude/ChatGPT do comparable work for $20) Brand voice training requires time investment upfront Can feel formulaic for creative or unconventional content Ideal user: Marketing teams, agencies managing multiple client brands, businesses with consistent high-volume content needs.\nScore breakdown: Quality 84 | Ease of use 87 | Value 76 | Features 92 = 85/100\n4. Surfer AI — 83/100 Price: $59/month (Essential) Best for: SEO content that actually ranks\nSurfer AI combines AI writing with real-time SEO optimization. As you write, it analyzes the top-ranking pages for your target keyword and scores your content against them — tracking keyword usage, structure, word count, and topical coverage.\nWhat Surfer does best:\nWriting content optimized for specific search rankings Identifying keyword gaps and missing subtopics Providing data-driven content briefs Real-time SEO scoring as you draft Surfer in action — SEO blog post test: Writing a post targeting \u0026ldquo;best home office chairs,\u0026rdquo; Surfer flagged missing keywords, recommended section topics based on competing pages, and scored the draft at 78/100 — with specific suggestions to improve. After adjustments, the score reached 91/100.\nLimitations:\nThe writing quality without SEO guidance is average The SEO-first approach can produce content that feels optimized but not authentic Expensive for low-volume publishers Ideal user: Content teams and SEO specialists who prioritize organic traffic and ranking.\nScore breakdown: Quality 78 | Ease of use 82 | Value 75 | Features 96 = 83/100\n5. Copy.ai — 81/100 Price: $36/month (Pro), $186/month (Team) Best for: Sales copy, cold email, and marketing copy at volume\nCopy.ai\u0026rsquo;s workflows excel at sales-oriented writing. The \u0026ldquo;Sales Copy\u0026rdquo; workflow can take a product description and generate landing page copy, email sequences, ad copy, and LinkedIn posts in one session.\nWhat Copy.ai does best:\nCold email sequences Sales page and landing page copy LinkedIn outreach messages Product launch campaigns Multi-channel campaign content Limitations:\nLong-form blog content is weaker than Claude or ChatGPT Can feel formulaic for creative brands The interface is more complex than it needs to be Ideal user: Sales teams, email marketers, businesses running paid ad campaigns.\nScore breakdown: Quality 79 | Ease of use 78 | Value 80 | Features 87 = 81/100\n6. Writesonic — 79/100 Price: $12-16/month (Individual) Best for: Budget-conscious content creators who need volume\nWritesonic\u0026rsquo;s AI Article Writer 6.0 generates complete blog posts from a keyword. At $12-16/month for an individual plan, it\u0026rsquo;s the most affordable option that produces publication-ready content.\nLimitations: Quality is noticeably lower than Claude or ChatGPT. Best used as a first-draft generator that requires editing.\nIdeal user: Bloggers and content creators who need volume and have strong editing skills.\nScore breakdown: Quality 72 | Ease of use 85 | Value 90 | Features 70 = 79/100\n7. Notion AI — 78/100 Price: $10/user/month (add-on to Notion) Best for: Integrated writing assistance within your existing Notion workspace\nNotion AI is exceptional for the specific workflow of thinking and writing inside Notion. It summarizes notes, continues drafts, improves existing text, and maintains context within your workspace.\nLimitations: Not a standalone tool. Limited for long-form content creation from scratch.\nIdeal user: Anyone who already uses Notion and wants writing help built into their workflow.\nRelated: Best Notion AI Templates for Productivity 2026 8. Grammarly AI — 73/100 Price: $12/month (Premium), $30/month (Business) Best for: Editing, improving, and polishing existing writing\nGrammarly has expanded beyond grammar checking into AI-powered rewriting, tone adjustment, and content generation. It\u0026rsquo;s still better at editing human-written text than at generating from scratch.\nIdeal user: Writers who want their own writing improved rather than replaced.\nHead-to-Head Comparisons For Blog Writing (1,000-3,000 words) Ranking: Claude \u0026gt; ChatGPT \u0026gt; Jasper \u0026gt; Surfer AI \u0026gt; Writesonic\nClaude and ChatGPT produce the highest quality first drafts. The gap between these two and the purpose-built tools (Jasper, Writesonic) is larger than most people expect.\nFor Marketing Copy (emails, ads, landing pages) Ranking: Copy.ai \u0026gt; Jasper \u0026gt; ChatGPT \u0026gt; Anyword \u0026gt; Claude\nPurpose-built marketing copy tools outperform general AI in this category, largely because of their templates and campaign workflows.\nFor SEO Content Ranking: Surfer AI \u0026gt; Jasper (with Surfer integration) \u0026gt; ChatGPT (with SEO prompts) \u0026gt; Claude (with SEO prompts)\nIf ranking on Google is your primary goal, Surfer AI\u0026rsquo;s integrated optimization is worth the premium.\nFor Social Media Content Ranking: ChatGPT \u0026gt; Jasper \u0026gt; Copy.ai \u0026gt; Claude \u0026gt; Canva Magic Write\nChatGPT\u0026rsquo;s combination of text generation, image creation, and iterative brainstorming makes it the best social media content tool.\nFor Teams and Brand Consistency Ranking: Jasper \u0026gt; Writer.com \u0026gt; ChatGPT Team \u0026gt; Copy.ai Teams\nJasper and Writer.com both offer the brand voice training and team collaboration features needed for consistent multi-person content operations.\nWhich AI Writing Tool Is Right for You? \u0026ldquo;I\u0026rsquo;m a freelance writer who needs the best quality output\u0026rdquo; Choose: Claude Pro ($20/month)\n\u0026ldquo;I\u0026rsquo;m a content marketer at a company\u0026rdquo; Choose: Jasper ($49-125/month) or ChatGPT Team ($25/user/month)\n\u0026ldquo;I need content that ranks on Google\u0026rdquo; Choose: Surfer AI ($59/month)\n\u0026ldquo;I\u0026rsquo;m a solopreneur who writes sales copy and email sequences\u0026rdquo; Choose: Copy.ai ($36/month)\n\u0026ldquo;I want the most versatile tool for varied tasks\u0026rdquo; Choose: ChatGPT Plus ($20/month)\n\u0026ldquo;I\u0026rsquo;m on a tight budget but need AI writing help\u0026rdquo; Choose: Writesonic Individual ($12-16/month) or try Claude/ChatGPT free tiers\n\u0026ldquo;I want AI writing built into my existing workspace\u0026rdquo; Choose: Notion AI ($10/user/month add-on)\nThe Case for Using Two Tools Many professional content creators use a general-purpose AI (Claude or ChatGPT) plus one specialized tool:\nClaude + Surfer AI — high-quality writing that ranks ChatGPT + Copy.ai — versatile content plus sales copy workflows Jasper + Grammarly — high-volume content with polish The total cost of two tools ($40-80/month) is still a fraction of a single junior content hire.\nTips for Getting Better Output From Any AI Writing Tool Write specific briefs. \u0026ldquo;Write a blog post about coffee\u0026rdquo; produces generic results. \u0026ldquo;Write a 1,200-word blog post for working parents about how to make cafe-quality espresso at home in under 5 minutes, tone: approachable and practical, no coffee jargon\u0026rdquo; produces usable content.\nGive examples of what you want. Paste 2-3 examples of writing you like and ask the AI to match the style.\nIterate, don\u0026rsquo;t regenerate. If the first output is 70% good, ask the AI to improve specific parts rather than starting over.\nAlways edit. AI writing is a first draft, not a final product. The best content creators use AI to eliminate blank-page paralysis, then add their own expertise and voice.\nUse AI to improve your own writing. Paste your draft and ask \u0026ldquo;make this more concise,\u0026rdquo; \u0026ldquo;improve the hook,\u0026rdquo; or \u0026ldquo;rewrite in active voice.\u0026rdquo;\nContent Skills That Open Doors AI writing proficiency is increasingly listed in job descriptions for marketing, content, and communications roles. Find your next career on doda — browse thousands of marketing and content roles on Japan\u0026rsquo;s leading job platform.\nSupercharge Your Content Workflow Our AI Content Creator\u0026rsquo;s Toolkit includes 80+ writing prompts, content calendar templates, and workflow blueprints for bloggers, freelancers, and content marketers — tested across all the tools reviewed here.\nRelated Reading:\nChatGPT vs Claude vs Gemini 2026 Best AI Tools for Small Business 2026 AI Prompt Engineering Tips for Beginners 2026 Related Tools Count words and characters in your text → Word Counter Compare texts and find differences → Text Diff Checker Calculate your ideal freelance rate → Freelance Rate Calculator Create a monthly budget → Budget Planner Need a markdown editor? → Markdown Preview — write and preview markdown live\nGenerate placeholder text for drafts → Lorem Ipsum Generator — instant filler content\nThis article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/ai-writing-tools-comparison-2026/","summary":"\u003chr\u003e\n\u003ch2 id=\"ai-writing-tools-comparison-2026-which-one-is-actually-worth-it\"\u003eAI Writing Tools Comparison 2026: Which One Is Actually Worth It?\u003c/h2\u003e\n\u003cp\u003eAI writing tools have multiplied faster than anyone can track. In 2024 there were dozens. In 2026, there are hundreds. And the quality gap between the best and worst has never been larger.\u003c/p\u003e\n\u003cp\u003eThis comparison cuts through the marketing noise. We tested 12 of the most popular AI writing tools across real use cases — blog posts, marketing copy, email sequences, social media, and technical writing — and scored them honestly.\u003c/p\u003e","title":"AI Writing Tools Comparison 2026: Best Options Ranked and Reviewed"},{"content":"This article contains affiliate links.\nIf you are running a small business and still spending Sunday evenings manually entering receipts, reconciling bank statements, and trying to remember whether that restaurant meal was a client dinner or a personal expense — this guide is for you. Bookkeeping automation using AI has reached a point in 2026 where a solo operator or small team can genuinely compress what used to be 8-10 hours of monthly bookkeeping into 1-2 hours of review and oversight.\nThis is not about replacing your accountant. It is about making sure your books are clean, current, and accurate so that when you do pay for professional accounting time, you are paying for strategic advice — not for someone to sort through your shoe box of receipts.\nWhy Bookkeeping Automation Matters More for Small Business Large companies have full-time accounting departments. They can afford to do bookkeeping slowly and manually because they have people dedicated to it. Small business owners do not have that luxury — every hour spent on bookkeeping is an hour not spent on sales, operations, or the actual work your business does.\nThe consequences of poor bookkeeping are severe and specific:\nOverpaid taxes from missed deductions Cash flow blindness — not knowing if you can afford a hire or investment until it is too late Late tax payments and associated penalties and interest Audit risk from inconsistent or incomplete records Loan denial when lenders cannot verify your financials AI-powered bookkeeping tools address all of these by automating the mechanical parts of accounting: data entry, categorization, reconciliation, and report generation. The judgment calls — strategy, tax planning, entity structure — still require human expertise.\nThe Four Layers of Bookkeeping Automation A fully automated small business bookkeeping system has four layers, each building on the last:\nData capture — Automatically pulling transaction data from banks, cards, and receipts Categorization — AI assigns transactions to the correct expense categories Reconciliation — AI matches transactions across accounts and flags discrepancies Reporting — AI generates financial statements, summaries, and tax prep documents Let us walk through each layer with specific tools.\nLayer 1: Automated Data Capture Bank and Credit Card Sync The foundation of bookkeeping automation is connecting your bank accounts and credit cards to your accounting software. Every modern accounting tool does this via Plaid or direct bank integrations:\nQuickBooks Online — Connects to 10,000+ financial institutions; transactions import automatically, typically within 24 hours FreshBooks — Strong bank sync; excellent for service-based businesses Wave — Free; solid bank sync for businesses with simpler finances Xero — Particularly strong for businesses with international transactions Setup rule: Use a dedicated business bank account and business credit card exclusively for business expenses. Mixing personal and business transactions is the single biggest obstacle to bookkeeping automation — the AI cannot categorize what it cannot distinguish.\nReceipt Capture AI The other side of data capture is inbound receipts — paper receipts, email receipts, vendor invoices. AI OCR (optical character recognition) tools extract data from these automatically:\nDext (formerly Receipt Bank) The most powerful receipt capture tool in 2026. How it works:\nPhoto a paper receipt with your phone → Dext AI extracts vendor, date, amount, and tax in seconds Forward an email receipt to your Dext inbox → same extraction Connect your email inbox and Dext monitors it for receipts automatically All data flows directly into QuickBooks or Xero Accuracy rate for receipt data extraction is now 95%+ for standard receipts, dropping to ~85% for handwritten or complex receipts (which still require a quick human review).\nPricing: $25-$50/month depending on volume\nHubdoc Free with QuickBooks or Xero subscription. Similar functionality to Dext but slightly less powerful for high-volume operations. Excellent for businesses handling under 100 receipts/month.\nAutoEntry Strong for businesses that receive many supplier invoices (retail, wholesale, manufacturing). Handles multi-page invoices well.\nMileage Tracking For businesses where mileage is deductible (sales calls, client visits, deliveries):\nMileIQ and Everlance use your phone\u0026rsquo;s GPS to automatically detect and log drives. At day\u0026rsquo;s end, you swipe right (business) or left (personal) on each trip. The IRS standard mileage rate in 2026 is significant — logging this accurately can mean thousands of dollars in deductions.\nBoth tools export mileage logs directly to QuickBooks.\nLayer 2: AI-Powered Expense Categorization This is where AI earns its keep. The tedious core of bookkeeping is assigning each transaction to the correct account: Office Supplies, Travel, Professional Services, etc. AI categorization engines handle this automatically by:\nLearning from past behavior — If you always categorize transactions from Adobe as \u0026ldquo;Software \u0026amp; Subscriptions,\u0026rdquo; the AI learns and does it automatically next time. Vendor intelligence — Most tools have a database of common vendors pre-mapped to standard categories. Starbucks → Meals \u0026amp; Entertainment; Amazon → Office Supplies (with possible sub-categorization). Pattern recognition — Unusual transactions that do not match patterns get flagged for human review rather than auto-categorized incorrectly. QuickBooks Online AI Categorization QuickBooks\u0026rsquo; AI categorization (\u0026ldquo;bank rules\u0026rdquo; + machine learning) is genuinely excellent for established businesses. After 3-6 months of use, most businesses see 85-92% of transactions auto-categorized correctly, leaving only 8-15% requiring manual review.\nSetup tips for better AI categorization:\nCreate specific bank rules for your most common vendors upfront (do not wait for the AI to learn them) Review and correct misclassifications consistently — each correction teaches the AI Split transactions when a single charge covers multiple categories (e.g., an Amazon order with both office supplies and personal items) Xero AI Categorization Xero\u0026rsquo;s \u0026ldquo;suggested\u0026rdquo; categorization is similarly strong, with the added benefit of multi-currency handling that matters for businesses with international vendors or clients.\nThe Chart of Accounts Setup AI categorization is only as good as your chart of accounts. If your categories are too broad (\u0026ldquo;General Expenses\u0026rdquo;) or poorly named, the AI has nowhere useful to put transactions.\nStandard chart of accounts for a service small business:\nIncome:\nService Revenue Product Revenue (if applicable) Other Income Operating Expenses:\nAdvertising \u0026amp; Marketing Bank Charges \u0026amp; Merchant Fees Contract Labor / Subcontractors Dues \u0026amp; Subscriptions Equipment \u0026amp; Technology Insurance Meals \u0026amp; Entertainment (50% deductible in US) Office Supplies Professional Development Professional Services (accounting, legal) Rent \u0026amp; Utilities (or Home Office) Software \u0026amp; SaaS Travel Vehicle / Mileage Set this up correctly from day one. Reorganizing categories mid-year is painful and confusing for the AI.\nLayer 3: AI-Powered Bank Reconciliation Bank reconciliation — matching transactions in your books to transactions in your bank statement — is the monthly task that catches errors, identifies missing transactions, and ensures your books reflect reality. AI tools have made this nearly automatic.\nHow AI Reconciliation Works in QuickBooks / Xero Import bank statement (or use live bank feed) AI automatically matches imported bank transactions to entries already in your books Matched transactions are marked as reconciled Unmatched items are flagged for review You work through the flagged items (typically 5-15% of transactions in a well-maintained system) Reconciliation complete A reconciliation that used to take 2-4 hours per month now takes 20-40 minutes. The key is running it monthly — the more often you reconcile, the fewer unmatched items accumulate.\nCommon AI Reconciliation Flags to Watch For Duplicate transactions — Bank feed and manual entry both recorded the same transaction Amount mismatches — You recorded $47.50 but the bank shows $47.05 (common with tip entries) Timing differences — A check issued in April that cleared in May Missing transactions — Cash expenses or international transactions that did not come through the bank feed The AI flags all of these; you make the judgment call.\nLayer 4: AI-Powered Reporting and Insights This layer is the one that moves bookkeeping from record-keeping to business intelligence.\nAutomated Financial Reports Set your accounting software to automatically generate and email reports on a schedule:\nProfit \u0026amp; Loss: Monthly, first business day of the following month Balance Sheet: Quarterly Accounts Receivable Aging: Weekly (who owes you and for how long) Cash Flow Statement: Monthly Budget vs. Actual: Monthly (if you have set a budget) In QuickBooks Online and Xero, these can be scheduled with 2-3 clicks.\nAI-Generated Business Insights QuickBooks Advanced and Xero\u0026rsquo;s Business Snapshot features now include AI narrative summaries of your financials:\n\u0026ldquo;Your revenue increased 12% month-over-month, driven primarily by service revenue. Your operating expenses grew 8%, led by increased contractor costs. Your net profit margin is 34%, compared to 29% last month.\u0026rdquo; For business owners who find raw financial statements intimidating, these plain-English summaries make the data accessible and actionable.\nCash Flow Forecasting AI Float (Xero partner app) and QuickBooks\u0026rsquo; Cash Flow Planner use AI to forecast your cash position 13-90 days out based on:\nRecurring expenses (payroll, rent, subscriptions) Outstanding invoices and their typical payment patterns Seasonal revenue trends from your historical data Upcoming large expenses you have flagged This forecasting is the difference between a business owner who is always surprised by cash crunches and one who sees them coming 60 days out and takes action.\nAccounts Receivable AI One of the highest-value automations for service businesses: AI-powered accounts receivable management.\nChaser and Melio (and QuickBooks\u0026rsquo; built-in AR features) automate invoice follow-up:\nInvoice sent → automatic email reminder at 3 days before due date Due date passes unpaid → automatic \u0026ldquo;gentle reminder\u0026rdquo; email 7 days past due → more urgent follow-up 14 days past due → escalation alert to you, option to generate a formal demand letter Studies consistently show that automated AR follow-up reduces average days outstanding (DSO) by 20-30%. For a business carrying $30,000 in receivables on average, that means better cash flow without a single phone call you had to make manually.\nAI for Tax Preparation Quarterly Estimated Tax Automation For self-employed business owners and S-corp shareholders paying estimated quarterly taxes:\nQuickBooks Self-Employed calculates your estimated quarterly tax payment automatically based on your current year\u0026rsquo;s profit and deductions. It generates a specific dollar amount to pay via IRS Direct Pay — no complex calculations required.\nTurboTax Business and TaxJar integrate with your accounting software to pull year-end data and pre-populate tax forms.\nSales Tax Automation (For Product Sellers) If you sell physical products or taxable services across state lines, sales tax compliance is a genuine nightmare manually. AI tools:\nTaxJar — Monitors your sales across all platforms, calculates tax liability by jurisdiction, files returns automatically in states where you are registered, and flags when you approach economic nexus thresholds (when you must register in a new state) Avalara — Enterprise-grade equivalent, better for high-volume businesses These tools save small businesses with product sales dozens of hours per year and prevent costly compliance errors.\nThe End-of-Year AI Checklist Use ChatGPT or your accounting software\u0026rsquo;s AI assistant to run through these at year-end:\nAre all bank and credit card accounts reconciled through December 31? Are all outstanding invoices either collected or written off as bad debt? Have you recorded all year-end accruals (services rendered but not yet invoiced)? Are contractor payments tracked for 1099 preparation (US: anyone paid $600+)? Have you captured all receipts from the last month of the year? Is your home office deduction calculated and documented? Have you reviewed the asset register for depreciation updates? Feeding this checklist to ChatGPT with your specific business details generates a customized year-end action list.\nBuilding Your Automated Bookkeeping Stack Here is a recommended setup by business size:\nSolo / Freelance / Side Business (Under $150K Revenue) Tool Function Cost/Month Wave Core accounting + invoicing Free Hubdoc Receipt capture Free with Wave MileIQ Mileage tracking $6 TaxJar Sales tax (if selling products) $19 Total ~$25/month Small Business, 1-5 Employees ($150K-$1M Revenue) Tool Function Cost/Month QuickBooks Online Plus Core accounting $85 Dext Receipt capture $35 Everlance Mileage tracking $12 Float Cash flow forecasting $59 Chaser AR automation $45 Total ~$236/month At $1M revenue, even a 1% cash flow improvement from better visibility and faster collections generates $10,000 — the stack pays for itself many times over.\nGrowing Business, 5-20 Employees ($1M-$5M Revenue) Add:\nXero (often better for multi-entity or international) Bill.com or Melio for accounts payable automation Gusto for payroll (with QuickBooks integration) Avalara for sales tax compliance Dedicated bookkeeper or fractional CFO for oversight and strategy The Human-AI Bookkeeping Partnership Fully automated bookkeeping does not mean no human involvement. The ideal model is:\nAI handles:\nData capture and entry (95%+ of transactions) Initial categorization Bank reconciliation matching Scheduled report generation Routine follow-ups (AR reminders, late payment notices) You (or your bookkeeper) handle:\nReviewing AI categorizations monthly (20-40 minutes) Resolving reconciliation exceptions Making judgment calls on complex categorizations Strategic financial decisions Tax strategy (ideally with a CPA) A bookkeeper who used to spend 15 hours/month on your books now spends 3-5 hours — and those hours go toward strategic review rather than data entry. Your cost goes down; the quality of insight goes up.\nGetting Started: Your 5-Day Implementation Plan Day 1: Open a dedicated business checking account and business credit card if you do not have them. Connect them to QuickBooks Online or Xero.\nDay 2: Set up your chart of accounts using the template above. Create bank rules for your 10 most common vendors.\nDay 3: Install Dext or Hubdoc. Photograph every receipt from the last 30 days and upload them. Forward all email receipts to your Dext inbox.\nDay 4: Run your first bank reconciliation for the current month. Work through the exceptions.\nDay 5: Set up automated reports (P\u0026amp;L, AR Aging) to email to you on the 1st of each month. Set up AR reminder automations for outstanding invoices.\nFor a complete implementation guide with specific settings, prompt templates for using ChatGPT to analyze your financial reports, and a 90-day bookkeeping calendar, see our AI Productivity Playbook available on Payhip . The finance module covers bookkeeping automation in depth alongside our Budget Tracker template for ongoing financial monitoring.\nFinal Thoughts Clean books are not just a tax compliance requirement — they are a strategic asset. Knowing your exact profit margin, your cash flow position, and which client or product line is most profitable gives you the information to make good business decisions. AI bookkeeping automation makes that information available without requiring you to become an accountant.\nThe investment is minimal. The time savings are real. And the peace of mind that comes from never dreading tax season again — that is genuinely priceless.\nIf you\u0026rsquo;re running a small business or freelancing in Japan, freee is worth a close look. It connects to Japanese bank accounts, automates expense categorization, and generates the reports you need for kakuteishinkoku — compressing your annual bookkeeping burden significantly.\nStart with the bank connection today. Build from there.\nFor ready-to-use financial tracking templates and AI prompt libraries for your business finances, visit our Payhip store . Our Budget Tracker and AI Productivity Playbook are designed for small business owners who want cleaner books and smarter financial decisions.\nRelated Tools Estimate self-employment taxes → Side Hustle Tax Calculator Create a monthly business budget → Budget Planner Plan debt payoff for business loans → Debt Payoff Calculator Calculate loan repayment schedules → Loan Repayment Calculator Related Templates Put these techniques into practice with our ready-made templates:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Copy-paste prompts for every situation The AI Productivity Playbook 2026 — 50+ AI workflows and automation strategies You May Also Like Best Accounting Software for Freelancers 2026: Full Compare freee Cloud Accounting Tutorial: The Complete English Guide for Freelancers in Japan (2026) AI Bookkeeping for Small Restaurants: Complete Automation Guide (2026) ","permalink":"https://productivity-works.com/posts/automate-bookkeeping-ai-small-business/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eIf you are running a small business and still spending Sunday evenings manually entering receipts, reconciling bank statements, and trying to remember whether that restaurant meal was a client dinner or a personal expense — this guide is for you. Bookkeeping automation using AI has reached a point in 2026 where a solo operator or small team can genuinely compress what used to be 8-10 hours of monthly bookkeeping into 1-2 hours of review and oversight.\u003c/p\u003e","title":"How to Automate Bookkeeping with AI for Small Business (2026 Guide)"},{"content":"This article contains affiliate links.\nBeginner Investing Guide 2026: Everything You Need to Start The most common investing mistake isn\u0026rsquo;t picking the wrong stock. It\u0026rsquo;s waiting too long to start.\nEvery year you delay investing is a year of compound growth you\u0026rsquo;ll never get back. A 25-year-old who invests $200/month until age 65 at a 7% average annual return ends up with approximately $525,000. A 35-year-old doing the exact same thing ends up with about $243,000 — less than half, despite only a 10-year difference.\nThis guide cuts through the complexity, jargon, and fear surrounding investing. By the end, you\u0026rsquo;ll know exactly what accounts to open, what to buy, and how to get started — even if you have no experience and a modest budget.\nPart 1: The Fundamentals Before You Invest a Dollar Do This First: Financial Prerequisites Before investing, make sure:\n1. You have an emergency fund. Keep 3-6 months of expenses in a high-yield savings account. This prevents you from being forced to sell investments at the worst time.\nSee: How to Build an Emergency Fund Fast 2. You have no high-interest debt. Paying off debt with a 20%+ interest rate (credit cards) is guaranteed to be more profitable than investing, which averages 7-10% annually.\nSee: How to Pay Off Debt Fast 3. You have at least $50/month you can commit consistently. Consistency matters more than the amount. Start with whatever you can sustain.\nWhat Investing Actually Is Investing is buying ownership in things that generate value over time:\nStocks: Ownership in companies Bonds: Loans to companies or governments that pay interest Real estate: Property that generates rent or appreciation Index funds: Baskets of hundreds of stocks that track a market index The goal is to put money to work so it grows without your active effort — this is the basis of building wealth.\nWhy the Stock Market Works Over Time Despite crashes, recessions, and crises, the US stock market (S\u0026amp;P 500) has returned an average of approximately 10% per year over the last century (7% after inflation). This isn\u0026rsquo;t a coincidence — it reflects the long-term growth of the global economy and human productivity.\nThe key word is time. Short-term, the market is unpredictable. Long-term, it trends upward. This is why time in the market beats timing the market.\nPart 2: Where to Put Your Money (Account Types) The account you invest in matters as much as what you invest in — because taxes can dramatically reduce your returns.\nEmployer 401(k) — Start Here if Available If your employer offers a 401(k) with a matching contribution, this is always your first stop:\nContributions: Pre-tax (traditional) or post-tax (Roth) — reduces your taxable income 2026 contribution limit: $23,500 ($31,000 if over 50) Employer match: Free money — contribute at least enough to get the full match (typically 3-6% of salary) Example: If your employer matches 100% up to 3% of your salary, and you earn $60,000 — contributing 3% ($1,800/year) gets you a free $1,800 match. That\u0026rsquo;s an instant 100% return.\nRoth IRA — The Best Account for Most Beginners The Roth IRA is many financial experts\u0026rsquo; favorite account for young or beginner investors:\nContributions: Post-tax dollars (you pay tax now, not in retirement) Growth: Tax-free — all gains are yours, no tax on withdrawal in retirement 2026 contribution limit: $7,000 ($8,000 if over 50) Income limit: Phases out above $150,000 single / $236,000 married (2026) Flexibility: You can withdraw your contributions (not earnings) any time without penalty Why Roth beats Traditional for most beginners: If you\u0026rsquo;re in a low-to-medium tax bracket now and expect to be in a higher bracket in retirement, paying tax now on contributions is better than paying tax later on a much larger account.\nTraditional IRA — Alternative to Roth Contributions: Pre-tax (deductible for most income levels) Growth: Tax-deferred — you pay tax when you withdraw in retirement 2026 limit: Same $7,000/$8,000 as Roth Best for: People in a high tax bracket now who expect to be in a lower bracket in retirement Taxable Brokerage Account — No Limits Once you\u0026rsquo;ve maximized your 401(k) match and Roth IRA, open a taxable brokerage account for additional investing:\nNo contribution limits No tax advantages, but full flexibility Capital gains taxed at 0-20% (lower than ordinary income for long-term investments) Where to Open These Accounts in 2026 Broker Best For Account Minimum Notable Feature Fidelity Most beginners $0 No fees, excellent research Vanguard Long-term index investors $0 Lowest expense ratios Charles Schwab Full-service investors $0 Great customer service Betterment Hands-off automation $0 Robo-advisor, auto-rebalance Wealthfront Automation + tax-loss harvesting $500 Best automated platform SoFi Invest Beginners wanting simplicity $1 Fractional shares, simple app Our recommendation for complete beginners: Fidelity or Schwab for self-directed, Betterment or Wealthfront for fully automated.\nPart 3: What to Buy (Investment Options) The Beginner\u0026rsquo;s Best Friend: Index Funds and ETFs Index funds and ETFs (Exchange-Traded Funds) are the foundation of almost every expert\u0026rsquo;s beginner recommendation — including Warren Buffett\u0026rsquo;s advice to non-professional investors.\nWhat they are: Funds that own hundreds or thousands of stocks simultaneously, tracking a market index like the S\u0026amp;P 500.\nWhy they\u0026rsquo;re ideal for beginners:\nInstant diversification (own 500+ companies with one purchase) Low fees (expense ratios as low as 0.03%) No expertise required (the index does the work) Match or beat most professional investors over 10+ years The most recommended index funds for beginners:\nFund Ticker What It Owns Expense Ratio Fidelity ZERO Total Market FZROX All US stocks 0.00% Vanguard Total Stock Market VTI All US stocks 0.03% Vanguard S\u0026amp;P 500 VOO 500 largest US companies 0.03% Vanguard Total World Stock VT Global stocks (US + international) 0.07% iShares Core S\u0026amp;P 500 IVV 500 largest US companies 0.03% Fidelity 500 Index FXAIX S\u0026amp;P 500 (mutual fund) 0.015% For most beginners, owning one or two of these funds is all you need.\nThe Simple Three-Fund Portfolio Many financial experts recommend the \u0026ldquo;three-fund portfolio\u0026rdquo; — a simple allocation that covers the entire global market:\nUS Total Stock Market Index Fund (~60%) International Stock Market Index Fund (~30%) US Bond Market Index Fund (~10%) Adjust the stock/bond ratio based on your time horizon: more stocks if you\u0026rsquo;re young and have decades to invest, more bonds as you near retirement.\nTarget Date Funds: The Truly Hands-Off Option Target date funds automatically adjust their allocation as you approach retirement:\nVanguard Target Retirement 2050 Fund (VFIFX) — designed for someone retiring around 2050 Starts aggressive (mostly stocks), gradually shifts to conservative (more bonds) automatically For someone who wants to set it and forget it, a target date fund is one of the best single investments available.\nBonds: The Stability Component Bonds reduce volatility in your portfolio. When stocks fall, bonds often hold steady or rise. Common bond index funds:\nVBTLX (Vanguard Total Bond Market) — broad US bond market BND (ETF equivalent) For investors under 40 with a long time horizon, bonds are often 0-20% of a portfolio.\nWhat to Avoid as a Beginner Individual stocks — exciting but risky without expertise Cryptocurrency — highly speculative, treat as \u0026lt;5% of portfolio if at all Actively managed funds — higher fees, most underperform index funds Leveraged ETFs — complex instruments, lose value over time in sideways markets Annuities — complex, high-fee products rarely suitable for beginners Any \u0026ldquo;hot tip\u0026rdquo; — the people promoting these rarely benefit you Part 4: How Much to Invest and How Often The Automation Principle The most reliable way to build wealth is to automate your investing so it happens before you can spend the money.\nSet up automatic transfers on payday:\nPaycheck arrives → automatic 401(k) contribution (handled by employer) Remaining paycheck → automatic transfer to Roth IRA From Roth IRA → automatic purchase of index fund This \u0026ldquo;pay yourself first\u0026rdquo; strategy removes willpower from the equation.\nStarting Points at Different Budgets Monthly Budget Strategy What to Buy $50-100 Roth IRA, fractional shares 1 broad index ETF (VTI or FXAIX) $100-500 Max Roth IRA over time 2-fund portfolio (US + International) $500-1,000 Roth IRA + taxable account 3-fund portfolio $1,000+ Maximize tax-advantaged accounts first 3-fund + automatic rebalancing Dollar Cost Averaging: Your Protection Against Bad Timing Dollar cost averaging (DCA) means investing a fixed amount on a regular schedule regardless of market conditions.\nExample: Investing $300/month every month, whether the market is up or down.\nWhen prices are high, your $300 buys fewer shares When prices are low, your $300 buys more shares This automatically makes you buy more when the market is \u0026ldquo;on sale\u0026rdquo; and less when it\u0026rsquo;s expensive — without requiring any judgment or timing.\nThe right time to invest: Now, and every month going forward.\nPart 5: Managing Your Investments How Often to Check Your Portfolio Recommended frequency: Once per quarter (3 months)\nThe biggest mistake beginners make is checking their portfolio daily and reacting emotionally to market swings. Short-term volatility is normal and irrelevant to long-term investors.\nA 30% market drop is scary. But if you\u0026rsquo;re 30 years from retirement, you\u0026rsquo;re not selling — you\u0026rsquo;re buying more at a discount.\nRebalancing: Keeping Your Allocation on Track Over time, a strong stock market will shift your portfolio allocation. If you started at 80% stocks / 20% bonds and stocks grow faster, you might end up at 90% stocks / 10% bonds.\nRebalancing means selling what\u0026rsquo;s overweight and buying what\u0026rsquo;s underweight to return to your target allocation.\nHow often: Once per year, or when any asset class drifts more than 5-10% from target.\nMost robo-advisors (Betterment, Wealthfront) rebalance automatically.\nTax Considerations 401(k) and IRA: Don\u0026rsquo;t worry about taxes on transactions — they\u0026rsquo;re handled at withdrawal Taxable accounts: Hold investments over 1 year for lower long-term capital gains rates (0-20% vs ordinary income rates) Tax-loss harvesting: Sell losing investments to offset capital gains — done automatically by robo-advisors Part 6: Common Beginner Questions How much do I need to start investing? Literally $1 with fractional shares at Fidelity, Schwab, or SoFi. The actual minimum to invest meaningfully: $50-100/month consistently.\nWhat if the market crashes right after I start? Stay the course. Every market crash in history has eventually recovered and reached new highs. The 2008 crash, 2020 COVID crash, and every other major downturn look like small blips on a long-term chart.\nShould I pay off my mortgage before investing? Not necessarily. If your mortgage rate is below the expected market return (~7-10%), mathematically you\u0026rsquo;re better off investing. But there\u0026rsquo;s psychological value in being debt-free — factor in both.\nWhat\u0026rsquo;s the difference between a stock and an ETF? A stock is ownership in a single company. An ETF owns hundreds or thousands of stocks in one package. For beginners, ETFs provide diversification that a single stock cannot.\nHow do I know when to sell? Most beginner investors should sell only when they need the money in retirement. Not when the market drops. Not when someone says a crash is coming. Staying invested through volatility is how long-term wealth is built.\nDo I need a financial advisor? For straightforward situations (young, investing in index funds, building toward retirement), a human advisor is optional. For complex situations (inheritance, business ownership, estate planning), a fee-only fiduciary advisor is worthwhile.\nPart 7: The Investing Timeline — What to Expect Year 1: Portfolio is small. Growth feels meaningless. This is normal. You\u0026rsquo;re building the habit and letting compound interest begin its work.\nYears 3-5: The habit is established. Contributions start to accumulate noticeably. You\u0026rsquo;ve experienced market volatility and stayed invested.\nYears 10-15: Compound growth becomes visible and exciting. Your investment returns may equal or exceed your annual contributions.\nYears 20-30: The compounding effect is dramatic. Investment returns may dwarf annual contributions. This is why starting early is so powerful.\nYour First Investment: A Step-by-Step Checklist Open an account (Fidelity, Schwab, or Betterment recommended) Fund your account with an initial deposit ($50 minimum) Set up automatic monthly contributions Purchase a broad index ETF (VTI, VOO, or FXAIX) Set a calendar reminder to review quarterly Read one book: The Little Book of Common Sense Investing by John Bogle Start Building Your Financial Future Based in Japan? Rakuten Securities is one of the most accessible brokerages for residents here — it supports NISA accounts, offers a broad range of index funds and ETFs, and has an English-friendly interface that makes getting started straightforward even if you\u0026rsquo;re new to Japanese financial services.\nThe best investing plan is the one you actually follow — simple, automated, and consistent.\nOur Personal Finance Starter Pack includes an investment tracker spreadsheet, budget templates, and a step-by-step account setup guide for all major brokerages.\nRelated Tools Calculate loan payments and payoff timeline → Loan Calculator Calculate percentages, discounts, and tips instantly → Percentage Calculator See how your investments grow → Compound Interest Calculator Estimate dividend income → Dividend Income Calculator Plan your retirement → Retirement Savings Calculator Track your total net worth → Net Worth Calculator Plan your path to financial independence → FIRE Calculator See how inflation affects your money → Inflation Calculator Related Reading:\nHow to Start Investing with $100 Best Index Funds for Beginners 2026 Roth IRA vs Traditional IRA: Which Is Better? Best Budgeting Apps 2026 ","permalink":"https://productivity-works.com/posts/beginner-investing-guide-2026/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"beginner-investing-guide-2026-everything-you-need-to-start\"\u003eBeginner Investing Guide 2026: Everything You Need to Start\u003c/h2\u003e\n\u003cp\u003eThe most common investing mistake isn\u0026rsquo;t picking the wrong stock. It\u0026rsquo;s waiting too long to start.\u003c/p\u003e\n\u003cp\u003eEvery year you delay investing is a year of compound growth you\u0026rsquo;ll never get back. A 25-year-old who invests $200/month until age 65 at a 7% average annual return ends up with approximately $525,000. A 35-year-old doing the exact same thing ends up with about $243,000 — less than half, despite only a 10-year difference.\u003c/p\u003e","title":"Beginner Investing Guide 2026: How to Start Building Wealth Today"},{"content":"Best AI Image Generators Free 2026 — Complete Guide Whether you\u0026rsquo;re designing a blog thumbnail, creating social media graphics, visualizing a product idea, or building marketing assets without a designer on call, AI image generators have become an essential tool. In 2026, the quality gap between AI-generated images and professional design has nearly closed — and the best tools are accessible to anyone, often for free.\nBut not all AI image generators are created equal. Some excel at photorealistic images, others at illustration or concept art. Some are free with no daily limits, others free in name only. And image prompting has its own learning curve — the same prompt that creates a stunning image on one tool can produce a confusing mess on another.\nHere\u0026rsquo;s what you\u0026rsquo;ll learn:\nThe best free AI image generators available in 2026 and what each is best for A side-by-side comparison of quality, speed, features, and true cost Real prompt examples you can copy and test immediately How to write better image prompts for any tool The top use cases for each tool so you pick the right one for your needs Why Free AI Image Generators Matter in 2026 The Productivity Gap A custom illustration used to cost $50–$500 and take days to commission. A professional photo shoot for product imagery? $500–$5,000 minimum. For startups, freelancers, content creators, and small businesses, that budget simply doesn\u0026rsquo;t exist for every piece of content. The result was mediocre stock photos and inconsistent branding.\nAI image generators have collapsed that barrier. You can now generate a brand-consistent hero image, a product mock-up, a blog thumbnail, or a custom illustration in under 60 seconds — often for free or for a fraction of the previous cost.\nHow AI Changes the Game What changed in 2026 isn\u0026rsquo;t just quality — it\u0026rsquo;s accessibility. Text-to-image models have become embedded in tools professionals already use: Microsoft Designer in Office, Adobe Firefly in Creative Cloud, Canva\u0026rsquo;s Magic Studio, and Google Slides\u0026rsquo; AI features. The barrier to using AI image generation is essentially zero if you have any of these subscriptions.\nFor those who want maximum creative control, dedicated tools like Midjourney and Stable Diffusion remain the gold standard for quality and customization.\nBest AI Image Generators 2026: Full Comparison Tool Free Tier Quality Speed Best For Paid Plan Ideogram 2.0 Yes — generous free tier Excellent (text in images) Fast Typography, logos, posters $8/month DALL-E 3 (via ChatGPT Free) Limited (15 images/day) Excellent Medium Realistic \u0026amp; creative images ChatGPT Plus $20/mo Adobe Firefly Yes (25 credits/month free) Professional quality Fast Commercial-safe images Creative Cloud $35+/mo Stable Diffusion (local) Completely free (runs locally) Excellent with tuning Varies Technical users, full control Free (hardware cost) Canva AI (Magic Media) Yes (limited) Good Fast Social media, presentations Canva Pro $13/mo Microsoft Designer / Copilot Image Creator Yes — unlimited via Bing Very good Fast Quick content creation M365 subscription Midjourney No free tier Best-in-class artistic Medium Art, concept, brand visuals $10–$60/month Playground AI Yes — 100 images/day Very good Fast Versatile general use $15/month Leonardo AI Yes — 150 credits/day Excellent Fast Game assets, characters, art $12/month Try It Free Quick verdict:\nBest fully free option: Ideogram 2.0 or Playground AI (most generous free tiers, excellent quality) Best for commercial use: Adobe Firefly (trained on licensed images, commercially safe) Best overall quality: Midjourney (but no free tier) Most versatile free tool: Microsoft Copilot Image Creator (unlimited, via Bing) Best for technical control: Stable Diffusion (free if you run it locally) Step-by-Step Guide: How to Get Great AI Images Step 1: Understand What Makes a Good Image Prompt AI image prompts have a different structure from text prompts. The best image prompts describe:\nSubject — What is in the image? Style — What does it look like visually? Lighting — How is it lit? Composition — How is it framed? Mood / atmosphere — What feeling does it convey? Technical details — Camera, lens, resolution, color palette Basic prompt (mediocre result):\na woman working on a laptop Strong prompt (excellent result):\nA focused young professional woman in her late 20s working on a laptop at a minimalist wooden desk, natural window light from the left, shallow depth of field, warm color palette of cream and beige, editorial photography style, shot on Sony A7R IV with 85mm f/1.4 lens, 8K resolution Step 2: Use Style Keywords That Work These style keywords consistently produce high-quality results across most tools:\nPhotography styles:\neditorial photography | cinematic | documentary | golden hour lighting | studio lighting | film grain | Kodachrome | high contrast | bokeh Illustration/Art styles:\nflat design illustration | isometric 3D | watercolor | concept art | pixel art | Studio Ghibli style | geometric minimalist | infographic style | vector illustration | oil painting | digital art Quality boosters (add to any prompt):\nhighly detailed | 8K resolution | photorealistic | masterpiece | sharp focus | professional | award-winning photography Step 3: Prompts for Common Use Cases Blog/Article Hero Image:\nMinimalist tech blog header image: abstract representation of artificial intelligence and human creativity merging, blue and white gradient color scheme, geometric shapes, clean and professional, wide aspect ratio 16:9, flat design illustration style, no text Product Mock-Up:\nPremium product photography: a sleek matte black coffee mug with a simple white logo on a white marble surface, soft studio lighting from above, clean shadow, isolated product shot, e-commerce style, ultra high resolution Social Media Post Background:\nVibrant abstract background for social media post: flowing gradient of coral and teal with soft light bokeh effects, modern and eye-catching, Instagram square format 1:1, no text, suitable for overlay text LinkedIn Profile Banner:\nProfessional LinkedIn banner: modern city skyline at dusk, dark blue and gold color palette, subtle geometric pattern overlay, horizontal banner format, premium corporate aesthetic, clean and sophisticated Presentation Slide Visual:\nData visualization concept illustration: interconnected glowing network nodes on dark background, blue and purple neon colors, abstract technology aesthetic, suitable as a presentation slide background, wide 16:9 format Logo Concept (try on Ideogram):\nMinimalist logo design for a tech startup called \u0026#34;NovaMind\u0026#34; — combines a stylized brain icon with a geometric neural network pattern, clean lines, deep blue and electric blue gradient, professional and modern, on white background Step 4: Iterate with Negative Prompts (Stable Diffusion / Advanced Tools) Negative prompts tell the AI what NOT to include. For tools that support them:\nPositive: professional business portrait, natural lighting, high detail, 4K Negative: blurry, ugly, distorted, extra fingers, watermark, text, low quality, cartoon, anime, painting, unrealistic Step 5: Upscale and Refine Most AI tools generate at 512×512 or 1024×1024 pixels. For web or print use:\nUse built-in upscalers (Midjourney\u0026rsquo;s \u0026ldquo;Upscale\u0026rdquo;, Leonardo\u0026rsquo;s upscale feature) Use free tools like Upscayl (open source, runs locally) to 4x resolution Adobe Firefly\u0026rsquo;s \u0026ldquo;Generative Fill\u0026rdquo; extends or modifies existing images Pro Tips \u0026amp; Advanced Techniques Common Mistakes to Avoid Mistake 1: Too short prompts. \u0026ldquo;A robot\u0026rdquo; produces forgettable generic art. Add subject details, lighting, style, mood, and technical specs. Length is your friend.\nMistake 2: Inconsistent style keywords. Mixing \u0026ldquo;photorealistic\u0026rdquo; with \u0026ldquo;watercolor illustration\u0026rdquo; confuses the model. Pick one coherent style direction.\nMistake 3: Ignoring aspect ratio. Most tools default to square. Always specify aspect ratio for your use case: 16:9 for presentations, 9:16 for mobile/reels, 1:1 for Instagram.\nMistake 4: Using copyrighted style references. Avoid prompting for \u0026ldquo;in the style of [living artist]\u0026rdquo; — it\u0026rsquo;s ethically murky and some platforms prohibit it. Use genre/movement terms instead: \u0026ldquo;impressionist style\u0026rdquo; instead of \u0026ldquo;like [specific artist].\u0026rdquo;\nMistake 5: Not trying multiple generations. AI images are probabilistic. Run the same prompt 4–8 times and pick the best. Almost all tools let you do this.\nPower User Strategies Strategy 1: Build style reference libraries. When you get an image you love, save the prompt. Over time you\u0026rsquo;ll have a library of proven prompt formulas for your brand\u0026rsquo;s visual style.\nStrategy 2: Use Canva AI + ChatGPT together. Use ChatGPT to write the image prompt, then run it in Canva\u0026rsquo;s AI image generator or DALL-E 3. ChatGPT is excellent at crafting image prompts for other tools.\nStrategy 3: Create consistent character/brand assets. For social media, use tools like Leonardo AI\u0026rsquo;s consistent character feature to generate your mascot or brand character in multiple poses and scenes.\nStrategy 4: Use AI images as starting points, not final products. Generate a base image, import it into Canva or Adobe, overlay text and branding, and you have professional-looking content in 5 minutes.\nStrategy 5: Generate variations for A/B testing. Create 4–6 versions of a thumbnail or ad creative with slight prompt variations and A/B test them. AI makes variation generation free and fast.\nRelated: Best AI Tools for Writing Emails Professionally Frequently Asked Questions Q: Are AI-generated images copyright-free? A: This is legally complex and jurisdiction-dependent. In the US, AI-generated images without significant human creative input are generally not copyrightable. However, the images you generate are typically yours to use commercially under most platforms\u0026rsquo; terms. Check each tool\u0026rsquo;s commercial use policy. Adobe Firefly is specifically trained on licensed images and is explicitly safe for commercial use.\nQ: Which free AI image generator has the best quality? A: In 2026, Ideogram 2.0 and Playground AI offer the best free-tier quality. For artistic images, Midjourney has no free tier but is widely considered the best overall. For quick, no-commitment use, Microsoft\u0026rsquo;s Copilot Image Creator (powered by DALL-E) is unlimited and very good.\nQ: Can AI image generators create text in images? A: Yes — Ideogram 2.0 is the best tool specifically for generating images with readable text (logos, posters, signs). Other tools (DALL-E 3, Midjourney) have improved but still occasionally misspell or distort text.\nQ: Are AI-generated images detectable? A: Yes, with specialized detection tools. Watermarks (visible and invisible) are increasingly common. For critical use cases (journalism, academic work), always disclose AI image use.\nQ: What\u0026rsquo;s the difference between free and paid tiers? A: Free tiers typically limit: number of images per day/month, resolution, commercial use rights, generation speed, and access to premium models. Paid tiers lift most or all of these restrictions.\nQ: Can I use AI images in my business without legal risk? A: For most standard business uses (blog graphics, social media, internal presentations), yes. For product packaging, advertising, or publishing, use tools explicitly designed for commercial use (Adobe Firefly, Getty AI) or consult legal counsel.\nQ: How do I make images look more consistent across a brand? A: Create a \u0026ldquo;style guide prompt\u0026rdquo; — a fixed string of style keywords you append to every image prompt. E.g., \u0026ldquo;flat design illustration, warm terracotta and navy palette, minimal, clean lines.\u0026rdquo; Append this to every generation for consistent visual branding.\nCreative AI Skills Are in High Demand Businesses are hiring people who can leverage AI image tools for marketing, design, and content creation. Find your next career on doda — explore design and marketing roles on Japan\u0026rsquo;s top job platform.\nConclusion \u0026amp; Call to Action The era of paying hundreds of dollars for basic business images is over. With the free tiers available in 2026, you can generate professional-quality blog graphics, social media visuals, product mock-ups, and presentation assets in under a minute — for free.\nKey takeaways:\nIdeogram 2.0, Playground AI, and Microsoft Copilot Image Creator offer the best free tiers Longer, more specific prompts consistently produce better results Always specify style, lighting, composition, and aspect ratio Adobe Firefly is the safest choice for commercial use Iterate — run 4–8 variations and pick the best rather than accepting the first result Want the complete prompt guide for AI image generation — including 50+ tested prompts for blog thumbnails, social media, product photography, logos, and presentations? It\u0026rsquo;s included in our Complete ChatGPT Prompt Collection , which has a dedicated image generation section.\nFor building a complete AI-powered content creation workflow (writing + images + publishing), our AI Productivity Playbook walks you through the whole system.\nRelated: AI Tools for Freelancers to Earn More Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nRelated Tools Calculate your ideal freelance rate → Freelance Rate Calculator Create a monthly budget → Budget Planner Related Templates Put these techniques into practice with our ready-made templates:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Copy-paste prompts for every situation The AI Productivity Playbook 2026 — 50+ AI workflows and automation strategies ","permalink":"https://productivity-works.com/posts/best-ai-image-generators-free-2026/","summary":"\u003ch1 id=\"best-ai-image-generators-free-2026--complete-guide\"\u003eBest AI Image Generators Free 2026 — Complete Guide\u003c/h1\u003e\n\u003cp\u003eWhether you\u0026rsquo;re designing a blog thumbnail, creating social media graphics, visualizing a product idea, or building marketing assets without a designer on call, AI image generators have become an essential tool. In 2026, the quality gap between AI-generated images and professional design has nearly closed — and the best tools are accessible to anyone, often for free.\u003c/p\u003e\n\u003cp\u003eBut not all AI image generators are created equal. Some excel at photorealistic images, others at illustration or concept art. Some are free with no daily limits, others free in name only. And image prompting has its own learning curve — the same prompt that creates a stunning image on one tool can produce a confusing mess on another.\u003c/p\u003e","title":"Best AI Image Generators Free 2026"},{"content":"Best AI Tools for Writing Emails Professionally — Complete Guide [2026] The average professional spends 28% of their workweek on email. That\u0026rsquo;s more than 11 hours every week — writing, reading, rewording, and wondering whether that last sentence sounds too passive-aggressive. Email is both unavoidable and brutally time-consuming. And for non-native English speakers or anyone who agonizes over tone, it can be genuinely stressful.\nAI tools have quietly become the most useful thing that ever happened to professional email. They draft for you, match your tone, adjust formality on command, handle difficult situations diplomatically, and write follow-ups without you having to think. The result: inbox zero feels achievable for the first time.\nHere\u0026rsquo;s what you\u0026rsquo;ll learn:\nThe best AI tools for email writing in 2026, compared head to head Copy-paste prompt templates for 12 common email situations How to set up AI so it writes emails in your voice, not a generic tone When to use AI email tools versus writing it yourself Pro strategies for handling the hardest email scenarios (rejection, conflict, late payments) Let\u0026rsquo;s get you out of email jail.\nWhy AI Email Writing Matters in 2026 The Productivity Gap Email is the great time thief of the modern workplace. It\u0026rsquo;s not just the volume — it\u0026rsquo;s the cognitive overhead. Every email requires a micro-decision: what tone, how much detail, how to phrase that awkward thing diplomatically. Multiply that by 50 emails a day and you\u0026rsquo;ve spent as much mental energy on inbox management as on actual strategic thinking.\nProfessionals who\u0026rsquo;ve integrated AI into their email workflow report writing emails 70% faster on average. More importantly, they report less anxiety around difficult emails — the AI handles the diplomatic heavy lifting and they review and send.\nHow AI Changes the Game AI doesn\u0026rsquo;t just auto-complete your sentences. Modern AI email tools understand context, adjust formality, match your existing communication style, handle edge cases, and generate multiple drafts in seconds. The shift from \u0026ldquo;writing\u0026rdquo; to \u0026ldquo;editing\u0026rdquo; is enormous. It\u0026rsquo;s far easier to review and tweak a solid draft than to start from a blank field.\nFor non-native English speakers, the value is even greater — AI eliminates the worry about grammar, phrasing, and cultural nuance, letting you communicate with the same polish as a native speaker.\nBest AI Email Writing Tools Compared Tool Integrated? Email Style Matching Cost Best For Microsoft Copilot (Outlook) Yes — native in Outlook Excellent (learns your style) M365 subscription ($6+/mo) Outlook users, enterprises Google Gemini (Gmail) Yes — native in Gmail Very good Google Workspace ($6+/mo) Gmail/Workspace users ChatGPT Plus Browser + browser extension Excellent with good prompts $20/month Versatile; any email client Claude Pro Browser Excellent for nuanced tone $20/month Difficult, sensitive emails Superhuman AI Native email client with AI Very good $30/month High-volume email pros Grammarly Go Browser extension Good $12/month (Pro) Grammar + tone suggestions Lavender Sales email focus Excellent for sales $27/month Sales reps, SDRs Try It Free Bottom line: If you\u0026rsquo;re in Outlook, Microsoft Copilot is the most seamless choice. Gmail users should try Gemini first. For the most versatile, highest-quality drafts — especially for complex or sensitive emails — ChatGPT Plus or Claude Pro with good prompts consistently outperform embedded tools.\nStep-by-Step Guide: How to Write Professional Emails with AI Step 1: Set Up Your Email Persona Give AI a persistent description of your communication style so every email sounds like you, not like a generic AI.\nYou are my email writing assistant. Here is my communication profile: - Name: [YOUR NAME] - Role: [YOUR JOB TITLE] at [COMPANY NAME] - Industry: [YOUR INDUSTRY] - Typical email audience: [CLIENTS / COLLEAGUES / EXECUTIVES / VENDORS] - My natural tone: [professional but warm / direct and concise / formal] - I dislike: corporate jargon, passive voice, overly long emails - I always: start with the key point, end with a clear call to action Use this profile for all email drafts unless I specify otherwise. Step 2: Use the Right Prompt Structure For each email, tell AI: (1) the situation, (2) the recipient and relationship, (3) the goal, (4) any specific constraints.\nFollow-Up Email Prompt:\nWrite a professional follow-up email with these details: - Recipient: Sarah Chen, Procurement Manager at TechCorp - Context: I sent a proposal 10 days ago for a $15,000 software implementation. She hasn\u0026#39;t responded. - Goal: Politely nudge for a response without seeming desperate - Tone: Warm and professional - Length: Under 100 words - End with: a specific call to action asking for a 15-min call this week Cold Outreach Email Prompt:\nWrite a cold outreach email: - Recipient: A marketing director at a mid-size e-commerce company - My offer: I\u0026#39;m a freelance conversion rate optimizer who helps e-commerce stores increase checkout conversion by 15–30% - I have: a relevant case study (a client who went from 2.1% to 3.4% conversion) - Tone: Confident, not salesy. Like a peer, not a vendor. - Length: Under 120 words - Include a PS line with the case study hook Difficult Client Email Prompt:\nWrite an email delivering bad news to a client: - Situation: A project deadline is going to slip by 2 weeks due to unexpected technical issues on our end - Client: Has been with us 3 years, generally understanding but values deadlines - Goal: Apologize sincerely, give a clear new timeline, explain why, rebuild trust - Tone: Accountable and professional, not defensive - Length: 150–200 words - Do NOT use \u0026#34;we apologize for any inconvenience\u0026#34; Salary Negotiation Email Prompt:\nWrite an email negotiating a salary offer: - Situation: I received a job offer for $72,000 but my target is $82,000 based on market research and my 6 years of experience - Tone: Confident but collaborative — I want the job, I\u0026#39;m not ultimatum-ing - Include: Expression of genuine excitement about the role + market data reference + clear counter-offer - Length: 150–200 words Payment Follow-Up Email Prompt:\nWrite a firm but professional payment follow-up email: - Context: Invoice #1047 for $3,200 is 45 days overdue - Client: Small business owner I\u0026#39;ve worked with twice before - Previous: Sent a gentle reminder 15 days ago, no response - Tone: Firm, professional, not aggressive — but make the urgency clear - Include: Invoice number, amount, original due date, and a deadline to pay by - Length: Under 150 words Step 3: Review, Personalize, and Send AI drafts are starting points, not final products. Before sending, check:\nDoes it sound like you? Add your natural phrases. Is the name/detail correct? AI may hallucinate names from previous context. Is the tone calibrated for this specific relationship? Is the call to action specific and actionable? Five minutes of review beats one hour of writing from scratch.\nStep 4: Build a Template Library Once AI writes a great email for a recurring situation, save it as a template with placeholders. Here\u0026rsquo;s a format that works:\nSubject: Following up on [PROJECT NAME] proposal Hi [FIRST NAME], I hope your [week/month/quarter] is going well. I wanted to follow up on the proposal I sent on [DATE] for [PROJECT DESCRIPTION]. I know schedules get busy — I\u0026#39;d love to hear your thoughts when you have a moment. Would a quick 15-minute call [DAY 1] or [DAY 2] work for you? Looking forward to connecting. [YOUR NAME] Pro Tips \u0026amp; Advanced Techniques Common Mistakes to Avoid Mistake 1: Sending AI drafts without reading them. AI occasionally adds filler phrases, overly formal language, or awkward transitions. Always read before you send.\nMistake 2: Not giving enough context. \u0026ldquo;Write an email to my client\u0026rdquo; is useless. Relationship history, specific context, and the emotional dynamic of the situation are what make an email land.\nMistake 3: Using AI for everything. Some emails — a personal thank you, a heartfelt apology, a genuinely delicate situation — benefit from being written by you, in your own words. Use AI for the routine work, not the relationship-defining moments.\nMistake 4: Ignoring subject lines. Ask AI to generate 3 subject line options. A/B test them if you\u0026rsquo;re sending campaigns. Subject lines are the most underprompted part of email writing.\nMistake 5: Not proofreading for specific details. Check names, numbers, dates, and company names. AI is accurate about your prompts but makes mistakes with specific data.\nPower User Strategies Strategy 1: Write a \u0026ldquo;master context\u0026rdquo; doc. Keep a saved text file with your name, role, company, key clients, and tone preferences. Paste it at the start of any AI session to get perfectly calibrated drafts immediately.\nStrategy 2: Use AI to diagnose cold email failures. Paste your cold email into ChatGPT and ask: \u0026ldquo;What\u0026rsquo;s weak about this email? Why might the recipient not respond? How would you improve it?\u0026rdquo; The feedback is brutally honest and usually accurate.\nStrategy 3: Draft entire email chains. Paste an incoming email chain and say \u0026ldquo;Draft a reply that [goal].\u0026rdquo; AI can read context from the full thread.\nStrategy 4: Generate multiple versions. Ask for \u0026ldquo;3 versions — one formal, one casual, one very brief\u0026rdquo; and pick the best one or combine elements.\nStrategy 5: Use AI to improve, not just create. Paste your own draft and say \u0026ldquo;Make this 30% shorter, more direct, and remove any passive voice.\u0026rdquo; The editing use case is often more valuable than writing from scratch.\nRelated: Best ChatGPT Prompts for Productivity 2026 Frequently Asked Questions Q: Will recipients know I used AI to write my emails? A: Not if you personalize and review before sending. AI drafts become indistinguishable from human writing when you add your specific details, adjust phrasing to match your voice, and remove any generic filler.\nQ: Is it ethical to use AI for professional emails? A: Yes. AI is a writing aid, like spellcheck or a thesaurus — just more powerful. The ideas, relationships, and intentions are yours. The AI helps you express them clearly and efficiently.\nQ: Which AI is best for writing sensitive emails (apologies, conflict resolution)? A: Claude Pro tends to handle sensitive, emotionally nuanced emails better than ChatGPT in many users\u0026rsquo; experience. Its outputs are more careful and less prone to over-formality. That said, both tools produce excellent results with good prompts.\nQ: Can AI help with email subject lines for marketing campaigns? A: Absolutely. Ask for 10 subject line options with different angles (curiosity, urgency, benefit-driven, question format) and A/B test the best performers.\nQ: How do I stop AI emails from sounding robotic? A: Give tone examples: \u0026ldquo;Write in the style of this email I wrote: [PASTE YOUR EMAIL].\u0026rdquo; And always add your personal phrases and sign-off in the review step.\nQ: Should I use an integrated tool (like Copilot in Outlook) or a standalone AI? A: Integrated tools win on convenience. Standalone tools (ChatGPT, Claude) win on quality and versatility. For high-stakes emails, use standalone AI. For routine replies, integrated tools save more time.\nQ: How much time can AI realistically save on email? A: Most professionals report 1–3 hours saved per day when they adopt an AI email workflow. The biggest gains come from drafting, not from the AI replying automatically.\nStrong Communicators Are Always in Demand Whether you\u0026rsquo;re job hunting or looking to advance, professional communication is a skill every employer values. Find your next career on doda — Japan\u0026rsquo;s leading job platform with thousands of roles across every industry.\nConclusion \u0026amp; Call to Action Email doesn\u0026rsquo;t have to consume your day. With the right AI tools and prompt templates, you can cut your email time by 50–70% while actually improving the quality of your communication. The key is building a system: a saved persona prompt, a library of situation-specific templates, and the discipline to review before sending.\nKey takeaways:\nCopilot for Outlook and Gemini for Gmail are the most seamless integrations ChatGPT Plus and Claude Pro deliver the highest quality for complex emails Always give AI full context: recipient relationship, goal, tone, and length Review every draft before sending — AI is a collaborator, not an autopilot Build a reusable template library from your best AI-generated emails Want 50+ ready-to-use email prompt templates organized by situation (follow-ups, cold outreach, negotiations, apologies, and more)? Our Complete ChatGPT Prompt Collection includes a dedicated email section you can use today.\nAnd if you want to build a full communication system that handles email, proposals, and client interactions with AI, the AI Productivity Playbook has you covered end-to-end.\nRelated: How to Automate Tasks with AI Step by Step Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nRelated Articles ChatGPT vs Claude vs Gemini: Full Comparison 2026 Best ChatGPT Prompts for Productivity 2026 AI Writing Tools Comparison 2026 How to Use ChatGPT for Data Analysis 2026 Best AI Tools for Small Business 2026 Related Tools Count words and characters in your text → Word Counter Compare texts and find differences → Text Diff Checker Calculate your ideal freelance rate → Freelance Rate Calculator Calculate your take-home pay → Salary Calculator Related Templates Put these techniques into practice with our ready-made templates:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Copy-paste prompts for every situation The AI Productivity Playbook 2026 — 50+ AI workflows and automation strategies ","permalink":"https://productivity-works.com/posts/best-ai-tools-for-writing-emails-professionally/","summary":"\u003ch1 id=\"best-ai-tools-for-writing-emails-professionally--complete-guide-2026\"\u003eBest AI Tools for Writing Emails Professionally — Complete Guide [2026]\u003c/h1\u003e\n\u003cp\u003eThe average professional spends 28% of their workweek on email. That\u0026rsquo;s more than 11 hours every week — writing, reading, rewording, and wondering whether that last sentence sounds too passive-aggressive. Email is both unavoidable and brutally time-consuming. And for non-native English speakers or anyone who agonizes over tone, it can be genuinely stressful.\u003c/p\u003e\n\u003cp\u003eAI tools have quietly become the most useful thing that ever happened to professional email. They draft for you, match your tone, adjust formality on command, handle difficult situations diplomatically, and write follow-ups without you having to think. The result: inbox zero feels achievable for the first time.\u003c/p\u003e","title":"Best AI Tools for Writing Emails Professionally"},{"content":"Small business owners wear every hat. You\u0026rsquo;re the marketer, the accountant, the customer service rep, and the strategist — often in the same afternoon. AI tools in 2026 won\u0026rsquo;t replace you, but they will let you punch far above your weight class.\nThis guide covers the best AI tools for small businesses across every function, with honest assessments of pricing, learning curves, and who each tool is actually for.\nHow We Evaluated These Tools Every tool on this list was assessed on five criteria:\nValue for money — Does the free tier do enough? Is the paid plan worth it for a small team? Ease of use — Can a non-technical founder get value in under an hour? Time savings — How many hours per week does this realistically save? Integration — Does it work with tools you already use? Reliability — Is the output quality consistent? We deliberately excluded tools that are impressive in demos but unreliable in daily use.\nAI Tools for Marketing and Content 1. ChatGPT (OpenAI) — Best All-Around Writing Assistant Price: Free (GPT-4o) | $20/month (Plus) | $25/user/month (Team)\nChatGPT remains the Swiss Army knife of AI tools. For small business marketing specifically:\nBlog posts and articles — Outline, draft, and refine content in a fraction of the time Social media captions — Generate a month of Instagram captions in 30 minutes Email campaigns — Draft promotional emails, A/B test subject lines, write sequences Ad copy — Facebook, Google, and LinkedIn ad variations on demand Product descriptions — Scale e-commerce listings quickly Best use: Create a custom GPT (available on Plus plan) trained on your brand voice, product catalog, and target customer persona. Every output will sound like you.\nLimitation: ChatGPT doesn\u0026rsquo;t know your business by default. The more context you feed it (via Custom Instructions or system prompts), the better the output.\n2. Jasper — Best for Marketing Teams with Brand Guidelines Price: $39/month (Creator) | $99/month (Pro, 3 seats) | Custom (Business)\nJasper is purpose-built for marketing content. Its main advantage over ChatGPT is the Brand Voice feature: you upload your guidelines, tone examples, and product info once, and every output respects them.\nBest for: Agencies, e-commerce brands, and small marketing teams that produce high volumes of content and need consistency.\nNot ideal for: Solo founders on a budget — ChatGPT with a good Custom Instruction does 80% of what Jasper does for $20/month less.\n3. Canva AI — Best for Visual Content Without a Designer Price: Free (basic) | $15/month (Pro)\nCanva\u0026rsquo;s AI features in 2026 are genuinely impressive for non-designers:\nMagic Design — Describe what you need and get a complete design in seconds Magic Edit — Edit photos using text prompts (\u0026ldquo;remove the background,\u0026rdquo; \u0026ldquo;add a sunset\u0026rdquo;) Magic Write — AI copywriting built directly into the design canvas Presentation AI — Generate slide decks from a text outline Brand Kit — Lock in your colors, fonts, and logos so every design stays on-brand Time saving: A small business that was spending 3-4 hours/week on graphics can cut that to under an hour with Canva AI.\n4. Opus Clip — Best for Repurposing Video Content Price: Free (limited) | $15/month (Starter) | $49/month (Pro)\nIf you create any long-form video content — YouTube, webinars, podcasts with video — Opus Clip automatically cuts it into short-form clips optimized for TikTok, Instagram Reels, and YouTube Shorts.\nIt uses AI to identify the most engaging moments, adds captions, reformats to vertical, and even scores each clip by predicted engagement. One 60-minute webinar can yield 10-15 usable short clips.\nROI: For small businesses investing in video content, the time savings alone justify the cost within the first month.\nAI Tools for Customer Service 5. Intercom Fin — Best AI Customer Support Agent Price: $0.99 per resolution (usage-based) | $74/month (Starter) for small teams\nIntercom\u0026rsquo;s Fin is an AI support agent that resolves customer queries automatically using your knowledge base, help docs, and website content. In 2026, it handles around 60-70% of common queries without human intervention.\nBest for: E-commerce stores, SaaS products, and service businesses with repetitive support questions (shipping, returns, pricing, how-to).\nSetup time: 2-4 hours to connect your help docs and test edge cases.\nHuman handoff: Fin automatically escalates complex or sensitive queries to a human agent — this is crucial and it does it well.\n6. Tidio — Best Budget AI Chatbot for Small Businesses Price: Free (basic) | $19/month (Starter) | $49/month (Growth)\nTidio is the most accessible AI chatbot for small businesses. The free plan lets you add a chatbot to your website that answers FAQs, captures leads, and qualifies customers 24/7.\nThe paid tiers add Lyro, Tidio\u0026rsquo;s AI agent, which handles more complex conversations. For businesses getting 50-200 support inquiries per month, Tidio is often sufficient without the Intercom price tag.\nAI Tools for Finance and Operations 7. QuickBooks AI — Best for AI-Assisted Accounting Price: $30/month (Simple Start) | $60/month (Plus) | $90/month (Advanced)\nQuickBooks\u0026rsquo; AI features have matured significantly. In 2026, the standout capabilities for small businesses are:\nAutomatic transaction categorization — Bank feeds are categorized with 90%+ accuracy after initial training Cash flow forecasting — AI predicts your 90-day cash position based on historical patterns Anomaly detection — Flags unusual transactions that may indicate errors or fraud Smart invoicing — Suggests invoice amounts based on previous work with the same client Mileage tracking with AI — Automatically classifies trips as business vs. personal Alternative: FreshBooks ($17/month) has strong AI invoicing and expense features and is friendlier for service-based solo businesses.\n8. Notion AI — Best for Operations Documentation Price: Add-on to Notion Plus ($10/member/month)\nIf your team uses Notion (see our Notion PM guide ), the AI add-on pays for itself quickly through:\nSOP drafting — Describe a process in bullets, AI expands it into a full procedure doc Meeting summaries — Paste transcripts, get structured summaries with action items Q\u0026amp;A over your wiki — Ask questions and AI surfaces answers from your internal docs For businesses that struggle to keep documentation up to date, Notion AI dramatically lowers the friction of writing.\n9. Zapier AI — Best for Workflow Automation Price: Free (limited) | $19.99/month (Starter) | $49/month (Professional)\nZapier\u0026rsquo;s AI features help non-technical owners build automations through plain English: \u0026ldquo;When a new lead fills out my Typeform, add them to HubSpot, send a welcome email from Gmail, and create a task in Notion.\u0026rdquo;\nIn 2026, Zapier\u0026rsquo;s AI Zap builder creates the entire workflow from that description. You review and activate — no coding required.\nBest automations for small businesses:\nLead capture → CRM entry → welcome email New invoice paid → update spreadsheet → send Slack notification New social media mention → Slack alert for monitoring Calendar booking → Zoom link creation + email confirmation AI Tools for HR and Hiring 10. Workable — Best AI-Powered Hiring for Small Teams Price: $149/month (Starter, up to 2 active jobs) | $299/month (Standard)\nWorkable uses AI to:\nWrite job descriptions from a brief Screen and rank applicants by fit Generate interview question sets tailored to the role Flag potential bias in job postings For small businesses hiring 2-10 people per year, Workable dramatically cuts the time from \u0026ldquo;we need to hire\u0026rdquo; to \u0026ldquo;offer accepted.\u0026rdquo;\nBudget alternative: Use ChatGPT for job descriptions + Notion for tracking applicants. Gets you 60% of the value for free.\n11. Lattice AI — Best for Performance Management Price: $11/person/month (Performance) | Custom (full suite)\nLattice\u0026rsquo;s AI features help small businesses run structured performance reviews without a dedicated HR team:\nAuto-generated review questions based on role and past feedback AI summaries of performance data for manager 1:1s Goal-setting assistance aligned to company objectives Who needs this: Teams of 10+ who want structured performance culture without hiring an HR director.\nAI Tools for Sales and CRM 12. HubSpot AI — Best Free CRM with AI Features Price: Free (CRM) | $15/month (Starter) | $800/month (Professional)\nHubSpot\u0026rsquo;s free CRM now includes several AI features that were previously paid add-ons:\nContent Assistant — Writes emails, social posts, and landing page copy inside HubSpot ChatSpot — Conversational interface for your CRM data (\u0026ldquo;Show me deals closing this month in the Northeast\u0026rdquo;) Predictive lead scoring — AI ranks leads by likelihood to close Call summaries — Automatically transcribes and summarizes sales calls For small businesses just building their sales process, HubSpot free + AI features is an extraordinary value.\n13. Apollo.io — Best AI for Sales Prospecting Price: Free (limited) | $49/month (Basic) | $99/month (Professional)\nApollo\u0026rsquo;s AI prospecting engine helps small B2B businesses:\nFind verified contact data for ideal customer profiles Write personalized outreach sequences at scale Score leads based on intent signals (job changes, funding rounds, tech stack) Analyze which outreach templates perform best Best for: B2B service businesses and SaaS companies doing outbound sales.\nAI Tools for Legal and Compliance 14. Ironclad — Best for Contract AI Price: Custom (generally $500+/month)\nFor businesses dealing with significant contract volume — agencies, consultancies, SaaS — Ironclad\u0026rsquo;s AI:\nExtracts and summarizes contract terms Flags non-standard clauses Automates contract routing and approval workflows Budget alternative: Notarize + ChatGPT for contract review. Not as robust, but workable for low-volume needs.\n15. DoNotPay / Spellbook — Best for Small Business Legal Tasks Price: $36/month (DoNotPay)\nDoNotPay handles smaller legal tasks: drafting demand letters, disputing charges, understanding contract terms, DMCA takedowns. Not a lawyer replacement — but useful for routine issues.\nThe Small Business AI Stack: Our Recommendations by Budget Bootstrap ($0-50/month) Tool Cost Purpose ChatGPT Free $0 Writing, research, ideation Canva Free $0 Graphics HubSpot Free $0 CRM Tidio Free $0 Website chatbot Zapier Free $0 5 automations/month Total $0 Core stack Growing ($50-200/month) Tool Cost Purpose ChatGPT Plus $20 Advanced writing + GPTs Canva Pro $15 Full design suite Notion + AI $10 PM + documentation Zapier Starter $20 Automations Tidio Starter $19 AI customer chat Total $84 Productive small team Scaling ($200-500/month) Tool Cost Purpose ChatGPT Team $25/user Team AI writing Jasper Pro $99 Brand-consistent content HubSpot Starter $15 CRM + AI emails Intercom Fin ~$50 AI customer support Zapier Professional $49 Advanced automations QuickBooks Plus $60 Accounting Total ~$300 Full business stack Common Mistakes Small Businesses Make with AI Tools Buying too many tools at once. Start with one AI tool, use it daily for 30 days, then add another. Tool overload leads to nothing getting used well.\nExpecting AI to replace thinking. AI excels at execution — drafting, formatting, researching. Strategy, judgment, and relationship-building still require you.\nSkipping the prompt investment. The quality of AI output correlates directly with the quality of your instructions. Spend time writing good prompts (or system prompts) for your most common tasks. Save them. Reuse them.\nNot tracking time savings. Before adopting a new AI tool, note how long a task currently takes. After 30 days, measure again. This justifies the cost and identifies what\u0026rsquo;s actually working.\nUsing AI for customer-facing content without review. AI-generated emails, social posts, and support responses need a human review pass before they go out. One tone-deaf automated message can undo months of brand building.\nAI Helps You Work Smarter — freee Keeps the Books Clean When you\u0026rsquo;re running a small business with AI tools, your finances need to keep up. Try freee — Japan\u0026rsquo;s #1 cloud accounting to automate invoicing, expense tracking, and tax filing so you can focus on growing your business.\nThe Bottom Line In 2026, the question isn\u0026rsquo;t whether small businesses should use AI tools — it\u0026rsquo;s which ones to prioritize. Our top picks by category:\nWriting \u0026amp; content: ChatGPT Plus Design: Canva Pro Customer service: Tidio (budget) or Intercom Fin (scale) CRM \u0026amp; sales: HubSpot Free Automation: Zapier Project management: Notion + AI Accounting: QuickBooks Start with the free tiers, prove the value, then upgrade. The small business owners winning with AI aren\u0026rsquo;t using the most tools — they\u0026rsquo;re using a focused set of tools, consistently.\nWant a done-for-you AI tool setup guide tailored to your specific business type? Check out our Small Business AI Toolkit on Payhip — includes ChatGPT prompt libraries, Zapier templates, and a step-by-step 30-day implementation plan.\nRelated reads:\nHow to Use Notion for Project Management 2026 Best Free Alternatives to ChatGPT 2026 How to Create a Resume with AI (Step-by-Step) Related Tools Create a business budget → Budget Planner Estimate your side hustle taxes → Side Hustle Tax Calculator Generate professional client invoices instantly → Invoice Generator Generate QR codes for marketing, menus, and payments → QR Code Generator Generate secure passwords instantly → Password Generator Generate SHA-256 and MD5 hashes → Hash Generator Related Templates Put these techniques into practice with our ready-made templates:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Copy-paste prompts for every situation The AI Productivity Playbook 2026 — 50+ AI workflows and automation strategies Related Articles Best AI Tools for Small Business Owners 2026: Complete Guide AI Tools for Freelancers to Earn More 2026 How to Automate Tasks with AI Step by Step ChatGPT vs Claude vs Gemini: The Definitive AI Comparison 2026 How to Make Money With AI in 2026: 15 Realistic Ways That Work This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/best-ai-tools-small-business-2026/","summary":"\u003cp\u003eSmall business owners wear every hat. You\u0026rsquo;re the marketer, the accountant, the customer service rep, and the strategist — often in the same afternoon. AI tools in 2026 won\u0026rsquo;t replace you, but they will let you punch far above your weight class.\u003c/p\u003e\n\u003cp\u003eThis guide covers the best AI tools for small businesses across every function, with honest assessments of pricing, learning curves, and who each tool is actually for.\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-we-evaluated-these-tools\"\u003eHow We Evaluated These Tools\u003c/h2\u003e\n\u003cp\u003eEvery tool on this list was assessed on five criteria:\u003c/p\u003e","title":"Best AI Tools for Small Business 2026: The Complete Roundup"},{"content":"※本記事にはアフィリエイト広告が含まれています。\nBest Budgeting Apps 2026 Comparison — Complete Guide Budgeting has a reputation problem. Most people associate it with restriction, spreadsheets, and the financial equivalent of eating plain rice cakes. But the right budgeting app can change that entirely — it becomes a tool that shows you exactly where your money goes, helps you direct it where you want, and makes financial goals feel achievable rather than distant.\nThe challenge in 2026 is that the landscape of personal finance apps has exploded. Mint\u0026rsquo;s shutdown in 2024 left millions of users searching for alternatives. New apps have emerged, subscription prices have shifted, and the feature sets have evolved dramatically. Some apps are brilliant; others are glorified expense trackers that don\u0026rsquo;t change behavior at all.\nThis guide cuts through the noise.\nIn this guide, you\u0026rsquo;ll learn:\nThe top budgeting apps available in 2026, compared in detail Which app works best for different budgeting styles and goals How to evaluate an app beyond the marketing hype Free vs. paid: when it\u0026rsquo;s worth spending money on a budget app Step-by-step instructions for getting started with any app The most common reasons budgeting apps \u0026ldquo;don\u0026rsquo;t work\u0026rdquo; — and how to fix them What Is a Budgeting App? Definition \u0026amp; How It Works A budgeting app is a software tool (typically mobile and web) that helps you track income, expenses, and financial goals by connecting to your bank accounts, credit cards, and other financial accounts.\nModern budgeting apps typically offer:\nAutomatic transaction import: Syncs with your bank to pull in transactions automatically Spending categorization: Labels transactions (groceries, dining, subscriptions, etc.) Budget creation: Allocates spending limits by category Goal tracking: Visual progress toward savings goals (emergency fund, vacation, down payment) Reports and insights: Spending trends, net worth tracking, cash flow analysis Bill reminders: Alerts for upcoming bills and due dates The core value proposition: understanding where your money goes is the first step to controlling where it goes. Most people dramatically underestimate their spending in 2–3 categories — and a good budgeting app makes this undeniable.\nBudgeting Philosophies: Which Approach Suits You? Not all apps are built on the same philosophy:\nZero-based budgeting (YNAB): Every dollar is assigned a job before you spend it. Proactive, not reactive. Best for people who want maximum control. Envelope budgeting (Goodbudget, Copilot): Digital version of the classic cash envelope system. Allocate money to categories before spending. Tracking-first (Copilot, Monarch Money): Track spending automatically and get insights, then adjust behavior. Lower barrier to entry, less rigid. Net worth focused (Personal Capital/Empower): Less about daily budgets, more about overall wealth picture. Better for high earners with investments. Debt payoff focused (Debt Payoff Planner, Tally): Specialized tools optimized for paying down specific debts. Best Budgeting Apps 2026: Top Options Compared App Price Free Version Best For Standout Feature Avg. Rating YNAB (You Need a Budget) $14.99/mo or $99/yr 34-day free trial Serious zero-based budgeters Proactive \u0026ldquo;give every dollar a job\u0026rdquo; approach 4.8/5 Monarch Money $14.99/mo or $99.99/yr 7-day trial Couples + comprehensive view Shared finances, net worth, investing 4.7/5 Copilot (iOS only) $12.99/mo or $95.99/yr 2-month free trial Apple ecosystem users Beautiful design, smart AI categorization 4.8/5 (iOS) Empower (Personal Capital) Free (premium available) Yes (core free) Investors + high earners Investment tracking + net worth 4.5/5 Goodbudget Free / $10/mo Yes (limited envelopes) Envelope budgeting Cash envelope method, couples sync 4.3/5 EveryDollar Free / $17.99/mo Yes (manual entry) Dave Ramsey followers Zero-based, connects to Ramsey+ ecosystem 4.4/5 Simplifi by Quicken $3.99/mo No Casual trackers Watchlists, simple UI, low cost 4.4/5 Honeydue Free Yes Couples, joint finances Designed for two-person households 4.0/5 PocketGuard Free / $7.99/mo Yes (basic) Overspenders \u0026ldquo;In My Pocket\u0026rdquo; available spend feature 4.1/5 Tiller Money $79/yr 30-day trial Spreadsheet lovers Auto-fills Google Sheets or Excel 4.6/5 Check current app pricing and offers Top 3 Budgeting Apps — Quick Comparison YNAB $99 / year Zero-based budgeting Give every $ a job 34-day free trial ⭐ 4.8 / 5 Best for: Behavior change Monarch Money $99.99 / year Tracking + net worth Couples \u0026amp; household 7-day free trial ⭐ 4.7 / 5 Best for: Couples Empower Free Investment tracking Net worth dashboard Always free core ⭐ 4.5 / 5 Best for: Investors Deep Dives: Top 5 Apps Reviewed YNAB (You Need a Budget) Best for: Anyone serious about changing their relationship with money\nYNAB is the gold standard of budgeting apps. Unlike passive trackers, YNAB is a proactive system: you assign every dollar a job before you spend it. You give money to categories (rent, groceries, savings goals) as soon as it arrives in your account, then spend from those allocations.\nThe result: YNAB users report saving an average of $600 in their first two months. The methodology genuinely works — but it requires commitment. You\u0026rsquo;ll spend 10–15 minutes per week maintaining it.\nPrice: $14.99/month or $99/year (free for 34 days; free for college students) Best feature: The \u0026ldquo;Age of Money\u0026rdquo; metric showing how old your dollars are before spending Weakness: Learning curve; overkill if you just want passive tracking Monarch Money Best for: Couples and comprehensive household financial management\nMonarch Money has emerged as one of the best Mint replacements, offering a clean interface, solid bank connections, investment tracking, and excellent shared finance features for couples. You get budgeting, net worth tracking, goal tracking, and cash flow all in one place.\nPrice: $14.99/month or $99.99/year Best feature: Partner sharing with customizable visibility Weakness: Slightly higher cost; investment tracking is less powerful than Empower Copilot Best for: iPhone/Mac users who want a beautiful, smart experience\nCopilot (iOS/macOS only) uses machine learning to categorize transactions with impressive accuracy and presents financial data in one of the cleanest interfaces available. It\u0026rsquo;s a pleasure to use, which is a surprisingly important factor in whether you\u0026rsquo;ll actually stick with budgeting.\nPrice: $12.99/month or $95.99/year (2-month free trial) Best feature: AI transaction categorization that learns from corrections Weakness: iOS/Mac only; no Android version Empower (formerly Personal Capital) Best for: Investors and high earners focused on net worth\nEmpower\u0026rsquo;s free tools are genuinely excellent for tracking investments, net worth, and overall financial picture. The free dashboard shows you all accounts, spending, and portfolio performance in one place. If you primarily want to track wealth rather than micromanage daily spending, Empower is hard to beat — and it\u0026rsquo;s free.\nPrice: Free (paid wealth management services available but not required) Best feature: Investment portfolio analyzer and fee analyzer Weakness: Less powerful for granular daily budgeting; paid advisors are expensive Simplifi by Quicken Best for: Budget-conscious users who want simple tracking without high costs\nAt $3.99/month, Simplifi is the most affordable full-featured budgeting app on the market. It offers automatic bank syncing, spending plans, and watchlists for custom spending categories. It\u0026rsquo;s not as powerful as YNAB or Monarch, but for casual budgeters who don\u0026rsquo;t need deep features, the lower price is attractive.\nPrice: $3.99/month (promotional pricing may vary) Best feature: \u0026ldquo;Spending plan\u0026rdquo; that combines budget + tracking with less setup friction Weakness: Less robust reporting than competitors How to Choose: Key Factors What to Look For 1. Your budgeting philosophy Do you want to control spending before it happens (YNAB, EveryDollar) or track and analyze after (Empower, Simplifi, Monarch)? The proactive approach is more powerful but requires more engagement.\n2. Bank connection reliability The most common complaint about budgeting apps is broken bank connections. Some apps use Plaid, others have proprietary connections. Read recent user reviews specifically about connection reliability with your banks.\n3. Couple vs. individual use If you\u0026rsquo;re managing finances with a partner, choose an app with strong sharing features: Monarch Money, Honeydue, or YNAB (which offers shared budgets).\n4. Investment tracking needs If you have significant investment accounts (401k, IRA, taxable brokerage), Empower\u0026rsquo;s free investment dashboard is excellent. Other apps show investment balances but don\u0026rsquo;t provide the portfolio analysis Empower offers.\n5. Price sensitivity $99–$120/year for a budgeting app seems like a lot, but if it helps you save $200/month, the ROI is extraordinary. However, if you won\u0026rsquo;t use the premium features, free options (Empower, Goodbudget basic, PocketGuard basic) work well.\nCommon Mistakes to Avoid Mistake 1: Downloading an app and never setting it up properly The most common failure: you download a budgeting app, connect your bank, and then\u0026hellip; do nothing with it. Budgeting apps require an initial 30–60 minute setup session where you create your budget categories and initial allocations. Schedule this time intentionally.\nMistake 2: Switching apps constantly App-hopping is a form of procrastination. Every app has weaknesses. The best budgeting app is the one you actually use consistently. Pick one, commit for three months, and only switch if it\u0026rsquo;s genuinely not working.\nMistake 3: Making your budget too restrictive A budget that gives you $50 for dining out when you normally spend $400 isn\u0026rsquo;t a budget — it\u0026rsquo;s a fantasy. Start by tracking actual spending for one month, then make realistic adjustments.\nMistake 4: Not reviewing the app weekly Budgeting apps work passively, but the behavior change requires active review. A weekly 10-minute check-in is the minimum for meaningful impact.\nMistake 5: Ignoring \u0026ldquo;miscellaneous\u0026rdquo; spending Miscellaneous categories are where budgets go to die. Give every recurring spend its own category. If you notice $300 in \u0026ldquo;other\u0026rdquo; spending, dig in — there are almost always surprises.\nRelated: How to Pay Off Debt Fast Step-by-Step Guide: Getting Started with a Budgeting App Step 1: Choose your app Based on the comparison above, pick one app that fits your style. If you\u0026rsquo;re genuinely unsure, start with YNAB\u0026rsquo;s 34-day free trial — the methodology will teach you budgeting principles that apply to any tool.\nStep 2: Connect your accounts Link all financial accounts: checking, savings, credit cards, investment accounts, and loans. Having a complete picture is essential for accurate tracking.\nStep 3: Review one month of past transactions Before setting a budget, look at your actual spending for the past 30 days. This is usually eye-opening. Most people are surprised by their restaurant spending, subscriptions, or Amazon purchases.\nStep 4: Create your budget categories Set up categories that match your actual life. Common categories:\nHousing (rent/mortgage, utilities, internet, renter\u0026rsquo;s insurance) Transportation (car payment, insurance, gas, parking, transit) Food (groceries, dining, coffee) Health (insurance, gym, medications) Personal (clothing, haircuts, personal care) Entertainment (streaming, hobbies, games) Savings goals (emergency fund, vacation, down payment) Debt payments (student loans, credit cards) Step 5: Allocate your income Assign dollar amounts to each category based on your realistic assessment. A common starting framework:\n50% needs (housing, food, transportation) 20% savings and debt paydown 30% wants (dining, entertainment, personal) Adjust based on your actual situation and goals.\nStep 6: Track for 30 days without judgment Your first month is a data-gathering exercise, not a graded test. Don\u0026rsquo;t panic when you go over in a category — just note it and use the information to set better targets next month.\nStep 7: Review and adjust monthly At the end of each month, review: Where did you overspend? Where did you underspend? Adjust categories for next month. This iterative process is how real behavior change happens.\nFrequently Asked Questions Q: Are budgeting apps safe? Is it secure to connect my bank accounts? A: Reputable apps use bank-level 256-bit encryption and read-only connections (they can view transactions but cannot move money). Most use Plaid, a widely trusted financial data platform used by thousands of apps. That said, no system is 100% risk-free. Use strong unique passwords and enable two-factor authentication.\nQ: What happened to Mint? What\u0026rsquo;s the best Mint alternative? A: Mint was shut down by Intuit in January 2024 and users were directed to Credit Karma. The best Mint replacements in 2026 are: Monarch Money (most similar feature set), Copilot (best design, iOS only), and Empower (best for investment tracking). Most former Mint users who committed to Monarch or YNAB report better results than they had with Mint.\nQ: Is YNAB worth the $99/year? A: YNAB is worth it if you actively use it. The average reported savings for new YNAB users is $600 in the first two months — a 6x return in year one. It\u0026rsquo;s not worth it if you pay for it, set it up once, and never look at it again.\nQ: What\u0026rsquo;s the best free budgeting app? A: Empower (formerly Personal Capital) is the best free option for a comprehensive financial dashboard. For budgeting specifically (not just tracking), Goodbudget and PocketGuard offer capable free tiers, though with limitations.\nQ: Do I need a budgeting app if I already use a spreadsheet? A: Only if the spreadsheet is actually working for you. Many people maintain a budgeting spreadsheet inconsistently. If you check it monthly at best, an automated app with bank syncing will likely serve you better. If you love spreadsheets, Tiller Money auto-populates Google Sheets with your transactions — the best of both worlds.\nQ: Should I use a budgeting app if I have a high income? A: Yes, especially if you\u0026rsquo;re not hitting savings or investment goals. High-income earners often have the biggest lifestyle inflation problem. Many six-figure earners are shocked to discover they\u0026rsquo;re saving less than $500/month. Empower or Monarch Money works well for higher-income households.\nTurn Budgeting Wins Into Investment Growth Once your budgeting app helps you find extra money every month, put it to work. Open a Rakuten Securities account and automate monthly contributions into index funds — turning your budgeting discipline into long-term wealth.\nConclusion: The Best Budgeting App Is the One You\u0026rsquo;ll Use Every app on this list is better than no app. The best budgeting app isn\u0026rsquo;t the one with the most features — it\u0026rsquo;s the one you\u0026rsquo;ll open every week, review honestly, and use to make better decisions.\nHere\u0026rsquo;s our recommendation matrix:\nWant maximum behavior change: YNAB Want the best all-around Mint replacement: Monarch Money Have an iPhone and want beautiful UX: Copilot Want free + investment tracking: Empower Budget is tight and you want low cost: Simplifi ($3.99/mo) Manage finances with a partner: Monarch Money or Honeydue Start the free trial of your top choice today. Commit to 30 days. The data you gather in that first month alone — just seeing where your money actually goes — is worth every minute of setup.\nDownload and start your free trial:\nStart YNAB free for 34 days Try Monarch Money free for 7 days Download Copilot — 2-month free trial (iOS) Related: How to Build an Emergency Fund Fast Disclaimer: This article is for informational purposes only and does not constitute financial advice. Investment decisions should be made based on your individual circumstances. Please consult a qualified financial advisor before making investment decisions. Information is current as of the publication date — verify details on official websites.\nDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nRelated Tools \u0026amp; Articles Calculate percentages, discounts, and tips instantly → Percentage Calculator Create your monthly budget plan → Budget Calculator Track your debt payoff progress → Debt Payoff Calculator See how savings grow over time → Compound Interest Calculator Calculate how long to reach any savings target → Savings Goal Calculator Calculate your mortgage payment → Mortgage Calculator Track your total net worth → Net Worth Calculator How to Build an Emergency Fund Fast (2026) How to Pay Off Debt Fast: Best Strategies 2026 Best High-Yield Savings Accounts 2026: Where to Earn 4-5% APY How to Start Investing with $100 in 2026 Passive Income Ideas That Actually Work 2026 This article contains affiliate links. We may earn a commission at no extra cost to you.\nRelated Templates Take control of your finances with these tools:\nSmart Budget Tracker (Excel Template) — Track income, expenses, and savings goals Side Hustle Starter Kit 2026 — Build additional income streams ","permalink":"https://productivity-works.com/posts/best-budgeting-apps-2026-comparison/","summary":"\u003cp\u003e※本記事にはアフィリエイト広告が含まれています。\u003c/p\u003e\n\u003ch1 id=\"best-budgeting-apps-2026-comparison--complete-guide\"\u003eBest Budgeting Apps 2026 Comparison — Complete Guide\u003c/h1\u003e\n\u003cp\u003eBudgeting has a reputation problem. Most people associate it with restriction, spreadsheets, and the financial equivalent of eating plain rice cakes. But the right budgeting app can change that entirely — it becomes a tool that shows you exactly where your money goes, helps you direct it where you want, and makes financial goals feel achievable rather than distant.\u003c/p\u003e\n\u003cp\u003eThe challenge in 2026 is that the landscape of personal finance apps has exploded. Mint\u0026rsquo;s shutdown in 2024 left millions of users searching for alternatives. New apps have emerged, subscription prices have shifted, and the feature sets have evolved dramatically. Some apps are brilliant; others are glorified expense trackers that don\u0026rsquo;t change behavior at all.\u003c/p\u003e","title":"Best Budgeting Apps 2026: Full Comparison"},{"content":"This article contains affiliate links.\nManaging money without a system is like navigating a city without a map. You might get somewhere eventually, but you\u0026rsquo;ll waste a lot of time and fuel along the way. Budgeting apps solve that problem by giving you a real-time picture of where your money goes — and more importantly, where it should go.\nAfter testing dozens of apps over several months, we narrowed the field to seven that genuinely earn a place on your phone. Here\u0026rsquo;s the unfiltered breakdown.\nQuick Comparison Table App Best For Price Syncs Bank Accounts Platform YNAB Debt payoff / proactive budgeting $14.99/mo or $99/yr Yes iOS, Android, Web Monarch Money Couples \u0026amp; households $14.99/mo or $99.99/yr Yes iOS, Android, Web Copilot iPhone users who want a premium UX $8.99/mo or $69.99/yr Yes iOS only Simplifi by Quicken Casual budgeters $3.99/mo Yes iOS, Android, Web PocketGuard Overspenders who need guardrails Free / $12.99/mo Plus Yes iOS, Android Goodbudget Envelope budgeters, no bank sync Free / $10/mo Plus No (manual) iOS, Android, Web Empower (Personal Capital) Investors tracking net worth Free Yes iOS, Android, Web 1. YNAB — Best for Changing Your Financial Habits YNAB (You Need A Budget) operates on a philosophy rather than just a feature set. Every dollar you have gets a job before you spend it. That proactive approach makes it the most effective app on this list for people who feel like money slips through their fingers.\nWhat Works Zero-based budgeting engine that forces intentionality with every transaction Outstanding educational resources — the free workshops alone are worth the price of admission Real-time sync with most US, UK, Canadian, and Australian banks Goal tracking that actually ties into your budget categories What Doesn\u0026rsquo;t The learning curve is steep. New users often spend a few weeks feeling confused before the system clicks Price is the highest of any app here Who Should Use It Anyone carrying credit card debt, living paycheck to paycheck, or simply tired of wondering where their money went. YNAB users report saving an average of $600 in their first two months.\n(Try YNAB free for 34 days at ynab.com)\n2. Monarch Money — Best for Couples and Households Monarch Money was built from the ground up with shared finances in mind. Multiple users can access the same account simultaneously, leave notes on transactions, and collaborate on budget categories without one partner overwriting the other\u0026rsquo;s work.\nWhat Works True multi-user collaboration with role permissions Cleaner, more modern interface than legacy competitors Flexible budgeting — you can use zero-based, percentage-based, or purely tracking modes Net worth tracking and investment accounts in the same dashboard What Doesn\u0026rsquo;t No bill pay or cash flow forecasting tools built in Slightly expensive for a single user who won\u0026rsquo;t use the collaboration features Who Should Use It Couples who have argued about money. Partners who want transparency without micromanagement. Families consolidating finances under one roof.\n(Try Monarch Money free for 7 days at monarchmoney.com)\n3. Copilot — Best iPhone Experience Available Copilot is iOS-only, which immediately disqualifies it for many users. If you\u0026rsquo;re an iPhone user, though, the interface is genuinely the best of any app in this category. Transactions import cleanly, categorization is accurate, and the subscription tracking feature surfaces recurring charges with unusual clarity.\nWhat Works Machine learning categorization that improves with your corrections Subscription tracking that flags price increases Beautiful, fast native iOS design Flexible budget rules and rollover options What Doesn\u0026rsquo;t No Android app and none planned Web interface is read-only Who Should Use It iPhone users who\u0026rsquo;ve bounced off clunky budgeting apps before and want something they\u0026rsquo;ll actually open every day.\n(Try Copilot free for 30 days at copilot.money)\n4. Simplifi by Quicken — Best for Casual Budgeters Not everyone needs a rigorous budgeting system. If you mostly want to see where your money goes and get alerts when you overspend a category, Simplifi delivers that at the lowest price point of any paid app here.\nWhat Works Extremely easy setup — most people are running in under 10 minutes Customizable spending plan that doesn\u0026rsquo;t require zero-based methodology Refund tracker is genuinely useful Good reporting and spending trends What Doesn\u0026rsquo;t Investment tracking is shallow Less suited to users who want envelope or zero-based budgeting Who Should Use It People who\u0026rsquo;ve never used a budgeting app before and want a gentle entry point. Also good for people whose finances are already broadly under control and just want a dashboard.\n5. PocketGuard — Best for Curbing Overspending PocketGuard\u0026rsquo;s signature feature is \u0026ldquo;In My Pocket\u0026rdquo; — a real-time number showing how much you have available to spend today after accounting for bills, savings goals, and necessities. It\u0026rsquo;s blunt and effective.\nWhat Works The \u0026ldquo;In My Pocket\u0026rdquo; metric is genuinely useful for impulse-spending control Automatic bill negotiation feature in the Plus tier Clean, simple interface with minimal configuration required Free tier is functional (not just a trial) What Doesn\u0026rsquo;t Free tier limits the number of linked accounts Customization is shallow compared to YNAB or Monarch Who Should Use It Anyone who overspends habitually and wants a simple brake pedal. Also good for students or younger users who want free functionality without a big commitment.\n6. Goodbudget — Best for Envelope Budgeting Without Bank Sync Goodbudget is based on the envelope method: you allocate money to digital envelopes at the start of the month, then manually track spending against them. There\u0026rsquo;s no automatic bank sync, which some users find is actually a feature — the act of manually entering transactions makes spending more deliberate.\nWhat Works Envelope method is proven and psychologically effective Works for people uncomfortable linking bank accounts to third-party apps Syncs between family members manually Free tier is generous (20 envelopes, 2 devices) What Doesn\u0026rsquo;t Manual entry is time-consuming if you have high transaction volume No investment tracking Who Should Use It Privacy-conscious users, people who want a cash-like digital budgeting experience, or those living in countries where automatic bank sync is poorly supported.\n7. Empower (formerly Personal Capital) — Best Free Net Worth Tracker Empower is technically a wealth management company, and the free app is a lead-generation tool for their advisory services. That means the budgeting features are secondary to investment tracking — but for users who want a free, comprehensive net worth dashboard, nothing else comes close.\nWhat Works Best-in-class investment tracking including fee analysis Net worth dashboard aggregates all accounts in one place Retirement planning tools are genuinely sophisticated Completely free What Doesn\u0026rsquo;t Budgeting features are basic You will receive calls from their advisory team Who Should Use It Investors and people building toward financial independence who want to track net worth, investment fees, and portfolio allocation without paying for a financial planner.\nHow to Choose the Right App Ask yourself three questions:\nDo I need to change my behavior, or just track it? Behavior change → YNAB. Tracking → Simplifi or Empower. Am I managing money with a partner? Yes → Monarch Money. Am I on iPhone and willing to pay for quality UX? Yes → Copilot. The worst outcome is spending three weeks choosing an app instead of actually using one. Pick the closest match to your situation, use it for 60 days, then reassess.\nFinal Verdict Goal Recommended App Break the paycheck-to-paycheck cycle YNAB Manage finances as a couple Monarch Money Best iPhone experience Copilot Easiest setup Simplifi Control overspending PocketGuard Envelope budgeting without sync Goodbudget Track investments for free Empower All seven apps offer free trials or free tiers. There\u0026rsquo;s no reason not to test your top pick today — your future bank balance will thank you.\nRunning a freelance or side business in Japan? Budgeting apps track personal spending, but freee goes further — it handles business invoicing, expense categorization, and tax filing in one platform designed for sole proprietors. A smart complement to whichever personal budgeting app you choose.\nRelated Tools Calculate loan payments and payoff timeline → Loan Calculator Calculate percentages, discounts, and tips instantly → Percentage Calculator Create a free monthly budget plan → Budget Planner Calculate your take-home pay → Salary Calculator See how savings compound over time → Compound Interest Calculator Calculate how long to reach any savings target → Savings Goal Calculator Track your total net worth → Net Worth Calculator See how inflation affects your money → Inflation Calculator Related Templates Take control of your finances with these tools:\nSmart Budget Tracker (Excel Template) — Track income, expenses, and savings goals Side Hustle Starter Kit 2026 — Build additional income streams You May Also Like Best Budgeting Apps 2026: Full Comparison How to Build an Emergency Fund Fast (2026) 10 Ways ChatGPT Can Save You $500/Month ","permalink":"https://productivity-works.com/posts/best-budgeting-apps-2026/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eManaging money without a system is like navigating a city without a map. You might get somewhere eventually, but you\u0026rsquo;ll waste a lot of time and fuel along the way. Budgeting apps solve that problem by giving you a real-time picture of where your money goes — and more importantly, where it \u003cem\u003eshould\u003c/em\u003e go.\u003c/p\u003e\n\u003cp\u003eAfter testing dozens of apps over several months, we narrowed the field to seven that genuinely earn a place on your phone. Here\u0026rsquo;s the unfiltered breakdown.\u003c/p\u003e","title":"7 Best Budgeting Apps in 2026 (Free \u0026 Paid Compared)"},{"content":"Best ChatGPT Prompts for Productivity 2026 — Complete Guide If you\u0026rsquo;ve ever stared at a blank ChatGPT window thinking, \u0026ldquo;I know this thing is powerful, but what do I even type?\u0026rdquo; — you\u0026rsquo;re not alone. The difference between users who save 10+ hours a week with AI and those who don\u0026rsquo;t isn\u0026rsquo;t access to a better tool. It\u0026rsquo;s knowing exactly what to say to it.\nThis guide gives you battle-tested, copy-paste ChatGPT prompts for every corner of your workday — from clearing your inbox to planning your quarter. Whether you\u0026rsquo;re a freelancer drowning in admin, a knowledge worker buried in meetings, or a small business owner wearing too many hats, these prompts will change how you work.\nHere\u0026rsquo;s what you\u0026rsquo;ll learn:\nWhy most people use ChatGPT wrong (and how to fix it in 5 minutes) The 30 best ChatGPT prompts for productivity, organized by use case A comparison of prompt strategies so you can pick the right approach Step-by-step instructions to build your own prompt library Pro tips from power users who\u0026rsquo;ve automated 80% of their repetitive tasks Stick around — by the end of this guide, you\u0026rsquo;ll have a folder of prompts you\u0026rsquo;ll actually use every single day.\nWhy ChatGPT Prompts for Productivity Matter in 2026 The Productivity Gap The average knowledge worker spends 28% of their workweek managing email and nearly 20% searching for information they already have. That\u0026rsquo;s nearly half your week gone before you\u0026rsquo;ve done any real work. Meanwhile, professionals who have adopted structured AI workflows report reclaiming 8–12 hours per week — time they spend on creative work, client relationships, or simply not burning out.\nThe gap isn\u0026rsquo;t about who has access to AI. ChatGPT, Claude, Gemini — they\u0026rsquo;re all a browser tab away. The gap is about prompts. A vague prompt gives you a generic, mostly useless response. A well-crafted prompt gives you something you can ship in five minutes.\nHow AI Changes the Game In 2026, AI isn\u0026rsquo;t a curiosity — it\u0026rsquo;s a competitive advantage. The professionals pulling ahead aren\u0026rsquo;t necessarily smarter or harder-working. They have better prompts. They\u0026rsquo;ve learned to treat ChatGPT like a brilliant collaborator who needs clear briefs, context, and specific output requirements.\nThink of it this way: if you hired a world-class assistant and your only instruction was \u0026ldquo;help me with email,\u0026rdquo; you\u0026rsquo;d get mediocre results. But if you said \u0026ldquo;Draft a polite follow-up email to a client who hasn\u0026rsquo;t paid in 30 days — keep it firm but friendly, under 150 words,\u0026rdquo; you\u0026rsquo;d get something usable immediately. That specificity is the whole game.\nChatGPT Prompt Category Grid Best prompt types for workplace productivity — organized by time saved ✉️ Email Drafting 3–5 hrs saved/wk Beginner 📋 Meeting Summaries 1–2 hrs saved/wk Beginner ✍️ Content Creation 4–6 hrs saved/wk Intermediate 🗓️ Project Planning 2–4 hrs saved/wk Intermediate 🔬 Research Synthesis 3–6 hrs saved/wk Advanced 📄 SOP Writing 3–5 hrs saved/wk Intermediate 📊 Data Summaries 2–3 hrs saved/wk Intermediate 💻 Code \u0026amp; Automation 5–10 hrs saved/wk Advanced Beginner / Intermediate Intermediate Highest time-savings potential Best ChatGPT Prompts Compared: Categories \u0026amp; Effectiveness Category Time Saved/Week Difficulty Best For Email drafting 3–5 hours Beginner Everyone Meeting summaries 1–2 hours Beginner Managers, consultants Content creation 4–6 hours Intermediate Writers, marketers Data summarization 2–3 hours Intermediate Analysts, researchers Project planning 2–4 hours Intermediate PMs, founders SOP writing 3–5 hours Intermediate Business owners Research synthesis 3–6 hours Advanced Researchers, strategists Code \u0026amp; automation 5–10 hours Advanced Developers, power users Try It Free Step-by-Step Guide: How to Use ChatGPT Prompts for Maximum Productivity Step 1: Set the Role Always open a prompt by telling ChatGPT who it should be. This single habit improves output quality by 40–60%.\nYou are an expert [role] with 10+ years of experience in [field]. Your tone is [professional/casual/direct]. You write for [audience]. Step 2: Give Clear Context Don\u0026rsquo;t make ChatGPT guess. Tell it what you\u0026rsquo;re working with.\nEmail Follow-Up Prompt:\nYou are a professional business writer. I need to follow up with a client named Sarah at Acme Corp. She missed our invoice payment due 30 days ago. The invoice is for $2,400 for web design services. Write a firm but polite follow-up email. Keep it under 150 words. End with a clear call to action asking her to pay by Friday. Meeting Summary Prompt:\nYou are an executive assistant. Below are my raw notes from a 45-minute team meeting. Extract: 1. Key decisions made 2. Action items with owners and deadlines 3. Open questions that need follow-up 4. A 3-sentence executive summary [PASTE YOUR NOTES HERE] Weekly Planning Prompt:\nYou are a productivity coach specializing in time-blocking. Here are my tasks for this week: [LIST TASKS] Here are my working hours: Monday–Friday, 9am–6pm My most important project is: [PROJECT NAME] My energy peaks in the morning. Create a time-blocked schedule for me. Group similar tasks. Protect 2 hours each morning for deep work on my most important project. Step 3: Specify the Output Format Tell ChatGPT exactly how you want the answer structured.\nFormat your response as: - Bullet points (not paragraphs) - Under 200 words total - Start each point with an action verb - No jargon Step 4: Iterate with Follow-Ups The first response is rarely the final one. Use these follow-up prompts:\nMake it 30% shorter and more direct. Rewrite this for a non-technical audience. Give me 3 alternative versions I can choose from. Step 5: Save Your Best Prompts Create a simple text file or Notion page called \u0026ldquo;My Prompt Library.\u0026rdquo; Paste in any prompt that saved you more than 20 minutes. After two weeks, you\u0026rsquo;ll have a personal toolkit worth its weight in gold.\nMore essential prompts to add to your library:\nContent Repurposing Prompt:\nYou are a social media strategist. Take the blog post below and repurpose it into: 1. A LinkedIn post (150–200 words, professional tone, end with a question) 2. 5 tweets/X posts (each under 280 characters, punchy, no hashtags) 3. A short email newsletter section (100 words, conversational) [PASTE BLOG POST HERE] SOP Writing Prompt:\nYou are a business operations expert. Write a Standard Operating Procedure (SOP) for the following process: [DESCRIBE PROCESS] Format: - Title - Purpose (1 sentence) - Who this applies to - Step-by-step instructions (numbered) - What to do if something goes wrong - Last updated: [DATE] Research Summary Prompt:\nYou are a research analyst. Read the following text and give me: 1. The 5 most important facts or findings 2. What this means for [YOUR INDUSTRY/ROLE] 3. 3 action items I could take based on this information [PASTE TEXT HERE] Pro Tips \u0026amp; Advanced Techniques Common Mistakes to Avoid Mistake 1: Being too vague. \u0026ldquo;Write me an email\u0026rdquo; produces garbage. \u0026ldquo;Write a 100-word follow-up email to a SaaS prospect who attended our webinar but hasn\u0026rsquo;t booked a demo\u0026rdquo; produces gold.\nMistake 2: Ignoring the persona. Skipping \u0026ldquo;You are a [role]\u0026rdquo; is like hiring an expert and not telling them their job. Always set the role.\nMistake 3: Accepting the first draft. ChatGPT\u0026rsquo;s first response is a starting point. Push back, ask for alternatives, or give specific revision instructions.\nMistake 4: No context about your audience. Always specify who will read this: \u0026ldquo;Write for a busy CFO who has 30 seconds to scan this email.\u0026rdquo;\nMistake 5: Starting fresh every session. Build a system prompt you paste at the start of each session that includes your company name, tone, and audience. This alone upgrades every single response.\nPower User Strategies Strategy 1: Chain prompts. Use the output of one prompt as the input for the next. Outline → Draft → Edit → Format → Done.\nStrategy 2: Create prompt templates with placeholders. Use [BRACKETS] for variable parts so you can reuse the same prompt structure for different tasks.\nStrategy 3: Build a \u0026ldquo;morning brief\u0026rdquo; prompt. Paste your task list, emails, and calendar each morning and ask ChatGPT to prioritize and suggest how to spend your day.\nStrategy 4: Use ChatGPT to improve your prompts. Ask: \u0026ldquo;How could I improve this prompt to get a better response?\u0026rdquo; It will tell you.\nStrategy 5: Batch similar tasks. Instead of one email at a time, paste 5 email drafts and ask ChatGPT to polish all of them in one go.\nRelated: How to Use ChatGPT for Data Analysis Frequently Asked Questions Q: Are these prompts compatible with ChatGPT Free (GPT-4o)? A: Yes. All prompts in this guide work with any version of ChatGPT, including the free tier. GPT-4o users may get slightly richer responses, but the structure works universally.\nQ: How long should a prompt be? A: There\u0026rsquo;s no rule, but most high-performing prompts are 50–200 words. The key is specificity, not length. A crisp 50-word prompt beats a rambling 500-word one.\nQ: Can I use these prompts for Claude or Gemini too? A: Absolutely. These prompt structures work across all major AI models. Claude in particular tends to respond very well to detailed role-setting and structured output requests.\nQ: How do I stop ChatGPT from giving generic responses? A: Add constraints. Specify tone, word count, format, and audience. The more specific your constraints, the more specific — and useful — the output.\nQ: Is it worth paying for ChatGPT Plus ($20/month)? A: For regular professionals, yes. The speed improvement, access to advanced models, and image generation features more than pay for themselves in time saved.\nQ: How do I handle sensitive information in prompts? A: Never paste real names, financial data, or confidential business details into ChatGPT unless you\u0026rsquo;re using an enterprise version with data privacy controls. Use generic descriptions like \u0026ldquo;a mid-size client in the retail sector\u0026rdquo; instead of real names.\nQ: How many prompts should I save in my library? A: Start with 10–15 core prompts for your most common tasks. Quality over quantity. A focused library of 20 perfect prompts beats a cluttered folder of 200.\nQ: What\u0026rsquo;s the best way to organize my prompt library? A: Organize by task type (Email, Planning, Writing, Research) in a simple Notion page or text file. Tag prompts with the time they save you so you prioritize the highest-value ones.\nProductivity Skills That Employers Pay For Professionals who master AI productivity tools are landing better jobs at higher salaries. Find your next career on doda — explore thousands of tech and business roles on Japan\u0026rsquo;s leading job platform.\nConclusion \u0026amp; Call to Action The professionals winning in 2026 aren\u0026rsquo;t working harder — they\u0026rsquo;re prompting smarter. The prompts in this guide cover the highest-leverage tasks: email, planning, content creation, research, and SOPs. Start with three prompts from your biggest time-waster this week. Build your library from there.\nKey takeaways:\nAlways set a role and provide context before making your request Specify the output format to get usable results immediately Iterate on the first draft — it\u0026rsquo;s a starting point, not a final product Save your best prompts in a personal library and reuse them Want 100+ ready-to-use prompts organized by job function? Check out our Complete ChatGPT Prompt Collection — covering marketing, sales, writing, HR, finance, and more. Copy, paste, and ship.\nReady to go deeper? Our AI Productivity Playbook walks you through building a complete AI-powered workflow for your specific role — from morning routine to end-of-day wrap-up. Thousands of professionals have used it to reclaim 10+ hours per week.\nRelated: AI Prompt Engineering Tips for Beginners Related Articles ChatGPT vs Claude vs Gemini: The Definitive AI Comparison 2026 Claude AI vs ChatGPT Comparison 2026 AI Prompt Engineering Tips for Beginners 2026 How to Make Money With AI in 2026: 15 Realistic Ways That Work Best AI Tools for Small Business 2026: The Complete Roundup Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nRelated Tools Calculate your ideal freelance rate → Freelance Rate Calculator Create a monthly budget → Budget Planner Stay focused with the Pomodoro Technique → Pomodoro Timer Count down to any date or event → Countdown Timer Compare texts and find differences → Text Diff Checker Related Templates Put these techniques into practice with our ready-made templates:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Copy-paste prompts for every situation The AI Productivity Playbook 2026 — 50+ AI workflows and automation strategies ","permalink":"https://productivity-works.com/posts/best-chatgpt-prompts-for-productivity-2026/","summary":"\u003ch1 id=\"best-chatgpt-prompts-for-productivity-2026--complete-guide\"\u003eBest ChatGPT Prompts for Productivity 2026 — Complete Guide\u003c/h1\u003e\n\u003cp\u003eIf you\u0026rsquo;ve ever stared at a blank ChatGPT window thinking, \u0026ldquo;I know this thing is powerful, but what do I even type?\u0026rdquo; — you\u0026rsquo;re not alone. The difference between users who save 10+ hours a week with AI and those who don\u0026rsquo;t isn\u0026rsquo;t access to a better tool. It\u0026rsquo;s knowing exactly what to say to it.\u003c/p\u003e\n\u003cp\u003eThis guide gives you battle-tested, copy-paste ChatGPT prompts for every corner of your workday — from clearing your inbox to planning your quarter. Whether you\u0026rsquo;re a freelancer drowning in admin, a knowledge worker buried in meetings, or a small business owner wearing too many hats, these prompts will change how you work.\u003c/p\u003e","title":"Best ChatGPT Prompts for Productivity 2026"},{"content":"ChatGPT gets most of the headlines, but it\u0026rsquo;s not always the best tool for the job — and its free tier has limitations that can frustrate power users. In 2026, several free alternatives have matured to the point where they outperform ChatGPT in specific use cases, and some are completely free with no token limits.\nThis guide covers the best free ChatGPT alternatives available today, what they\u0026rsquo;re actually good at, and how to choose the right one for your specific needs.\nQuick Comparison Table Tool Developer Free Tier Context Window Best At Claude Anthropic Yes (generous) 200K tokens Long documents, nuanced writing Gemini Google Yes 1M tokens Research, Google integration Copilot Microsoft Yes (unlimited) Large Web search, everyday tasks Perplexity Perplexity AI Yes Moderate Real-time web research Meta AI Meta Yes Large Creative tasks, social media Mistral Le Chat Mistral AI Yes 32K tokens European privacy, coding Grok xAI Yes (limited) 128K tokens Real-time X/Twitter data You.com You.com Yes Moderate Search + chat hybrid 1. Claude (Anthropic) — Best Overall ChatGPT Alternative Free tier: Yes — Claude.ai offers free access to Claude 3.5 Sonnet with daily usage limits Paid tier: $20/month (Pro)\nClaude is the closest rival to ChatGPT in terms of general capability, and it surpasses it in several meaningful ways.\nWhat Claude Does Better Than ChatGPT Long document analysis. Claude\u0026rsquo;s 200,000-token context window on paid plans (and a generous window even on free) means you can paste an entire book, legal contract, or research paper and ask questions about it. ChatGPT Free is limited to shorter contexts.\nNuanced, human-quality writing. Claude\u0026rsquo;s writing output is consistently praised as more natural and less \u0026ldquo;AI-sounding\u0026rdquo; than ChatGPT. It avoids the over-enthusiastic opener phrases and generic structures that ChatGPT defaults to. If you\u0026rsquo;re writing anything meant to sound like a human — emails, articles, creative work — test Claude first.\nFollowing complex instructions. Claude excels at tasks with multiple, nested requirements. Give it a detailed system prompt or a multi-part task and it tends to follow all constraints more faithfully.\nThoughtful, nuanced reasoning. Claude tends to engage with moral ambiguity, edge cases, and complex tradeoffs more carefully — useful for analytical work, research, and writing that requires balance.\nCoding assistance. Claude 3.5 Sonnet scores highly on coding benchmarks and is particularly strong at explaining code in plain English, debugging, and writing modular, well-commented functions.\nLimitations Claude doesn\u0026rsquo;t browse the internet in the free tier. If you need current information, you\u0026rsquo;ll need Claude Pro or a different tool. It also doesn\u0026rsquo;t generate images.\nBest Use Cases for Claude (Free) Writing and editing long-form content Analyzing uploaded documents (PDFs, text files) Complex coding tasks Email drafting that sounds natural Research synthesis from provided materials Get it at: claude.ai\n2. Google Gemini — Best for Google Workspace Users Free tier: Yes — Gemini.google.com, unlimited (with Google account) Paid tier: $19.99/month (Advanced, includes Gemini 1.5 Ultra)\nGoogle\u0026rsquo;s Gemini has improved dramatically since its rocky launch. In 2026, the free tier (Gemini 1.5 Flash and Gemini 1.5 Pro) is genuinely capable and has some distinct advantages.\nWhat Gemini Does Better Than ChatGPT Massive context window. Gemini 1.5 Pro offers a 1 million token context window — the largest available in any chatbot. This means you can analyze multiple long documents simultaneously. Ask it to compare two lengthy reports, analyze an entire codebase, or work through a very long conversation without losing earlier context.\nGoogle integration. If you\u0026rsquo;re a Google Workspace user, Gemini integrates directly with Gmail, Docs, Sheets, and Drive. Ask it to summarize your recent emails, draft a Docs document, or analyze a spreadsheet — without copying and pasting content.\nMultimodal understanding. Gemini handles text, images, audio, and video natively. Upload a photo and ask questions about it, or describe an image for generation.\nReal-time information. Gemini can access current web search results by default in many cases — no need for a plugin or paid tier.\nImage generation. Gemini Free includes image generation via Imagen, which ChatGPT Free no longer provides.\nLimitations Gemini\u0026rsquo;s writing quality is generally a step below Claude and ChatGPT for nuanced, long-form content. It can feel more generic. For creative writing or anything requiring a distinctive voice, other tools perform better.\nBest Use Cases for Gemini (Free) Research with real-time web access Google Workspace users automating docs/email tasks Working with very long documents Image generation Multimodal tasks (images, audio) Get it at: gemini.google.com\n3. Microsoft Copilot — Best Free Option with No Limits Free tier: Yes — copilot.microsoft.com, no account required Paid tier: $20/month (Pro, includes priority GPT-4o access)\nMicrosoft Copilot (formerly Bing Chat) is powered by GPT-4o — the same model as ChatGPT Plus — but available completely free, with no daily message limits on the standard tier.\nWhat Copilot Does Better Than ChatGPT Free No daily limits. ChatGPT Free can hit usage limits during peak hours or after heavy use. Copilot provides access to GPT-4o with no usage cap on the free tier (though speed may throttle under heavy load).\nBuilt-in web search. Copilot searches the web by default for every query, providing up-to-date information with cited sources. ChatGPT Free doesn\u0026rsquo;t have web browsing.\nDeep Windows/Office integration. If you use Microsoft 365, Copilot is embedded in Word, Excel, PowerPoint, Outlook, and Teams. The integration goes beyond what Gemini offers in Google Workspace — particularly for data analysis in Excel.\nImage generation (DALL-E 3). Copilot includes free image generation via DALL-E 3, with no daily limit on the standard account tier.\nNo account required. You can use Copilot at copilot.microsoft.com without creating an account — useful for occasional use or trying it without commitment.\nLimitations Copilot\u0026rsquo;s conversation memory is shorter than ChatGPT or Claude. Very long, multi-step conversations can lose earlier context. It\u0026rsquo;s also more conservative on certain creative writing tasks.\nBest Use Cases for Copilot (Free) Web research with cited sources Everyday writing assistance without hitting limits Microsoft Office users (Word, Excel, PowerPoint help) Free image generation Quick tasks without logging in Get it at: copilot.microsoft.com\n4. Perplexity AI — Best for Real-Time Research Free tier: Yes — 5 Pro searches/day, unlimited standard searches Paid tier: $20/month (Pro, unlimited)\nPerplexity is not a typical chatbot — it\u0026rsquo;s an AI-powered search engine that provides answers with cited sources. For research tasks, it\u0026rsquo;s genuinely more useful than ChatGPT.\nWhat Perplexity Does Better Than ChatGPT Cited, verifiable answers. Every Perplexity response links to source URLs. You can verify information, explore original sources, and trust that responses are grounded in real documents. ChatGPT can and does hallucinate facts confidently — Perplexity\u0026rsquo;s source citations provide a check on this.\nCurrent information by default. Perplexity searches the live web for every query. No knowledge cutoff limitations.\nResearch threading. Perplexity\u0026rsquo;s Spaces feature lets you create research projects — a curated set of sources and conversations on a topic. It\u0026rsquo;s excellent for multi-session research projects.\nAcademic search. Perplexity can search academic databases (PubMed, arXiv, etc.) directly, making it valuable for research in science, medicine, and technology.\nLimitations Perplexity is optimized for research and factual Q\u0026amp;A. For creative writing, coding, or long-form content generation, ChatGPT and Claude are stronger. The free tier limits you to 5 \u0026ldquo;Pro\u0026rdquo; (deeper) searches per day.\nBest Use Cases for Perplexity (Free) Factual research requiring verification Current events and news Academic research Competitive intelligence and market research Any task where you need to cite sources Get it at: perplexity.ai\n5. Meta AI — Best for Social Media and Creative Tasks Free tier: Yes — completely free, no paid tier Integration: Built into WhatsApp, Instagram, Messenger, Facebook\nMeta AI (powered by Llama 3) is embedded directly in Meta\u0026rsquo;s social platforms and available at meta.ai. It\u0026rsquo;s 100% free with no paid tier.\nWhat Meta AI Does Better Than ChatGPT Zero cost, no limits. Meta AI is completely free with no usage caps or premium tiers. For users who need a capable AI without any subscription cost, it\u0026rsquo;s a strong option.\nSocial media integration. Access Meta AI directly inside WhatsApp, Instagram DMs, and Facebook — no context switching required. Ask for help writing a caption while you\u0026rsquo;re already in the app.\nReal-time web search. Meta AI can search the web and provides current information.\nImage generation. Meta AI generates images (via Imagine with Meta AI) for free, including animated GIFs.\nCreative and casual tasks. Meta AI performs well on creative writing, brainstorming, casual conversation, and social media content.\nLimitations Meta AI is less capable than ChatGPT, Claude, or Gemini for complex analytical tasks, coding, and long-form content generation. Privacy-conscious users should be aware of Meta\u0026rsquo;s data practices.\nBest Use Cases for Meta AI (Free) Social media content and captions Quick questions while already in Meta apps Image generation at no cost Casual creative tasks Users who want a completely free option with no account needed beyond Meta Get it at: meta.ai or directly in WhatsApp/Instagram/Messenger\n6. Mistral Le Chat — Best for Privacy-Conscious Users and Coding Free tier: Yes — le.chat.mistral.ai Paid tier: Starting at $14.99/month (Pro)\nMistral is a European AI company, and Le Chat is its consumer-facing chatbot. It runs on Mistral\u0026rsquo;s own models, which are among the best open-source models available.\nWhat Le Chat Does Better Than ChatGPT European privacy standards. Mistral complies with GDPR and is subject to EU data protection law — significant for European users or anyone with privacy concerns about US-based AI companies.\nCode generation. Mistral\u0026rsquo;s models score very highly on coding benchmarks, rivaling GPT-4o and Claude for programming tasks.\nSpeed. Le Chat often responds faster than ChatGPT or Claude, particularly on shorter tasks.\nOpen-source foundation. Mistral\u0026rsquo;s models can be run locally or via API, giving technical users maximum flexibility and privacy.\nDocument analysis. Le Chat can analyze uploaded documents in the free tier.\nLimitations Le Chat\u0026rsquo;s general knowledge depth and writing quality are somewhat behind the frontier models from OpenAI and Anthropic. For non-technical writing tasks, ChatGPT or Claude tend to produce better output.\nBest Use Cases for Le Chat (Free) Coding and programming assistance European users and GDPR-sensitive contexts Fast, straightforward tasks API integration (developers) Get it at: chat.mistral.ai\n7. Grok (xAI) — Best for Real-Time Twitter/X Data Free tier: Yes, on X (limited) Paid tier: Included in X Premium ($8/month)\nGrok, built by Elon Musk\u0026rsquo;s xAI, has one feature no other AI chatbot offers: real-time access to X (formerly Twitter) data. For monitoring trends, analyzing public opinion, or following breaking news through social media, Grok is unique.\nWhat Grok Does Better Than ChatGPT Real-time X data. Ask Grok what people are saying about a brand, event, or topic on X right now. It pulls from the live feed, something no other mainstream AI chatbot can do.\nUnfiltered responses. Grok has a reputation for being less conservative on certain topics than ChatGPT or Claude. For users who feel other AI tools are overly cautious, Grok may feel more direct.\nCurrent events via X. For news that breaks on social media before mainstream outlets, Grok often has it first.\nLimitations Grok\u0026rsquo;s general reasoning and writing quality lag behind GPT-4o and Claude 3.5 on most benchmarks. Free access is limited — meaningful use requires X Premium. It\u0026rsquo;s also most valuable for X-specific use cases; for general tasks, other free options are stronger.\nGet it at: x.com/i/grok (requires X account)\nHow to Choose the Right Free AI Chatbot Your Need Best Free Tool General writing and editing Claude (free) or Copilot Real-time research with sources Perplexity or Gemini Everyday use with no limits Copilot Google Workspace integration Gemini Long document analysis Claude or Gemini Coding assistance Claude or Mistral Le Chat Image generation Copilot (DALL-E 3) or Meta AI Social media content Meta AI X/Twitter trend monitoring Grok Privacy (GDPR) Mistral Le Chat No account, instant access Copilot Using Multiple AI Tools Strategically Power users don\u0026rsquo;t pick one AI tool — they build a small stack optimized for different tasks:\nResearch workflow: Start with Perplexity to gather current, cited information. Bring that research into Claude or ChatGPT for synthesis, analysis, and writing.\nWriting workflow: Draft in Claude (better prose quality), fact-check with Perplexity, polish with Grammarly.\nCoding workflow: Describe the problem in ChatGPT or Claude, get a solution, use Copilot in VS Code for inline suggestions while implementing.\nContent creation: Use Gemini for image generation, Claude for the written content, Canva AI for design.\nAI Skills Are the New Career Currency Knowing which AI tools to use — and how to use them effectively — is increasingly what separates candidates in competitive job markets. Find your next career on doda — Japan\u0026rsquo;s top job platform with thousands of tech and knowledge-work roles.\nThe Honest Take on Free Tiers Every free tier has trade-offs: slower speeds during peak hours, message limits, older model versions, or missing features. If you use AI daily for professional work, a $20/month paid subscription to your most-used tool will pay for itself quickly.\nThat said, the free tiers available in 2026 are genuinely remarkable. The combination of Copilot (unlimited GPT-4o access) + Perplexity (research with sources) + Claude (long-form writing) gives you a powerful AI workflow at zero cost.\nThe tools that have earned paid subscriptions from the Productivity Works team: Claude Pro for writing-heavy work, and Perplexity Pro for research-intensive projects. Both are worth the $20/month if those use cases match your workflow.\nWant a structured system for using these AI tools more effectively? Our AI Productivity Starter Pack on Payhip includes prompt templates for each major AI tool, a decision framework for choosing the right tool per task, and a 5-day quick-start plan — for writers, freelancers, and small business owners.\nRelated reads:\nBest AI Tools for Small Business 2026 How to Create a Resume with AI (Step-by-Step) How to Use Notion for Project Management 2026 Related Tools Create a monthly budget → Budget Planner Calculate your ideal freelance rate → Freelance Rate Calculator Generate secure passwords instantly → Password Generator Related Templates Put these techniques into practice with our ready-made templates:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Copy-paste prompts for every situation The AI Productivity Playbook 2026 — 50+ AI workflows and automation strategies This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/best-free-chatgpt-alternatives-2026/","summary":"\u003cp\u003eChatGPT gets most of the headlines, but it\u0026rsquo;s not always the best tool for the job — and its free tier has limitations that can frustrate power users. In 2026, several free alternatives have matured to the point where they outperform ChatGPT in specific use cases, and some are completely free with no token limits.\u003c/p\u003e\n\u003cp\u003eThis guide covers the best free ChatGPT alternatives available today, what they\u0026rsquo;re actually good at, and how to choose the right one for your specific needs.\u003c/p\u003e","title":"Best Free Alternatives to ChatGPT 2026: Complete Comparison"},{"content":"※本記事にはアフィリエイト広告が含まれています。\nThis article contains affiliate links.\nBest Index Funds for Beginners 2026 — Complete Guide If you\u0026rsquo;ve been wondering how ordinary people build extraordinary wealth over time, the secret is often hiding in plain sight: index funds. Warren Buffett famously recommended that his own estate be invested in low-cost S\u0026amp;P 500 index funds after his death. That\u0026rsquo;s not a throwaway comment — it\u0026rsquo;s a testament to a strategy that has consistently outperformed most professional money managers over the long run.\nYet for many beginners, the world of investing feels overwhelming. Which fund do you choose? How much do you need to start? What if the market crashes? These are exactly the questions we\u0026rsquo;re going to answer in this guide.\nIn this article, you\u0026rsquo;ll learn:\nWhat index funds are and why they beat most actively managed funds The best index funds available to beginners in 2026 How to compare funds by expense ratio, minimum investment, and performance Common mistakes new investors make — and how to avoid them A step-by-step plan to make your first investment today Whether you have $100 or $10,000 to start, index fund investing is one of the most proven, evidence-based paths to long-term financial freedom. Let\u0026rsquo;s dive in.\nWhat Is an Index Fund? The Basics Explained Definition \u0026amp; How It Works An index fund is a type of investment fund — either a mutual fund or an ETF (exchange-traded fund) — designed to replicate the performance of a specific market index, like the S\u0026amp;P 500 or the total U.S. stock market.\nInstead of a fund manager picking and choosing individual stocks (active management), an index fund simply buys all — or a representative sample — of the securities in a given index. If the S\u0026amp;P 500 goes up 12%, a fund tracking it goes up roughly 12% too.\nHere\u0026rsquo;s what makes index funds powerful:\nLow costs: No need to pay a team of analysts. Expense ratios on top index funds are as low as 0.03% per year. Instant diversification: One share of an S\u0026amp;P 500 index fund gives you exposure to 500 of the largest U.S. companies. Consistent performance: Decades of data show that most actively managed funds underperform their benchmark index over 10–15 years. Tax efficiency: Lower turnover means fewer taxable events compared to actively managed funds. For example, if you invest $10,000 in an index fund with a 0.03% expense ratio, you\u0026rsquo;ll pay just $3 per year in fees. An actively managed fund charging 1% would cost you $100 — and likely deliver worse returns.\nPros and Cons of Index Funds Pros:\nVery low fees (expense ratios often under 0.10%) Broad diversification reduces individual stock risk Historically strong long-term performance Simple to understand and manage Available in tax-advantaged accounts (401k, Roth IRA, HSA) No need to research individual companies Cons:\nYou\u0026rsquo;ll never \u0026ldquo;beat the market\u0026rdquo; — only match it Market downturns affect the whole fund (no defensive maneuvering) Some indexes are more concentrated than they appear (top 10 S\u0026amp;P 500 stocks = ~35% of the index) Limited control over what you\u0026rsquo;re invested in (may include companies you\u0026rsquo;d prefer to avoid) Related: Roth IRA vs Traditional IRA: Which Is Better? Best Index Funds for Beginners 2026: Top Options Compared The following table compares the most popular index funds available to U.S. investors in 2026. These are consistently recommended by financial educators, fee-conscious investors, and independent research.\nFund Name Ticker Type Expense Ratio Min. Investment Index Tracked 10-Yr Avg Return* Vanguard 500 Index Fund Admiral VFIAX Mutual Fund 0.04% $3,000 S\u0026amp;P 500 ~13.2% Vanguard Total Stock Market ETF VTI ETF 0.03% $1 (1 share) CRSP US Total Market ~13.5% Fidelity ZERO Total Market Index FZROX Mutual Fund 0.00% $0 Fidelity US Total Market ~13.4% iShares Core S\u0026amp;P 500 ETF IVV ETF 0.03% $1 (1 share) S\u0026amp;P 500 ~13.2% Schwab S\u0026amp;P 500 Index Fund SWPPX Mutual Fund 0.02% $0 S\u0026amp;P 500 ~13.1% Vanguard Total International Stock ETF VXUS ETF 0.07% $1 (1 share) FTSE Global All Cap ex US ~7.8% Fidelity 500 Index Fund FXAIX Mutual Fund 0.015% $0 S\u0026amp;P 500 ~13.2% Vanguard Total Bond Market ETF BND ETF 0.03% $1 (1 share) Bloomberg US Aggregate Bond ~3.1% Schwab Total Stock Market Index SWTSX Mutual Fund 0.03% $0 Dow Jones US Total Stock Market ~13.4% Vanguard Balanced Index Fund VBIAX Mutual Fund 0.07% $3,000 60% stocks / 40% bonds ~9.8% Historical returns are approximate and not a guarantee of future performance.\nCheck Current Fund Details How to Choose: Key Factors What to Look For Choosing the right index fund comes down to five key factors:\n1. Expense Ratio This is the annual fee charged as a percentage of your investment. Even a 0.5% difference compounds dramatically over decades. Always aim for funds with expense ratios below 0.10%.\n2. The Index Being Tracked Different indexes represent different slices of the market. S\u0026amp;P 500 = 500 large U.S. companies. Total market = the entire U.S. stock market. International = non-U.S. stocks. Know what you\u0026rsquo;re buying.\n3. Fund Size and Liquidity Larger funds (measured by assets under management, or AUM) tend to be more stable and have tighter bid-ask spreads if they\u0026rsquo;re ETFs. Stick with funds managing at least $1 billion in assets.\n4. Tax Location Index funds in a taxable brokerage account generate capital gains distributions. ETFs are generally more tax-efficient than mutual funds for taxable accounts. For retirement accounts (401k, Roth IRA), this matters less.\n5. Minimum Investment If you\u0026rsquo;re just starting out, funds with $0 minimums (like Fidelity\u0026rsquo;s ZERO funds or Schwab\u0026rsquo;s index funds) remove the barrier to entry completely.\nCommon Mistakes to Avoid Mistake 1: Chasing past performance Last year\u0026rsquo;s top performer is rarely next year\u0026rsquo;s winner. Focus on low costs and diversification, not recent returns.\nMistake 2: Checking your portfolio too often Frequent checking leads to emotional decisions. Index fund investing works over years and decades, not days and weeks.\nMistake 3: Not investing in tax-advantaged accounts first Before opening a taxable brokerage account, max out your 401k (at least to the employer match) and your Roth IRA. The tax savings are enormous.\nMistake 4: Waiting for the \u0026ldquo;perfect\u0026rdquo; time to invest Studies consistently show that \u0026ldquo;time in the market\u0026rdquo; beats \u0026ldquo;timing the market.\u0026rdquo; Every year you wait costs you compounding growth.\nMistake 5: Owning too many funds You don\u0026rsquo;t need 15 different index funds. A simple three-fund portfolio (US stocks + international stocks + bonds) covers virtually everything.\nRelated: How to Start Investing with $100 Step-by-Step Guide: How to Invest in Index Funds Step 1: Choose a brokerage account Open an account at a reputable broker. Top choices for beginners in 2026:\nFidelity — No-fee index funds, excellent research tools, $0 minimum Charles Schwab — Great customer service, fractional shares, $0 minimum Vanguard — The original index fund company, best for long-term buy-and-hold M1 Finance — Excellent for automated investing with \u0026ldquo;pies\u0026rdquo; (portfolio allocations) Step 2: Decide on the account type\nRoth IRA: Best for most beginners. Contributions are after-tax, but growth and withdrawals in retirement are tax-free. Contribution limit: $7,000/year in 2026 (under age 50). Traditional IRA: Contributions may be tax-deductible now, but you\u0026rsquo;ll pay taxes on withdrawals in retirement. 401k: If your employer offers a match, contribute at least enough to get the full match before opening an IRA. Free money is hard to beat. Taxable brokerage: Use this after maxing out tax-advantaged accounts. Step 3: Pick your index funds For most beginners, a simple two- or three-fund portfolio works best:\nOption A (Simple): 100% VTI or FZROX (total U.S. stock market) Option B (Balanced): 80% VTI + 20% VXUS (U.S. + international) Option C (Classic Three-Fund): 60% VTI + 30% VXUS + 10% BND Step 4: Set up automatic contributions Automate your investing so you invest a fixed amount each month regardless of market conditions. This is called dollar-cost averaging (DCA), and it removes emotion from the equation.\nStep 5: Rebalance annually Once a year, check if your allocation has drifted significantly from your target (e.g., stocks grew to 90% when you wanted 80%). Rebalance by selling some of the overweight asset and buying the underweight one.\nStep 6: Leave it alone Seriously. The biggest risk to your index fund returns is your own behavior. Don\u0026rsquo;t panic-sell during downturns. Market crashes are temporary; missing the recovery is permanent.\nFrequently Asked Questions Q: How much money do I need to start investing in index funds? A: With many brokers offering $0 minimums (like Fidelity and Schwab) and ETFs available for the price of one share (sometimes under $100), you can start with as little as $1. Fidelity\u0026rsquo;s ZERO funds require no minimum investment at all.\nQ: Are index funds safe? A: All investments carry risk, including index funds. Your investment can lose value if the market declines. However, index funds are broadly diversified, which reduces (but doesn\u0026rsquo;t eliminate) risk. Historically, broad U.S. stock market index funds have always recovered from downturns — though past performance doesn\u0026rsquo;t guarantee future results.\nQ: What is a good expense ratio for an index fund? A: Anything below 0.10% is excellent. The best funds charge 0.03% or less. Avoid any fund charging more than 0.5% without a compelling reason.\nQ: Can I lose all my money in an index fund? A: Theoretically, if every company in the index went to zero, yes. In practice, this has never happened with a broad market index fund. The scenario would require a complete collapse of the entire U.S. economy, which would make your cash and bonds worthless too.\nQ: Should I invest in a mutual fund or ETF version of an index fund? A: Both are excellent choices. ETFs are more tax-efficient in taxable accounts and can be traded intraday. Mutual funds are easier to set up automatic investments with specific dollar amounts. For most beginners in a Roth IRA, either works well.\nQ: How often should I check on my index fund investments? A: Once a quarter is plenty. Annual rebalancing is sufficient for most investors. More frequent checking tends to lead to worse decisions, not better ones.\nQ: What\u0026rsquo;s the difference between an S\u0026amp;P 500 fund and a total market fund? A: An S\u0026amp;P 500 fund tracks the 500 largest U.S. companies. A total market fund includes those 500 plus thousands of mid-cap and small-cap companies. Both are excellent; total market funds offer slightly broader diversification.\nQ: Can I invest in index funds inside a 401k? A: Yes, and you should. Most 401k plans offer at least a few index fund options. Look for funds with low expense ratios. If your plan only offers expensive options, contribute enough to get the employer match, then invest additional savings in a Roth IRA.\nConclusion: Start Investing in Index Funds Today Index fund investing is not a get-rich-quick scheme — it\u0026rsquo;s a get-rich-slowly strategy that has made millions of ordinary Americans financially independent. The math is simple: keep costs low, stay diversified, invest consistently, and let compound interest do the heavy lifting over decades.\nThe best index fund is the one you actually invest in, starting today. Don\u0026rsquo;t let perfection be the enemy of good. Open an account, choose a simple total market fund, set up automatic contributions, and resist the urge to tinker.\nYour future self will thank you.\nInvesting from Japan? Rakuten Securities offers NISA accounts, a wide selection of low-cost index funds including global ETFs, and an English-friendly interface — a strong choice for residents in Japan looking to build a long-term index fund portfolio.\nReady to start?\nOpen a Fidelity account with $0 minimum Compare index fund options at Charles Schwab Start a Roth IRA at Vanguard Related: ETF vs Mutual Fund — Which Should I Choose? Disclaimer: This article is for informational purposes only and does not constitute financial advice. Investment decisions should be made based on your individual circumstances. Please consult a qualified financial advisor before making investment decisions. Information is current as of the publication date — verify details on official websites.\nDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nRelated Tools \u0026amp; Articles Calculate percentages, discounts, and tips instantly → Percentage Calculator Dividend Income Calculator — Calculate dividend income from ETFs with DRIP compounding Compound Interest Calculator — See how investments grow over time Retirement Savings Calculator — Plan your 401(k) and IRA contributions Savings Goal Calculator — Calculate how long to reach any savings target Track your total net worth → Net Worth Calculator Plan your path to financial independence → FIRE Calculator ETF vs Mutual Fund: Which Should I Choose? Roth IRA vs Traditional IRA: Which Is Better? How to Start Investing with $100 in 2026 Related Templates Take control of your finances with these tools:\nSmart Budget Tracker (Excel Template) — Track income, expenses, and savings goals Side Hustle Starter Kit 2026 — Build additional income streams ","permalink":"https://productivity-works.com/posts/best-index-funds-for-beginners-2026/","summary":"\u003cp\u003e※本記事にはアフィリエイト広告が含まれています。\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"best-index-funds-for-beginners-2026--complete-guide\"\u003eBest Index Funds for Beginners 2026 — Complete Guide\u003c/h1\u003e\n\u003cp\u003eIf you\u0026rsquo;ve been wondering how ordinary people build extraordinary wealth over time, the secret is often hiding in plain sight: index funds. Warren Buffett famously recommended that his own estate be invested in low-cost S\u0026amp;P 500 index funds after his death. That\u0026rsquo;s not a throwaway comment — it\u0026rsquo;s a testament to a strategy that has consistently outperformed most professional money managers over the long run.\u003c/p\u003e","title":"Best Index Funds for Beginners 2026"},{"content":"Best Notion AI Templates for Productivity 2026 — Complete Guide Notion is already one of the most powerful productivity tools ever built — a flexible workspace that combines notes, databases, wikis, and project management in one place. Add Notion AI to the mix, and you have something remarkable: a workspace that doesn\u0026rsquo;t just store your thinking, it actively helps you do it.\nBut Notion\u0026rsquo;s flexibility is also its biggest challenge. An empty Notion workspace is like a blank canvas — infinite possibility, zero direction. Most people set up their workspace poorly, never discover the AI features that would change their daily workflow, and end up using Notion like a fancy notes app.\nThis guide fixes that.\nHere\u0026rsquo;s what you\u0026rsquo;ll learn:\nThe best Notion AI templates for every productivity use case in 2026 How to set up Notion AI to actually work with your workflow The specific AI features most users overlook (and how to activate them) Step-by-step setup guides for the 5 most valuable template categories How to build custom AI-enhanced templates tailored to your work Whether you\u0026rsquo;re a knowledge worker, project manager, freelancer, or content creator — by the end of this guide, your Notion will be doing work you didn\u0026rsquo;t know was possible.\nWhy Notion AI Templates for Productivity Matter in 2026 The Productivity Gap The average Notion user is using about 15% of the tool\u0026rsquo;s capability. They have a notes database, maybe a task list, and a collection of half-finished templates they found online. Meanwhile, power users have built systems where Notion AI automatically summarizes meeting notes, generates weekly plans, drafts project briefs, and surfaces action items from messy brainstorm pages.\nThe difference isn\u0026rsquo;t intelligence — it\u0026rsquo;s template architecture. A well-designed template with AI actions built in transforms Notion from a storage tool into an active productivity partner.\nHow AI Changes the Game Notion AI (powered by an underlying large language model) is embedded directly in your workspace. Unlike using ChatGPT in a separate tab, Notion AI acts on your actual content. It can summarize a 10-page document you wrote last month, extract action items from your meeting notes, draft a project brief from your bullet-point idea, and translate your content — all without leaving your workspace.\nCombined with the right template structure, this makes Notion a genuinely intelligent second brain.\nBest Notion AI Templates Compared Template Category Built-in AI Use Complexity Best For Weekly Planner + Review Auto-generate weekly agenda, AI review summary Beginner Everyone Project Management Hub AI brief generation, status summaries Intermediate PMs, freelancers Meeting Notes System Auto-summarize, extract action items Beginner Teams, managers Content Calendar AI content ideas, brief generation Intermediate Writers, marketers Second Brain / PKM AI-powered search, synthesis, linking Advanced Researchers, knowledge workers CRM (Client Tracker) AI follow-up drafts, status summaries Intermediate Freelancers, sales Goal Setting System AI check-ins, progress analysis Intermediate Entrepreneurs, executives Reading List + Notes AI summaries, key insight extraction Beginner Learners, researchers Step-by-Step Guide: How to Set Up the Best Notion AI Templates Step 1: Set Up Your Weekly Planner with AI Review This is the highest-leverage template for most people. It structures your week in advance and uses AI to review what happened.\nTemplate structure:\nCreate a page called \u0026ldquo;Weekly Operating System\u0026rdquo; with these sections:\n--- ## Week of [DATE] ### This Week\u0026#39;s Focus (The Big 3) 1. [MOST IMPORTANT OUTCOME THIS WEEK] 2. [SECOND PRIORITY] 3. [THIRD PRIORITY] ### Monday — Friday: Daily Plan Each day has: - Top task (1 thing that must happen) - Supporting tasks (3–5 items) - Time blocks (using Notion\u0026#39;s timeline view) - End-of-day reflection (2 sentences) ### Friday: Weekly Review [AI Block — Auto-generate from the week\u0026#39;s content] Use Notion AI to generate your weekly review:\nIn the Weekly Review section, type \u0026ldquo;/\u0026rdquo; to open the command menu, then \u0026ldquo;AI\u0026rdquo; → \u0026ldquo;Generate from page\u0026rdquo; with this prompt:\nBased on everything recorded on this page this week, write a structured weekly review covering: 1. Key wins (2–3 things that went well) 2. What didn\u0026#39;t get done and why 3. Lessons learned 4. Top 3 priorities for next week Keep it under 200 words. Be honest and direct. Step 2: Build a Meeting Notes System with AI Action Items Every meeting should generate a structured summary automatically.\nTemplate for each meeting:\n# Meeting: [TITLE] Date: [DATE] Attendees: [NAMES] Purpose: [1 SENTENCE] --- ## Raw Notes [Paste or type notes during the meeting — messy is fine] --- ## AI Summary [Generate with Notion AI] --- ## Action Items [Extract with Notion AI] --- ## Decisions Made [Extract with Notion AI] After every meeting, use these AI prompts in Notion:\nFor the Summary section:\nSummarize these meeting notes in 3–5 bullet points. Focus on decisions made, key information shared, and the overall outcome of the meeting. Skip small talk and off-topic tangents. For Action Items:\nExtract all action items from these notes. For each item, list: - The task - Who owns it (if mentioned) - The deadline (if mentioned) Format as a numbered list. For Decisions:\nList all decisions that were made or agreed upon during this meeting. Present as a bulleted list of clear, declarative statements. Pro tip: Create a Master Meetings Database with a linked view on your Weekly Planner page. Filter to show only this week\u0026rsquo;s meetings. Notion AI can then generate a \u0026ldquo;week in review\u0026rdquo; that pulls from all meeting summaries.\nStep 3: Build a Content Calendar with AI Brief Generation For creators, marketers, and freelancers who produce content regularly.\nDatabase properties for your Content Calendar:\nProperty Type Values Content Title Title — Type Select Blog, LinkedIn, Email, Video, Social Status Select Idea, Brief, Draft, Review, Published Publish Date Date — Target Keyword Text — Platform Multi-select Website, LinkedIn, Twitter, Email AI Brief Toggle Embedded AI-generated brief AI Content Brief Prompt (run in each content card):\nWrite a content brief for the following piece: Title: [TITLE] Type: [BLOG POST / LINKEDIN / EMAIL / etc.] Target keyword: [KEYWORD] Target audience: [DESCRIBE AUDIENCE] Goal: [WHAT THIS CONTENT SHOULD ACHIEVE] Include: 1. Recommended structure (H2 headings) 2. The strongest angle/hook 3. 3 key points to cover 4. Suggested call to action 5. Estimated word count Step 4: Build a Second Brain with AI-Powered Synthesis A Second Brain in Notion stores everything you learn and lets you connect ideas across notes, articles, and projects.\nCore databases for your Second Brain:\nNotes Database — Everything you capture (articles, ideas, quotes, observations) Projects Database — Active work with linked notes Areas Database — Ongoing responsibilities (health, finance, work, learning) Resources Database — Reference material and archived notes AI use cases in your Second Brain:\nAfter capturing 10 notes on a topic, ask AI to synthesize:\nI\u0026#39;ve collected these notes on [TOPIC]: [PASTE OR LINK NOTES] Synthesize the key insights: 1. What are the 3 most important things these notes collectively say? 2. What tensions or contradictions exist between different notes? 3. What questions do these notes raise that I haven\u0026#39;t answered yet? 4. What\u0026#39;s one actionable insight I could apply this week? For weekly review of your notes:\nI\u0026#39;ve added [X] new notes this week. Based on their titles and content, what themes are emerging in my thinking? What topics am I gravitating toward? What does this say about where my focus is right now? Step 5: Build an AI-Powered Goal Tracker Goals without check-in systems don\u0026rsquo;t get achieved. This template builds in AI-powered progress analysis.\nGoal Card Template:\n# Goal: [GOAL TITLE] --- ## What I Want to Achieve [Clear, measurable outcome — include a metric if possible] --- ## Why This Matters [Your personal reason — this is your motivation anchor] --- ## Key Results / Milestones - [ ] [Milestone 1] — Target: [DATE] - [ ] [Milestone 2] — Target: [DATE] - [ ] [Milestone 3] — Target: [DATE] --- ## Weekly Progress Notes [Append progress notes each week] --- ## AI Check-In (Monthly) [Generate with Notion AI] Monthly AI check-in prompt:\nBased on the progress notes for this goal, write an honest monthly check-in that covers: 1. Progress so far (what % complete based on milestones) 2. Momentum assessment (accelerating, on track, stalling, off track) 3. The #1 blocker or risk 4. A specific recommendation for the next 30 days 5. A brief encouraging note that is honest, not generic Be direct. If I\u0026#39;m behind, say so clearly with a specific suggestion. Pro Tips \u0026amp; Advanced Techniques Common Mistakes to Avoid Mistake 1: Building templates that are too complex. A gorgeous 20-section template you never use beats a simple 5-section template that you use daily. Start with the minimum viable template.\nMistake 2: Not using Notion AI on existing content. Most users only use Notion AI when creating new content. The biggest gains come from using it to process existing notes: summarize that 30-page project doc, extract action items from last quarter\u0026rsquo;s notes, find the common thread across 50 meeting notes.\nMistake 3: Ignoring Notion\u0026rsquo;s database features. AI on top of a well-structured database is where Notion really shines. Flat pages are fine for notes, but databases with filtered views enable AI to work on structured, organized data.\nMistake 4: Not connecting databases with relations. Your meetings database should relate to your projects database. Your notes should link to areas and projects. This cross-linking is what makes Notion AI\u0026rsquo;s synthesis powerful.\nMistake 5: Treating Notion AI as a search engine. Notion AI doesn\u0026rsquo;t search the web — it works on your content. Use it for synthesis, summarization, and generation based on what you\u0026rsquo;ve written. Use web search for external research.\nPower User Strategies Strategy 1: Build a \u0026ldquo;Weekly Operating System\u0026rdquo; meeting every Monday morning. Open Notion, let AI generate your weekly plan from last week\u0026rsquo;s carryover items, upcoming calendar events (you copy-paste these in), and your current project statuses. 15 minutes → a fully structured week.\nStrategy 2: Use Notion AI for client-facing documents. Keep your client project database in Notion. When a status update is due, use AI to generate a draft from your progress notes. Review, adjust, copy to email or PDF.\nStrategy 3: Build a \u0026ldquo;capture everything, synthesize weekly\u0026rdquo; habit. Throughout the week, dump all ideas, links, and notes into a single \u0026ldquo;Inbox\u0026rdquo; page in Notion. Every Friday, ask AI to synthesize the week\u0026rsquo;s captures into themes and action items. This is the most underused habit in personal productivity.\nStrategy 4: Use AI to write your Notion database documentation. If you\u0026rsquo;re building a complex Notion system for a team, ask AI to write onboarding documentation explaining how each database works, what each property means, and how to use it correctly.\nStrategy 5: Combine Notion with Zapier for automated AI workflows. New project created in Notion → Zapier sends it to ChatGPT for a project brief → the brief is pasted back into Notion automatically. Notion + Zapier + AI = your personal automated EA.\nRelated: How to Automate Tasks with AI Step by Step Frequently Asked Questions Q: Is Notion AI worth the cost? A: Notion AI is $10/month added to any Notion plan. If you spend even 1 hour per week summarizing notes or drafting content that AI could do in 5 minutes, it pays for itself many times over. For knowledge workers, it\u0026rsquo;s one of the clearest AI ROI calculations.\nQ: What\u0026rsquo;s the difference between Notion AI and ChatGPT? A: Notion AI is embedded in your workspace and can act on your actual Notion content. ChatGPT is a standalone conversational AI that you copy-paste information into. For synthesizing and generating from your own notes and databases, Notion AI is far more convenient. For versatile, complex tasks, ChatGPT is more capable.\nQ: Can I use free Notion AI templates from the web? A: Yes. Notion\u0026rsquo;s template gallery (notion.so/templates) has hundreds of free templates. For AI-enhanced templates, search \u0026ldquo;AI\u0026rdquo; in the gallery. Also check communities on Reddit (r/Notion) and YouTube for free template shares.\nQ: Can Notion AI access the internet? A: No. Notion AI only has access to the content in your Notion workspace, plus knowledge from its training data. It doesn\u0026rsquo;t browse the web or access real-time information.\nQ: Is Notion good for team use? A: Yes — Notion\u0026rsquo;s Business plan ($16/user/month) is excellent for teams. Notion AI is available across all team plans. It\u0026rsquo;s particularly useful for shared meeting notes, project documentation, and team wikis.\nQ: How do I migrate from other tools (Evernote, OneNote, Obsidian) to Notion? A: Notion has built-in importers for Evernote and other tools. For Obsidian (Markdown files), export as .md files and import into Notion using their Markdown importer. Expect some formatting cleanup.\nQ: What\u0026rsquo;s the best Notion template for a complete beginner? A: Start with Notion\u0026rsquo;s official \u0026ldquo;Getting Things Done\u0026rdquo; or \u0026ldquo;Personal Home\u0026rdquo; template from their gallery — both are simple enough to start using immediately and can be expanded with AI features once you\u0026rsquo;re comfortable.\nQ: Can I build Notion templates and sell them? A: Yes. There\u0026rsquo;s a thriving market for premium Notion templates on Gumroad, Etsy, and direct landing pages. High-quality templates sell for $15–$99. AI-enhanced templates command premium prices.\nNotion Skills Are Increasingly a Career Asset Project managers, content creators, and operations professionals who master Notion are landing better roles. Find your next career on doda — Japan\u0026rsquo;s leading job platform with thousands of knowledge-work and operations roles.\nConclusion \u0026amp; Call to Action Notion AI transforms Notion from a passive note-taking tool into an active productivity partner. The templates in this guide — weekly planner, meeting notes system, content calendar, second brain, and goal tracker — cover the five highest-leverage use cases for most professionals.\nKey takeaways:\nStart with the weekly planner and meeting notes system — highest immediate ROI Use AI for synthesis and summarization on your existing content, not just new creation Well-structured databases + Notion AI = far more powerful than flat pages Connect databases with relations to enable cross-project AI synthesis Combine Notion with Zapier for fully automated AI workflows Want pre-built Notion templates with AI prompts already embedded? Our Complete ChatGPT Prompt Collection includes a Notion-specific section with copy-paste AI prompts for every template type in this guide — so you don\u0026rsquo;t have to write the prompts from scratch.\nFor building a complete AI-powered productivity system (Notion + automation + ChatGPT all working together), our AI Productivity Playbook is the end-to-end guide you\u0026rsquo;ve been looking for.\nRelated: Best ChatGPT Prompts for Productivity 2026 Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nRelated Tools Create a monthly budget → Budget Planner Calculate your ideal freelance rate → Freelance Rate Calculator Stay focused with the Pomodoro Technique → Pomodoro Timer Related Templates Put these techniques into practice with our ready-made templates:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Copy-paste prompts for every situation The AI Productivity Playbook 2026 — 50+ AI workflows and automation strategies ","permalink":"https://productivity-works.com/posts/best-notion-ai-templates-for-productivity-2026/","summary":"\u003ch1 id=\"best-notion-ai-templates-for-productivity-2026--complete-guide\"\u003eBest Notion AI Templates for Productivity 2026 — Complete Guide\u003c/h1\u003e\n\u003cp\u003eNotion is already one of the most powerful productivity tools ever built — a flexible workspace that combines notes, databases, wikis, and project management in one place. Add Notion AI to the mix, and you have something remarkable: a workspace that doesn\u0026rsquo;t just store your thinking, it actively helps you do it.\u003c/p\u003e\n\u003cp\u003eBut Notion\u0026rsquo;s flexibility is also its biggest challenge. An empty Notion workspace is like a blank canvas — infinite possibility, zero direction. Most people set up their workspace poorly, never discover the AI features that would change their daily workflow, and end up using Notion like a fancy notes app.\u003c/p\u003e","title":"Best Notion AI Templates for Productivity 2026"},{"content":" Best Productivity Apps for Remote Workers in 2026 Remote work is mainstream. But \u0026ldquo;working from home\u0026rdquo; and \u0026ldquo;working productively from home\u0026rdquo; are two entirely different things — and the difference often comes down to the tools you use.\nAfter testing 40+ apps across a year of fully remote work, these are the tools that actually made us more productive, less stressed, and better at communicating with distributed teams.\nThis is not a list of every app that exists. It\u0026rsquo;s the tools we\u0026rsquo;d actually recommend to a friend starting remote work today.\nHow We Evaluated These Apps Every app in this guide was tested for at least 30 days by remote workers across different roles: writers, developers, project managers, designers, and business owners. We evaluated:\nActual time saved (tracked, not estimated) Integration quality with other tools Learning curve vs. payoff Free tier vs. paid tier difference Mobile app quality (important for async work) Category 1: Communication and Meetings 1. Slack — 9.5/10 Price: Free (limited history) | $7.25/user/month (Pro) Best for: Team communication, async messaging\nSlack remains the gold standard for remote team communication in 2026. Its AI-powered features have improved significantly:\nSlack AI summarizes channel activity you missed (massive for async teams) Huddles for quick voice/video calls without calendar friction Canvas for collaborative documents within conversations Workflow Builder for automating routine communications Why it still wins: The combination of threaded conversations, channel organization, and integrations with 2,000+ tools makes Slack the backbone of most remote teams\u0026rsquo; communication.\nBiggest limitation: It can become a distraction machine if you don\u0026rsquo;t set strong notification boundaries.\nBest practice: Use status messages religiously, set Do Not Disturb outside working hours, and check Slack on a schedule rather than leaving it open all day.\n2. Loom — 9/10 Price: Free (25 videos) | $12.50/user/month (Business) Best for: Async video communication\nLoom has changed how remote teams communicate about complex topics. Instead of writing a long explanation or scheduling a meeting, you record a 2-minute screen share and send the link.\nWhen Loom shines:\nExplaining a bug or technical issue visually Giving feedback on design work Onboarding new team members with recorded walkthroughs Replacing \u0026ldquo;let me hop on a quick call\u0026rdquo; with something actually quicker The AI features now auto-generate transcripts, chapter markers, and summaries from your recordings.\nTime saved: Teams using Loom consistently report replacing 30-50% of their synchronous meetings.\n3. Zoom — 8/10 Price: Free (40-min limit) | $13.33/month (Pro) Best for: Video meetings, webinars, large team calls\nZoom remains the default for video meetings. AI Companion now provides real-time transcription, meeting summaries, and action item extraction — valuable features that reduce the burden of note-taking.\nAlternatives worth considering:\nGoogle Meet — excellent free option for Google Workspace users Whereby — simpler, browser-based, no download required Around — better for casual team hangouts 4. Otter.ai — 8.5/10 Price: Free (300 minutes/month) | $10/month (Pro) Best for: Meeting transcription and notes\nOtter joins your meetings (Zoom, Google Meet, Teams) and transcribes everything in real time. Post-meeting, it generates a summary with action items. For remote workers who attend many meetings, Otter eliminates the cognitive burden of note-taking.\nAI features in 2026: Otter can now answer questions about past meeting content (\u0026ldquo;What did we decide about the Q3 budget?\u0026rdquo;) — useful for catching up on meetings you missed.\nCategory 2: Project Management and Organization 5. Notion — 9.5/10 Price: Free | $8/month (Plus) | $15/month (Business) Best for: All-in-one workspace: notes, wikis, project management, databases\nNotion has become the central operating system for thousands of remote teams. Its flexibility means you can build almost any workflow:\nPersonal task management Team wikis and SOPs Project databases with multiple views Meeting notes and documentation Goal tracking and OKRs Notion AI ($10/user/month add-on) integrates writing assistance, document summarization, and database automation directly into your workspace.\nBiggest strength: One tool that replaces Confluence (wikis), Trello (project boards), and Google Docs (documents) for most teams.\nSee our full guide: Notion Project Management Guide 2026 6. Linear — 9/10 Price: Free | $8/user/month (Standard) Best for: Software development teams and technical project management\nIf you work in tech, Linear\u0026rsquo;s speed and design are remarkable. Issues are created, updated, and triaged faster than any other tool. The keyboard-first interface and automatic cycle tracking make it significantly more efficient than Jira for small to mid-size teams.\nFor non-tech teams: Linear is over-engineered. Use Notion or Asana instead.\n7. Asana — 8/10 Price: Free | $10.99/user/month (Premium) Best for: Non-technical project management and team task tracking\nAsana\u0026rsquo;s AI features (workflow automation, smart summaries, goal tracking) have made it significantly more powerful. The Timeline view for project planning and the Goals feature for tracking OKRs are genuinely useful.\nBest for: Marketing teams, operations, HR, and any team that needs structured task and project tracking without the complexity of Jira.\n8. Todoist — 8.5/10 Price: Free | $4/month (Pro) Best for: Personal task management for individuals\nFor individual remote workers managing their own tasks, Todoist remains one of the best options. Natural language input (\u0026ldquo;Meeting with Sarah tomorrow at 2pm #work\u0026rdquo;) makes task capture fast. The AI assistant suggests due dates, priorities, and labels.\nAlternative: Any.do and TickTick are strong competitors with similar feature sets.\nCategory 3: Focus and Deep Work 9. Freedom — 8.5/10 Price: $3.33/month (annual plan) Best for: Blocking distracting websites and apps\nRemote work\u0026rsquo;s biggest challenge is distraction. Freedom blocks distracting websites and apps across all your devices simultaneously — so you can\u0026rsquo;t just switch from your laptop to your phone to check Twitter.\nSession options: Scheduled recurring blocks, locked sessions (can\u0026rsquo;t be turned off), and custom blocklists.\nTime saved: Studies show distraction-free work produces results 40-60% faster. If Freedom\u0026rsquo;s blocks save you 1 hour of productive work per day, it\u0026rsquo;s worth hundreds of dollars a month.\n10. Focusmate — 8/10 Price: Free (3 sessions/week) | $6.99/month (Pro) Best for: Accountability and motivation\nFocusmate matches you with a stranger for a 50-minute virtual co-working session. You both share what you\u0026rsquo;re working on, mute yourselves, and work. The social accountability dramatically increases follow-through.\nSounds odd. Works incredibly well. Remote workers who struggle with motivation consistently cite Focusmate as transformational.\n11. Brain.fm — 8/10 Price: $6.99/month Best for: Focus music backed by neuroscience\nBrain.fm uses AI-generated music designed to induce flow states. Unlike regular background music, Brain.fm\u0026rsquo;s audio includes patterns specifically engineered to sustain attention. In blind tests, users report 20-30% higher focus duration compared to no music or regular playlists.\nAlternatives: Focus@Will, Endel — similar neuroscience-backed focus audio.\nCategory 4: File Management and Documentation 12. Notion (see above) 13. Google Drive / Google Workspace — 9/10 Price: Free (15GB) | $6-18/user/month (Workspace) Best for: File storage, collaborative documents\nGoogle Drive + Docs + Sheets + Slides remains one of the most productive ecosystems for remote collaboration. Real-time collaboration in Docs is still best-in-class. Gemini AI integrations are now embedded throughout.\n14. Dropbox — 7.5/10 Price: $11.99/month (Plus) Best for: File sync and sharing when you need non-Google options\nDropbox\u0026rsquo;s sync reliability is exceptional, and Dropbox Paper provides collaborative documentation. Less compelling in 2026 given Google Drive\u0026rsquo;s improvements, but still preferred by many creative teams.\nCategory 5: Automation and AI Productivity 15. Zapier — 9/10 Price: Free (100 tasks/month) | $19.99/month (Starter) Best for: Connecting apps and automating repetitive workflows\nZapier connects 6,000+ apps and includes AI-powered steps that can write, classify, or transform data during automation. For remote workers, common high-value automations include:\nSending Slack notifications when new leads come in Logging time entries automatically from calendar events Routing support emails to the right team member Weekly digest emails of team activity Time saved: Teams with thoughtful Zapier automation typically save 5-10 hours per week across repetitive tasks.\n16. ChatGPT Plus or Claude Pro — 9.5/10 Price: $20/month each Best for: AI assistance across writing, research, analysis\nFor remote workers, an AI assistant is now as essential as email. The ability to draft communications, summarize documents, analyze data, and brainstorm solutions in seconds changes how much any individual can accomplish.\nBest AI workflows for remote workers:\nSummarize long email threads before responding Draft Slack messages and emails in your voice Research topics before meetings Create meeting agendas from bullet points Analyze and explain data in spreadsheets See our comparison: ChatGPT vs Claude vs Gemini 2026 17. Toggl Track — 8.5/10 Price: Free | $9/user/month (Starter) Best for: Time tracking for freelancers and remote workers\nToggl\u0026rsquo;s automatic time tracking (desktop app detects what you\u0026rsquo;re working on) and simple manual tracking make it the most frictionless time tracker available. Essential for freelancers billing clients, and valuable for anyone who wants to understand where their time actually goes.\nCategory 6: Health and Wellbeing Tools 18. Headspace or Calm — 8/10 Price: $12.99/month (Headspace) | $14.99/month (Calm) Best for: Stress management and mental health\nRemote work blurs boundaries between work and personal life. Meditation apps provide structured ways to decompress, manage stress, and mentally transition between work mode and home mode.\nFree alternative: Insight Timer has hundreds of free guided meditations.\n19. Stretchly — 7.5/10 Price: Free (open source) Best for: Forced break reminders\nStretchly automatically interrupts your computer session for micro-breaks (20 seconds every 20 minutes) and longer breaks. Following these intervals reduces eye strain, back pain, and cognitive fatigue.\nThe Essential Remote Work Stack (By Budget) Free Stack (Zero Cost) Communication: Slack (free) + Google Meet Project management: Notion (free) + Todoist (free) File storage: Google Drive (15GB) AI: ChatGPT Free + Claude Free Automation: Zapier (free) Focus: Stretchly (free) Total: $0/month\nProfessional Stack (~$50-70/month) Slack Pro ($7.25/month) Notion Plus ($8/month) ChatGPT Plus or Claude Pro ($20/month) Otter.ai Pro ($10/month) Zapier Starter ($19.99/month) Freedom ($3.33/month) Total: ~$68/month\nPower User Stack (~$100-130/month) Everything in the Professional Stack, plus:\nLoom Business ($12.50/month) Linear Standard ($8/month) Toggl Track ($9/month) Total: ~$97-130/month\nSetting Up Your Remote Work Environment for Success Apps alone won\u0026rsquo;t make you productive. The setup around them matters:\n1. Dedicated workspace A separate room or at minimum a specific desk creates psychological separation between work and home modes.\n2. Consistent schedule Remote work flexibility is valuable, but consistent start/end times prevent the \u0026ldquo;always on\u0026rdquo; trap.\n3. Notification discipline Turn off all non-critical notifications. Check Slack and email on a schedule, not constantly.\n4. Morning routine that includes a \u0026ldquo;commute substitute\u0026rdquo; A 10-minute walk, workout, or reading session creates the mental transition that a commute previously provided.\n5. End-of-day shutdown ritual Closing tabs, writing tomorrow\u0026rsquo;s task list, and physically leaving your workspace signals to your brain that work is done.\nFrequently Asked Questions What\u0026rsquo;s the most important remote work app? If we had to pick one: a reliable communication tool (Slack or equivalent). Everything else is secondary to being able to collaborate effectively with your team.\nDo I need to pay for productivity apps? Free tiers of most apps are genuinely useful. We recommend starting free, identifying where you feel friction, and only upgrading tools where paid features solve a real problem.\nHow many productivity apps are too many? More than 8-10 regularly used tools is usually a problem. Tool switching creates cognitive overhead. Find a core stack and resist adding more.\nAre AI tools essential for remote workers in 2026? Increasingly yes. Remote workers who use AI assistants effectively consistently outperform those who don\u0026rsquo;t — in writing speed, research quality, and communication clarity.\nRelated Tools Pick colors and convert between formats → Color Picker Check your BMI and healthy weight range → BMI Calculator Plan your monthly budget → Budget Planner Calculate your take-home pay → Salary Calculator Working across time zones? → Timezone Converter — convert times between any cities instantly\nFind a Remote Job That Matches Your Productivity Setup Having the perfect remote work toolkit means nothing without the right remote role. Find your next career on doda to discover remote-friendly positions where your productivity skills will be valued and rewarded. Stay focused with the Pomodoro Technique → Pomodoro Timer Track time on tasks with lap timer support → Stopwatch Count down to any date or event → Countdown Timer Build Your Ideal Remote Work Setup Our Remote Work Productivity Pack includes Notion templates for remote teams, a complete tool evaluation framework, and a 30-day productivity optimization plan.\nRelated Reading:\nWork From Home Tips for Beginners 2026 Best Remote Work Tools 2026 ChatGPT vs Claude vs Gemini 2026 Notion Project Management Guide 2026 This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/best-productivity-apps-remote-workers-2026/","summary":"\u003chr\u003e\n\u003ch2 id=\"best-productivity-apps-for-remote-workers-in-2026\"\u003eBest Productivity Apps for Remote Workers in 2026\u003c/h2\u003e\n\u003cp\u003eRemote work is mainstream. But \u0026ldquo;working from home\u0026rdquo; and \u0026ldquo;working productively from home\u0026rdquo; are two entirely different things — and the difference often comes down to the tools you use.\u003c/p\u003e\n\u003cp\u003eAfter testing 40+ apps across a year of fully remote work, these are the tools that actually made us more productive, less stressed, and better at communicating with distributed teams.\u003c/p\u003e\n\u003cp\u003eThis is not a list of every app that exists. It\u0026rsquo;s the tools we\u0026rsquo;d actually recommend to a friend starting remote work today.\u003c/p\u003e","title":"Best Productivity Apps for Remote Workers 2026: Tested and Ranked"},{"content":"Remote work has matured. The chaotic first wave of distributed work — everyone on Zoom all day, Slack notifications firing constantly, nobody sure where to find anything — has given way to more deliberate practices and better tooling.\nThe best remote setups in 2026 share a common characteristic: fewer tools, used better. This guide covers the essential categories and the tools worth using in each, with honest assessments of what each one actually solves.\nThe Core Remote Work Stack A functional remote setup needs exactly six things:\nCommunication — synchronous (meetings) and asynchronous (messages) Project and task management — where work lives and how it progresses Documentation — the shared brain of the team Video conferencing — for the meetings that still need to happen Focus and deep work — protecting time from fragmentation Security — protecting data and access on distributed networks Everything else is optional or category-specific.\nCategory 1: Communication Slack — Still the Standard for Async Team Communication Slack remains the dominant team messaging platform for good reason. The channel structure, thread model, and integration ecosystem are genuinely better than competitors for teams of 5+.\nWhat makes it work:\nChannels create clear homes for topics, projects, and teams Threads keep conversations organized without cluttering channels Integrations with GitHub, Jira, Google Calendar, and hundreds of other tools bring context into conversations Huddles provide lightweight, instant voice/video connection without scheduling a meeting What makes it fail: Slack fails when teams treat it like email — every message expecting an immediate response, no clear norms for what belongs in a thread vs. a new message, and channels that balloon into noise.\nThe tool is only as good as the norms around it. Teams that win with Slack define response time expectations explicitly (e.g., \u0026ldquo;respond within 4 hours during work hours\u0026rdquo;) and default to public channels over DMs.\n[Upgrade to Slack Pro at slack.com/pricing]\nAlternative: Discord — Better for smaller, informal teams or those with voice-heavy workflows. Free tier is generous. Less suited to enterprise environments.\nLoom — The Async Video Replacement for Meetings Loom lets you record a screen + camera video and share a link within seconds. For any communication that requires showing something — a product demo, a code review, a design walkthrough, a project status update — a Loom video is faster to create than writing the equivalent and faster to consume than a real-time meeting.\nBest uses:\nReplacing \u0026ldquo;can I get 30 minutes on your calendar\u0026rdquo; messages Walkthrough of complex feedback on a document or design Team updates for people in different time zones Onboarding walkthroughs for new hires Realistic impact: Teams that use async video effectively report 20–30% fewer scheduled meetings. For remote teams across multiple time zones, async video is often the only viable alternative to meetings that work for nobody\u0026rsquo;s schedule.\n[Upgrade to Loom Business at loom.com/pricing]\nCategory 2: Project and Task Management Linear — Best for Engineering and Technical Teams Linear has become the preferred project management tool for product and engineering teams. It\u0026rsquo;s fast, opinionated in the right ways, and designed around how developers actually work — sprints, cycles, issues, and statuses.\nStrengths:\nSub-100ms interface speed (the fastest in this category) Clear issue tracking with git integration Sprint planning and roadmap views built in Automation that moves issues based on PR status Limitations: Primarily designed for engineering workflows. Less suited to marketing, operations, or cross-functional project management.\nNotion — Best for Knowledge Work Teams Notion functions as project management, documentation, and team wiki simultaneously. For teams that create and manage knowledge-heavy work — content, strategy, research, marketing — it provides a unified workspace where context lives alongside the work.\nStrengths:\nDatabases for project tracking, content calendars, CRM-lite, and more Documentation and wikis that stay connected to ongoing work Flexible enough to mold to any team\u0026rsquo;s workflow Notion AI for drafting, summarizing, and searching content Limitations: Requires intentional setup; out-of-box templates need customization. Can become disorganized without clear ownership.\n[Upgrade to Notion Plus at notion.so/pricing]\nAsana — Best for Cross-Functional Project Management Asana handles the complexity of projects that involve multiple teams with different workflows. Timeline views, dependency tracking, and workload management make it well-suited for marketing campaigns, product launches, and operational projects.\nWhen to choose Asana over Notion: When you have clearly defined projects with milestones, dependencies, and stakeholders across departments — and you need Gantt-style visibility into progress.\nCategory 3: Documentation Notion (as a wiki) — Best for Most Teams Notion\u0026rsquo;s wiki functionality is its most underrated use case. A well-structured Notion wiki becomes the institutional memory of a remote team — how decisions were made, how processes work, who owns what.\nThe structure that works:\nCompany-level pages: Mission, values, org chart, policies Team-level spaces: Each team owns their section Project documentation: Linked to project databases Runbooks: Step-by-step processes for recurring tasks Confluence — Best for Large Engineering Organizations Confluence (Atlassian) is the enterprise-grade documentation tool and pairs naturally with Jira for software development teams. More structured than Notion, with better permissions management for large organizations.\nChoose Confluence if: You\u0026rsquo;re in an enterprise environment already using Atlassian products, or you need advanced permissions and audit trails.\nCategory 4: Video Conferencing The Current Landscape Tool Best For Standout Feature Zoom Teams relying heavily on meetings Reliability, meeting rooms, webinars Google Meet Google Workspace teams Zero friction, no downloads Microsoft Teams Microsoft 365 environments Deep Office integration Around Small team casual meetings Spatial audio, less formal feel The honest take: For most remote teams, Google Meet is perfectly adequate and has the least friction. Zoom remains the gold standard for reliability and features when meetings are central to your workflow.\nThe more interesting question is not which video tool to use, but how to reduce the number of meetings that happen in the first place. Async-first teams use video conferencing for relationship-building and genuinely complex discussions — not for status updates that belong in a Slack channel or a Loom video.\nCategory 5: Focus and Deep Work Reclaim.ai — AI Scheduling for Protected Focus Time Reclaim.ai connects to your calendar and automatically schedules focus time, habit blocks, and buffer time around your meetings. It moves these blocks intelligently when meetings are added, protecting your deep work time without requiring manual calendar management.\nBest feature: Habit scheduling that automatically finds the best time for recurring tasks (gym, reading, writing blocks) based on your calendar and priorities.\n[Try Reclaim.ai at reclaim.ai]\nFreedom — Blocking Distractions Across Devices Freedom blocks distracting websites and apps across all your devices simultaneously. Unlike browser extensions that only work on one device and can be bypassed, Freedom\u0026rsquo;s blocking applies across desktop, mobile, and browser.\nThe productivity case: Research on knowledge workers consistently shows that context switching — checking your phone or a website mid-task — adds 20–30 minutes to the time required to complete cognitive work. Freedom makes that context switching structurally difficult.\n[Try Freedom at freedom.to]\nFocusmate — Social Accountability for Solo Remote Workers Focusmate pairs you with a stranger via video for a 25 or 50-minute co-working session. You each state your goal at the start, work silently, and check in at the end. The social accountability effect is surprisingly powerful — the presence of another person watching (even silently) significantly reduces procrastination.\nParticularly useful for: Freelancers and solopreneurs who lack the ambient accountability of an office environment.\nCategory 6: Security Remote work creates security vulnerabilities that office environments manage automatically. Every remote worker needs to address these.\nVPN — Essential for Shared or Public Networks A VPN (Virtual Private Network) encrypts your internet traffic, protecting sensitive work data when you\u0026rsquo;re not on a trusted network. Non-negotiable if you ever work from coffee shops, coworking spaces, hotels, or anywhere outside your home.\nRecommended options:\nNordVPN — Fast, reliable, large server network ExpressVPN — Consistently fast, good apps for all platforms Mullvad — Best privacy credentials, slightly fewer features 1Password — Team Password Management Password reuse is the most common cause of account compromises in remote teams. 1Password provides:\nSecure password generation and storage for individuals Shared vaults for team credentials Two-factor authentication management Secure document storage Alternative: Bitwarden — Open-source, cheaper, slightly less polished. The free individual tier is genuinely functional.\n[Try 1Password Teams at 1password.com]\nYubikey — Hardware 2FA for High-Security Accounts For accounts that would cause serious damage if compromised (admin accounts, financial accounts, code repositories), a hardware security key provides protection that no software-based 2FA can match. A Yubikey costs $25–$50 and essentially eliminates phishing attacks on enrolled accounts.\nThe Minimal Viable Remote Stack If you\u0026rsquo;re setting up from scratch and want to minimize cost:\nCategory Free Option When to Upgrade Communication Slack (free tier) Team \u0026gt; 5 or you need message history Project mgmt Notion (free tier) Team collaboration needed Video Google Meet Almost never — Meet is free and good Documentation Notion (free tier) Same as above Focus Browser extension (free) When you need cross-device blocking Security Bitwarden (free) + free VPN trial Day one — security is not optional The Tools That Sound Good But Often Don\u0026rsquo;t Stick Microsoft Teams: Excellent if you\u0026rsquo;re deep in Microsoft 365. Otherwise, an awkward tool trying to be everything.\nTrello: Good for simple kanban boards; falls short for complex projects or growing teams.\nBasecamp: Philosophically interesting (async-first by design), but adoption is difficult for teams coming from Slack/Notion environments.\nClubhouse / Shortcut: Solid project management, but lost market share to Linear for engineering teams.\nGet Paid More for Your Remote Work Skills The right tools make you more productive — but the right employer makes you more money. Find your next career on doda to explore remote and hybrid opportunities where your mastery of modern work tools is a competitive advantage.\nSetting Up Your Remote Stack: The Right Order Start with communication — get everyone on the same messaging platform before anything else Add documentation — create a wiki before you have too many informal decisions floating in chat history Add project management — when \u0026ldquo;track it in Slack\u0026rdquo; stops working Add security — ideally on day one; never skip this step Add focus tools — once the basics are working and distraction has been identified as a real problem The worst outcome is adopting too many tools simultaneously and switching between them constantly. Each tool you add has an adoption cost — in time, in training, and in mental overhead. Add tools only when the problem they solve is clearly present.\nA remote team with three tools used well outperforms a team with twelve tools used poorly. Every time.\nRelated Tools Check your BMI and healthy weight range → BMI Calculator Calculate your take-home pay → Salary Calculator Plan your monthly budget → Budget Planner Stay focused with the Pomodoro Technique → Pomodoro Timer Track time on tasks with lap timer support → Stopwatch Count down to any date or event → Countdown Timer Working across time zones? → Timezone Converter — convert times between any cities instantly\nRelated Templates Put these techniques into practice with our ready-made templates:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Copy-paste prompts for every situation The AI Productivity Playbook 2026 — 50+ AI workflows and automation strategies This article contains affiliate links. We may earn a commission at no extra cost to you.\nYou May Also Like Best Productivity Apps for Remote Workers 2026: Tested and Ranked Work From Home Tips for Beginners 2026: Everything You Need to Succeed The Ultimate Notion Setup for Maximum Productivity ","permalink":"https://productivity-works.com/posts/best-remote-work-tools-2026/","summary":"\u003cp\u003eRemote work has matured. The chaotic first wave of distributed work — everyone on Zoom all day, Slack notifications firing constantly, nobody sure where to find anything — has given way to more deliberate practices and better tooling.\u003c/p\u003e\n\u003cp\u003eThe best remote setups in 2026 share a common characteristic: fewer tools, used better. This guide covers the essential categories and the tools worth using in each, with honest assessments of what each one actually solves.\u003c/p\u003e","title":"Best Remote Work Tools in 2026: The Complete Stack"},{"content":"Running an Etsy shop in 2026 means competing with millions of listings. The sellers breaking through aren\u0026rsquo;t necessarily the most talented makers — they\u0026rsquo;re the ones who\u0026rsquo;ve mastered their shop\u0026rsquo;s visibility and customer experience. ChatGPT has quietly become one of the most powerful tools in a top Etsy seller\u0026rsquo;s toolkit.\nThis guide gives you everything you need: how Etsy SEO actually works, where AI helps most, and 20+ specific prompts you can use today to write better listings, find better tags, and market your shop more effectively.\nUnderstanding Etsy SEO Before Using AI Before using ChatGPT effectively for Etsy, you need to understand how Etsy\u0026rsquo;s search algorithm works — because if you don\u0026rsquo;t understand the goal, AI-generated content might miss it entirely.\nHow Etsy Ranks Listings Etsy\u0026rsquo;s search algorithm (called \u0026ldquo;Rank\u0026rdquo;) considers:\nRelevancy: How well your title and tags match what a buyer searched Listing quality score: Click-through rate and conversion rate from search Recency: Newer listings get a temporary boost Shop quality score: Reviews, completion rate, response time Customer and market experience: How often your shop delivers good experiences The critical insight: Tags and titles are where your SEO keywords live. Etsy\u0026rsquo;s algorithm doesn\u0026rsquo;t read your description for keyword ranking purposes — it reads your title and tags. This changes how you use AI.\nThe Title Formula That Works The highest-performing Etsy titles typically follow this structure:\n[Most Searched Keyword Phrase] | [Secondary Keyword] | [Descriptive Feature] | [Gift Occasion or Use Case] Example for a ceramic mug: \u0026ldquo;Handmade Ceramic Coffee Mug | Pottery Mug Handmade | Personalized Gift for Coffee Lover | Mom Birthday Gift\u0026rdquo;\nUnderstanding Tags You get 13 tags, each up to 20 characters. Use all 13. Every tag should be a phrase that your ideal buyer might search — not a single word, not a brand name, and not something already in your title (Etsy\u0026rsquo;s algorithm already reads your title).\nPart 1: Product Title Optimization Prompts Prompt 1: Generate an Optimized Etsy Title I sell [PRODUCT] on Etsy. Write 5 different Etsy product title variations optimized for search. Each title should: - Be under 140 characters - Lead with the most searchable keyword phrase - Include a gift occasion or use case angle - Separate ideas with the | pipe symbol - Sound natural and readable, not like a keyword dump - NOT use all caps or excessive punctuation My product: [DESCRIBE YOUR ITEM IN DETAIL] My target buyer: [WHO BUYS THIS?] Common gift occasions for this item: [BIRTHDAY, WEDDING, HOLIDAY, ETC.] My main material/technique: [DESCRIBE] What makes it unique: [YOUR DIFFERENTIATOR] Prompt 2: Find Your Main SEO Keyword I make [TYPE OF PRODUCT] on Etsy. Help me find the best primary keyword phrase to lead my title with. Consider what a buyer would actually type into Etsy search — not what I would call it as a maker. Think about: - Short-tail searches (2-3 words, high competition but high volume) - Long-tail searches (4-6 words, lower competition but more buyer intent) - Gift-oriented searches (\u0026#34;gift for [person]\u0026#34;) - Style/aesthetic searches (cottagecore, minimalist, boho, etc.) - Occasion searches (wedding, graduation, housewarming) Give me 15 keyword phrase options ranked from most competitive to least competitive, with a brief explanation of who each phrase targets. My product: [DESCRIBE] My style/aesthetic: [DESCRIBE] Prompt 3: Competitor Title Analysis Here are 5 Etsy product titles from my competitors that rank well for my category. Analyze what SEO strategies they\u0026#39;re using, what keywords they prioritize, and what patterns you notice. Then suggest how I can write a title that\u0026#39;s similar in strategy but differentiated. Competitor titles: 1. [TITLE 1] 2. [TITLE 2] 3. [TITLE 3] 4. [TITLE 4] 5. [TITLE 5] My product: [DESCRIBE] My differentiator from these competitors: [WHAT MAKES ME DIFFERENT] Part 2: Product Description Prompts Remember: descriptions don\u0026rsquo;t rank your listing, but they do convert browsers into buyers. A great description answers every question, tells a story, and removes purchase hesitation.\nPrompt 4: Full Product Description (Standard) Write a compelling Etsy product description for my listing. The description should: - Open with a hook that connects emotionally with the buyer (not just facts) - Describe what the item looks, feels, or smells like in sensory detail - Cover all practical details in bullet points (measurements, materials, care instructions) - Address common buyer questions proactively - Explain what makes this item special or unique - Close with a warm, brand-appropriate call to action - Keep total length between 200-350 words Product details: - Item: [NAME] - Dimensions: [SIZE] - Materials: [WHAT IT\u0026#39;S MADE OF] - How it\u0026#39;s made: [HAND-POURED / WHEEL-THROWN / HAND-STITCHED / ETC.] - Colors/variations available: [LIST] - Lead time: [HOW LONG TO MAKE AND SHIP] - Care instructions: [WASHING, DISPLAY, STORAGE] - Who it\u0026#39;s perfect for: [TARGET BUYER / GIFT RECIPIENT] - Gift occasions: [LIST] - What makes it unique: [YOUR DIFFERENTIATOR] - My shop\u0026#39;s vibe/brand: [AESTHETIC / TONE] Prompt 5: Gift-Oriented Description Rewrite this product description specifically for gift buyers — people who are shopping for someone else, not themselves. Emphasize giftability, packaging, personalization options, and the emotional experience of giving and receiving this item. Original description or product details: [PASTE YOUR INFO] Target gift-givers: [MOM BUYING FOR DAUGHTER / BRIDESMAIDS GIFT / BOSS GIFT / ETC.] Gift occasions to mention: [BIRTHDAY, ANNIVERSARY, HOLIDAY, ETC.] Packaging details: [HOW DO YOU PACKAGE IT? GIFT BOX? RIBBON?] Personalization available? [YES — WHAT OPTIONS / NO] Prompt 6: Variation Descriptions I have a product that comes in [#] variations. Write a short description (2-3 sentences) for each variation that explains what makes that specific variation appealing, who it\u0026#39;s best for, or how it looks/feels differently. These will appear in my listing\u0026#39;s variation dropdown section. Product: [MAIN PRODUCT NAME] Variation type: [COLOR / SIZE / SCENT / STYLE / ETC.] Variations: - Option 1: [NAME] — [BRIEF NOTES ON THIS VARIATION] - Option 2: [NAME] — [BRIEF NOTES] - Option 3: [NAME] — [BRIEF NOTES] [Continue for all variations] Part 3: Tag Research Prompts This is where ChatGPT saves Etsy sellers the most time. Generating 13 relevant, non-redundant, buyer-intent tags for every listing is genuinely hard.\nPrompt 7: Generate All 13 Etsy Tags Generate exactly 13 Etsy tags for my product listing. Each tag must: - Be under 20 characters (including spaces) - Be a phrase a buyer would actually search (not just a single word) - NOT duplicate words already prominent in my title - Cover different angles: style/aesthetic, material, use case, gift occasion, recipient type, room/setting, size, technique - Be in lowercase Product: [DESCRIBE IN DETAIL] My title is: [PASTE YOUR TITLE] My target buyer: [DESCRIBE] Gift occasions: [LIST] Style/aesthetic: [MINIMALIST, COTTAGECORE, BOHO, FARMHOUSE, ETC.] Material/technique: [DESCRIBE] Format your answer as a numbered list with a brief note on why each tag was chosen. Prompt 8: Tag Refresh for Underperforming Listing This Etsy listing has been active for [# MONTHS] but is getting low views. Here are my current tags. Suggest a complete replacement set of 13 tags using different angles, longer-tail phrases, or gift/occasion-based searches I may have missed. Product: [DESCRIBE] Current tags (to be replaced): [LIST YOUR CURRENT 13 TAGS] Current listing views per month: [#] Current listing favorites: [#] Conversion rate: [% if known] What angles have I NOT tried that might attract more specific buyer searches? Prompt 9: Seasonal Tag Strategy I sell [PRODUCT TYPE] on Etsy year-round, but I want to add seasonal tags during peak shopping periods. Create seasonal tag sets for each major shopping season: Product: [DESCRIBE] My existing evergreen tags (don\u0026#39;t duplicate): [LIST] Create seasonal tag sets (13 tags each) for: 1. Spring (March-May, including Mother\u0026#39;s Day) 2. Summer (June-August, including Father\u0026#39;s Day, graduations) 3. Fall (September-November, including Halloween, back to school) 4. Holiday (November-December, Christmas, Hanukkah, gifts) 5. Valentine\u0026#39;s Day 6. Back to School / Teacher Appreciation Part 4: Shop Branding and About Section Prompts Prompt 10: Etsy Shop About Section Write an engaging \u0026#34;About\u0026#34; section for my Etsy shop. It should: - Tell my maker\u0026#39;s story authentically - Explain why I make what I make - Describe my creative process - Build trust and connection with potential buyers - Be conversational, warm, and genuine — not corporate - Mention my values around craftsmanship, sustainability, or whatever is true for my brand - End with an invitation to connect or explore the shop - Target length: 250-350 words My story: - Name: [YOUR NAME] - Location: [CITY/REGION — broad is fine for privacy] - What I make: [PRODUCTS] - How long I\u0026#39;ve been making/selling: [TIMEFRAME] - Why I started: [YOUR ORIGIN STORY] - How I make my products: [PROCESS] - What makes my work unique: [DIFFERENTIATOR] - My studio/workspace: [HOME STUDIO / GARAGE / SHARED SPACE] - Values important to my brand: [SUSTAINABILITY, HANDMADE QUALITY, LOCAL SOURCING, ETC.] - Who my customers typically are: [DESCRIBE] Prompt 11: Shop Policies That Convert Write friendly, clear Etsy shop policies that protect me while building buyer confidence. Make them warm and human, not legalistic. Cover: - Processing and shipping times - Custom/personalized order process - Returns and exchanges - Damaged item policy - Communication response time My details: - Processing time: [# DAYS] - Shipping method: [USPS / UPS / ETC.] - Domestic shipping time: [# DAYS] - International shipping: [YES / NO — to which countries] - Returns accepted? [YES/NO — under what conditions] - Exchanges accepted? [YES/NO] - Personalized orders: [DO YOU OFFER THEM?] - How to reach me: [ETSY MESSAGES] Part 5: Customer Communication Prompts Prompt 12: Response to \u0026ldquo;When Will My Order Arrive?\u0026rdquo; Write a friendly, reassuring response to a customer asking when their order will arrive. The response should: - Acknowledge their inquiry warmly - Provide specific timeline information - Offer to provide tracking if available - Set expectations realistically without alarming them - Close warmly Context: - Order placed: [DATE] - My processing time: [# DAYS] - Shipping method: [METHOD] - Estimated delivery window: [RANGE] - Has it shipped yet? [YES / NO / SHIPPING DATE] - Tracking available? [YES — number / NOT YET] Prompt 13: Response to a Negative Review Write a professional, empathetic public response to this negative Etsy review. The response should: - Thank the buyer for their feedback - Acknowledge their experience without being defensive - Briefly explain any relevant context (without making excuses) - Offer a solution or what I\u0026#39;ve learned - Keep it under 150 words - Be something future buyers will read and trust me MORE after seeing The review: \u0026#34;[PASTE THE REVIEW]\u0026#34; The actual situation: [WHAT HAPPENED FROM YOUR PERSPECTIVE] Did you resolve it? [HOW] Prompt 14: Custom Order Inquiry Response Write a warm, professional response to a custom order inquiry that confirms I can help, asks for the information I need to complete the order, explains my process for custom orders, and gives a realistic timeline and price range. Customer\u0026#39;s inquiry: \u0026#34;[PASTE THEIR MESSAGE]\u0026#34; Can I fulfill this request? [YES / PARTIALLY — EXPLAIN / NO] Information I need from them: [LIST WHAT YOU NEED] My custom order process: [DESCRIBE STEPS] Timeline for custom orders: [# WEEKS] Approximate price range: $[RANGE] Prompt 15: Order Shipped Notification Message Write a brief, warm Etsy message to send when I ship a customer\u0026#39;s order. Make it feel personal and excited — like the handmade item is being sent with care. Include tracking info and what to do if there\u0026#39;s an issue. Details: - Customer name: [NAME or \u0026#34;there\u0026#34; if unknown] - What they ordered: [ITEM] - Tracking number: [#] - Shipping carrier: [USPS/UPS/FEDEX] - Estimated delivery: [DATE/RANGE] - Packaging note: [ANYTHING SPECIAL ABOUT HOW IT\u0026#39;S PACKED] - CTA: Encourage a review after receiving Part 6: Social Media Marketing Prompts for Etsy Sellers Prompt 16: Instagram Product Launch Caption Write an Instagram caption to launch a new Etsy listing. Make it engaging and visual — help followers see the item in their mind. Include a clear CTA and relevant hashtags. Product: [DESCRIBE] What\u0026#39;s special about it: [UNIQUE FEATURE] Price: $[AMOUNT] Link location: [Link in bio / Linktree / etc.] My shop name: [@HANDLE] My brand aesthetic: [COTTAGECORE / MODERN MINIMAL / BOHO / ETC.] Target emotion: [COZY, LUXURY, PLAYFUL, ELEGANT] Include: - Main caption (under 150 words) - 25-30 relevant hashtags including: Etsy niche tags, aesthetic tags, gift-related tags, and small business tags Prompt 17: \u0026ldquo;Behind the Scenes\u0026rdquo; Content Ideas I make [PRODUCT] and sell on Etsy. Generate 10 \u0026#34;behind the scenes\u0026#34; content ideas that would perform well on Instagram Reels, TikTok, and Pinterest. Each idea should show my creative process in an authentic, engaging way. For each idea, include: - The concept/video idea - The hook (first 3 seconds) - Best platform for this content - Suggested caption angle My product: [DESCRIBE] My making process: [SUMMARIZE] My workspace: [HOME STUDIO / OUTDOOR / RETAIL] My brand vibe: [DESCRIBE] Prompt 18: Pinterest Pin Description Write an optimized Pinterest pin description for my Etsy listing. Pinterest descriptions should be keyword-rich (Pinterest functions as a search engine), tell people what they\u0026#39;re looking at and why they want it, and include a clear link to my shop. Product: [DESCRIBE] Etsy listing URL: [URL] Target audience on Pinterest: [WHO IS SEARCHING FOR THIS] Top keywords to include: [LIST 5-7 TERMS] Board this will be pinned to: [BOARD NAME] Write 3 versions: one short (50 words), one medium (100 words), one long (150 words). Prompt 19: Holiday Marketing Email to Past Customers Write a holiday email to send to my past Etsy customers promoting my shop for the holiday season. The tone should be warm and personal — like a letter from the maker, not a corporate marketing email. My shop name: [NAME] Holiday: [CHRISTMAS / HANUKKAH / GENERAL HOLIDAY SEASON] Products I want to feature: [LIST 2-3] Any promotions: [DISCOUNT CODE / FREE SHIPPING / GIFT WRAPPING] Deadline for holiday delivery: [DATE] Personal touch: [ADD SOMETHING GENUINE ABOUT YOUR SEASON] Keep it under 250 words. Include a subject line suggestion. Prompt 20: Monthly Shop Update Caption for Social Write a casual, engaging \u0026#34;monthly shop update\u0026#34; post for Instagram or Facebook. It should feel like a personal update from the maker — not a sales pitch. Mention what I\u0026#39;ve been creating, what\u0026#39;s new, and what\u0026#39;s coming soon. This month\u0026#39;s highlights: - New products launched: [LIST] - Behind-the-scenes update: [WHAT YOU\u0026#39;VE BEEN WORKING ON] - Personal creative note: [WHAT INSPIRED YOU THIS MONTH] - Coming soon: [PREVIEW OF NEXT MONTH] - Any thank-yous: [MILESTONES, SALES NUMBERS, ETC.] - Shop link: [URL] Part 7: Listing Audit and Optimization Using ChatGPT to Audit Your Entire Shop Once a quarter, audit your lowest-performing listings. Use this prompt:\nPrompt 21: Full Listing Audit Audit this Etsy listing and give me specific, actionable improvements. Rate each element 1-10 and explain what to fix. My listing details: Title: [PASTE TITLE] 13 Tags: [PASTE ALL 13] Description (first 200 words): [PASTE] Price: $[AMOUNT] Number of photos: [#] Processing time listed: [# DAYS] Star rating: [#] from [#] reviews Views this month: [#] Favorites: [#] Sales: [#] Audit these elements: 1. Title strength and keyword placement 2. Tag quality and coverage 3. Description hook (first 2 sentences) 4. Price positioning for this product category 5. What I\u0026#39;m likely missing that my ideal buyer is searching for Advanced Strategies for 2026 Strategy 1: Seasonal Listing Calendar Plan your listing optimizations 6-8 weeks ahead of major shopping events. Create a calendar:\nJanuary: Valentine\u0026rsquo;s Day prep, winter clearance March: Spring/Mother\u0026rsquo;s Day prep begins August: Back to school, fall launch, early holiday prep October: Holiday listing optimization begins November: Holiday peak — all listings optimized Strategy 2: A/B Testing Titles Create two versions of a listing with different titles. Run each for 30 days and compare views and click-through rates. Use the winning title and apply the same keyword approach to similar listings.\nStrategy 3: Niche Down Your Tags Generic tags (\u0026ldquo;handmade gift\u0026rdquo;) have massive competition. Niche tags (\u0026ldquo;gift for plant lover\u0026rdquo;) have lower competition but higher buyer intent. Use ChatGPT to brainstorm 50+ niche tag ideas for your products, then test the most promising ones.\nStrategy 4: Review Request Automation Use ChatGPT to write 3-4 review request message variations. Rotate them when following up with buyers after delivery. Personalize with the product they bought.\nWrite 3 variations of a warm, non-pushy Etsy message asking a buyer to leave a review. Each should be under 75 words, feel genuine (not automated), and make it easy for them to leave a review. My shop name: [NAME] The product they bought: [ITEM] Approximate time since delivery: 1-2 weeks Common Mistakes When Using AI for Etsy Mistake 1: Using AI descriptions that sound generic. Always add specific details only you would know — the texture of your clay, the scent profile of your candle wax, the story behind a design. AI gives you structure; you add the soul.\nMistake 2: Keyword stuffing titles. Etsy penalizes listings that read as spam. Your title should make sense when read aloud. If it sounds absurd, a buyer will scroll past it.\nMistake 3: Ignoring character limits. Tags must be under 20 characters. Always count. ChatGPT will sometimes generate tags that are too long — always verify.\nMistake 4: Not customizing for your aesthetic. A luxury jewelry shop and a whimsical children\u0026rsquo;s toy shop need very different language. Give ChatGPT your brand voice in every prompt.\nMistake 5: Setting and forgetting. Etsy SEO changes. Trends change. Re-audit your top listings quarterly and update tags and titles based on current search trends.\nGo Beyond Etsy — Build Your Own Shop Online Etsy is a great starting point, but your own website puts you in full control of your brand and profits. Get your domain on Onamae.com and register your brand domain today — the foundation for a standalone store that complements your Etsy presence.\nConclusion The Etsy sellers who will thrive in 2026 are those who use AI to handle the time-consuming, analytical writing work — so they can spend more time on what actually matters: making beautiful things.\nChatGPT won\u0026rsquo;t make your products. It won\u0026rsquo;t take your photos. It won\u0026rsquo;t build the reputation that earns you five-star reviews. But it will write your 47th product description without losing quality, generate tag ideas you\u0026rsquo;d never have thought of, and help you respond to a difficult customer review with grace instead of defensiveness.\nStart with Prompt 7 (the 13-tag generator) for your worst-performing listing. Compare your views over the next 30 days. The results will convince you to keep going.\nProductivity Works publishes weekly guides for creative entrepreneurs and small business owners. Subscribe for more Etsy strategy, AI tool reviews, and shop-building resources.\nRelated Tools Calculate your Etsy shop taxes → Side Hustle Tax Calculator Track your shop\u0026rsquo;s profitability → Budget Planner Related Templates Grow your Etsy business with these resources:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Product descriptions, SEO tags, marketing copy Side Hustle Starter Kit 2026 — Complete guide to scaling your online business This article contains affiliate links. We may earn a commission at no extra cost to you.\nYou May Also Like How to Use ChatGPT for Etsy Shop Descriptions (Complete 2026 Guide) Etsy SEO: How to Generate Perfect Tags with ChatGPT (Step-by-Step) 7 Passive Income Ideas Using Digital Products ","permalink":"https://productivity-works.com/posts/chatgpt-etsy-seller-complete-guide-2026/","summary":"\u003cp\u003eRunning an Etsy shop in 2026 means competing with millions of listings. The sellers breaking through aren\u0026rsquo;t necessarily the most talented makers — they\u0026rsquo;re the ones who\u0026rsquo;ve mastered their shop\u0026rsquo;s visibility and customer experience. ChatGPT has quietly become one of the most powerful tools in a top Etsy seller\u0026rsquo;s toolkit.\u003c/p\u003e\n\u003cp\u003eThis guide gives you everything you need: how Etsy SEO actually works, where AI helps most, and 20+ specific prompts you can use today to write better listings, find better tags, and market your shop more effectively.\u003c/p\u003e","title":"ChatGPT for Etsy Sellers: Descriptions, SEO \u0026 Marketing 2026"},{"content":"Running an Etsy shop is equal parts craft and copywriting. You might make the most stunning hand-poured soy candles or the most intricate watercolor prints in the marketplace — but if your product descriptions are flat, keyword-free, or just plain boring, buyers scroll right past you. The problem is that most Etsy sellers are artists and makers, not professional copywriters. And hiring one for every listing? Not scalable.\nThis is exactly where ChatGPT changes the game.\nThis guide is written specifically for Etsy sellers — not generic e-commerce store owners, not Shopify dropshippers — you, the person who hand-makes or carefully curates products and needs words that sell them authentically. We will walk through exactly how to use ChatGPT to write product descriptions, shop announcements, your About section, FAQs, and even your Etsy SEO titles and tags — all tuned to how Etsy\u0026rsquo;s search algorithm actually works in 2026.\nWhy Etsy Descriptions Are Harder Than They Look Before we get into the prompts, let\u0026rsquo;s acknowledge the specific challenges of Etsy copywriting:\nEtsy\u0026rsquo;s search algorithm prioritizes keyword relevance in titles and the first 40 words of descriptions. Generic AI output often misses this. Buyers on Etsy are looking for story and craft, not just specs. They want to know who made it, why, and how it feels to own it. Character and word limits on titles (140 characters) and tags (20 characters each, 13 allowed) require precise, intentional language. Niche vocabulary matters. A ceramics buyer speaks differently than a vintage clothing buyer. Your descriptions need to match. ChatGPT handles all of this — when you give it the right instructions.\nSetting Up ChatGPT for Etsy Success You don\u0026rsquo;t need ChatGPT Plus (the paid version) to do most of what\u0026rsquo;s in this guide, though GPT-4o gives noticeably better results for nuanced copy. If you use the free tier, you\u0026rsquo;ll still get solid output.\nStep 1: Give ChatGPT a \u0026ldquo;System Context\u0026rdquo; at the Start of Every Session Open a new chat and paste this at the very beginning:\n\u0026ldquo;You are an expert Etsy copywriter who specializes in handmade and artisan products. You understand Etsy\u0026rsquo;s search algorithm, buyer psychology, and the importance of balancing keyword optimization with warm, human storytelling. When I give you product details, write descriptions that feel personal and crafted — not corporate or generic. Always lead with the most important keyword naturally in the first sentence.\u0026rdquo;\nThis single step dramatically improves every piece of output you get in that session.\nWriting Product Descriptions with ChatGPT The single most common use case — and the one that saves the most time. A good Etsy product description serves three masters simultaneously: Etsy\u0026rsquo;s search algorithm, the buyer\u0026rsquo;s emotions, and Google (yes, Etsy listings rank on Google too).\nThe Prompt Formula Here is the fill-in-the-blank prompt structure that consistently produces excellent Etsy descriptions:\nWrite an Etsy product description for the following item: Product name: [your product name] Category: [e.g., ceramic mug, watercolor print, hand-knit scarf] Materials: [list them] Dimensions / size options: [include if relevant] Key features / what makes it special: [your USP] Target buyer: [e.g., coffee lovers, new moms, minimalist home decor fans] Tone: [e.g., warm and artisanal, playful, luxurious and premium] Primary keyword to include naturally: [e.g., \u0026#34;hand-thrown ceramic mug\u0026#34;] Secondary keywords: [list 2-3] The description should be 150-200 words, lead with the primary keyword in the first sentence, include a sensory detail, mention care instructions briefly, and end with a soft call-to-action. Do not use bullet points — write in flowing paragraphs. Real Example Output Input:\nProduct: Hand-poured soy candle in a vintage amber jar Materials: 100% soy wax, cotton wick, phthalate-free fragrance oils Scent: Cedarwood and vanilla Key feature: Burns clean for 50+ hours, made in small batches Target buyer: Cozy home decor enthusiasts, gift buyers Primary keyword: \u0026ldquo;soy candle handmade\u0026rdquo; Secondary keywords: \u0026ldquo;cedarwood candle,\u0026rdquo; \u0026ldquo;cozy home gift\u0026rdquo; ChatGPT Output (after using the prompt above):\nThis soy candle handmade in small batches fills every room with the grounding warmth of cedarwood and a whisper of vanilla — a scent that feels like a Sunday morning with nowhere to be. Poured using 100% natural soy wax and a lead-free cotton wick, it burns clean and slow for 50+ hours, releasing fragrance without the soot you get from paraffin. The vintage amber glass jar glows beautifully when lit and looks just as good on a shelf when it\u0026rsquo;s not. Our phthalate-free fragrance oils are skin-safe and pet-friendly, so you can breathe easy. When the candle is finished, rinse the jar and reuse it as a succulent planter, a desk organizer, or a cocktail glass — the possibilities are endless. This makes a genuinely thoughtful cozy home gift for friends, partners, or yourself. Trim the wick to ¼ inch before each burn for best results. Ready to ship within 2-3 business days.\nNotice how it naturally includes the primary keyword, adds sensory language, covers care/maintenance, and ends with a CTA — all without sounding robotic.\nWriting Etsy SEO Titles with ChatGPT Etsy titles have a 140-character limit and should front-load your most important keywords. The general wisdom in 2026 is to put your two or three highest-priority keywords in the first 40 characters since that\u0026rsquo;s what shows in search results.\nThe SEO Title Prompt Write 5 Etsy SEO title options for this product: [product description in one sentence]. Rules: - Maximum 140 characters each - Start with the most important keyword - Use commas or pipes (|) to separate keyword phrases naturally - Do not use all caps - Make it readable as a sentence fragment, not just a keyword dump - Include at least one long-tail variation Primary keyword: [keyword] Secondary keywords: [keyword list] Example Output for the Soy Candle Soy Candle Handmade | Cedarwood Vanilla | Clean Burn 50+ Hours | Cozy Gift in Amber Jar Handmade Soy Candle, Cedarwood \u0026amp; Vanilla Scent, Natural Wax, Cozy Home Gift, Vintage Jar Cedarwood Vanilla Soy Candle | Handmade Small Batch | Natural Clean Burn | Gift for Her Soy Candle in Amber Jar | Handmade Cedarwood Vanilla | 50 Hour Burn | Eco Friendly Gift Cozy Home Gift Candle | Hand Poured Soy Wax, Cedarwood Vanilla, Phthalate Free, 50hr Burn Pick the one that resonates, or mix-and-match phrases across options. Option 3 or 4 tends to perform well because they include \u0026ldquo;handmade\u0026rdquo; early and have a clear gift angle.\nGenerating Etsy Tags with ChatGPT Etsy gives you 13 tags, each up to 20 characters. This is where most sellers leave performance on the table — either repeating what\u0026rsquo;s in the title, or using single words instead of multi-word phrases.\nThe Tag Prompt Generate 13 Etsy tags for this product: [product name and brief description]. Rules: - Each tag must be 20 characters or fewer (including spaces) - Prioritize 2-3 word phrases over single words - Do not repeat exact phrases already in the title - Mix: one broad tag, several mid-tail, several long-tail - Think about what a buyer would literally type into Etsy search Product: Handmade soy candle, cedarwood vanilla, amber jar, 50-hour burn Sample Output:\nsoy wax candle handmade candle cedarwood candle vanilla candle gift cozy home decor small batch candle natural wick candle amber jar candle clean burn candle gifts for her hygge home gift artisan soy candle eco candle gift Each one is within 20 characters and covers angles the title might miss — including \u0026ldquo;hygge,\u0026rdquo; which is a buyer search term that resonates specifically with the cozy-home niche.\nWriting Your Etsy Shop About Section Your About section is prime real estate that most sellers underpopulate. It\u0026rsquo;s indexed by Google, it builds trust with buyers, and it\u0026rsquo;s often the deciding factor between two similar shops.\nThe About Section Prompt Write an Etsy shop About section for my shop called [shop name]. About me: [2-3 sentences about who you are] What I make: [your product category] Why I started: [your origin story in 1-2 sentences] What makes my shop different: [your USP] Where I\u0026#39;m based: [city/region, country] Tone: [warm and personal / professional and refined / playful] Length: 200-300 words. Write in first person. End with an invitation to browse or reach out. This prompt produces a genuinely personal About section that sounds like you — not a press release. Customize the output by swapping in specific details (the name of a family member who inspired you, the year you started, your studio location).\nWriting Shop Policies and FAQs Policy sections are the ones sellers dread writing. They need to be legally clear but not cold and robotic. ChatGPT handles this well.\nFAQ Prompt Write an Etsy FAQ section for my shop. I sell [product type]. Common questions buyers ask include: 1. How long does shipping take? 2. Do you accept custom orders? 3. What is your return policy? 4. How should I care for this product? 5. Can I buy in bulk or wholesale? Answers should be friendly and clear, 2-4 sentences each. Tone: warm but professional. My actual answers: - Shipping: 3-5 business days domestic, 10-14 international - Custom orders: Yes, contact me first - Returns: Accepted within 14 days, buyer pays return shipping - Care: [your care instructions] - Wholesale: Yes, contact for pricing The output eliminates the dreaded blank page problem and gives you a warm, readable FAQ you can paste directly into Etsy.\nWriting Seasonal and Sale Announcements Etsy\u0026rsquo;s shop announcement section and listing descriptions benefit from seasonal updates. Instead of stressing over what to write for Mother\u0026rsquo;s Day or the holiday rush, use this:\nWrite a short Etsy shop announcement for [season/holiday]. Context: I sell [product type]. This season I\u0026#39;m offering [any discount or promotion]. My shipping cutoff for [holiday] is [date]. Tone: Warm, excited, grateful. Length: 80-100 words. Batch-generate all your seasonal announcements for the year in one session and schedule them in your calendar. This alone can save hours per quarter.\nCommon Mistakes to Avoid 1. Using AI output without personalizing it. ChatGPT doesn\u0026rsquo;t know your grandmother\u0026rsquo;s quilt pattern or the specific clay you source from Portugal. Add those real details back in — they\u0026rsquo;re what make Etsy buyers fall in love.\n2. Ignoring Etsy\u0026rsquo;s keyword research. ChatGPT generates keywords based on general knowledge, not live Etsy search data. Cross-reference its suggestions with Etsy\u0026rsquo;s own search autocomplete, eRank, or Sale Samurai before committing.\n3. Copying the exact description for multiple listings. Etsy can flag duplicate content within a shop. Use the same prompt structure but vary the details, tone, or angle for each listing.\n4. Forgetting the first 40 words. Etsy only shows the first ~160 characters of your description in search results. Ensure your most important keyword and your strongest hook appear there.\n5. Skipping the human review pass. Read every AI draft out loud. If it doesn\u0026rsquo;t sound like something you\u0026rsquo;d actually say, edit it. Your voice is a competitive advantage — don\u0026rsquo;t let it get optimized away.\nBuilding a Prompt Library for Your Shop If you sell multiple product lines or run a high-volume Etsy shop, the most powerful thing you can do is build a personal prompt library — a document where you save the prompts that worked best, tagged by product type, season, and use case.\nThis pairs perfectly with our ChatGPT Prompt Templates pack, which includes 200+ pre-tested prompts for e-commerce sellers, including 40+ Etsy-specific templates covering descriptions, titles, tags, emails, and social media captions. It is available on our Payhip store and it cuts setup time from hours to minutes.\nReady to Take Your Etsy Shop to the Next Level? Start with Your Own Domain. Serious sellers who want to build a brand beyond Etsy need their own web presence. Get your domain on Onamae.com — Japan\u0026rsquo;s leading domain registrar with affordable plans and easy setup.\nYour Etsy + ChatGPT Action Plan Here is a realistic workflow to implement this week:\nDay 1: Set up your ChatGPT session context. Rewrite your top 5 product descriptions using the prompt formula above. Day 2: Regenerate SEO titles and tags for those same 5 listings. Update them in Etsy. Day 3: Write or rewrite your About section and FAQ. Day 4: Batch-generate seasonal announcements for the next 3 months. Day 5: Review, personalize, and publish everything. Check Etsy analytics baseline. Revisit in 30 days. Etsy SEO takes time to register, but sellers consistently report measurable view and conversion increases within 4-6 weeks of a thorough description overhaul.\nFinal Thoughts ChatGPT is not a replacement for your creative voice or your product expertise — it is a drafting partner that eliminates the blank page and handles the structural copywriting work so you can focus on what you actually love: making things. The prompts in this guide are your starting point. Customize them, save the ones that work, and keep iterating.\nYour products deserve descriptions that do them justice. Now you have the tools to write them — in a fraction of the time.\nWant 200+ ready-to-use prompts for your Etsy shop? Check out our ChatGPT Prompt Templates pack on Payhip — built specifically for e-commerce sellers, makers, and creative entrepreneurs.\nRelated Tools Calculate your Etsy income taxes → Side Hustle Tax Calculator Related Templates Grow your Etsy business with these resources:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Product descriptions, SEO tags, marketing copy Side Hustle Starter Kit 2026 — Complete guide to scaling your online business This article contains affiliate links. We may earn a commission at no extra cost to you.\nYou May Also Like ChatGPT for Etsy Sellers: Descriptions, SEO \u0026amp; Marketing 2026 Etsy SEO: How to Generate Perfect Tags with ChatGPT (Step-by-Step) AI Writing Tools Comparison 2026: Best Options Ranked and Reviewed ","permalink":"https://productivity-works.com/posts/chatgpt-etsy-shop-descriptions/","summary":"\u003cp\u003eRunning an Etsy shop is equal parts craft and copywriting. You might make the most stunning hand-poured soy candles or the most intricate watercolor prints in the marketplace — but if your product descriptions are flat, keyword-free, or just plain boring, buyers scroll right past you. The problem is that most Etsy sellers are artists and makers, not professional copywriters. And hiring one for every listing? Not scalable.\u003c/p\u003e\n\u003cp\u003eThis is exactly where ChatGPT changes the game.\u003c/p\u003e","title":"How to Use ChatGPT for Etsy Shop Descriptions (Complete 2026 Guide)"},{"content":"Every real estate agent knows the struggle: you have 20 listings to write, a deadline in two hours, and your brain is completely blank. The property is a perfectly nice 3-bedroom colonial, but somehow \u0026ldquo;cozy and well-maintained\u0026rdquo; is all you can manage to type.\nChatGPT changes this completely. Not by writing generic filler — but by helping you craft listing descriptions that actually stop scrollers in their tracks, highlight the right emotional hooks, and move buyers toward action.\nThis guide gives you 10 copy-and-paste prompt templates for every property type you\u0026rsquo;re likely to encounter, plus real before/after examples so you can see exactly what the difference looks like.\nFor the complete collection, see our ChatGPT Prompts for Real Estate Agents guide .\nWhy Listing Descriptions Still Matter in 2026 With video tours, 3D walkthroughs, and photo-heavy platforms dominating search, you might wonder if written descriptions even move the needle. The answer is: absolutely yes, and more than ever.\nHere\u0026rsquo;s why:\nSearch indexing. Platforms like Zillow and Realtor.com index listing text for keyword searches. A well-written description improves your listing\u0026rsquo;s discoverability. Emotional priming. Buyers often read the description before or after viewing photos to confirm the \u0026ldquo;feeling\u0026rdquo; they got. Good copy validates that emotional response. Differentiation. When three similar properties hit the market the same week, the one with the most compelling description gets the showing requests. Agent credibility. A polished description signals professionalism. Buyers trust agents who clearly put effort into their listings. The problem is that writing good copy is time-consuming — unless you have the right system.\nHow to Use These Templates Each template below is a structured ChatGPT prompt. To use them:\nCopy the prompt into ChatGPT (GPT-4o recommended) Fill in the bracketed fields with your property\u0026rsquo;s actual details Ask ChatGPT to generate 2-3 variations Pick the best version, then edit for accuracy and local flavor You\u0026rsquo;ll get a solid first draft in under 2 minutes. Final polish takes another 5. That\u0026rsquo;s a 20-minute task compressed to 7.\nTemplate 1: Single-Family Home (Suburban) The prompt:\nWrite a compelling real estate listing description for a single-family home with these details: - Bedrooms: [number] - Bathrooms: [number] - Square footage: [sqft] - Lot size: [size] - Year built: [year] - Key features: [list 5-7 features, e.g., updated kitchen, hardwood floors, two-car garage] - Neighborhood highlights: [schools, proximity to parks, commute notes] - Listing price range: [price] - Tone: warm and aspirational, written for a family buyer - Length: 150-200 words - Do NOT use the words \u0026#34;charming,\u0026#34; \u0026#34;cozy,\u0026#34; or \u0026#34;nestled\u0026#34; Before (generic):\nCharming 4-bed, 2-bath colonial in desirable neighborhood. Updated kitchen with granite counters. Hardwood floors throughout. Large backyard perfect for entertaining. Two-car garage. Close to schools and shopping.\nAfter (ChatGPT-assisted):\nPicture Sunday mornings in a sunlit kitchen with quartz counters and custom cabinetry — the kind of space where coffee tastes better. This 4-bedroom colonial sits on a quiet cul-de-sac just minutes from Riverside Elementary, with hardwood floors that flow from a welcoming foyer into open living and dining areas. The backyard is genuinely large: room for a playset, a vegetable garden, and a patio setup for summer evenings. A two-car garage with extra storage completes the picture. Homes on this street rarely come to market. Don\u0026rsquo;t miss your window.\nThe second version creates a scene. It gives buyers something to imagine themselves into. That\u0026rsquo;s what moves people to book showings.\nTemplate 2: Luxury Property The prompt:\nWrite a luxury real estate listing description for a high-end property: - Property type: [single-family / penthouse / estate] - Bedrooms: [number] - Bathrooms: [number] - Square footage: [sqft] - Premium features: [e.g., chef\u0026#39;s kitchen, home theater, wine cellar, pool, smart home system] - Location highlights: [views, waterfront, gated community, proximity to amenities] - Recent renovations or unique architectural details: [details] - Tone: sophisticated, aspirational, premium — avoid \u0026#34;luxury\u0026#34; as an adjective - Length: 200-250 words Key tip: The word \u0026ldquo;luxury\u0026rdquo; is overused to the point of meaninglessness. Ask ChatGPT explicitly to demonstrate luxury through specifics rather than stating it.\nTemplate 3: Condo / Urban Apartment The prompt:\nWrite a real estate listing description for an urban condo: - Floor: [floor number] of [total floors] - Bedrooms: [number] - Bathrooms: [number] - Square footage: [sqft] - Building amenities: [e.g., rooftop terrace, concierge, gym, bike storage] - Unit features: [e.g., floor-to-ceiling windows, in-unit laundry, updated bathrooms] - Walkability highlights: [restaurants, transit, parks within walking distance] - Buyer profile: young professional or urban couple - Tone: energetic, modern, lifestyle-forward - Length: 150-175 words What works for condos: Lead with lifestyle, not square footage. Urban buyers are buying a way of living, not just a floor plan.\nTemplate 4: Fixer-Upper / Investment Property The prompt:\nWrite a real estate listing description for a property being sold as a fixer-upper or investment opportunity: - Property type: [type] - Bedrooms/bathrooms: [numbers] - Square footage: [sqft] - Current condition: [describe honestly — what needs work] - Upside potential: [location benefits, lot size, comparable renovated homes in the area] - Target buyer: investor, house flipper, or ambitious first-time buyer - Tone: honest and opportunity-focused — don\u0026#39;t hide the condition, frame the upside - Length: 150-200 words Before:\nProperty sold as-is. Needs TLC. Great bones.\nAfter (ChatGPT-assisted):\nAttention investors and renovators: this 3-bed, 1-bath bungalow on a 6,200 sq ft lot is priced to reflect its current condition — and the upside is real. Comparable fully renovated homes on the same street are selling at $485K+. The structure is sound, the lot is deep, and the location — two blocks from the new Midtown transit stop — only gets better. This is the kind of deal that doesn\u0026rsquo;t stay on market long. Bring your contractor and your vision.\nTemplate 5: Vacant Land / Lot The prompt:\nWrite a listing description for a vacant lot or undeveloped land: - Total acreage or lot size: [size] - Zoning: [residential / commercial / agricultural / mixed-use] - Location and access: [street access, proximity to utilities, nearest town] - Topography and features: [flat, wooded, waterfront, views] - Development potential or restrictions: [what can be built, any easements or covenants] - Target buyer: builder, developer, or private buyer - Tone: factual and opportunity-focused - Length: 125-150 words Template 6: Vacation / Short-Term Rental Property The prompt:\nWrite a listing description for a vacation home or short-term rental investment: - Property type: [cabin / beach house / mountain retreat / lake house] - Bedrooms/bathrooms: [numbers] - Key features: [fireplace, hot tub, game room, outdoor kitchen, dock access] - Location highlights: [distance to ski slopes, beach, national park, etc.] - Current rental income or revenue potential: [if applicable] - Buyer profile: vacation home buyer OR investor seeking rental income - Tone: experiential and evocative — help buyers picture themselves there - Length: 175-225 words Pro tip for vacation properties: Sensory language is especially effective here. Ask ChatGPT to use sounds, smells, and physical sensations — the crackle of a fire, the smell of pine, the sound of the lake at dawn.\nTemplate 7: New Construction / Builder Spec Home The prompt:\nWrite a listing description for a newly constructed home: - Builder: [name if notable] - Bedrooms/bathrooms: [numbers] - Square footage: [sqft] - Standard inclusions: [appliances, finishes, smart home features] - Community features: [pool, clubhouse, walking trails, HOA amenities] - Estimated completion / move-in date: [date] - Warranty details: [builder warranty terms] - Tone: forward-looking and modern, emphasizing quality and newness - Length: 175-200 words Template 8: Senior Living / Downsizing Property The prompt:\nWrite a listing description for a property ideal for downsizing or senior buyers: - Property type: [ranch home / active adult community / accessible condo] - Bedrooms/bathrooms: [numbers] - Accessibility features: [single-floor living, wide doorways, walk-in shower, elevator access] - Low-maintenance highlights: [HOA-maintained exterior, smaller yard, newer systems] - Community and lifestyle features: [proximity to medical facilities, social amenities, transportation] - Tone: reassuring and lifestyle-positive — emphasize freedom and simplicity, not limitations - Length: 150-175 words Template 9: Multi-Family / Duplex Investment The prompt:\nWrite a listing description for a multi-family investment property: - Property type: [duplex / triplex / small apartment building] - Total units: [number] - Unit breakdown: [e.g., two 2-bed/1-bath units] - Current occupancy and rental income: [details or \u0026#34;available upon request\u0026#34;] - Recent improvements: [roof, systems, units renovated] - Location: [neighborhood, proximity to employment centers or universities] - Target buyer: real estate investor or house-hacker - Tone: numbers-focused and opportunity-driven - Length: 150-200 words Template 10: Historic or Architecturally Significant Home The prompt:\nWrite a listing description for a historic or architecturally significant property: - Year built and architectural style: [year and style, e.g., 1908 Craftsman bungalow] - Historic designation or registry status: [if applicable] - Original features preserved: [e.g., original hardwood floors, built-in bookcases, claw-foot tubs] - Modern updates: [what has been sensitively updated] - Notable history: [any interesting provenance, if applicable and verifiable] - Tone: reverent and storytelling — this home has a history, help buyers connect with it - Length: 200-250 words Before:\nBeautiful 1920s home with original character. Updated kitchen. Historic neighborhood.\nAfter (ChatGPT-assisted):\nBuilt in 1922, when this neighborhood was first laid out by streetcar developers who believed in wide front porches and deep eaves, this Craftsman bungalow has been held by only three families in its 104-year history. The original fir floors have never been covered. The built-in bookcases in the living room still wear their first coat of white paint — applied by the family that watched the Depression come and go from this very address. The kitchen was updated sensitively in 2019: new appliances and soapstone counters that feel as period-appropriate as they are functional. What you\u0026rsquo;re buying isn\u0026rsquo;t just square footage — it\u0026rsquo;s a home with continuity, integrity, and a story you\u0026rsquo;ll be proud to pass on.\nReady to Grow Your Real Estate Career? Agents who combine market expertise with AI-powered productivity are closing more deals with less effort. Find your next career on doda — Japan\u0026rsquo;s top job platform with real estate and business development opportunities.\nHow to Get Consistent Results A few extra tips for making ChatGPT listing descriptions reliably good:\nGive it constraints. The more specific your prompt, the better the output. Tell ChatGPT which words to avoid (overused terms like \u0026ldquo;charming,\u0026rdquo; \u0026ldquo;stunning,\u0026rdquo; \u0026ldquo;rare find\u0026rdquo;). Tell it the tone, the buyer profile, and the length.\nAsk for variations. Always request 2-3 versions. You\u0026rsquo;ll find that variation 2 often has a better opening line, while variation 3 has the best closing. Combine the best elements.\nFact-check everything. ChatGPT doesn\u0026rsquo;t know your property — it can only work with what you give it. Read every output carefully for accuracy before publishing.\nMaintain your voice. After generating a draft, do a quick pass to add local references, remove anything that doesn\u0026rsquo;t sound like you, and ensure compliance with fair housing guidelines.\nRelated Tools Estimate your mortgage payments → Mortgage Calculator Calculate your ideal freelance rate → Freelance Rate Calculator Related Templates Ready to upgrade your entire listing workflow?\nChatGPT Prompt Templates for Real Estate Agents — A complete toolkit of 50+ tested prompts covering listings, client emails, market reports, social media, and more. Built specifically for agents who want to save hours every week without sacrificing quality. This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/chatgpt-property-listing-description-templates/","summary":"\u003cp\u003eEvery real estate agent knows the struggle: you have 20 listings to write, a deadline in two hours, and your brain is completely blank. The property is a perfectly nice 3-bedroom colonial, but somehow \u0026ldquo;cozy and well-maintained\u0026rdquo; is all you can manage to type.\u003c/p\u003e\n\u003cp\u003eChatGPT changes this completely. Not by writing generic filler — but by helping you craft listing descriptions that actually stop scrollers in their tracks, highlight the right emotional hooks, and move buyers toward action.\u003c/p\u003e","title":"How to Write Property Listing Descriptions with ChatGPT (10 Templates)"},{"content":"The real estate industry moves fast, and agents who master AI tools in 2026 are closing more deals with less burnout. ChatGPT has become an indispensable tool for top-performing agents — not to replace the human relationship, but to handle the time-consuming writing work that used to eat hours every day.\nThis guide gives you 30 battle-tested, copy-paste prompts organized by the exact tasks you face every week. No fluff. Just prompts that work.\nWhy Real Estate Agents Need ChatGPT in 2026 The average real estate agent spends 11 hours per week on administrative writing tasks: drafting property descriptions, following up with clients, preparing listing presentations, and managing social media. That\u0026rsquo;s 11 hours you\u0026rsquo;re not prospecting, showing homes, or closing deals.\nChatGPT eliminates most of that friction. With the right prompts, you can:\nWrite a compelling 300-word property description in 60 seconds Draft a follow-up email sequence in 5 minutes instead of 45 Generate a comparative market analysis summary instantly Create a month\u0026rsquo;s worth of social media content in one sitting The key is knowing how to ask. Vague prompts produce generic output. The prompts in this guide are engineered to give you real estate-specific, professional-grade results.\nPart 1: Property Description Prompts Property descriptions are where most agents waste the most time — and where AI delivers the fastest ROI.\nPrompt 1: Standard MLS Listing Description Write a compelling MLS property description for the following home. Keep it under 250 words, lead with the most impressive feature, and use active, sensory language. Avoid clichés like \u0026#34;cozy\u0026#34; or \u0026#34;charming\u0026#34; unless they are truly the best word. Property details: - Address: [ADDRESS] - Bedrooms: [#] | Bathrooms: [#] - Square footage: [#] sq ft - Year built: [YEAR] - Lot size: [SIZE] - Key features: [LIST 5-8 FEATURES] - Neighborhood highlights: [NEARBY AMENITIES] - Recent upgrades: [LIST ANY RENOVATIONS] - Asking price: $[PRICE] - Target buyer: [DESCRIBE IDEAL BUYER] Prompt 2: Luxury Property Description Write a luxury real estate listing description for a high-end property. Use elevated language that evokes lifestyle and aspiration — not just features. Emphasize exclusivity, craftsmanship, and the experience of living there. Aim for 300-350 words. Property details: - Property type: [SINGLE FAMILY / CONDO / ESTATE] - Price point: $[PRICE] - Standout architectural features: [LIST] - Premium finishes and appliances: [LIST] - Outdoor/entertaining spaces: [DESCRIBE] - Views or setting: [DESCRIBE] - Smart home features: [LIST] - Nearest luxury amenities: [GOLF, MARINA, PRIVATE SCHOOLS, ETC.] Prompt 3: Investment Property Description (for Investors) Write a property listing description targeting real estate investors rather than owner-occupants. Lead with the financial opportunity. Include language about cash flow potential, appreciation, and value-add opportunities. Keep it under 200 words and factual. Property details: - Property type: [DUPLEX / MULTIFAMILY / COMMERCIAL / SINGLE FAMILY RENTAL] - Current rent roll: $[AMOUNT]/month - Cap rate: [%] - Gross rent multiplier: [#] - Occupancy: [%] - Recent CapEx investments: [LIST] - Value-add opportunities: [DESCRIBE] - Zoning: [ZONE] - Market vacancy rate: [%] Prompt 4: Fixer-Upper Description (Turning Negatives into Positives) Write a listing description for a fixer-upper property that honestly acknowledges its condition while emphasizing its potential and value. Target buyers who enjoy renovation projects or want to build equity. Be honest but optimistic. Under 200 words. Property details: - Condition: [DESCRIBE HONESTLY] - Price vs. comparable renovated homes: [$ BELOW MARKET] - Structural soundness: [FOUNDATION, ROOF STATUS] - What\u0026#39;s already been updated: [LIST] - What needs work: [LIST HONESTLY] - Neighborhood trajectory: [IMPROVING / ESTABLISHED / GENTRIFYING] - ARV estimate: $[AMOUNT] Prompt 5: Condo/HOA Property Description Write a condo listing description that highlights the lifestyle benefits of the community as much as the unit itself. Mention HOA amenities, fees (framed positively as what they include), and the lock-and-leave lifestyle appeal. Under 250 words. Unit details: - Floor: [#] of [TOTAL FLOORS] - Unit size: [SQ FT] - Views: [DESCRIBE] - Unit features: [LIST] - HOA fee: $[AMOUNT]/month — includes: [LIST INCLUSIONS] - Community amenities: [POOL, GYM, CONCIERGE, ETC.] - Pet policy: [YES/NO/RESTRICTIONS] - Target lifestyle: [PROFESSIONALS / RETIREES / INVESTORS] Part 2: Client Email Templates Email follow-up is where deals are won and lost. These prompts generate professional, personalized emails in seconds.\nPrompt 6: First-Contact Email After Inquiry Write a warm, professional email response to someone who just inquired about a property listing online. The goal is to build rapport, answer their likely questions, and schedule a showing or call. Keep it under 150 words. Do not be pushy. Context: - Property they inquired about: [ADDRESS / BRIEF DESCRIPTION] - My name: [NAME] - My brokerage: [BROKERAGE] - Next available showing times: [TIMES] - One interesting fact about the property not in the listing: [FACT] Prompt 7: Post-Showing Follow-Up Email Write a post-showing follow-up email for a buyer who just toured a property. The tone should be friendly and consultative, not salesy. Ask for honest feedback, address any concerns they mentioned, and suggest a logical next step. Context: - Property shown: [ADDRESS] - Buyer\u0026#39;s name: [NAME] - Showing date: [DATE] - Concerns they mentioned during the tour: [LIST ANY OBJECTIONS] - Their strongest positive reactions: [WHAT THEY LIKED] - Suggested next step: [2ND SHOWING / MAKE AN OFFER / ALTERNATIVE PROPERTY] Prompt 8: Price Reduction Announcement to Buyer List Write an email to my buyer prospect list announcing a price reduction on a property they may have previously considered. Frame this as good news and an opportunity, not desperation. Create urgency without being manipulative. Under 150 words. Details: - Property: [ADDRESS + BRIEF DESCRIPTION] - Original price: $[AMOUNT] - New price: $[AMOUNT] ([$SAVINGS] reduction) - Days on market: [#] - Reason for reduction (if shareable): [SELLER MOTIVATION] - Call to action: [SCHEDULE A SHOWING / MAKE AN OFFER] Prompt 9: Expired Listing Outreach Email Write a prospecting email to a homeowner whose listing recently expired with another agent. The tone must be empathetic and solution-focused — not critical of the previous agent. Offer a fresh perspective and a specific reason to work with me. Under 200 words. Context: - Homeowner name: [NAME] - Property address: [ADDRESS] - How long it was listed: [# MONTHS] - My specific insight about why it may not have sold: [1-2 REASONS] - My differentiated approach: [WHAT I DO DIFFERENTLY] - Low-pressure CTA: [OFFER A FREE CONSULTATION / MARKET ANALYSIS] Prompt 10: Closing Gift Thank-You and Referral Ask Write a heartfelt thank-you email to send after closing, along with a natural, non-awkward request for referrals. The email should feel personal and celebratory, not transactional. Context: - Client name(s): [NAMES] - Property they bought/sold: [ADDRESS] - Closing date: [DATE] - One personal detail or memorable moment from the transaction: [DETAIL] - Closing gift I\u0026#39;m sending: [GIFT] - Referral ask: frame it as \u0026#34;if you know anyone who could use my help\u0026#34; Part 3: Market Analysis Summaries Prompt 11: Neighborhood Market Update (for Newsletter or Social) Write a concise neighborhood market update suitable for a real estate newsletter or social media. Use the data below to tell a clear story about what the market is doing and what it means for buyers and sellers. Keep it under 300 words. Avoid jargon. Market data for [NEIGHBORHOOD/ZIP CODE], [MONTH/YEAR]: - Median sale price: $[AMOUNT] (vs. [PRIOR PERIOD]: $[AMOUNT]) - Months of inventory: [#] - Average days on market: [#] - List-to-sale price ratio: [%] - Number of active listings: [#] - Number of sales this month: [#] - Notable trends: [ANY OBSERVATIONS] Prompt 12: Comparable Sales Summary for Sellers (CMA Narrative) Write a plain-English CMA narrative to accompany a formal market analysis for a seller. Explain what the comps mean, why I chose them, and how they support my recommended list price. This should be professional but conversational — something I can read aloud in a listing presentation. CMA details: - Subject property: [ADDRESS + KEY SPECS] - Recommended list price: $[AMOUNT] - Comp 1: [ADDRESS, SOLD PRICE, DATE, KEY DIFFERENCES] - Comp 2: [ADDRESS, SOLD PRICE, DATE, KEY DIFFERENCES] - Comp 3: [ADDRESS, SOLD PRICE, DATE, KEY DIFFERENCES] - Market conditions: [BUYER\u0026#39;S / SELLER\u0026#39;S / BALANCED] - Key adjustments made: [EXPLAIN ANY PLUS/MINUS ADJUSTMENTS] Prompt 13: Investment Property ROI Summary Write a one-page ROI summary for a potential real estate investor evaluating this property. Present the numbers clearly and explain what they mean. Use bullet points for the financial metrics and prose for the narrative context. Property financials: - Purchase price: $[AMOUNT] - Down payment: $[AMOUNT] ([%]) - Monthly mortgage (PITI): $[AMOUNT] - Gross monthly rent: $[AMOUNT] - Vacancy allowance: [%] - Operating expenses: $[AMOUNT]/month - Net operating income: $[AMOUNT] - Cash-on-cash return: [%] - 5-year appreciation estimate: [%] Part 4: Listing Presentation Content Prompt 14: Opening Statement for Listing Presentation Write a 2-minute verbal opening statement for a seller listing presentation. It should establish my credibility, show that I\u0026#39;ve done my homework on their specific property and neighborhood, and set the agenda for the meeting. Conversational but confident. My background: - Years in real estate: [#] - Recent sales in their neighborhood: [LIST 2-3] - My average list-to-sale ratio: [%] - My average days on market: [#] - Unique marketing tools I use: [LIST] - What I know about their specific property: [NOTES] - Seller\u0026#39;s stated goal: [SELL FAST / MAX PRICE / SPECIFIC DATE] Prompt 15: Marketing Plan Section for Listing Presentation Write a compelling \u0026#34;Marketing Your Home\u0026#34; section for a listing presentation. Each marketing channel should be described with specific actions I take and why each one matters. Make it sound premium and systematic. Use headers for each channel. My marketing channels: - Professional photography: [YES + ANY DETAILS] - Virtual tour/video: [YES/NO] - MLS exposure: [SYNDICATION NETWORKS] - Social media: [PLATFORMS + PAID ADS?] - Email marketing: [LIST SIZE / FREQUENCY] - Open houses: [STRATEGY] - Agent network outreach: [DESCRIBE] - Print/direct mail: [YES/NO] - Any unique tactics: [ADD ANY DIFFERENTIATORS] Prompt 16: Objection Handler — \u0026ldquo;Your Commission Is Too High\u0026rdquo; Write a calm, confident, non-defensive response to a seller who says my commission is too high. Focus on demonstrating value and ROI rather than defending the number. Keep it under 150 words and make it feel natural to say out loud. My value points: - Average sale price I achieve vs. discount brokers: [DATA IF AVAILABLE] - Marketing investment I make upfront: $[AMOUNT] - Average days on market vs. market average: [COMPARISON] - Services included: [LIST] - Net proceeds calculation: [FRAME COMMISSION IN CONTEXT OF NET] Part 5: Social Media Content Prompts Prompt 17: \u0026ldquo;Just Listed\u0026rdquo; Instagram Caption Write an Instagram caption for a just-listed property. Make it engaging, lead with the lifestyle, and include a call to action. Include 20 relevant hashtags at the end. Under 150 words for the main text. Property details: - Address/area: [NEIGHBORHOOD] - Price: $[AMOUNT] - Key features: [LIST 3-4] - Lifestyle angle: [DESCRIBE THE IDEAL LIFE IN THIS HOME] - CTA: [LINK IN BIO / DM ME / CALL TO SCHEDULE] - My Instagram handle: [@HANDLE] Prompt 18: \u0026ldquo;Just Sold\u0026rdquo; Social Post Write a \u0026#34;Just Sold\u0026#34; social media post (works for Instagram, Facebook, and LinkedIn). Celebrate the win, thank the clients (without naming them), mention any notable aspects of the transaction, and invite future clients to reach out. Include a subtle humble-brag about the result. Under 120 words. Transaction details: - Neighborhood: [AREA] - Result: [SOLD OVER ASK / SOLD IN X DAYS / MULTIPLE OFFERS / ETC.] - Any challenge overcome: [OPTIONAL] - Client sentiment (if they gave permission to reference): [QUOTE] Prompt 19: Educational Carousel Post (Home Buying Tips) Write the text for a 7-slide educational Instagram carousel about [TOPIC]. Each slide should have a bold headline and 2-3 sentences of supporting content. Make it genuinely informative — not a veiled ad. Topic: [e.g., \u0026#34;5 Things First-Time Buyers Always Get Wrong\u0026#34; / \u0026#34;How to Win in a Multiple-Offer Situation\u0026#34; / \u0026#34;When to Buy vs. Rent in 2026\u0026#34;] My market: [CITY / REGION] My expertise angle: [BUYER\u0026#39;S AGENT / LUXURY / INVESTMENT / FIRST-TIME BUYERS] Prompt 20: LinkedIn Article for Sphere of Influence Write a 500-word LinkedIn article I can post to stay top-of-mind with my professional network (not real estate agents — my sphere of potential clients). Make it informative, locally relevant, and subtly establish my authority without being overtly promotional. Topic: [e.g., \u0026#34;What the [CITY] Real Estate Market Looks Like Heading into Summer 2026\u0026#34; / \u0026#34;3 Mistakes I See Sellers Make in a Slowing Market\u0026#34;] My market: [CITY] Key data points I want to include: [LIST] Tone: professional but personable Part 6: Bonus Prompts for Common Scenarios Prompt 21: Difficult Client Situation — Managing Expectations Help me draft a frank but diplomatic email to a seller client whose price expectations are significantly above market value. I need to reset their expectations using data, not opinions, while preserving the relationship and keeping them as a client. Situation: - Their desired list price: $[AMOUNT] - Market-supported price range: $[RANGE] - Gap: $[AMOUNT] - Data I have to support my position: [COMPS, DAYS ON MARKET, ETC.] - Their motivation for high price: [REASON] - My suggested approach: [START AT X, REVIEW IN 30 DAYS / ETC.] Prompt 22: New Agent Bio for Website Write a compelling real estate agent bio for my website. Make it personal and story-driven — not a list of credentials. Help potential clients feel like they know me and would enjoy working with me. Around 250 words. My details: - Name: [NAME] - Years in real estate: [#] - Background before real estate: [CAREER / EDUCATION] - Why I got into real estate: [PERSONAL STORY] - Specialties: [BUYER\u0026#39;S SIDE / SELLER\u0026#39;S / LUXURY / FIRST-TIME / ETC.] - Neighborhoods/areas I know best: [LIST] - Personal life (optional): [FAMILY, HOBBIES, LOCAL INVOLVEMENT] - One thing clients always say about working with me: [ADJECTIVE / PHRASE] Prompt 23: Tenant Move-Out Notice to Seller\u0026rsquo;s Tenants Write a professional, legally neutral letter to inform tenants that the property they rent has been listed for sale. Explain the showing process, their rights, and how we will minimize disruption. Tone should be respectful and reassuring. Details: - Tenant name(s): [NAMES] - Property address: [ADDRESS] - Listing date: [DATE] - Showing notice requirement per local law: [# HOURS] - Preferred showing window: [DAYS/TIMES] - Tenant\u0026#39;s lease end date: [DATE] - Offer of showing courtesy (e.g., gift card): [OPTIONAL] Prompt 24: Referral Partner Outreach (to Mortgage Brokers, Attorneys) Write a short, professional outreach email to a mortgage broker or real estate attorney proposing a mutual referral relationship. Make it specific about what I offer and what I\u0026#39;m looking for. Not spammy. Under 150 words. My details: - Name: [NAME] - Brokerage: [BROKERAGE] - My specialty/volume: [DETAILS] - Their name: [NAME] - Why I\u0026#39;m reaching out to them specifically: [SPECIFIC REASON] - Proposed meeting format: [COFFEE / 15-MIN CALL / LUNCH] Prompt 25: Year-End Client Gift Note Write a brief, warm, handwritten-style note to accompany a year-end gift to past clients. Reference their transaction if possible. Express genuine gratitude and keep the door open for future business without being transactional. Under 80 words. Details: - Client name(s): [NAMES] - Transaction: [BRIEF REFERENCE TO THEIR BUY/SELL] - Gift: [WHAT YOU\u0026#39;RE SENDING] - Personal touch: [ONE SPECIFIC MEMORY FROM WORKING TOGETHER] Advanced Tips for Using These Prompts Tip 1: Always Add Specifics The difference between a generic output and a great one is the detail you provide. Fill in every bracket. The more specific your inputs, the more specific and useful the output.\nTip 2: Use the Refinement Chain After getting an initial response, use follow-up prompts to refine:\n\u0026ldquo;Make this more conversational\u0026rdquo; \u0026ldquo;Shorten this by 30%\u0026rdquo; \u0026ldquo;Add more urgency to the call to action\u0026rdquo; \u0026ldquo;Rewrite the opening sentence to be more attention-grabbing\u0026rdquo; \u0026ldquo;Make this sound like it\u0026rsquo;s coming from a luxury brand\u0026rdquo; Tip 3: Build Your Personal Style Guide Ask ChatGPT to analyze 3-5 emails or posts you\u0026rsquo;ve written that you\u0026rsquo;re proud of, then say: \u0026ldquo;Describe my writing style in bullet points.\u0026rdquo; Save this description and paste it into future prompts as a style guide.\nWrite this in my personal writing style: [PASTE YOUR STYLE DESCRIPTION] Content to write: [YOUR PROMPT] Tip 4: Create Property-Specific Templates For each listing, build a master prompt that contains all property details, and reuse it across all the prompts in this guide. Store these in a simple Google Doc or Notion page.\nTip 5: Stay Compliant Always review AI-generated content for Fair Housing compliance before publishing. Avoid language that could be construed as steering (e.g., descriptions of demographics, schools in ways that imply discriminatory intent). When in doubt, consult your broker.\nAI-Savvy Agents Are Winning More Listings Real estate professionals who leverage AI tools for marketing and communication have a clear edge. Find your next career on doda — browse real estate and business development roles on Japan\u0026rsquo;s leading job platform.\nThe 5 Prompts to Start With Today If you\u0026rsquo;re new to using ChatGPT in your real estate business, start with these five:\nPrompt 1 — Write your next property description (you\u0026rsquo;ll see the ROI immediately) Prompt 6 — Set up an inquiry response email template for each active listing Prompt 11 — Write a neighborhood market update for your newsletter Prompt 17 — Create a just-listed Instagram caption for your next listing Prompt 22 — Rewrite your agent bio Each of these tasks used to take 30-60 minutes. With the right prompt, they take 5-10 minutes. Over a month, that\u0026rsquo;s 10-20 hours returned to your calendar.\nConclusion AI isn\u0026rsquo;t replacing real estate agents — it\u0026rsquo;s replacing the agents who don\u0026rsquo;t use AI. The personal trust, local knowledge, and negotiation skill that great agents bring cannot be replicated by any tool. But the writing, the follow-up, the marketing content? That\u0026rsquo;s where ChatGPT becomes your most productive team member.\nSave this guide. Bookmark the prompts most relevant to your business. And start with just one prompt today — that\u0026rsquo;s how every productive habit begins.\nWant more AI productivity tools for real estate professionals? Subscribe to the Productivity Works newsletter for weekly tips.\nRelated Tools Estimate your mortgage payments → Mortgage Calculator Calculate your ideal freelance rate → Freelance Rate Calculator Related Templates Supercharge your real estate business with these AI tools:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Property listings, client emails, market analysis The AI Productivity Playbook 2026 — 50+ AI workflows for business professionals This article contains affiliate links. We may earn a commission at no extra cost to you.\nYou May Also Like How to Write Property Listing Descriptions with ChatGPT (10 Templates) Best AI Tools for Real Estate Agents 2026: Complete Guide Real Estate Investment in Japan for Salaried Workers: How It Compares to NISA and iDeCo ","permalink":"https://productivity-works.com/posts/chatgpt-real-estate-agent-prompts-2026/","summary":"\u003cp\u003eThe real estate industry moves fast, and agents who master AI tools in 2026 are closing more deals with less burnout. ChatGPT has become an indispensable tool for top-performing agents — not to replace the human relationship, but to handle the time-consuming writing work that used to eat hours every day.\u003c/p\u003e\n\u003cp\u003eThis guide gives you 30 battle-tested, copy-paste prompts organized by the exact tasks you face every week. No fluff. Just prompts that work.\u003c/p\u003e","title":"ChatGPT Prompts for Real Estate Agents: 30 Templates (2026)"},{"content":"This article contains affiliate links.\nHere\u0026rsquo;s the uncomfortable truth that career coaches sometimes dance around: ageism in hiring is real. Recruiters do make snap judgments. Some hiring managers do have unconscious (or conscious) biases about candidates with 25+ years of experience.\nHere\u0026rsquo;s the equally true but less-discussed counterpoint: experienced candidates who know how to present themselves have enormous advantages that younger candidates simply cannot replicate. Deep industry knowledge. Proven judgment under pressure. Networks built over decades. Track records that speak for themselves.\nThe gap between how experienced candidates are perceived and how they could be positioned is where ChatGPT becomes your most valuable tool.\nThis guide is for professionals over 50 who are changing careers — either by choice or necessity — and want to use AI to craft a resume that converts decades of experience into competitive advantage rather than red flags.\nFor the complete step-by-step resume writing process with AI, see our Create a Resume with AI: Step-by-Step Guide .\nThe Real Challenges (and Why They\u0026rsquo;re Solvable) Before jumping into prompts, it\u0026rsquo;s worth being honest about the specific challenges career changers over 50 face — because understanding the problem precisely is how you solve it.\nChallenge 1: The \u0026ldquo;overqualified\u0026rdquo; perception When a 30-year veteran applies for a mid-level role, hiring managers sometimes assume the candidate won\u0026rsquo;t be satisfied with the pay, will leave quickly for something better, or will be difficult to manage because they know more than their prospective boss. None of these need to be true, but they\u0026rsquo;re common assumptions.\nThe solution: Your resume and cover letter need to proactively address why you want this specific role at this specific level. ChatGPT can help you craft language that reframes \u0026ldquo;overqualified\u0026rdquo; as \u0026ldquo;uniquely prepared.\u0026rdquo;\nChallenge 2: Irrelevant experience dominating the resume When you have 30 years of experience across multiple roles, there\u0026rsquo;s a natural temptation to list everything — you\u0026rsquo;ve earned it, after all. But a resume that reads like a complete career history is actually less effective than one that\u0026rsquo;s strategically curated for the target role.\nThe solution: Ruthless curation. ChatGPT can help you identify which of your experiences are actually relevant to your target role and how to position them.\nChallenge 3: Skills that feel outdated Your core competencies are real and valuable. But if your resume says things like \u0026ldquo;proficient in Microsoft Office 2007\u0026rdquo; or lists skills that signal a different era, it creates doubt. Meanwhile, the skills that make you exceptional — stakeholder management, cross-functional leadership, pattern recognition from experience — often go unstated because they feel too obvious to mention.\nThe solution: Translate your experience into the language your target industry uses today, and surface the non-obvious competencies that differentiate you.\nChallenge 4: Not knowing how to frame a pivot Career changes are increasingly common, but many experienced professionals struggle to write a coherent narrative around them. \u0026ldquo;I spent 25 years in manufacturing and now want to move into supply chain consulting\u0026rdquo; sounds like a reasonable pivot — but your resume needs to tell that story convincingly.\nThe solution: A strategic \u0026ldquo;summary\u0026rdquo; section at the top of your resume that frames your experience as preparation for where you\u0026rsquo;re going, not documentation of where you\u0026rsquo;ve been.\nThe Core Strategy: Forward-Facing, Not Backward-Looking The most important mindset shift for career-changing resumes over 50 is this: your resume should face forward, not backward.\nTraditional resumes are written like history documents — here\u0026rsquo;s what I did, in order. A strategic career-change resume is written like a pitch — here\u0026rsquo;s why I\u0026rsquo;m exactly the right person for this next chapter.\nThis means:\nYour summary section leads with your target direction, not your origin Your most recent experiences are highlighted; older experiences are summarized or condensed Every bullet point is evaluated for relevance to your target role, not just accuracy Skills are presented in language that matches the job posting, not the industry you\u0026rsquo;re leaving ChatGPT is extremely good at this kind of strategic repositioning — if you give it the right inputs.\nPhase 1: Inventory and Translation with ChatGPT Before you write a single resume bullet, you need to do an inventory of your transferable skills. This is where most career changers underestimate themselves.\nThe skills inventory prompt I\u0026#39;m preparing to change careers at age [your age]. I have [X] years of experience in [your current/previous industry/field]. I want to transition into [target field or role — be specific]. Here is a summary of my work history: [paste a brief description of each role — company, title, years, and 3-5 key responsibilities or achievements for each] Please help me: 1. Identify the top 10 transferable skills from my background that are directly relevant to my target field of [target field] 2. For each skill, explain how to reframe it in language that resonates with hiring managers in [target field] 3. Identify any skills I may be undervaluing or not mentioning — things that would be unusual or impressive to someone entering [target field] from a traditional path 4. Flag any gaps between my experience and typical requirements for [target role] — and suggest how I might address each gap in my resume or cover letter Don\u0026#39;t soften the gap analysis — I want an honest assessment. This prompt is valuable because it forces you to articulate your history AND gives ChatGPT enough context to provide genuinely useful skill translation.\nThe \u0026ldquo;translate my experience\u0026rdquo; prompt Once you have your skills inventory, use this prompt to rewrite specific experiences in the language of your target industry:\nI\u0026#39;m transitioning from [current field] to [target field]. Here is a bullet point from my current resume: \u0026#34;[Paste your existing bullet]\u0026#34; Please rewrite this bullet in two ways: 1. Using language and terminology that resonates with hiring managers in [target field] 2. Emphasizing the transferable impact, not the industry-specific context Also suggest what type of evidence (metrics, outcomes, scale) would make this bullet even stronger if I can provide it. The target role I\u0026#39;m applying to: [job title and brief description] Run this for your top 10-15 most relevant experience bullets. The goal is a portfolio of translated bullets you can draw from.\nPhase 2: The Age-Positive Summary Section The summary (or professional profile) section at the top of your resume is the most important real estate on the page. For career changers over 50, it does three jobs simultaneously:\nEstablishes your direction (where you\u0026rsquo;re going, not just where you\u0026rsquo;ve been) Signals self-awareness about the transition Leads with your most compelling differentiators The summary generation prompt Write a professional resume summary for a career changer with this profile: Background: [2-3 sentences describing your field and most notable experience] Target role: [specific job title or type of role] Key transferable strengths: [list 4-5 from your skills inventory] Most impressive career accomplishment relevant to the target field: [describe briefly] What makes my pivot make sense: [briefly explain your motivation for the career change — this helps position the transition as logical, not random] The summary should: - Be 4-6 sentences or 80-120 words - Open with a forward-facing statement (what I bring to [target field]) rather than backward-looking (I have X years of experience in Y) - Avoid clichés: \u0026#34;results-driven,\u0026#34; \u0026#34;passionate,\u0026#34; \u0026#34;team player,\u0026#34; \u0026#34;dynamic\u0026#34; - Avoid language that signals age-anxiety (\u0026#34;despite my age,\u0026#34; \u0026#34;even though I\u0026#39;m transitioning\u0026#34;) - Sound confident and specific — this person knows what they\u0026#39;re doing and why they want this - Use language that matches the job posting vocabulary for [target field] Good summary (AI-assisted, career changer):\nHealthcare operations leader turning two decades of clinical administration into strategic consulting value. Built and scaled three regional health system networks across 40+ facilities — the systems knowledge, stakeholder management, and change leadership this work required translate directly into health tech go-to-market advisory. Now focused on helping digital health companies understand how their solutions land in real clinical environments. Known for turning abstract strategy into operational reality.\nLess effective summary:\nExperienced healthcare professional with 22 years in hospital administration seeking new opportunities in healthcare technology. Strong background in operations and management. Looking to leverage my extensive experience in a new direction.\nThe first version faces forward and leads with specific, impressive context. The second version is essentially a backward-looking table of contents.\nPhase 3: Reframing Decades of Experience One of the most common resume mistakes for experienced candidates is the \u0026ldquo;complete career history\u0026rdquo; approach — listing every role going back to 1998. This is almost always wrong for a career-change resume.\nThe 15-year rule For most career-change resumes over 50, experience older than 15 years should either be omitted entirely or condensed into a single line (company, title, years) with no bullets. The exceptions:\nThe older experience is directly and unusually relevant to your target role The older role was prestigious enough to serve as a credentialing signal (e.g., early career at a notable company) You\u0026rsquo;re trying to show a through-line that requires the historical context The condensation prompt I have the following work history that predates 2010: [list each role with basic details] I\u0026#39;m applying for roles in [target field]. My post-2010 experience is [summarize]. Please help me decide which pre-2010 roles (if any) to include on a targeted resume, and how to condense them. For each role, tell me: - Keep or omit? (based on relevance to [target field]) - If keep: suggest a one-line version with no bullets - If omit: is there any specific achievement from this role worth surfacing elsewhere in the resume? My goal is a resume that doesn\u0026#39;t look like I\u0026#39;m hiding anything, but that focuses attention on my recent and most relevant experience. Presenting a 25-30 year career compactly If you have a very long career, use this structure:\nPROFESSIONAL EXPERIENCE [Most recent role — full treatment: title, company, dates, 4-6 bullets] [Second most recent role — moderate treatment: title, company, dates, 3-4 bullets] [Third most recent role — light treatment: title, company, dates, 2-3 bullets, choose only highest-impact items] Earlier Career: [Role at Company A (years)], [Role at Company B (years)], [Role at Company C (years)] Notable: [One line about a major early-career achievement if genuinely impressive] This approach communicates a full, accomplished career without overwhelming the reader or highlighting how long ago you started.\nPhase 4: Age-Positive Language Throughout Language choices on a resume can inadvertently signal age in ways that trigger bias — even when the content itself is strong. ChatGPT can audit your resume for these signals.\nThe age-signal audit prompt Please review this resume for language patterns that might inadvertently signal age to a recruiter or hiring manager who has unconscious ageist biases. I\u0026#39;m NOT asking you to hide my experience — I want to present it effectively. Specifically, look for: 1. Technology mentions that signal a specific era (old software versions, outdated platforms) 2. Job title conventions that have changed over time (e.g., \u0026#34;secretary\u0026#34; vs. \u0026#34;administrative coordinator\u0026#34;) 3. Education formatting that draws attention to graduation year rather than credentials 4. Skills listed that were once differentiators but are now baseline (e.g., \u0026#34;Microsoft Word proficient\u0026#34;) 5. Any language patterns that signal defensiveness about experience level For each issue found, suggest a specific rewrite. [Paste your resume text here] Technology and tools language One common age signal is listing technology skills that were impressive in 2005 but are now table stakes. Instead of:\n\u0026ldquo;Proficient in Microsoft Office Suite, email, internet research\u0026rdquo; Write:\n\u0026ldquo;Advanced Excel (financial modeling, pivot tables, Power Query); Slack, Notion, Salesforce CRM; currently completing [relevant certification if applicable]\u0026rdquo; If you\u0026rsquo;ve made an effort to stay current with modern tools — and you should — name them specifically.\nThe \u0026ldquo;currently learning\u0026rdquo; signal One of the most effective age-mitigation strategies is proactively demonstrating currency:\nProfessional Development: - [Certification name], [Platform], completed [recent date] - Currently enrolled: [Course or certification in progress] - [Conference or industry event attended recently] This signals that you\u0026rsquo;re actively engaged in your own development, not coasting on past credentials.\nPhase 5: Skills Translation for Common Career Pivots Here are specific ChatGPT prompts for the most common career-change scenarios for professionals over 50.\nFrom corporate management to consulting/advisory I spent [X] years in [corporate function] at [types of companies]. I want to position myself as an independent consultant or advisor in [target area]. Help me translate these corporate titles and responsibilities into consulting-relevant language: [list your roles and key responsibilities] Specifically: - How do I describe my experience in terms of client value delivered, not company outcomes? - What does \u0026#34;internal management\u0026#34; experience translate to in consulting contexts? - How should I frame my interest in consulting as a strategic choice, not a fallback? - What should my resume\u0026#39;s summary say to signal that I\u0026#39;m a credible consultant, not just a former executive? From military/government to private sector I\u0026#39;m transitioning from [X] years in [military branch / government agency] to the private sector, specifically targeting roles in [target area: e.g., operations, logistics, project management, cybersecurity]. Help me translate this military/government background for a private-sector resume: [describe your roles and responsibilities in your own terms] I need help: 1. Replacing acronyms and jargon with civilian equivalents 2. Translating rank/grade into equivalent private-sector authority level 3. Reframing mission-critical government work in terms of business value 4. Identifying which of my experiences are most compelling to private-sector hiring managers in [target field] From education to corporate learning and development I spent [X] years as a [teacher/professor/administrator] in [K-12 / higher education / vocational training]. I want to transition into corporate Learning and Development (L\u0026amp;D) or instructional design roles. Help me translate my education background for corporate L\u0026amp;D resumes: [describe your roles and teaching/curriculum areas] Focus on: - Curriculum design experience → instructional design - Assessment and evaluation → learning measurement - Classroom management → facilitation skills - Parent/student communication → stakeholder communication - Administrative experience → program management Also: what specific L\u0026amp;D terminology should I be using in my resume to signal that I understand the corporate training world? Putting It All Together: The Final Resume Review Prompt After you\u0026rsquo;ve drafted your resume using the prompts above, run this final review:\nPlease review this complete resume for a [target job title] position. The applicant is a career changer with [X] years of experience in [previous field] transitioning into [new field]. Review the resume for: 1. Narrative coherence: Does the career change story make sense? Is the transition logical? 2. Relevance focus: Are the most relevant experiences getting the most space? 3. Language alignment: Does the terminology match what hiring managers in [target field] use? 4. Strength of evidence: Which bullets are vague (\u0026#34;led team,\u0026#34; \u0026#34;managed projects\u0026#34;) and need metrics or specifics? 5. Summary effectiveness: Does the summary make a compelling case in the first 10 seconds? 6. Length and formatting: Is this appropriately concise (ideally 1.5-2 pages for this experience level)? 7. Age signals: Any language that might trigger unconscious bias? Be direct and specific. I want actionable feedback, not reassurance. [Paste your full resume here] A Note on Confidence Experienced professionals often underestimate themselves when writing resumes — especially when changing fields. Years of doing one thing well can make it hard to see your own value from the outside.\nChatGPT won\u0026rsquo;t manufacture qualifications you don\u0026rsquo;t have. But it is very good at helping you see and articulate the genuine value that\u0026rsquo;s already there — and at presenting your real experience in language that resonates with your target audience.\nThe career change you\u0026rsquo;re making isn\u0026rsquo;t a retreat. It\u0026rsquo;s a pivot. There\u0026rsquo;s a big difference. Your resume should say the same.\nMaking a career change in Japan? doda specializes in career transitions and offers bilingual agent support — their advisors can help match your reframed experience to roles where your background is genuinely valued.\nRelated Tools Calculate your take-home pay → Salary Calculator Check your tax bracket → Tax Bracket Calculator Related Templates Ready to move beyond the resume and prepare for interviews?\nJob Interview Prep Guide — A comprehensive AI-powered interview preparation system with prompts for answering tough questions about career changes, age concerns, gaps in employment, and salary negotiation. Includes scripts for the most common interview scenarios and practice frameworks you can use with ChatGPT to simulate real interviews before the real thing. ","permalink":"https://productivity-works.com/posts/chatgpt-resume-over-50-career-change/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s the uncomfortable truth that career coaches sometimes dance around: ageism in hiring is real. Recruiters do make snap judgments. Some hiring managers do have unconscious (or conscious) biases about candidates with 25+ years of experience.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s the equally true but less-discussed counterpoint: experienced candidates who know how to present themselves have enormous advantages that younger candidates simply cannot replicate. Deep industry knowledge. Proven judgment under pressure. Networks built over decades. Track records that speak for themselves.\u003c/p\u003e","title":"ChatGPT Resume Tips for Career Changers Over 50 (2026)"},{"content":"A ChatGPT Plus subscription costs $20 a month. If you use it strategically for personal finance, it can realistically save you ten to twenty-five times that amount every month. Most people never get close to that return because they use AI for the wrong things.\nThis article covers ten concrete, tested approaches — each with example prompts you can copy and use right now.\n1. Negotiate Your Bills Over the Phone (With a Script ChatGPT Wrote) Cable, internet, insurance, and gym memberships are all negotiable — most people just never ask. The barrier isn\u0026rsquo;t the phone call itself, it\u0026rsquo;s not knowing what to say.\nHow to do it: Tell ChatGPT your current provider, what you\u0026rsquo;re paying, and how long you\u0026rsquo;ve been a customer. Ask it to write a negotiation script that includes a competitive alternative quote and a threat to cancel.\nExample prompt:\n\u0026ldquo;I pay $89/month for internet from Comcast. I\u0026rsquo;ve been a customer for 4 years. Write me a 3-minute phone script to negotiate this down to $60, mentioning that a competitor is offering $55/month. Include a response if they say no the first time.\u0026rdquo;\nRealistic savings: $20–$60/month across 2–3 bills. Annual impact: $240–$720.\n2. Audit Subscriptions You\u0026rsquo;ve Forgotten About The average American household spends over $270/month on subscriptions. A significant portion of that goes to services that haven\u0026rsquo;t been used in months.\nHow to do it: Export your bank or credit card statement as a CSV. Paste the transaction list into ChatGPT and ask it to identify recurring charges, categorize them, and flag any that appear infrequent or duplicate.\nExample prompt:\n\u0026ldquo;Here is a list of my transactions for the last 3 months. Identify all recurring subscription charges, sort them by monthly cost, and flag any I\u0026rsquo;ve been charged for fewer than 3 times in the period.\u0026rdquo;\nRealistic savings: $30–$100/month for the average household on first audit.\n3. Build a Meal Plan That Matches Your Grocery Budget Grocery costs are one of the most controllable line items in a household budget — but most people don\u0026rsquo;t plan, so they overspend and waste food. ChatGPT can generate a complete weekly meal plan with a shopping list tailored to a specific dollar amount.\nExample prompt:\n\u0026ldquo;Create a 7-day meal plan for 2 adults with a grocery budget of $80. Include breakfast, lunch, and dinner. Generate a consolidated shopping list. Prioritize whole foods, minimize food waste by reusing ingredients across meals, and avoid recipes that take more than 30 minutes.\u0026rdquo;\nRealistic savings: Households that meal plan typically spend 20–30% less on food. On a $600/month grocery budget, that\u0026rsquo;s $120–$180/month.\n4. Find Hidden Tax Deductions Before Filing ChatGPT cannot replace a CPA, but it can help you understand what deductions and credits you might qualify for — so you arrive at your tax appointment better prepared, or catch things TurboTax might have missed.\nExample prompt:\n\u0026ldquo;I\u0026rsquo;m a W-2 employee who also does freelance graphic design on the side. I work from home 3 days a week and bought a new laptop this year. I have a student loan and contributed to a Roth IRA. What tax deductions or credits should I research before filing? I\u0026rsquo;m in the US.\u0026rdquo;\nRealistic savings: One missed deduction caught can easily mean $200–$1,000 back. Do this every year before you file.\n5. Write Dispute Letters for Bank Fees, Medical Bills, and Errors Banks charge overdraft fees, late fees, and annual fees — many of which get waived simply because the customer asked. Medical bills frequently contain billing errors. A well-written dispute letter dramatically improves your odds.\nExample prompt:\n\u0026ldquo;Write a professional letter disputing a $35 overdraft fee on my Chase checking account. This is my first overdraft in 2 years. I\u0026rsquo;m a long-standing customer. The letter should request a one-time courtesy waiver.\u0026rdquo;\nRealistic savings: $35–$150 per successful dispute. Medical bill audits can recover thousands.\n6. Compare Insurance Quotes Intelligently Insurance comparison shopping is tedious because each company asks different questions. ChatGPT can help you understand what factors influence your premiums, what coverage you actually need (vs. what\u0026rsquo;s being oversold), and what questions to ask before switching.\nExample prompt:\n\u0026ldquo;I\u0026rsquo;m 34, own a 2021 Honda CR-V, and currently pay $180/month for full coverage auto insurance. What factors should I consider when comparing quotes? What coverage levels are genuinely necessary vs. add-ons I probably don\u0026rsquo;t need? What questions should I ask each insurer?\u0026rdquo;\nRealistic savings: Switching auto or home insurance can save $300–$800/year. Better yet, use ChatGPT to understand your coverage before you\u0026rsquo;re underinsured.\n7. Create a Debt Payoff Plan and Model Different Scenarios ChatGPT can run debt payoff calculations instantly, model the difference between avalanche and snowball methods, and show you exactly how much interest you\u0026rsquo;ll save by making an extra $100 payment this month.\nExample prompt:\n\u0026ldquo;I have three debts: a credit card with $4,200 balance at 22% APR, a personal loan with $8,500 at 11% APR, and a car loan with $12,000 at 6.9% APR. My minimum payments total $430/month. I can afford $600/month total. Show me the payoff timeline and total interest paid under both avalanche and snowball methods.\u0026rdquo;\nRealistic savings: Optimizing payoff order on a $25,000 debt load can save $1,000–$3,000 in interest over the payoff period.\n8. Research Big Purchases Before You Buy Before buying anything over $100 — a TV, a mattress, a vacuum cleaner — use ChatGPT to understand the category, identify reliable brands, learn what specs actually matter vs. marketing noise, and find out when prices typically drop.\nExample prompt:\n\u0026ldquo;I\u0026rsquo;m buying a robot vacuum for a 1,200 sq ft apartment with hardwood floors and a medium-hair cat. Budget is $300. Explain what specs actually matter for this use case, which brands have the best reliability history, and whether there\u0026rsquo;s a better time of year to buy.\u0026rdquo;\nRealistic savings: Making one better purchasing decision per month — buying the right product at the right time, instead of returning and repurchasing — easily saves $50–$200/month.\n9. Optimize Your Credit Card Strategy Most people use one or two credit cards with suboptimal rewards for their spending patterns. ChatGPT can analyze your spending categories and suggest which cards to use for what, maximizing cash back or travel points without taking on new debt.\nExample prompt:\n\u0026ldquo;I spend roughly $400/month on groceries, $200 on gas, $300 on restaurants, and $500 on miscellaneous retail. I don\u0026rsquo;t pay annual fees currently but I\u0026rsquo;m open to a card with a fee if the rewards justify it. What credit card setup would maximize my rewards? I\u0026rsquo;m in the US.\u0026rdquo;\nRealistic savings: Optimizing credit card rewards on $1,400/month in spending can yield an extra $300–$600/year in cash back versus a flat 1% card.\n10. Write a Job Offer Negotiation Email This is the highest-ROI use on this list. A single successful salary negotiation is worth thousands of dollars — not just in year one, but compounded over your entire career because future raises build on the base.\nExample prompt:\n\u0026ldquo;I received a job offer for $78,000/year as a marketing manager in Austin, TX. Market data from LinkedIn and Glassdoor shows $85,000–$92,000 for this role and experience level. Write me a professional, warm negotiation email asking for $88,000, mentioning my 6 years of experience and a specific achievement (I grew email revenue by 40% at my last job). I don\u0026rsquo;t want to seem aggressive.\u0026rdquo;\nRealistic savings (or rather, earnings): The average successful negotiation adds $5,000–$15,000 to base salary. At a 3% annual raise, that $5,000 gap compounds to over $160,000 over a 30-year career.\nThe $500/Month Math Add it up conservatively:\nStrategy Monthly Savings Bill negotiation $40 Subscription audit $50 Meal planning $120 Better purchasing decisions $75 Credit card optimization $40 Debt payoff optimization $80 (interest savings) Fee disputes (amortized) $15 Total $420/month That\u0026rsquo;s $420/month without salary negotiation. Add one successful negotiation every few years and the annualized figure crosses $500 easily.\nWhat ChatGPT Can\u0026rsquo;t Do Be clear-eyed about the limits:\nIt cannot access your actual accounts without integrations Tax advice should always be verified with a professional It can make arithmetic errors — double-check any calculations it produces It doesn\u0026rsquo;t know your full financial picture unless you tell it Use it as a knowledgeable thinking partner, not a licensed advisor. The judgment calls remain yours.\nTurn Your Savings Into a Side Hustle Once ChatGPT helps you save $500/month, why not use it to make money online too? Get your domain on Onamae.com and launch your own blog or online business — putting both your savings and your AI skills to work.\nGetting Started Today Pick one item from this list — the easiest one — and spend 20 minutes on it this week. Negotiate a bill, audit your subscriptions, or paste your grocery budget into ChatGPT for a meal plan.\nThe goal isn\u0026rsquo;t to become a financial expert. It\u0026rsquo;s to stop leaving money on the table that\u0026rsquo;s already yours.\n[Upgrade to ChatGPT Plus at openai.com/chatgpt]\nRelated Tools Create a personalized budget → Budget Planner See how your savings compound → Compound Interest Calculator Pay off debt faster → Debt Payoff Calculator How much emergency fund do you need? → Emergency Fund Calculator Calculate how long to reach any savings target → Savings Goal Calculator Calculate your mortgage payment → Mortgage Calculator See how inflation affects your money → Inflation Calculator Related Templates Take control of your finances with these tools:\nSmart Budget Tracker (Excel Template) — Track income, expenses, and savings goals Side Hustle Starter Kit 2026 — Build additional income streams This article contains affiliate links. We may earn a commission at no extra cost to you.\nYou May Also Like Best Budgeting Apps 2026: Full Comparison How to Build an Emergency Fund Fast (2026) How to Pay Off Debt Fast: Best Strategies 2026 ","permalink":"https://productivity-works.com/posts/chatgpt-save-money/","summary":"\u003cp\u003eA ChatGPT Plus subscription costs $20 a month. If you use it strategically for personal finance, it can realistically save you ten to twenty-five times that amount every month. Most people never get close to that return because they use AI for the wrong things.\u003c/p\u003e\n\u003cp\u003eThis article covers ten concrete, tested approaches — each with example prompts you can copy and use right now.\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"1-negotiate-your-bills-over-the-phone-with-a-script-chatgpt-wrote\"\u003e1. Negotiate Your Bills Over the Phone (With a Script ChatGPT Wrote)\u003c/h2\u003e\n\u003cp\u003eCable, internet, insurance, and gym memberships are all negotiable — most people just never ask. The barrier isn\u0026rsquo;t the phone call itself, it\u0026rsquo;s not knowing what to say.\u003c/p\u003e","title":"10 Ways ChatGPT Can Save You $500/Month"},{"content":" ChatGPT vs Claude vs Gemini 2026: Which AI Assistant Wins? Three giants. One winner — for your specific situation.\nIn 2026, AI assistants have moved from novelty to necessity. ChatGPT, Claude, and Gemini now power millions of workflows across writing, coding, research, and business. But choosing the wrong one costs you time and money every single day.\nThis guide gives you the most thorough head-to-head comparison available. We tested all three across 15 real-world tasks, analyzed their pricing tiers, and spoke with professionals across industries to give you a definitive answer.\nSkip ahead:\nFeature Comparison Table Performance by Use Case Pricing Breakdown Who Should Use Which AI Background: How Each AI Has Evolved in 2026 ChatGPT (OpenAI) OpenAI\u0026rsquo;s flagship product has expanded dramatically. GPT-4o and its successors now handle voice, vision, code execution, web browsing, and deep research natively. The GPT Store has over 3 million custom GPTs. ChatGPT remains the most-used AI assistant globally with over 200 million weekly active users.\nClaude (Anthropic) Anthropic\u0026rsquo;s Claude has built its reputation on safety, nuance, and long-context performance. Claude 3.7 and beyond excel at reasoning through complex documents, following nuanced instructions, and producing writing that sounds genuinely human. Anthropic\u0026rsquo;s \u0026ldquo;Constitutional AI\u0026rdquo; approach means Claude tends to be more careful and less prone to hallucination on sensitive topics.\nGemini (Google DeepMind) Google\u0026rsquo;s Gemini Ultra 2.0 integrates deeply with the entire Google ecosystem — Gmail, Docs, Drive, Calendar, and Search. For anyone already living inside Google Workspace, Gemini\u0026rsquo;s native integrations are unmatched. Gemini also leads in multimodal capabilities, handling images, audio, and video with impressive accuracy.\nFeature Comparison Feature ChatGPT (Plus) Claude (Pro) Gemini (Advanced) Monthly Price $20 $20 $19.99 (Google One AI) Context Window 128K tokens 200K tokens 1M tokens Web Browsing Yes Yes (limited) Yes (native Google Search) Image Generation Yes (DALL-E 3) No Yes (Imagen 3) Code Execution Yes No (yet) Yes File Upload (PDF/Docs) Yes Yes Yes Voice Mode Advanced Basic Advanced API Access Yes Yes Yes Google Workspace Integration Limited Limited Native Microsoft 365 Integration Native (Copilot) No Limited Custom Instructions Yes Yes Yes Memory/Personalization Yes Limited Growing Plugins/Extensions GPT Store (3M+) Limited Google extensions Free Tier Yes (GPT-4o limited) Yes (Claude 3.5 Haiku) Yes (Gemini 1.5 Flash) Performance by Use Case 1. Long-Form Writing and Content Creation Winner: Claude\nClaude consistently produces the most natural-sounding prose. In our tests writing 3,000-word articles, blog posts, and professional reports, Claude\u0026rsquo;s output required the least editing. It follows complex stylistic instructions better than the others and is less prone to filler phrases and generic structure.\nChatGPT is a close second — especially with well-crafted system prompts. Gemini tends toward a more corporate, slightly stilted tone that needs more polish.\nBest for: Bloggers, authors, content marketers, professional writers.\n2. Coding and Software Development Winner: ChatGPT\nGPT-4o\u0026rsquo;s coding performance remains exceptional. The code interpreter lets you run Python directly in the chat, debug in real time, and analyze data without switching tools. ChatGPT also has the largest library of coding-specific custom GPTs.\nClaude is a surprisingly strong second — particularly for explaining complex code logic and refactoring. Gemini\u0026rsquo;s code quality has improved but still trails on nuanced debugging tasks.\nBest for: Developers, data analysts, technical freelancers.\n3. Research and Document Analysis Winner: Claude\nClaude\u0026rsquo;s 200K token context window (with extended context available to API users at 1M+) means you can feed it entire research papers, legal contracts, or long reports and ask detailed questions. The answers stay accurate even deep into long documents.\nGemini\u0026rsquo;s 1M context window is technically larger, but in our testing, comprehension quality degraded on very long documents. ChatGPT handles medium-length documents well but struggles with truly massive files.\nBest for: Researchers, lawyers, analysts, academics.\n4. Real-Time Information and News Winner: Gemini\nGemini\u0026rsquo;s integration with Google Search is unmatched. It can pull current news, prices, event information, and web data seamlessly. The answers feel current in a way that other AI assistants can\u0026rsquo;t match without browser plugins.\nChatGPT\u0026rsquo;s browsing plugin works well but sometimes feels like a wrapper. Claude\u0026rsquo;s web access is more limited.\nBest for: Journalists, traders, anyone who needs up-to-the-minute information.\n5. Business Productivity and Email Winner: Gemini (Google Workspace users) / ChatGPT (Microsoft users)\nIf your business runs on Google Workspace, Gemini is a game-changer. It drafts emails, summarizes threads, creates calendar events, and synthesizes information across Docs and Drive — all without copy-pasting.\nFor Microsoft 365 users, Microsoft Copilot (powered by GPT-4) does the same across Outlook, Word, and Excel. Claude works well for standalone email drafting but lacks these integrations.\nBest for: Office workers, executives, remote teams.\n6. Creative Projects and Brainstorming Winner: ChatGPT\nChatGPT\u0026rsquo;s combination of image generation (DALL-E 3), custom GPTs, and conversational flexibility makes it the best creative companion. You can brainstorm a concept, generate visuals, write copy, and prototype marketing materials all in one session.\nClaude excels at creative writing and story generation but lacks image output. Gemini generates solid images with Imagen 3.\nBest for: Marketers, designers, entrepreneurs, creative professionals.\n7. Education and Studying Winner: Claude\nFor students and lifelong learners, Claude\u0026rsquo;s patient explanations, willingness to break down complex topics, and high accuracy on factual questions make it the best study partner. It rarely oversimplifies, and it handles follow-up questions exceptionally well.\nSee our full guide: How to Use ChatGPT for Studying for specific prompt templates.\nBest for: Students, educators, researchers.\nPricing Breakdown 2026 Free Tiers All three offer free access, but with limits:\nChatGPT Free: GPT-4o with daily message limits, no code interpreter, no image gen Claude Free: Claude 3.5 Haiku (fast, capable), limited messages per day Gemini Free: Gemini 1.5 Flash, integrated with Google apps at basic level Paid Plans Plan Price Best Value For ChatGPT Plus $20/month Developers, creatives, power users ChatGPT Team $25/user/month Small business teams ChatGPT Enterprise Custom Large organizations Claude Pro $20/month Writers, researchers, analysts Claude Team $25/user/month Professional teams Gemini Advanced (Google One AI) $19.99/month Google Workspace users Google Workspace + Gemini $30/user/month Business teams on Google API Pricing (per 1M tokens, approximate) Model Input Output GPT-4o $5 $15 Claude 3.7 Sonnet $3 $15 Gemini 1.5 Pro $3.50 $10.50 For developers building applications, Claude and Gemini offer better price-to-performance ratios for most tasks.\nWho Should Use Which AI Use ChatGPT if you: Need image generation built into your workflow Are a developer who wants code execution and debugging Want access to the massive GPT Store ecosystem Use Microsoft 365 and want native Copilot integration Need a jack-of-all-trades AI for varied tasks Use Claude if you: Write long-form content professionally Analyze lengthy documents (contracts, reports, research) Need the most natural-sounding writing output Work on tasks requiring nuanced, careful reasoning Prioritize AI safety and reduced hallucinations Use Gemini if you: Live inside Google Workspace (Gmail, Docs, Drive, Calendar) Need real-time web search integrated with answers Work with multimodal content (images, audio, video) Want the largest context window for big documents Are already paying for Google One Can You Use All Three? (Yes, and Here\u0026rsquo;s How) Many professionals use all three AI tools strategically:\nClaude for writing and research — first drafts, document analysis, nuanced reasoning ChatGPT for coding and creative work — code debugging, image generation, brainstorming Gemini for real-time information and Google Workspace — email, calendar, current events The combined cost is about $60/month — comparable to a single software subscription — and the productivity gains typically justify it many times over.\nIf you can only afford one, here\u0026rsquo;s the tiebreaker: ChatGPT Plus at $20/month remains the most versatile single subscription for most users, while Claude Pro is the best choice for professional writers and researchers.\nCommon Questions Is Gemini better than ChatGPT in 2026? For Google Workspace users and real-time information tasks, yes. For general versatility, coding, and creative work, ChatGPT still leads.\nWhich AI is most accurate? Claude tends to have the lowest hallucination rate on factual tasks and long-document comprehension. Always verify critical information with any AI.\nIs there a free AI as good as these? The free tiers of all three are genuinely useful for casual tasks. For professional use, paid plans are worth it.\nWhich AI is best for business? It depends on your stack. Google Workspace teams: Gemini. Microsoft 365 teams: ChatGPT/Copilot. Independent professionals: Claude or ChatGPT Plus.\nLevel Up Your Career with AI Expertise Professionals who can evaluate and choose the right AI tools are increasingly valuable across industries. Find your next career on doda — discover thousands of tech and business roles on Japan\u0026rsquo;s leading job platform.\nFinal Verdict There\u0026rsquo;s no single \u0026ldquo;best\u0026rdquo; AI in 2026 — there\u0026rsquo;s only the best AI for your specific workflow.\nBest overall: ChatGPT Plus (most versatile) Best for writing and research: Claude Pro Best for Google users: Gemini Advanced Best free option: Claude Free (Haiku) or ChatGPT Free The good news: all three have improved dramatically, and any of them will make you more productive than working without AI assistance.\nTake Your Productivity Further Ready to get more from AI tools? Our AI Productivity Templates Pack includes 50+ tested prompts for ChatGPT, Claude, and Gemini — covering writing, research, email, and business workflows.\nRelated Reading:\nBest AI Tools for Small Business 2026 AI Writing Tools Comparison 2026 How to Make Money with AI 2026 Related Articles Claude AI vs ChatGPT Comparison 2026 AI Writing Tools Comparison 2026: Best Options Ranked and Reviewed Best AI Tools for Small Business 2026: The Complete Roundup Best ChatGPT Prompts for Productivity 2026 How to Make Money With AI in 2026: 15 Realistic Ways That Work Related Tools Create a monthly budget → Budget Planner Calculate your ideal freelance rate → Freelance Rate Calculator This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/chatgpt-vs-claude-vs-gemini-comparison-2026/","summary":"\u003chr\u003e\n\u003ch2 id=\"chatgpt-vs-claude-vs-gemini-2026-which-ai-assistant-wins\"\u003eChatGPT vs Claude vs Gemini 2026: Which AI Assistant Wins?\u003c/h2\u003e\n\u003cp\u003eThree giants. One winner — for your specific situation.\u003c/p\u003e\n\u003cp\u003eIn 2026, AI assistants have moved from novelty to necessity. ChatGPT, Claude, and Gemini now power millions of workflows across writing, coding, research, and business. But choosing the wrong one costs you time and money every single day.\u003c/p\u003e\n\u003cp\u003eThis guide gives you the most thorough head-to-head comparison available. We tested all three across 15 real-world tasks, analyzed their pricing tiers, and spoke with professionals across industries to give you a definitive answer.\u003c/p\u003e","title":"ChatGPT vs Claude vs Gemini: The Definitive AI Comparison 2026"},{"content":"Claude AI vs ChatGPT Comparison 2026 — Complete Guide Choosing between Claude AI and ChatGPT in 2026 feels a bit like choosing between a Ferrari and a Porsche — both are exceptional, both have passionate fans, and the \u0026ldquo;right\u0026rdquo; answer depends entirely on how you drive. But the stakes are real. Pick the wrong AI assistant for your workflow and you\u0026rsquo;ll waste time, money, and effort that could be spent actually getting things done.\nThis guide cuts through the hype with an honest, practical comparison. We tested both tools across dozens of real work scenarios — writing, analysis, coding, research, and customer interaction — so you don\u0026rsquo;t have to.\nHere\u0026rsquo;s what you\u0026rsquo;ll learn:\nA side-by-side feature comparison across 12 dimensions Which tool wins for specific use cases (writing, coding, research, business) Pricing breakdown for 2026 and which plan delivers the best value Real prompt examples to test both tools yourself The one scenario where you should use both simultaneously Whether you\u0026rsquo;re a freelancer, a startup founder, or a team leader shopping for an AI stack, this comparison will give you a clear, defensible recommendation.\nWhy Claude AI vs ChatGPT Matters in 2026 The Productivity Gap By 2026, AI assistants have become as fundamental to knowledge work as spreadsheets and email clients. The question is no longer \u0026ldquo;should I use AI?\u0026rdquo; but \u0026ldquo;which AI gives me the best return on my 20 minutes per day of interaction?\u0026rdquo; The gap between a well-matched AI tool and a poorly matched one can be 3–5 hours of productivity per week — that\u0026rsquo;s real money if you\u0026rsquo;re billing by the hour or building a business.\nThe competition between Claude (made by Anthropic) and ChatGPT (made by OpenAI) has driven both products to improve dramatically. Both support long context windows, image analysis, code generation, and web browsing. The differences are now about philosophy, writing style, safety calibration, and nuanced performance on specific task types.\nHow AI Changes the Game The old paradigm: you search for information, synthesize it yourself, then write a draft from scratch. The new paradigm: you describe your goal, AI drafts the output, and you review and refine. The shift from creator to editor saves enormous time — but only if your AI\u0026rsquo;s \u0026ldquo;first draft\u0026rdquo; is good enough to work from. That\u0026rsquo;s where Claude and ChatGPT diverge in interesting ways.\nClaude AI vs ChatGPT: Full Feature Comparison Feature Claude (Sonnet/Opus) ChatGPT (GPT-4o) Winner Writing quality Nuanced, natural prose Strong, versatile Claude (slight edge) Coding ability Strong Excellent ChatGPT Context window 200K tokens 128K tokens Claude Image generation No (Claude) Yes (DALL-E 3) ChatGPT Web browsing Yes Yes Tie Document analysis Excellent Excellent Tie Instruction following Very precise Very good Claude (slight edge) Safety / refusals More conservative Balanced Depends on use case API pricing (per 1M tokens input) $3 (Sonnet) $5 (GPT-4o) Claude Free tier Yes (Claude.ai) Yes (GPT-4o mini) Tie Pro plan pricing $20/month $20/month Tie Plugins/integrations Growing Large ecosystem ChatGPT Try It Free Step-by-Step Guide: How to Choose the Right AI for Your Needs Step 1: Identify Your Primary Use Case Before choosing a plan, be honest about how you\u0026rsquo;ll use it most. Here\u0026rsquo;s a quick decision framework:\nUse Claude if you primarily:\nWrite long-form content (articles, reports, proposals) Need to analyze lengthy documents (contracts, research papers) Value nuanced, thoughtful responses over raw speed Work in sensitive domains where careful language matters Use ChatGPT if you primarily:\nGenerate images alongside text Build custom GPTs or use the plugin ecosystem Code or debug software regularly Need the broadest third-party integrations Use both if:\nYou run a content business or agency You\u0026rsquo;re doing serious research that benefits from two perspectives You\u0026rsquo;re building AI-powered products and testing models Step 2: Test Both with Your Real Tasks Don\u0026rsquo;t trust reviews — test with your actual work. Here are two parallel prompts you can run right now on both tools:\nTest Prompt: Long-Form Writing\nWrite a 400-word executive summary for a software startup that helps small retailers manage inventory with AI. The audience is non-technical retail store owners. Tone: confident but approachable. Include: - The core problem being solved - How the product works (in plain English) - Three measurable business benefits - A one-sentence call to action Test Prompt: Document Analysis\nBelow is a 500-word section from a vendor contract. Identify: 1. Any clauses that favor the vendor over the buyer 2. Missing standard protections (liability cap, data privacy, exit clause) 3. Your overall risk assessment (Low / Medium / High) with one-sentence rationale [PASTE CONTRACT TEXT] Test Prompt: Creative Problem Solving\nI run a solo consulting business. I have 3 client projects, 2 proposals to write, and a keynote to prepare — all due this week. I have 30 hours available. Help me prioritize and create a realistic daily schedule. Be direct. Don\u0026#39;t sugarcoat if something has to be cut. After running these on both tools, you\u0026rsquo;ll have a clear intuition for which writing style and reasoning approach suits you better.\nStep 3: Evaluate the Outputs on These Criteria Accuracy: Did it get the facts right? Tone match: Did it sound like what you\u0026rsquo;d actually send? Format: Was the structure usable without heavy reformatting? Length: Did it know when to stop? Iteration: How well did it handle your follow-up revision requests? Step 4: Pick a Plan Based on Volume Usage Level Recommendation Monthly Cost Casual (\u0026lt; 30 min/day) Free tier either tool $0 Regular professional Claude Pro or ChatGPT Plus $20 Heavy professional Both Pro plans $40 Team/business Claude for Work or ChatGPT Teams $25–$30/user Developer/API Both APIs — pay per token Variable Pro Tips \u0026amp; Advanced Techniques Common Mistakes to Avoid Mistake 1: Declaring a permanent winner. Both tools update frequently. Something ChatGPT handled better in Q1 2026 may be matched or surpassed by Claude in Q3. Reassess quarterly.\nMistake 2: Using only the default settings. ChatGPT\u0026rsquo;s custom instructions and Claude\u0026rsquo;s system prompt capabilities let you set persistent context (your role, tone, company name) that dramatically improves every response.\nMistake 3: Ignoring the API. If you\u0026rsquo;re a developer or technically curious professional, the API for both tools is far more powerful and flexible than the consumer interface — and often cheaper per session.\nMistake 4: Forgetting about context limits in practice. Claude\u0026rsquo;s 200K context window sounds enormous, but if you\u0026rsquo;re pasting large documents regularly, monitor usage. Quality can degrade near the top of even large context windows.\nMistake 5: Not using both for cross-checking. For high-stakes documents — investor pitches, legal summaries, technical specs — run the same prompt on both and compare. The differences surface blind spots in each model.\nPower User Strategies Strategy 1: Use Claude for drafts, ChatGPT for editing. Claude tends to produce more natural first drafts. ChatGPT\u0026rsquo;s editing suggestions are often more systematic. The combination is powerful.\nStrategy 2: Use ChatGPT\u0026rsquo;s custom GPTs for specialized tasks. The GPT store has thousands of purpose-built assistants for specific jobs. No equivalent exists for Claude yet.\nStrategy 3: Use Claude\u0026rsquo;s Projects feature for long-running work. Claude\u0026rsquo;s Projects let you maintain persistent context across multiple sessions for the same project — excellent for ongoing research or client work.\nStrategy 4: Stack prompts for complex analysis. First run a broad analysis prompt, then drill into specific sections with focused follow-up prompts. Both tools handle this well.\nStrategy 5: Export and save great responses. Both tools allow export to Markdown. Build a response library of high-quality AI outputs you can reference or adapt.\nRelated: Best ChatGPT Prompts for Productivity 2026 Frequently Asked Questions Q: Is Claude or ChatGPT smarter in 2026? A: \u0026ldquo;Smart\u0026rdquo; depends on the task. Claude Opus tends to outperform on nuanced reasoning and long-document analysis. ChatGPT GPT-4o tends to outperform on coding and multimodal tasks. For most everyday knowledge work, they\u0026rsquo;re within 10% of each other.\nQ: Which AI is better for writing? A: Claude has a slight edge in producing natural, human-sounding prose. ChatGPT is stronger when you need precise formatting or structured documents like resumes, proposals, and reports with specific templates.\nQ: Which is better for coding? A: ChatGPT is generally preferred by developers for code generation, debugging, and explanation. Claude is competitive but lags slightly on very complex, multi-file codebases.\nQ: Are there privacy differences between Claude and ChatGPT? A: Both have consumer products that may use your conversations for training (you can opt out). Both have enterprise/API versions with strong privacy controls. Anthropic (Claude\u0026rsquo;s maker) has built safety and privacy into its founding mission, which influences product design.\nQ: Can I use Claude for free? A: Yes. Claude.ai offers a free tier with access to Sonnet-class models. There are daily usage limits but it\u0026rsquo;s generous enough for casual use.\nQ: Which has a better mobile app? A: ChatGPT\u0026rsquo;s mobile app is more mature and feature-rich. Claude\u0026rsquo;s mobile app has improved significantly but ChatGPT remains ahead on mobile UX as of 2026.\nQ: Do I need both? A: For most people, one tool used well beats two tools used poorly. Start with one, build habits, then add the second if your work demands it.\nQ: What about Gemini? Where does it fit? A: Google Gemini Ultra is competitive with both, particularly for users deep in the Google Workspace ecosystem. It\u0026rsquo;s worth testing if you live in Google Docs, Sheets, and Gmail.\nPut Your AI Knowledge to Work in a New Role AI expertise is a growing differentiator in hiring — companies are actively looking for people who understand these tools. Find your next career on doda — Japan\u0026rsquo;s top job platform with thousands of roles where AI skills make the difference.\nConclusion \u0026amp; Call to Action There\u0026rsquo;s no universal winner between Claude and ChatGPT in 2026 — there\u0026rsquo;s only the right tool for your specific work. For rich, nuanced writing and long-document analysis, Claude holds a genuine edge. For multimodal work, coding, and ecosystem breadth, ChatGPT is the stronger choice. For most knowledge workers, the $20/month Pro subscription for either tool delivers clear ROI within the first week.\nKey takeaways:\nClaude wins on writing quality and context window size ChatGPT wins on image generation, coding, and integrations Both cost $20/month for Pro — test both before committing The real productivity gain comes from mastering prompts, not from the tool itself Speaking of prompts — want 100+ ready-to-use prompts that work brilliantly on both Claude and ChatGPT? Our Complete ChatGPT Prompt Collection includes templates tested on both platforms, organized by task type, so you get great results regardless of which AI you\u0026rsquo;re using.\nAnd if you want a full AI workflow system that integrates both tools into your daily work, our AI Productivity Playbook shows you exactly how to set up your personal AI stack from scratch.\nRelated: AI Prompt Engineering Tips for Beginners Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nRelated Tools Create a monthly budget → Budget Planner Calculate your ideal freelance rate → Freelance Rate Calculator Related Templates Put these techniques into practice with our ready-made templates:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Copy-paste prompts for every situation The AI Productivity Playbook 2026 — 50+ AI workflows and automation strategies ","permalink":"https://productivity-works.com/posts/claude-ai-vs-chatgpt-comparison-2026/","summary":"\u003ch1 id=\"claude-ai-vs-chatgpt-comparison-2026--complete-guide\"\u003eClaude AI vs ChatGPT Comparison 2026 — Complete Guide\u003c/h1\u003e\n\u003cp\u003eChoosing between Claude AI and ChatGPT in 2026 feels a bit like choosing between a Ferrari and a Porsche — both are exceptional, both have passionate fans, and the \u0026ldquo;right\u0026rdquo; answer depends entirely on how you drive. But the stakes are real. Pick the wrong AI assistant for your workflow and you\u0026rsquo;ll waste time, money, and effort that could be spent actually getting things done.\u003c/p\u003e","title":"Claude AI vs ChatGPT Comparison 2026"},{"content":"This article contains affiliate links.\nJob searching in 2026 means competing with more applicants than ever — many of whom are using AI tools to craft their applications. The good news: if you use AI strategically, you don\u0026rsquo;t just save time. You produce a resume that\u0026rsquo;s sharper, more targeted, and better optimized than most of what hiring managers see.\nThis guide walks you through the entire process of building a resume with AI, from raw notes to a polished, ATS-optimized document tailored to a specific job posting.\nWhat AI Can (and Can\u0026rsquo;t) Do for Your Resume Let\u0026rsquo;s set realistic expectations upfront.\nAI is excellent at:\nTranslating vague job duties into clear, impactful bullet points Identifying keywords from job postings for ATS optimization Suggesting better action verbs and quantified phrasing Formatting and structuring content consistently Tailoring a master resume to specific job descriptions Proofreading for grammar, clarity, and tone AI cannot:\nInvent experience you don\u0026rsquo;t have Replace your judgment on what\u0026rsquo;s most relevant for a specific role Guarantee ATS compatibility in every system (format matters separately) Know what actually made you effective in a role — you have to provide that raw material The best AI-assisted resumes start with rich human input and end with careful human review.\nTools You\u0026rsquo;ll Need Tool Purpose Price ChatGPT (Plus recommended) Core writing and tailoring Free / $20 per month Teal, Kickresume, or Resume.io Formatted resume template Free / $10-24 per month Jobscan ATS keyword matching Free (5 scans/month) / $49 per month Grammarly Final proofreading Free / $12 per month You can do this entire process with just ChatGPT Free and a Word document, but the paid tools make the process faster and the output more polished.\nStep 1: Gather Your Raw Material Before touching any AI tool, spend 20-30 minutes doing a brain dump. For each job in the last 10-15 years, note:\nYour job title, company, and dates (month and year) 5-10 things you did in that role (tasks, projects, responsibilities) 3-5 results or outcomes you\u0026rsquo;re proud of — include numbers if you remember them (revenue generated, percentage improvement, team size, budget managed) Any tools, technologies, or methodologies you used Any promotions, awards, or recognitions Also note:\nYour educational background (degrees, certifications, relevant coursework) Key hard skills (software, languages, platforms) Soft skills you\u0026rsquo;d back up with examples (leadership, communication, problem-solving) This raw material is the input for your AI. Garbage in, garbage out — the more specific you are, the better your resume will be.\nStep 2: Build Your Master Resume with ChatGPT The Core Prompt Open ChatGPT and use this prompt structure:\nI need help writing a professional resume. I\u0026#39;ll share my raw work history and you\u0026#39;ll help me turn it into polished, impactful resume bullet points. My target roles are: [Job Title 1], [Job Title 2] My industry: [Your industry] Years of experience: [X years] Here is my raw work history: [COMPANY NAME, Job Title, Dates] - [raw notes about what you did] - [results you achieved] - [tools used] [Repeat for each role] Please: 1. Write 4-6 bullet points for each role using strong action verbs 2. Quantify results wherever possible — ask me if you need numbers I haven\u0026#39;t provided 3. Focus on impact and outcomes, not just tasks 4. Keep bullets to 1-2 lines maximum 5. Use present tense for current role, past tense for previous roles Reviewing and Iterating ChatGPT\u0026rsquo;s first draft is a starting point, not a final product. After the initial output:\nAsk for variations: \u0026ldquo;Give me three alternative phrasings for bullet point #2 in my Marketing Manager role — I want one that emphasizes team leadership, one that emphasizes ROI, and one that emphasizes growth.\u0026rdquo;\nPush for more specificity: \u0026ldquo;The bullet about \u0026lsquo;improving customer satisfaction\u0026rsquo; is vague. I actually increased NPS from 32 to 61 over 18 months by implementing a new feedback loop process. Rewrite it.\u0026rdquo;\nChallenge generic language: If ChatGPT writes \u0026ldquo;responsible for managing social media,\u0026rdquo; prompt it: \u0026ldquo;That\u0026rsquo;s weak — rewrite as an achievement, not a responsibility.\u0026rdquo;\nStrong resume bullets follow this structure: [Action Verb] + [What You Did] + [Result/Impact]\nExamples:\nWeak: \u0026ldquo;Responsible for sales team oversight\u0026rdquo; Strong: \u0026ldquo;Led 8-person sales team to 127% of annual quota, generating $4.2M in new ARR\u0026rdquo; Writing Your Professional Summary Your summary (2-4 sentences at the top of the resume) is the highest-value real estate. Use this prompt:\nBased on the resume content we just created, write a professional summary for my resume. The summary should: - Be 2-4 sentences (under 75 words) - Lead with my years of experience and core specialty - Mention 2-3 key strengths backed by the experience below - End with my career objective or what I\u0026#39;m looking for - Target: [specific role/industry] - Tone: confident and specific, not generic Avoid clichés like \u0026#34;results-driven,\u0026#34; \u0026#34;team player,\u0026#34; and \u0026#34;passionate.\u0026#34; Generate 3 variations and choose the strongest, or combine the best elements.\nStep 3: Format Your Resume Properly Choosing a Template AI writes content. Formatting is separate. Use one of these tools for a clean template:\nTeal (free) — Excellent ATS-friendly templates, built-in job tracker, AI writing assistance. Best free option.\nKickresume ($19/month) — Over 100 designer templates, AI resume builder, cover letter matching. Good if aesthetics matter for your industry.\nResume.io ($24/month) — Clean, recruiter-approved templates with strong ATS optimization.\nGoogle Docs or Word — For maximum ATS compatibility, a simple one-column text document often outperforms fancy PDFs. Use a clean template from Google Docs template gallery.\nATS-Friendly Formatting Rules Applicant Tracking Systems are software that parse your resume before a human ever sees it. Many resumes get rejected at this stage due to formatting issues.\nDo:\nUse standard section headings: Summary, Experience, Education, Skills Use a single-column layout (two-column layouts confuse many ATS systems) Use standard fonts: Arial, Calibri, Garamond, or Times New Roman Save as PDF (unless the job posting specifically requests Word) Use bullet points with standard symbols (•), not custom icons Include your email, phone, LinkedIn URL, and location (city/state or city/country) Don\u0026rsquo;t:\nUse headers/footers for contact info (ATS often can\u0026rsquo;t read them) Include photos, graphics, charts, or icons in a text-based resume Use tables for your main content (can break ATS parsing) Use fancy fonts, colored backgrounds, or heavy design elements Abbreviate your job titles (write \u0026ldquo;Senior Marketing Manager\u0026rdquo; not \u0026ldquo;Sr. Mktg. Mgr.\u0026rdquo;) Step 4: Tailor Your Resume for Each Job This is where most candidates fail — and where AI gives you the biggest advantage.\nSending the same resume to 50 jobs is less effective than sending a tailored resume to 10. AI lets you tailor quickly.\nExtract Keywords from the Job Posting Paste the full job posting into ChatGPT and use this prompt:\nHere is a job description I\u0026#39;m applying for: [PASTE FULL JOB DESCRIPTION] Please: 1. Extract the 15-20 most important keywords and phrases (skills, tools, competencies, qualifications) 2. Separate them into: Required skills | Preferred skills | Soft skills 3. Identify the 3-5 themes or priorities this role seems most focused on Match Your Resume to the Job Then:\nHere is my current resume: [PASTE YOUR RESUME TEXT] Here are the key keywords and themes from the job posting I want to target: [PASTE THE LIST FROM ABOVE] Please: 1. Identify which of these keywords are already in my resume 2. Suggest which bullet points I should modify to incorporate missing keywords naturally 3. Suggest any additional skills I should add to my Skills section 4. Flag any experiences I have that I\u0026#39;m underemphasizing relative to what this role values Do NOT invent experience I don\u0026#39;t have. Only suggest modifications that are truthful and based on the experience I\u0026#39;ve already described. Rewrite Targeted Bullet Points For the highest-priority roles, ask ChatGPT to rewrite specific bullets to better align with the job:\nRewrite this bullet point to better emphasize [skill/outcome from job posting], while keeping it truthful to my experience: Original: [your current bullet] Context: [any additional context about what you actually did] Optimize with Jobscan After tailoring, run your resume through Jobscan. It compares your resume text to the job description and gives you a match score, flagging missing keywords. Aim for 75%+ match score. Use ChatGPT to work in remaining keywords naturally.\nStep 5: Write Your Cover Letter with AI Most candidates write weak cover letters. Use AI to write one that actually addresses the job.\nWrite a cover letter for the following job application: My resume highlights: [paste 3-5 key bullet points from your tailored resume] The job posting emphasizes: [paste the 3-5 themes you identified] Specific things I want to mention: - Why I want THIS company specifically: [your reason] - A concrete example of relevant achievement: [specific story] - How I heard about the role: [source, if notable] Format: - 3 paragraphs, under 300 words - First paragraph: hook + why I\u0026#39;m right for this specific role - Second paragraph: 1-2 concrete examples of relevant impact - Third paragraph: enthusiasm, culture fit, call to action Tone: professional but not stiff — like a smart person who respects the reader\u0026#39;s time. Review carefully. Add anything personal that AI can\u0026rsquo;t know (a genuine connection to the company\u0026rsquo;s mission, a referral, etc.).\nStep 6: Final Review Checklist Before sending, go through this checklist:\nContent:\nEvery bullet starts with a strong action verb At least 50% of bullets include quantified results No personal pronouns (I, my, we) No clichés (\u0026ldquo;results-driven,\u0026rdquo; \u0026ldquo;dynamic,\u0026rdquo; \u0026ldquo;passionate about\u0026rdquo;) Professional summary is specific and compelling Skills section reflects actual skills, not keyword stuffing Format:\nOne page (under 5 years experience) or two pages maximum Consistent formatting: same font, bullet style, date format throughout No spelling or grammar errors (run Grammarly) Contact info is current and professional (no nicknames, working email) File named: FirstName-LastName-Resume.pdf Tailoring:\nResume matches the job title language (if they say \u0026ldquo;Marketing Director,\u0026rdquo; your title was \u0026ldquo;Marketing Director\u0026rdquo; — not \u0026ldquo;Director of Marketing\u0026rdquo;) Top 5 keywords from job posting appear naturally in resume Most recent and relevant experience is at the top Common AI Resume Mistakes Using AI-generated text verbatim. AI produces plausible-sounding language, but sometimes it\u0026rsquo;s generic or slightly off. Read every word out loud. If it doesn\u0026rsquo;t sound like how you\u0026rsquo;d describe your work, rewrite it.\nOver-optimizing for ATS at the expense of readability. If your resume reads like a keyword list, humans will notice. Recruiters and hiring managers still read resumes — often after ATS filtering. Write for both.\nIdentical bullet structures. If every bullet follows the exact same pattern, it reads like it was AI-generated (because it was). Vary sentence structure, start verbs, and detail levels.\nIgnoring the human review step. AI doesn\u0026rsquo;t know that you left that company under difficult circumstances, that the \u0026ldquo;small team\u0026rdquo; was actually a department of 200, or that your \u0026ldquo;increased revenue\u0026rdquo; was partly luck and partly skill. You decide what to include, how to frame it, and what to leave out.\nNot updating the master resume. After each round of tailoring, if you discover new ways to phrase your experience that are stronger, update your master resume. Your resume should get better with every application.\nSample Prompt Library Save these for reuse:\nBullet rewrite: \u0026ldquo;Rewrite this resume bullet to be more impactful. Use a stronger action verb, quantify the result if possible, and keep it under 20 words: [bullet]\u0026rdquo;\nSkills section: \u0026ldquo;Based on this job description, list 15 relevant hard skills I should include in my resume skills section. Format as a comma-separated list: [job description]\u0026rdquo;\nAchievement excavation: \u0026ldquo;I did [vague task description] in my job. Ask me 5 questions to help uncover the quantifiable impact of this work so I can write a strong resume bullet.\u0026rdquo;\nGap period explanation: \u0026ldquo;I have a [X month] employment gap because [reason]. Help me address this professionally in a cover letter without drawing unnecessary attention to it.\u0026rdquo;\nBuilding a great resume with AI takes about 2-3 hours for a strong first draft — compared to the days most people spend staring at a blank document. The work is real, but it\u0026rsquo;s focused: gathering your raw material, prompting strategically, reviewing critically.\nOnce your resume is polished, you need the right platform to put it in front of employers. If you\u0026rsquo;re job hunting in Japan, doda offers bilingual job listings and recruiter support for English-speaking professionals — a strong starting point for putting your AI-optimized resume to work.\nWant to skip the setup? Our AI Resume Toolkit on Payhip includes 25 tested ChatGPT prompts for resumes, a master resume template, a cover letter template, and an ATS keyword guide — everything you need to run this process for any job application.\nRelated reads:\nBest AI Tools for Small Business 2026 Best Free Alternatives to ChatGPT 2026 Related Tools Count words and characters in your text → Word Counter Calculate your take-home pay for any salary → Salary Calculator Check your tax bracket → Tax Bracket Calculator Related Templates Ace your next career move with these resources:\nJob Interview Prep Guide: AI-Powered — 50 questions, STAR method templates, salary negotiation scripts ChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Resume and interview prep prompts included ","permalink":"https://productivity-works.com/posts/create-resume-with-ai-step-by-step/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eJob searching in 2026 means competing with more applicants than ever — many of whom are using AI tools to craft their applications. The good news: if you use AI strategically, you don\u0026rsquo;t just save time. You produce a resume that\u0026rsquo;s sharper, more targeted, and better optimized than most of what hiring managers see.\u003c/p\u003e\n\u003cp\u003eThis guide walks you through the entire process of building a resume with AI, from raw notes to a polished, ATS-optimized document tailored to a specific job posting.\u003c/p\u003e","title":"How to Create a Resume with AI (Step-by-Step Guide for 2026)"},{"content":"※本記事にはアフィリエイト広告が含まれています。\nETF vs Mutual Fund Which Should I Choose — Complete Guide [2026] Walk into any investing forum or financial planning conversation and you\u0026rsquo;ll quickly encounter the ETF vs. mutual fund debate. Both are pooled investment vehicles. Both can offer diversification, low costs, and access to broad market exposure. But they have meaningful differences that can affect your taxes, costs, flexibility, and overall investing experience.\nThe good news: for most beginners, the difference is less dramatic than the debate suggests — especially when comparing index ETFs to index mutual funds. The bad news: the nuances matter, and getting it wrong costs you money.\nIn this guide, you\u0026rsquo;ll learn:\nThe precise differences between ETFs and mutual funds When each type of fund is clearly superior How the tax treatment differs — and why it matters a lot in taxable accounts Which to choose for your 401k, Roth IRA, and taxable brokerage The hidden costs and considerations most investors miss A clear recommendation framework for beginners What Are ETFs and Mutual Funds? Definition \u0026amp; How They Work ETF (Exchange-Traded Fund) An ETF is a basket of securities (stocks, bonds, commodities, etc.) that trades on a stock exchange, just like an individual stock. You buy and sell ETF shares throughout the trading day at market prices.\nKey characteristics:\nTrades intraday on exchanges (NYSE, NASDAQ) Price fluctuates throughout the trading day Most require buying at least one share (though many brokers now offer fractional ETF shares) Passive (index-tracking) ETFs are the most common and most popular type Can be bought commission-free at most major brokerages Mutual Fund A mutual fund pools money from many investors and invests it in a portfolio of securities. Unlike ETFs, mutual funds are priced once per day after market close (the Net Asset Value, or NAV), and orders execute at that end-of-day price.\nKey characteristics:\nPriced once daily at market close Orders execute at the end-of-day NAV regardless of when you place the order Can invest exact dollar amounts (e.g., \u0026ldquo;$500 into FXAIX\u0026rdquo; regardless of share price) Both actively managed and passive (index) versions exist Some have minimum investment requirements (Vanguard Admiral shares require $3,000) Pros and Cons ETF Pros:\nTrade intraday with pricing flexibility Generally more tax-efficient in taxable accounts Fractional share availability at most brokers No minimum investment beyond one share (often under $200) Can be traded with limit orders, stop-loss orders, etc. ETF Cons:\nCannot invest fractional amounts via automatic plans as easily (though improving) Bid-ask spread is a small additional cost Intraday trading can encourage overactive behavior (bad for long-term investors) Mutual Fund Pros:\nEasy to invest exact dollar amounts (ideal for automatic investing) No intraday trading temptation — all orders execute at EOD price Some have zero expense ratios (Fidelity ZERO funds) Easier for systematic investing of a fixed monthly amount Mutual Fund Cons:\nLess tax-efficient in taxable accounts (capital gains distributions) May have minimum investment requirements Cannot trade intraday Actively managed mutual funds often have high expense ratios and underperform benchmarks Related: Best Index Funds for Beginners 2026 ETF vs Mutual Fund: Complete Comparison Table Feature ETF Index Mutual Fund Trading Intraday on exchanges Once daily at NAV Pricing Fluctuates throughout day Set at end of trading day Minimum investment Price of 1 share (fractional at many brokers) Often $0–$3,000 depending on fund Expense ratios Very low (0.03–0.10% for index ETFs) Very low to very high (0.00–1.0%+) Tax efficiency (taxable accounts) Higher — fewer capital gains distributions Lower — may distribute capital gains annually Automatic investing Possible but requires fractional shares Easy — invest exact dollar amounts Dividend reinvestment DRIP available (may involve fractional shares) Seamless at fund level Order types Market, limit, stop-loss, etc. Market only (at end-of-day NAV) Available in 401k? Rarely (some plans do offer ETFs) Yes — primary 401k investment vehicle Best account type Taxable brokerage, Roth IRA 401k, Roth IRA (mutual funds work anywhere) Behavioral risk Higher (intraday trading temptation) Lower (once-daily pricing reduces tinkering) The Tax Efficiency Difference: Why It Matters This is where ETFs have a clear, often decisive advantage in taxable brokerage accounts.\nHow mutual fund capital gains distributions work: When investors redeem (sell) shares in an actively managed mutual fund, the fund manager must sell underlying securities to raise cash. These sales may generate capital gains, which the fund distributes to all remaining shareholders — including you, even if you didn\u0026rsquo;t sell anything. You owe taxes on these distributions.\nHow ETFs avoid this problem: ETFs use a mechanism called \u0026ldquo;in-kind creations and redemptions.\u0026rdquo; Large institutional investors called \u0026ldquo;authorized participants\u0026rdquo; can exchange ETF shares for the underlying basket of securities (and vice versa) without triggering taxable events inside the fund. This means ETFs rarely distribute capital gains to shareholders.\nReal-world impact: In years with significant market activity or redemptions, some actively managed mutual funds distributed capital gains of 5–15% of their net asset value — meaning you owed taxes on substantial gains even if your fund actually lost value that year.\nIndex mutual funds distribute far fewer capital gains than actively managed funds, making the ETF vs. index mutual fund tax difference relatively small. But for large balances in taxable accounts, even small differences in tax efficiency matter.\nBottom line on taxes:\nIn tax-advantaged accounts (401k, Roth IRA): Tax efficiency doesn\u0026rsquo;t matter — the account shields all growth from taxes. Use whatever type of fund is cheapest and most convenient. In taxable brokerage accounts: ETFs have a tax advantage, especially over actively managed mutual funds. For index fund investing, the advantage over index mutual funds is smaller but still real. How to Choose: Key Factors What to Look For 1. Account type determines which is better\n401k: You likely don\u0026rsquo;t have a choice — 401k plans almost always offer mutual funds, not ETFs. Choose the lowest-cost index mutual fund available. Roth IRA: Both work equally well. Use ETFs if you like intraday flexibility or have a fractional share platform. Use index mutual funds if you prefer exact dollar automatic investing. Taxable brokerage: ETFs generally win due to tax efficiency. Vanguard\u0026rsquo;s unique fund structure makes their mutual funds equally tax-efficient, but for everyone else, ETFs are preferable in taxable accounts. 2. Expense ratio — the most important factor Don\u0026rsquo;t choose between ETF and mutual fund based on structure alone — check the expense ratio of specific funds you\u0026rsquo;re considering. The Fidelity ZERO Index funds charge 0.00% expense ratio (as mutual funds). VTI (ETF) charges 0.03%. Both are excellent. The difference on $10,000 is $3/year.\n3. Minimum investment requirements If you\u0026rsquo;re starting with a small amount:\nETFs: Buy fractional shares starting from $1 at Fidelity, Schwab, or M1 Finance Mutual funds: Fidelity ZERO funds have $0 minimum; Vanguard Admiral shares require $3,000 4. Automation preferences If you want to invest exactly $500/month automatically, index mutual funds are slightly easier — you specify the dollar amount and the fund handles share fractions. Many brokers now support fractional ETF automatic investing, reducing this difference.\nCommon Mistakes to Avoid Mistake 1: Choosing actively managed mutual funds over index ETFs or index mutual funds The biggest mistake isn\u0026rsquo;t ETF vs. mutual fund — it\u0026rsquo;s actively managed vs. passive (index). Decades of data show that 80–90% of actively managed funds underperform their benchmark index over 15-year periods. A passively managed ETF or index mutual fund beats most active managers.\nMistake 2: Paying attention to intraday ETF prices The ability to trade ETFs throughout the day is mostly a liability for long-term investors, not an asset. If you find yourself checking ETF prices multiple times a day, consider switching to index mutual funds — the once-daily pricing removes the temptation.\nMistake 3: Assuming all ETFs are index funds ETFs can be actively managed too. Leveraged ETFs, inverse ETFs, and thematic ETFs (cryptocurrency, cannabis, etc.) are ETFs that are definitively not suitable as core long-term holdings for beginners. Stick to broad market index ETFs.\nMistake 4: Ignoring the expense ratio in favor of structure An index mutual fund with a 0.04% expense ratio is better than an ETF with a 0.50% expense ratio. Always compare specific fund costs, not just categories.\nMistake 5: Holding actively managed mutual funds in taxable accounts If you\u0026rsquo;re in a taxable brokerage account, actively managed mutual funds are close to the worst possible choice — high fees, high turnover, and annual capital gains distributions you\u0026rsquo;ll owe taxes on. Replace them with index ETFs.\nStep-by-Step Guide: Building Your Portfolio with ETFs or Index Funds Step 1: Identify your account type Determine where you\u0026rsquo;re investing: 401k, Roth IRA, or taxable brokerage. This determines the tax efficiency question.\nStep 2: Choose your strategy For most beginners, a simple two- or three-fund portfolio works perfectly:\nOption A — Two-fund portfolio (simpler):\n80% VTI or FZROX (total U.S. stock market) 20% VXUS (international stocks) Option B — Three-fund portfolio (classic):\n60% VTI (U.S. stocks) 30% VXUS (international stocks) 10% BND (bonds) Option C — One-fund portfolio (easiest):\n100% Target-date retirement fund (e.g., Fidelity Freedom Index 2055) Step 3: Choose ETF or mutual fund versions\nFor 401k: choose whatever index mutual funds are available with lowest fees For Roth IRA at Fidelity: FZROX (mutual fund, 0% ER) or ITOT (ETF, 0.03% ER) — both excellent For taxable brokerage: VTI, IVV, or ITOT (ETFs) for tax efficiency Step 4: Open your account and set up automatic investing\nAt Fidelity, Schwab, or Vanguard: open a Roth IRA or taxable brokerage account Set up automatic monthly contributions (e.g., $200/month) Enable dividend reinvestment (DRIP) Step 5: Rebalance annually Once per year, check if your allocation has drifted. If stocks grew from 80% to 90% of your portfolio, sell some stocks and buy more bonds/international to return to your target allocation.\nThe Three Most Popular Comparisons VTI vs VTSAX (ETF vs Mutual Fund tracking the same index) These two Vanguard funds track the same index (CRSP US Total Market Index). Both are excellent:\nVTI (ETF): 0.03% ER, buy fractional shares, better for taxable accounts VTSAX (mutual fund): 0.04% ER, $3,000 minimum, easy dollar-amount automatic investing For taxable accounts: VTI wins slightly on tax efficiency and ER. For Roth IRAs: coin flip. For 401k: neither is likely available; choose the best S\u0026amp;P 500 index fund offered.\nIVV vs FXAIX (S\u0026amp;P 500 ETF vs Mutual Fund) IVV (iShares, ETF): 0.03% ER, excellent liquidity FXAIX (Fidelity mutual fund): 0.015% ER, lowest cost S\u0026amp;P 500 fund available Cost winner: FXAIX is literally half the price. If you\u0026rsquo;re at Fidelity, FXAIX wins on expense ratio. The difference is $1.50/year per $10,000 invested — trivial, but why not choose cheaper?\nActively Managed vs Index Fund (Any Account) The clear winner: index funds (ETF or mutual fund). Standard \u0026amp; Poor\u0026rsquo;s SPIVA report consistently shows ~80% of actively managed large-cap U.S. equity funds underperform the S\u0026amp;P 500 over 15 years. The extra cost of active management (typically 0.5–1.0%/year in fees, plus trading costs) creates a persistent drag that most active managers cannot overcome.\nFrequently Asked Questions Q: Are ETFs safer than mutual funds? A: Neither is inherently safer than the other. Safety depends on what the fund holds, not whether it\u0026rsquo;s structured as an ETF or mutual fund. A total market index ETF and a total market index mutual fund hold essentially the same securities and carry the same market risk.\nQ: Can I convert my mutual funds to ETFs? A: Not directly in most cases. You would sell the mutual fund shares (potentially triggering taxes in a taxable account) and purchase ETF shares. At Vanguard, you can convert mutual fund shares to the equivalent ETF shares tax-free within their system.\nQ: Do ETFs pay dividends? A: Yes. Most broad market ETFs pay dividends quarterly. You can set up automatic dividend reinvestment (DRIP) through your brokerage to have dividends automatically reinvested into additional ETF shares.\nQ: What\u0026rsquo;s the difference between an ETF and a stock? A: A stock represents ownership in one company. An ETF is a basket of many securities (which may include hundreds or thousands of stocks). Buying one share of VTI gives you indirect ownership in about 3,700 U.S. companies.\nQ: Is it better to hold ETFs or mutual funds in a Roth IRA? A: In a Roth IRA, tax efficiency differences don\u0026rsquo;t matter — all growth is tax-free. Choose based on cost (expense ratio) and convenience. Fidelity\u0026rsquo;s ZERO funds (0% ER mutual funds) are an excellent choice inside a Roth IRA.\nQ: What is a fund\u0026rsquo;s NAV? A: NAV (Net Asset Value) is the per-share value of a fund, calculated by dividing the total value of the fund\u0026rsquo;s assets minus liabilities by the number of shares outstanding. For mutual funds, this is the price at which you buy and sell. For ETFs, the market price can differ slightly from NAV (the difference is called the premium/discount, and it\u0026rsquo;s typically very small for popular ETFs).\nQ: Can I lose money in an ETF or mutual fund? A: Yes. Both can decline in value if the underlying securities decline. There is no guarantee of positive returns. However, broadly diversified index funds have historically recovered from all major market downturns, though past performance does not guarantee future results.\nPut Your Investment Strategy Into Practice Understanding ETFs vs. mutual funds is step one — actually investing is step two. Open a Rakuten Securities account to access a wide selection of low-cost index ETFs and mutual funds with no minimum investment.\nConclusion: The Answer for Most Investors For the vast majority of beginning investors, the ETF vs. mutual fund question comes down to:\nIn a 401k: Use index mutual funds (you likely don\u0026rsquo;t have ETF options anyway) In a Roth IRA: Use either index ETFs or index mutual funds — both are excellent In a taxable brokerage account: Prefer index ETFs for their superior tax efficiency The most important decision is not ETF vs. mutual fund — it\u0026rsquo;s passive (index) vs. active management. Choosing a low-cost index fund in either structure will put you ahead of most investors. Worrying about the ETF-vs-mutual-fund distinction without first addressing expenses and diversification is like optimizing the fuel injectors on a car with flat tires.\nStart simple. Stay consistent. Keep costs low.\nOpen your investment account today:\nStart investing with Fidelity — $0 minimum, ZERO fee index funds Compare ETF options at Charles Schwab Invest automatically with M1 Finance Related: Best Index Funds for Beginners 2026 Disclaimer: This article is for informational purposes only and does not constitute financial advice. Investment decisions should be made based on your individual circumstances. Please consult a qualified financial advisor before making investment decisions. Information is current as of the publication date — verify details on official websites.\nDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nRelated Tools \u0026amp; Articles Compare long-term growth of your investments → Compound Interest Calculator Estimate dividend income from ETFs → Dividend Income Calculator Plan your retirement savings → Retirement Savings Calculator Calculate how long to reach any savings target → Savings Goal Calculator Plan your path to financial independence → FIRE Calculator How to Start Investing with $100 in 2026 Best Index Funds for Beginners 2026 401k vs IRA Differences Explained (2026) Roth IRA vs Traditional IRA: Which Is Better? Passive Income Ideas That Actually Work 2026 This article contains affiliate links. We may earn a commission at no extra cost to you.\nRelated Templates Take control of your finances with these tools:\nSmart Budget Tracker (Excel Template) — Track income, expenses, and savings goals Side Hustle Starter Kit 2026 — Build additional income streams ","permalink":"https://productivity-works.com/posts/etf-vs-mutual-fund-which-should-i-choose/","summary":"\u003cp\u003e※本記事にはアフィリエイト広告が含まれています。\u003c/p\u003e\n\u003ch1 id=\"etf-vs-mutual-fund-which-should-i-choose--complete-guide-2026\"\u003eETF vs Mutual Fund Which Should I Choose — Complete Guide [2026]\u003c/h1\u003e\n\u003cp\u003eWalk into any investing forum or financial planning conversation and you\u0026rsquo;ll quickly encounter the ETF vs. mutual fund debate. Both are pooled investment vehicles. Both can offer diversification, low costs, and access to broad market exposure. But they have meaningful differences that can affect your taxes, costs, flexibility, and overall investing experience.\u003c/p\u003e\n\u003cp\u003eThe good news: for most beginners, the difference is less dramatic than the debate suggests — especially when comparing index ETFs to index mutual funds. The bad news: the nuances matter, and getting it wrong costs you money.\u003c/p\u003e","title":"ETF vs Mutual Fund: Which Should I Choose?"},{"content":"If you\u0026rsquo;ve ever spent 45 minutes agonizing over 13 Etsy tags — refreshing your Erank dashboard, swapping one word in and out, wondering whether \u0026ldquo;handmade gift\u0026rdquo; or \u0026ldquo;unique gift\u0026rdquo; performs better — this guide is for you.\nEtsy tags are one of the highest-leverage points in your entire shop. The right tags can take a listing from invisible to consistently appearing on the first page. And yet most sellers either rush through them or waste enormous time guessing.\nChatGPT, used correctly, gives you a systematic way to generate high-quality tag candidates in minutes. This guide explains exactly how to do it — including how Etsy\u0026rsquo;s algorithm actually works, what makes a tag effective, and the specific prompts to use for different product categories and seasons.\nFor a broader look at running your Etsy shop with AI, see our Complete ChatGPT Guide for Etsy Sellers .\nHow Etsy\u0026rsquo;s Search Algorithm Actually Ranks Tags Before we talk about prompts, you need to understand what Etsy is actually doing with your tags. Most sellers have a fuzzy understanding here, and it leads to wasted tags.\nTags are one input among many Etsy\u0026rsquo;s search algorithm — called the Etsy Search Experience (ESX) — considers many signals:\nRelevancy: How closely does your listing match the search query? Tags, title, and category all contribute. Listing quality score: Click-through rate, conversion rate, and favorites all feed into this. Customer experience score: Your shop\u0026rsquo;s review history, completion rate, and dispute rate. Shipping price and speed: Competitive shipping improves ranking. Recency: New listings get a temporary boost in the first 24-72 hours. Tags matter most for relevancy. A tag that matches a buyer\u0026rsquo;s search query word-for-word is more powerful than one that\u0026rsquo;s merely related.\nThe long-tail principle Etsy\u0026rsquo;s most competitive keywords are broad ones: \u0026ldquo;birthday gift,\u0026rdquo; \u0026ldquo;wall art,\u0026rdquo; \u0026ldquo;candle.\u0026rdquo; Millions of listings compete for these. As a smaller shop, you\u0026rsquo;re rarely going to win on broad terms.\nLong-tail phrases are where you can actually compete. \u0026ldquo;Personalized birth month flower candle gift for mom\u0026rdquo; is searched by fewer people — but those who search it are usually ready to buy, and your competition is dramatically thinner.\nEach tag can be up to 20 characters (including spaces). Use the full 20 characters when possible with multi-word phrases.\nExact-match vs. partial-match Etsy\u0026rsquo;s algorithm uses both exact-match and partial-match for tags. If your tag is \u0026ldquo;silver hoop earrings,\u0026rdquo; your listing can show up for the search \u0026ldquo;silver hoop earrings\u0026rdquo; (exact) and also for \u0026ldquo;silver earrings\u0026rdquo; or \u0026ldquo;hoop earrings\u0026rdquo; (partial).\nThis means you don\u0026rsquo;t need to use every variation of a keyword separately. One well-chosen multi-word tag covers multiple search variants.\nWhat Makes a Tag Effective vs. Wasted You have 13 tags per listing. Every one is valuable real estate. Here\u0026rsquo;s how to think about whether a tag is earning its spot:\nHigh-value tags:\nSpecific multi-word phrases (long-tail) Match your buyer\u0026rsquo;s actual purchase intent (\u0026ldquo;gift for nurse graduation\u0026rdquo;) Seasonal or occasion-based when relevant (\u0026ldquo;christmas gift for teacher\u0026rdquo;) Describe what the item IS and what it\u0026rsquo;s FOR Low-value tags:\nSingle generic words (\u0026ldquo;gift,\u0026rdquo; \u0026ldquo;handmade,\u0026rdquo; \u0026ldquo;art\u0026rdquo;) — too broad to compete on Duplicate words already in your title (though some overlap is fine) Category words Etsy already captures in its classification system Trends you\u0026rsquo;re not actually part of The golden rule: Think like a buyer, not like a maker. You might describe your product as \u0026ldquo;hand-stamped sterling silver disc necklace with custom initial.\u0026rdquo; A buyer searches \u0026ldquo;personalized necklace gift for daughter.\u0026rdquo; These are different. Your tags need to bridge both worlds.\nThe Core ChatGPT Prompt for Etsy Tags Here is the foundation prompt. Use this as your starting point for any product.\nI sell on Etsy and need help generating SEO tags for one of my listings. Product: [describe your product in 2-3 sentences — what it is, how it\u0026#39;s made, what it\u0026#39;s for] Materials: [list key materials] Who typically buys this: [describe your buyer — e.g., gift-givers, home decorators, brides] Occasions it\u0026#39;s suitable for: [list occasions] Price point: [rough price] Please generate 20 candidate Etsy tags for this product. Rules for the tags: - Each tag must be 20 characters or fewer (including spaces) - Prioritize multi-word long-tail phrases over single words - Think from the buyer\u0026#39;s perspective — what would they search to find this? - Include a mix of: what the item IS, what it\u0026#39;s FOR, who it\u0026#39;s FOR, and the occasion/season - Do NOT repeat words from the Etsy title: [paste your listing title here] - Avoid generic single-word tags like \u0026#34;gift,\u0026#34; \u0026#34;art,\u0026#34; or \u0026#34;handmade\u0026#34; After the list, briefly explain your reasoning for the top 5 most important tags. This prompt produces far better output than \u0026ldquo;give me Etsy tags for a necklace\u0026rdquo; because it gives ChatGPT the context it needs to think like your buyer.\nStep-by-Step: From Product to Final 13 Tags Step 1: Run the core prompt Generate 20 candidates as described above. You need more than 13 so you have room to select.\nStep 2: Validate with a research tool ChatGPT doesn\u0026rsquo;t have access to real-time Etsy search volume data. It can generate excellent logical candidates, but you should validate them against an actual keyword research tool before finalizing.\nFree/low-cost options: Erank (free tier), Marmalead (paid), Alura (free tier), Etsy\u0026rsquo;s own search bar autocomplete.\nCheck each candidate tag:\nDoes it show real search volume in Erank or Marmalead? How competitive is it? (High volume + low competition = ideal) Does Etsy\u0026rsquo;s autocomplete suggest this phrase? If buyers are typing it, it shows up in autocomplete. Step 3: Run a refinement prompt After you\u0026rsquo;ve identified which candidates look strong and which are weak, go back to ChatGPT:\nHere are the 20 Etsy tag candidates you generated: [paste list] After research, I found these tags have good search volume: [list strong tags] These tags have either too-low volume or too much competition: [list weak tags] Please replace the weak tags with 8 new candidates. Focus on: - Variations of the strong tags (different word order, slight rewording) - Adjacent buyer searches I might be missing - Gift-occasion specific phrases Step 4: Build your final 13 Select your final tags using this rough structure:\n3-4 tags describing what the product IS (material, style, form) 3-4 tags describing what it\u0026rsquo;s FOR (occasion, use case) 3-4 tags describing who it\u0026rsquo;s FOR (recipient, buyer) 1-2 seasonal or trending tags (if relevant) Step 5: Refresh seasonally Don\u0026rsquo;t set-and-forget. Etsy search patterns shift dramatically by season. A static tag set is losing you traffic for months at a time.\nChatGPT Prompts for Specific Product Categories For jewelry and accessories Generate 20 Etsy tag candidates for a piece of jewelry: - Item: [e.g., \u0026#34;sterling silver birthstone ring, personalized with initial\u0026#34;] - Style: [e.g., minimalist, boho, vintage-inspired] - Target buyers: [e.g., women buying for themselves, gift-givers looking for presents for women] - Common occasions: [e.g., birthday, Mother\u0026#39;s Day, anniversary] - Price: [$X] - My listing title: [title] Focus heavily on gift-intent searches — phrases a person would use when buying this as a gift. Also include metal/material-specific searches since buyers often filter by material. For home decor and wall art Generate 20 Etsy tag candidates for a home decor item: - Item: [e.g., \u0026#34;abstract watercolor print, 8x10, unframed\u0026#34;] - Style aesthetic: [e.g., maximalist, Scandinavian minimal, boho, traditional] - Room it typically goes in: [e.g., bedroom, nursery, living room, home office] - Color palette: [e.g., earth tones, pastels, black and white] - Buyer: [e.g., new homeowner, apartment renter decorating on a budget, gift-giver] - My listing title: [title] Include tags that combine room + style (e.g., \u0026#34;boho bedroom wall art\u0026#34;), color + room, and gift context. For digital downloads and printables Generate 20 Etsy tag candidates for a digital download product: - Item: [e.g., \u0026#34;weekly meal planning printable, A4 and Letter size, instant download\u0026#34;] - Primary use case: [e.g., household organization, meal prep planning] - Who uses this: [e.g., busy parents, health-conscious adults, new homeowners] - Format: [PDF / PNG / editable Canva / etc.] - My listing title: [title] For digital products, include format-specific tags (\u0026#34;printable PDF,\u0026#34; \u0026#34;instant download\u0026#34;), use-case tags, and \u0026#34;digital\u0026#34; variants of physical search terms people might use. Also include tags for the size/format options. For clothing and wearables Generate 20 Etsy tag candidates for a clothing item: - Item: [e.g., \u0026#34;unisex vintage-washed graphic tee, AI-generated artwork design\u0026#34;] - Target wearer: [age range, gender if applicable, lifestyle] - Occasions for wearing: [e.g., casual daily wear, music festivals, gift] - Aesthetic: [e.g., streetwear, cottagecore, retro 90s] - My listing title: [title] Include style-community tags (buyers often search for their aesthetic), gift tags, and size-neutral descriptive tags since you can\u0026#39;t use size in Etsy tags anyway. Seasonal Tag Strategy with ChatGPT One of the biggest missed opportunities for Etsy sellers is seasonal tag rotation. Etsy\u0026rsquo;s search traffic spikes dramatically around key shopping periods, and your tags should reflect what buyers are searching RIGHT NOW.\nHere\u0026rsquo;s a ChatGPT prompt for building a seasonal tag calendar:\nI sell [describe your product category] on Etsy. Please create a seasonal Etsy tag strategy for my shop across the following periods: - Valentine\u0026#39;s Day (late January through February 14) - Spring / Easter (March-April) - Mother\u0026#39;s Day (April-May) - Summer / Graduation (May-June) - Back to School (July-August) - Halloween (September-October) - Thanksgiving / Fall (October-November) - Christmas and holiday season (November-December) - New Year / New Beginnings (December-January) For each period, suggest 4-6 seasonal tags I could swap into my most gift-relevant listings. My permanent tags handle general discovery — these seasonal tags should capture holiday and occasion-specific searches. Product examples from my shop: [list 3-5 products] Run this once a year. Save it in a spreadsheet. Set calendar reminders to update your listings 4-6 weeks before each season (buyers start searching earlier than most sellers realize).\nCommon Tag Mistakes ChatGPT Can Help You Avoid Mistake 1: Repeating the same word across multiple tags ChatGPT can audit your tag list: \u0026ldquo;Here are my 13 tags: [list]. Identify any words that appear in multiple tags, and suggest replacement tags that cover different semantic ground.\u0026rdquo;\nMistake 2: Ignoring the \u0026ldquo;for who\u0026rdquo; angle Many sellers describe what the item is but forget to target the gift-giver. Prompt: \u0026ldquo;For each of my 13 tags, assess whether it targets the gift-giver or the end user. Suggest 5 additional tags that specifically target someone searching for a gift for [specific recipient].\u0026rdquo;\nMistake 3: Not using all 20 characters ChatGPT can pad out your shorter tags: \u0026ldquo;These tags are under 15 characters: [list]. For each one, suggest a longer version that adds specificity without losing the core keyword, keeping under 20 characters.\u0026rdquo;\nMistake 4: Ignoring niche aesthetics Aesthetic communities on Etsy (cottagecore, dark academia, grandmillennial, coastal grandmother) search by their aesthetic identity. If your products fit any aesthetic, targeting these terms can be extremely effective.\nBuild Your Brand Beyond Etsy — Start with a Domain Top Etsy sellers who want to reduce platform dependence establish their own website. Get your domain on Onamae.com — Japan\u0026rsquo;s most trusted domain registrar with simple setup and competitive pricing.\nPutting It All Together A solid Etsy tag workflow with ChatGPT looks like this:\nWrite a detailed product description (this is your investment — better input = better output) Run the core tag generation prompt → get 20 candidates Validate candidates in Erank or Marmalead → identify strong vs. weak Run the refinement prompt to replace weak tags Build your final 13 using the structural mix described above Repeat quarterly, plus seasonal swaps per your seasonal calendar Done consistently, this process will take your tag quality from \u0026ldquo;good enough\u0026rdquo; to genuinely competitive — and you\u0026rsquo;ll spend less time doing it than you used to.\nRelated Tools Count words and characters in your text → Word Counter Estimate taxes on your Etsy profits → Side Hustle Tax Calculator Resize product images for Etsy listings and social media → Image Resizer Generate a QR code to link your Etsy shop from physical products → QR Code Generator Related Templates Want a complete ChatGPT prompt toolkit for your Etsy shop?\nChatGPT Prompt Templates for Etsy Sellers — Includes tag generation prompts, product description templates, shop announcement copy, customer message scripts, and more. Everything an Etsy seller needs to use AI effectively across their entire business. This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/etsy-seo-tags-chatgpt-optimization/","summary":"\u003cp\u003eIf you\u0026rsquo;ve ever spent 45 minutes agonizing over 13 Etsy tags — refreshing your Erank dashboard, swapping one word in and out, wondering whether \u0026ldquo;handmade gift\u0026rdquo; or \u0026ldquo;unique gift\u0026rdquo; performs better — this guide is for you.\u003c/p\u003e\n\u003cp\u003eEtsy tags are one of the highest-leverage points in your entire shop. The right tags can take a listing from invisible to consistently appearing on the first page. And yet most sellers either rush through them or waste enormous time guessing.\u003c/p\u003e","title":"Etsy SEO: How to Generate Perfect Tags with ChatGPT (Step-by-Step)"},{"content":"Excel proficiency is not evenly distributed. Most office workers can enter data, write a SUM formula, and make a bar chart. A small percentage can build dynamic financial models, automate repetitive workflows, and translate raw data into business decisions. That gap in capability translates directly into a compensation gap.\nAccording to Burning Glass labor market data, job postings requiring advanced Excel skills pay 20–35% more than equivalent roles requiring only basic proficiency. That differential compounds across a career.\nThis guide covers the specific Excel skills that move you from \u0026ldquo;can use Excel\u0026rdquo; to \u0026ldquo;uses Excel to create business value\u0026rdquo; — the distinction employers actually pay for.\nThe Four Tiers of Excel Proficiency Understanding where you are helps you focus on where the salary leverage lies.\nTier Skills Typical Roles Basic SUM, AVERAGE, basic charts, formatting Data entry, admin, junior roles Intermediate VLOOKUP, pivot tables, IF/ELSE, filters Analyst, coordinator, accountant Advanced INDEX/MATCH, Power Query, array formulas, macros Senior analyst, financial modeler, operations manager Expert VBA automation, Power BI integration, complex dashboards Finance director, data lead, senior consultant The jump from Basic to Intermediate is where most salary gains begin. The jump from Intermediate to Advanced is where they accelerate significantly.\nThe Skills That Deliver the Most Career Value 1. XLOOKUP (and Why It Replaced VLOOKUP) VLOOKUP has been the most-cited Excel skill for 20 years, but XLOOKUP — introduced in 2019 and now universally available — is superior in almost every way. If you\u0026rsquo;re still using VLOOKUP, switching immediately makes your work more robust and readable.\nWhy XLOOKUP is better:\nSearches in any direction (left, right, up, down — not just right) Handles errors gracefully with a built-in if-not-found argument Default exact match (VLOOKUP defaults to approximate, which causes silent errors) No need to count column numbers Syntax:\n=XLOOKUP(lookup_value, lookup_array, return_array, [if_not_found]) Example: Find the salary for employee ID 1042 in a table:\n=XLOOKUP(1042, A:A, C:C, \u0026#34;Not found\u0026#34;) Mastering XLOOKUP takes about 2 hours. Every financial analyst, operations professional, and business analyst uses lookup functions daily — being fluent in the current standard is table stakes for analyst-level roles.\n2. PivotTables — The Fastest Way to Analyze Data PivotTables allow you to summarize, group, and filter large datasets in seconds without writing a single formula. A task that takes 30 minutes manually — \u0026ldquo;show me total sales by region, by product category, by quarter\u0026rdquo; — takes 3 minutes with a PivotTable.\nCore PivotTable skills:\nCreating a PivotTable from a structured dataset Using Row Labels, Column Labels, Values, and Filters fields Grouping dates (by month, quarter, year) Calculated fields for derived metrics Connecting slicers for interactive filtering Refreshing when source data changes The PivotTable mindset: Think of every dataset as a potential question. \u0026ldquo;What\u0026rsquo;s the top-performing product in each region?\u0026rdquo; is a PivotTable question. \u0026ldquo;Which sales reps are below target this quarter?\u0026rdquo; is a PivotTable question. Fluency in PivotTables means you can answer these questions in minutes rather than hours.\n3. Power Query — The Skill That Separates Analysts from Clerks Power Query (Data \u0026gt; Get \u0026amp; Transform in Excel) is the most underutilized high-value tool in Excel. It allows you to:\nConnect to external data sources (databases, web APIs, other Excel files, CSV exports) Clean and transform messy data automatically Combine data from multiple sources Apply the same transformation to new data with one click Why this matters for salary: A huge amount of analyst and operations work involves importing data, cleaning it, and restructuring it for analysis. Most people do this manually, every time. Power Query automates this — so a task that takes 3 hours manually takes 15 minutes after the initial setup, and 30 seconds on subsequent months.\nProfessionals who know Power Query take on projects others can\u0026rsquo;t complete quickly. That visibility leads to recognition and promotion.\nWhere to learn: Microsoft\u0026rsquo;s own Power Query documentation is good. \u0026ldquo;Leila Gharani\u0026rdquo; on YouTube has excellent free tutorials.\n4. Dynamic Array Formulas Excel\u0026rsquo;s dynamic array functions (introduced broadly in 2020) fundamentally changed how formulas work. Instead of returning a single value, these functions can return an array of values that automatically \u0026ldquo;spill\u0026rdquo; into adjacent cells.\nThe most valuable dynamic array functions:\nFILTER: Returns only rows that meet a condition. =FILTER(A:C, B:B=\u0026quot;London\u0026quot;) — returns all rows where column B contains \u0026ldquo;London\u0026rdquo; SORT: Returns a sorted version of an array. No more manual sorting that breaks when data updates. UNIQUE: Returns a list of unique values from a range. Instantly deduplicate any column. SEQUENCE: Generates a sequence of numbers. Useful for building dynamic date ranges. XLOOKUP (also dynamic): Can return multiple columns at once. These functions replace complex helper column setups that analysts used before they existed. Knowing them makes you faster and your spreadsheets more maintainable.\n5. INDEX/MATCH — Still Relevant for Complex Lookups Despite XLOOKUP\u0026rsquo;s advantages, INDEX/MATCH remains important because:\nIt works in older Excel versions that recipients might use It\u0026rsquo;s more flexible in certain 2D lookup scenarios Many existing models use it; you need to read and modify them Syntax:\n=INDEX(return_range, MATCH(lookup_value, lookup_range, 0)) The 0 at the end forces exact match — always use this unless you explicitly need approximate.\nTwo-way lookup (row and column):\n=INDEX(data_table, MATCH(row_value, row_headers, 0), MATCH(col_value, col_headers, 0)) This returns the intersection of a row and column — useful for pricing matrices, scheduling grids, and cross-referenced tables.\n6. Financial Modeling Fundamentals For finance, accounting, FP\u0026amp;A, and consulting roles, financial modeling is the skill with the clearest salary premium. A financial model is a structured Excel workbook that represents a company\u0026rsquo;s financial performance — typically including an income statement, balance sheet, cash flow statement, and assumptions page.\nCore financial modeling skills:\nBuilding a 3-statement model from scratch Structuring assumptions separately from calculations (never hard-code numbers into formulas) Sensitivity analysis using Data Tables Scenario modeling with named ranges or INDEX/MATCH on scenario selectors NPV and IRR calculations for investment decisions The discipline that matters most: Model integrity. A model is only as valuable as its accuracy and auditability. This means:\nEvery formula traces back to an input No circular references (except deliberate ones for iterative calculations) Clear labeling of every section Consistent formatting (blue font for hard-coded inputs is the universal convention) [Wall Street Prep financial modeling course at wallstreetprep.com] [CFI financial modeling course at corporatefinanceinstitute.com]\n7. Charts That Communicate, Not Decorate Most Excel charts are made to satisfy a request, not to communicate a finding. Charts that get cited in executive presentations and business cases tell a clear story with minimal noise.\nThe principles of effective Excel charts:\nChoose the right chart type: Bar/column for comparisons, line for trends over time, scatter for correlations, waterfall for variance analysis. Pie charts are almost always the wrong choice. Remove chart junk: Delete gridlines, unnecessary axes, legends that duplicate data labels. Every element should justify its existence. Title with the conclusion: \u0026ldquo;Q3 Revenue By Region\u0026rdquo; is a label. \u0026ldquo;Western Region Drives 60% of Revenue Growth\u0026rdquo; is a title. The latter is more useful. Use consistent colors: One accent color for the data point you want to highlight. Grey for everything else. A person who can turn raw data into a clean, persuasive chart that a CEO understands at a glance is worth far more than someone who can crunch numbers.\n8. Basic VBA and Macros VBA (Visual Basic for Applications) is Excel\u0026rsquo;s programming language. You don\u0026rsquo;t need to become a developer — but knowing how to record, edit, and run macros for repetitive tasks multiplies your productivity.\nWhat macros are good for:\nReformatting data that arrives in the same messy format every week Generating standard reports with one click Automating copy-paste workflows between sheets Sending emails with Excel data (via Outlook integration) How to start:\nEnable the Developer tab (File \u0026gt; Options \u0026gt; Customize Ribbon) Use \u0026ldquo;Record Macro\u0026rdquo; to capture a workflow you do manually Open the VBA editor (Alt+F11) and look at the generated code Edit the code to make it more efficient or handle edge cases Assign the macro to a button on the ribbon You don\u0026rsquo;t need to write VBA from scratch. ChatGPT writes surprisingly good Excel VBA when you describe what you want in plain English. Your job is to understand what it produces, test it, and modify it.\nThe Salary Impact: What the Data Shows Skill Added Typical Salary Increase Basic → PivotTables + XLOOKUP 10–15% at next role change + Power Query + Dynamic Arrays Additional 10–20% + Financial modeling (for finance roles) 25–40% vs. non-modelers + VBA automation 10–15% additional Full advanced proficiency 40–80% vs. basic users over career These are lifetime effects, not single-year effects. A $15,000 salary increase at age 30, maintained with normal 3% raises, is worth over $700,000 in cumulative earnings by age 60.\nHow to Actually Learn These Skills The mistake: Watching video tutorials without building anything. Passive learning produces passive retention — you\u0026rsquo;ll recognize the skill when you see it but won\u0026rsquo;t be able to apply it independently.\nWhat works:\nTake a structured course (Excel MVP YouTube channels or paid platforms) for the conceptual foundation Immediately apply each new skill to a real work dataset Rebuild something you currently do manually using the new skill Teach it to a colleague (explanation solidifies understanding) Set aside 30–45 minutes, three days per week. Most people reach solid intermediate proficiency in 90 days with this schedule. Advanced proficiency takes 6–12 months of regular application.\n[Excel with Business certificate at excelwithbusiness.com] [Chandoo Excel courses at chandoo.org]\nReady to Turn Excel Skills Into a Higher Salary? Mastering these Excel skills opens doors — but finding the right employer to pay you what you\u0026rsquo;re worth is the other half. Find your next career on doda — Japan\u0026rsquo;s leading job platform with thousands of analyst, finance, and operations roles that reward advanced Excel proficiency.\nThe Bottom Line The professionals earning the most from Excel proficiency aren\u0026rsquo;t using features that are technically complex. They\u0026rsquo;re solving real business problems faster and more accurately than their colleagues. Power Query, XLOOKUP, PivotTables, and clear data visualization are not exotic skills — they\u0026rsquo;re the current standard for analyst-grade work.\nThe gap between where most people are and where they need to be is smaller than it looks. The investment is 3–6 months of deliberate practice. The return is a career-long salary differential that compounds.\nRelated Tools Calculate percentages, discounts, and tips instantly → Percentage Calculator Calculate your take-home pay → Salary Calculator Create a monthly budget → Budget Planner See how investing your raise compounds → Compound Interest Calculator Related Templates Start using these techniques right away with our Excel templates:\nSmart Budget Tracker (Excel Template) — Automated calculations and visual dashboards The AI Productivity Playbook 2026 — AI-powered Excel automation workflows Related Articles Google Sheets vs Excel 2026: Which Is Better for You? How to Use AI for Excel Automation 2026 How to Automate Tasks with AI Step by Step Best ChatGPT Prompts for Productivity 2026 Best AI Tools for Small Business 2026: The Complete Roundup This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/excel-skills-double-salary/","summary":"\u003cp\u003eExcel proficiency is not evenly distributed. Most office workers can enter data, write a SUM formula, and make a bar chart. A small percentage can build dynamic financial models, automate repetitive workflows, and translate raw data into business decisions. That gap in capability translates directly into a compensation gap.\u003c/p\u003e\n\u003cp\u003eAccording to Burning Glass labor market data, job postings requiring advanced Excel skills pay 20–35% more than equivalent roles requiring only basic proficiency. That differential compounds across a career.\u003c/p\u003e","title":"Excel Skills That Will Double Your Salary"},{"content":"This article contains affiliate links.\nFreelancing offers freedom, flexibility, and the ability to work on your own terms. It also comes with a tax situation that catches most new freelancers completely off guard — often to the tune of thousands of dollars.\nThis guide covers the tax fundamentals every freelancer in the United States needs to understand in 2026: how self-employment tax works, when and how to pay estimated taxes, which expenses are deductible, and how to structure your finances to keep more of what you earn.\nDisclaimer: This guide is for general educational purposes and does not constitute tax or legal advice. Tax laws change annually. Consult a qualified CPA or tax professional for advice specific to your situation.\nThe Biggest Shock: Self-Employment Tax When you work for an employer, your payroll taxes (Social Security and Medicare) are split 50/50 between you and your employer. You pay 7.65%, your employer pays 7.65%.\nWhen you freelance, you\u0026rsquo;re both the employee and the employer. You pay both halves: 15.3% on net self-employment income (12.4% Social Security + 2.9% Medicare), up to the Social Security wage base ($176,100 in 2026 — check the IRS site for the current figure), plus 2.9% Medicare on income above that base.\nWhat this means in practice: A freelancer earning $80,000 net will owe roughly $11,304 in self-employment tax alone, before regular income tax.\nThe silver lining: you can deduct 50% of your self-employment tax from your gross income. This doesn\u0026rsquo;t eliminate the tax, but it reduces your adjusted gross income (AGI) and thus your regular income tax.\nThe Tax Rates You\u0026rsquo;ll Actually Pay In 2026, your federal tax bill as a freelancer comes from two sources:\nSelf-employment (SE) tax — 15.3% on net self-employment income Federal income tax — Based on your taxable income after deductions Taxable Income (Single) Federal Income Tax Rate $0 – $11,925 10% $11,926 – $48,475 12% $48,476 – $103,350 22% $103,351 – $197,300 24% $197,301 – $250,525 32% $250,526 – $626,350 35% Over $626,350 37% (2026 brackets are indexed to inflation — verify current brackets at irs.gov)\nMost freelancers earning $50,000-$150,000 net will pay a combined effective federal rate (SE tax + income tax) of roughly 25-35%. Add your state income tax and the total can exceed 40% in high-tax states like California and New York.\nRule of thumb: Set aside 25-30% of every payment you receive for taxes. Put it in a separate savings account immediately. Don\u0026rsquo;t touch it until tax time.\nQuarterly Estimated Tax Payments As a freelancer, no employer is withholding taxes from your paychecks. You\u0026rsquo;re responsible for paying taxes throughout the year via estimated quarterly payments.\nThe IRS requires you to pay estimated taxes if you expect to owe at least $1,000 in federal taxes for the year. Miss these payments and you\u0026rsquo;ll face underpayment penalties on top of your tax bill.\n2026 Estimated Tax Due Dates Payment Period Due Date January 1 – March 31 April 15, 2026 April 1 – May 31 June 16, 2026 June 1 – August 31 September 15, 2026 September 1 – December 31 January 15, 2027 (Dates shift when they fall on weekends or federal holidays)\nHow to Calculate Your Estimated Payments Method 1: The safe harbor rule (easiest) Pay at least 100% of what you owed in federal taxes last year (or 110% if your prior year AGI exceeded $150,000) divided by four. This protects you from underpayment penalties even if you earn much more this year.\nMethod 2: Current-year estimate (more accurate) Estimate your current year\u0026rsquo;s taxable income, calculate the tax you\u0026rsquo;ll owe, subtract withholding (if any), and divide by four.\nMethod 3: Annualized income method Used when income is uneven across quarters. More complex — use tax software or a CPA.\nHow to Pay Pay online at IRS Direct Pay (free) or via the IRS2Go mobile app. You can also mail Form 1040-ES with a check. Direct Pay is the fastest and most reliable method.\nAlways keep your payment confirmation number.\nDeductible Business Expenses This is where freelancers recover significant tax savings. Every legitimate business expense reduces your net self-employment income, which reduces both your SE tax and your income tax.\nThe Golden Rule of Deductions An expense is deductible if it is \u0026ldquo;ordinary and necessary\u0026rdquo; for your business: ordinary means common in your industry, necessary means helpful and appropriate for your work. You must keep documentation for every deduction.\nHome Office Deduction One of the most valuable deductions for freelancers — and one of the most misunderstood.\nTo qualify, you must have a space in your home used regularly and exclusively for business. A dedicated office is ideal. A kitchen table where you also eat does not qualify.\nMethod 1: Simplified method $5 per square foot of your home office, up to 300 square feet. Maximum deduction: $1,500. Easy to calculate, no depreciation concerns.\nMethod 2: Regular method Calculate the percentage of your home used for business (office square footage / total home square footage). Apply that percentage to actual home expenses: rent or mortgage interest, utilities, insurance, repairs, and depreciation.\nExample: 200 sq ft office / 2,000 sq ft home = 10%. If your monthly rent is $2,000, you can deduct $2,400/year (10% × $24,000 annual rent).\nThe regular method typically yields a larger deduction but requires more documentation.\nTechnology and Equipment Deductions Expense Deductible? Notes Laptop, desktop, tablet Yes Business use percentage if personal use External monitors Yes Full if business-only Smartphone Partial Business use percentage only Software subscriptions Yes Business-use tools (Adobe, Notion, Slack) Cloud storage (Dropbox, Drive) Yes Business accounts Domain name and hosting Yes Your professional site Hardware peripherals Yes Keyboard, mouse, webcam, microphone For equipment costing over $2,500, you may need to depreciate over multiple years OR use Section 179 expensing to deduct the full cost in the year of purchase (up to $1.16M in 2026 — verify current limit).\nCommunication and Marketing Deductions Internet service — Deduct the business-use percentage (if you work from home and have no other office, 50-80% is typically defensible) Phone plan — Business-use percentage Zoom, Slack, communication tools — 100% if business-only Website maintenance — 100% Business cards, marketing materials — 100% Advertising (Google Ads, Facebook Ads, paid promotions) — 100% Professional photography — For headshots, product photos, etc. Professional Development Deductions Online courses, workshops, webinars directly related to your business Books, publications, and subscriptions (industry journals, trade publications) Conference registration fees and associated travel Professional memberships and dues Important: Education must maintain or improve current skills, not qualify you for a new career.\nTravel and Vehicle Deductions Business travel:\nAirfare, hotels, and meals when traveling for business (meals are 50% deductible) Rental cars Taxis, rideshares, parking, tolls Must be primarily for business (personal side trips don\u0026rsquo;t count) Vehicle expenses (two methods, choose one):\nStandard mileage rate (2026): The IRS sets a per-mile rate for business driving (approximately $0.67-0.70/mile in recent years — check irs.gov for the 2026 rate). Track every business mile driven.\nActual expenses: Deduct the business-use percentage of gas, insurance, repairs, depreciation, registration.\nKeep a mileage log (date, destination, business purpose, miles). Apps like MileIQ or Everlance automate this.\nHealth Insurance Deduction Self-employed individuals can deduct 100% of health insurance premiums paid for themselves, their spouse, and dependents — as an adjustment to income (not a business expense on Schedule C). This is a substantial deduction that many freelancers miss.\nYou cannot take this deduction for any month you were eligible for employer-subsidized health coverage (e.g., through a spouse\u0026rsquo;s employer).\nRetirement Account Contributions Freelancers have access to retirement accounts that provide both tax-advantaged savings and significant deductions:\nAccount Type 2026 Contribution Limit Who It\u0026rsquo;s For SEP-IRA Up to 25% of net SE income, max ~$69,000 Any freelancer, easy to set up Solo 401(k) $23,500 employee + 25% employer, max ~$69,000 Higher earners, more flexibility SIMPLE IRA $16,500 Freelancers with a few employees Traditional IRA $7,000 ($8,000 age 50+) Supplement to above (Verify 2026 limits at irs.gov — these are indexed to inflation)\nA freelancer maxing a SEP-IRA at $30,000 reduces their taxable income by $30,000. At a 24% income tax rate + SE tax deduction, that\u0026rsquo;s roughly $8,000-10,000 in tax savings.\nBusiness Structure: Sole Proprietor vs. LLC vs. S-Corp Most freelancers start as sole proprietors — you\u0026rsquo;re automatically one if you freelance without forming a business entity. All income is reported on Schedule C, subject to full SE tax.\nSingle-Member LLC: A LLC offers liability protection (keeps personal assets separate from business debts/lawsuits) but is taxed identically to a sole proprietorship by default. Worth forming for the legal protection; doesn\u0026rsquo;t change your tax bill.\nS-Corporation Election: The most significant tax strategy for higher-earning freelancers. Here\u0026rsquo;s how it works:\nForm an LLC or corporation Elect S-Corp status with the IRS (Form 2553) Pay yourself a \u0026ldquo;reasonable salary\u0026rdquo; through payroll Take remaining profits as distributions The tax advantage: Only your salary is subject to SE tax. Distributions are not. A freelancer earning $150,000 net who takes a $75,000 salary and $75,000 distribution saves approximately $10,000+ in SE tax annually.\nThe costs: Payroll administration ($50-150/month), more complex accounting, potentially a CPA. Typically worthwhile when net self-employment income exceeds $50,000-60,000/year.\nConsult a CPA before making this election — the math depends on your specific income, state, and business structure.\nRecord-Keeping: What to Track and How Good records are your protection in an audit and your source of accurate deductions.\nWhat to Keep All income: Every invoice and payment received. Bank statements showing deposits. All business expenses: Receipts for every deductible purchase. Electronic receipts count. Mileage log: Date, destination, purpose, miles — for every business trip. Home office: Floor plan or measurements, utility bills, lease/mortgage statements. Bank and credit card statements: Separate business account is strongly recommended. The Separate Account Rule Open a dedicated business checking account and credit card. Run all business income and expenses through them exclusively. This makes bookkeeping trivial, makes you audit-proof, and eliminates the nightmare of combing through personal bank statements for business expenses.\nRecommended: Mercury (free, no minimums, designed for freelancers and startups) or Relay (similar, excellent budgeting features).\nTools for Freelance Bookkeeping Tool Price Best For FreshBooks $17/month Service freelancers, invoicing-heavy QuickBooks Self-Employed $15/month Solo freelancers, mileage tracking Wave Free Budget-conscious freelancers Bonsai $21/month Contracts + invoicing + taxes in one HoneyBook $16/month Client management + bookkeeping Freelancing in Japan? freee is built specifically for sole proprietors and small business owners here — it handles invoice creation, expense tracking, and guides you through kakuteishinkoku step by step, in a workflow that doesn\u0026rsquo;t require accounting expertise.\nConnect your business bank account and credit card. These tools categorize transactions automatically, track mileage, and generate profit/loss statements that make tax prep far easier.\nKeep records for at least 3 years from the filing date (the IRS has 3 years to audit standard returns). Keep records 6 years if you underreported income by more than 25%.\nTax Forms Freelancers Need to Know Form What It Is Schedule C Reports profit/loss from your freelance business Schedule SE Calculates your self-employment tax Form 1040-ES Used for quarterly estimated tax payments 1099-NEC Sent by clients who paid you $600+ in a year W-9 You provide this to clients so they can send you a 1099 Form 8829 Home office deduction (regular method) You\u0026rsquo;ll receive 1099-NEC forms from clients by January 31 of the following year. Note: you must report ALL income — even from clients who paid you less than $600 and didn\u0026rsquo;t send a 1099. The IRS compares your reported income to what your clients report paying you.\nYour 2026 Freelance Tax Calendar Month Action January Collect all 1099-NEC forms; reconcile Q4 books February Compile all expense receipts and records March Schedule CPA appointment (before April rush); prep estimated payment April 15 Q1 estimated taxes due; file or extend personal return June 16 Q2 estimated taxes due September 15 Q3 estimated taxes due October 15 Extended return deadline (if you filed for extension) November Year-end tax planning: retirement contributions, defer/accelerate income December Max out retirement accounts; make final estimated payment calculations January 15 Q4 estimated taxes due 5 Costly Mistakes to Avoid 1. Not paying quarterly estimates. The penalty is relatively small but completely avoidable. Set calendar reminders the moment you start freelancing.\n2. Mixing personal and business finances. Commingling money makes bookkeeping painful, reduces your deductions (missed expenses), and is a red flag in an audit. Separate accounts from day one.\n3. Missing the home office deduction. If you have a qualifying home office — and most full-time freelancers do — this is one of your largest deductions. Don\u0026rsquo;t leave it on the table.\n4. Forgetting about SE tax when setting rates. Many freelancers price themselves as if their effective tax rate is their income tax rate. Remember that SE tax adds 15.3% on top. Factor this into your hourly or project rates.\n5. Not saving for taxes at all. This is the most common and most painful mistake. Getting a $15,000 tax bill in April with no savings to cover it is genuinely devastating. Automate a 25-30% transfer to a dedicated \u0026ldquo;tax savings\u0026rdquo; account every time a payment hits.\nFreelance taxes are genuinely more complex than W-2 taxes, but they\u0026rsquo;re manageable with the right systems. The key habits: separate accounts, quarterly payments, consistent record-keeping, and maximizing legitimate deductions — especially retirement contributions.\nIf you earn $75,000+ as a freelancer, working with a CPA who specializes in self-employed clients typically pays for itself many times over in tax savings.\nWant a ready-made system? Our Freelance Finance Toolkit on Payhip includes a quarterly tax tracker spreadsheet, expense categorization template, client invoice tracker, and a 2026 tax deduction checklist — everything to manage freelance finances without the chaos.\nRelated reads:\nBest AI Tools for Small Business 2026 How to Use Notion for Project Management 2026 Related Tools \u0026amp; Articles Count words and characters in your text → Word Counter Calculate percentages, discounts, and tips instantly → Percentage Calculator Calculate your ideal freelance hourly rate → Freelance Rate Calculator Create professional invoices for your clients → Invoice Generator Side Hustle Tax Calculator — Calculate SE tax, federal tax, and quarterly payments on freelance income Tax Bracket Calculator — See your 2026 federal tax bracket and effective rate US Salary Calculator — See your W-2 take-home pay breakdown Budget Planner — Plan your monthly budget including tax set-asides Savings Goal Calculator — Calculate how long to reach any savings target How to Start Freelancing in 2026: Complete Beginner\u0026rsquo;s Guide Side Job Tax Rules in Japan AI Tools for Freelancers to Earn More 2026 Related Templates Start your freelance or side hustle journey with these resources:\nSide Hustle Starter Kit 2026 — 15 proven ideas with step-by-step guides ChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Boost your productivity instantly ","permalink":"https://productivity-works.com/posts/freelance-tax-guide-2026/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eFreelancing offers freedom, flexibility, and the ability to work on your own terms. It also comes with a tax situation that catches most new freelancers completely off guard — often to the tune of thousands of dollars.\u003c/p\u003e\n\u003cp\u003eThis guide covers the tax fundamentals every freelancer in the United States needs to understand in 2026: how self-employment tax works, when and how to pay estimated taxes, which expenses are deductible, and how to structure your finances to keep more of what you earn.\u003c/p\u003e","title":"Freelance Tax Guide 2026: Everything You Need to Know"},{"content":" Google Sheets vs Excel 2026: The Complete Comparison The spreadsheet debate has raged for decades. In 2026, both Google Sheets and Microsoft Excel have evolved dramatically — but they\u0026rsquo;ve also pulled further apart in their strengths.\nWhether you\u0026rsquo;re a student, freelancer, small business owner, or enterprise analyst, picking the right spreadsheet tool affects your daily productivity in ways most people underestimate. Wrong choice, and you\u0026rsquo;re fighting your tools instead of working with them.\nThis guide breaks down every meaningful difference so you can make an informed decision once and for all.\nQuick Verdict Choose Google Sheets if:\nCollaboration and real-time sharing are priorities You\u0026rsquo;re budget-conscious (or broke) Your data lives in Google Workspace You want accessibility from any device Choose Microsoft Excel if:\nYou work with large, complex datasets Advanced data analysis and Power BI integration matter You need the most powerful formula and function library You use Microsoft 365 across your organization Use both if:\nYour team spans both ecosystems You need Google\u0026rsquo;s sharing for external stakeholders and Excel\u0026rsquo;s power for internal analysis Pricing Comparison 2026 Product Price Storage AI Features Google Sheets Free 15GB Google Drive Gemini AI (limited) Google Workspace Individual $9.99/month 1TB Gemini AI included Google Workspace Business Starter $6/user/month 30GB/user Gemini AI included Microsoft Excel (web) Free 5GB OneDrive Copilot (limited) Microsoft 365 Personal $6.99/month 1TB OneDrive Copilot included Microsoft 365 Business Basic $6/user/month 1TB/user Copilot (add-on) Microsoft 365 Business Standard $12.50/user/month 1TB/user Copilot included Bottom line on pricing: Google Sheets is free for individual users and remains cheaper for small teams. Excel\u0026rsquo;s free web version is significantly more limited than Google Sheets\u0026rsquo; free version. For professional Excel features, plan on $6.99-12.50/month.\nFeature-by-Feature Comparison Collaboration and Sharing Winner: Google Sheets — by a wide margin\nGoogle Sheets pioneered real-time collaboration and still does it best. Multiple users can edit simultaneously, see each other\u0026rsquo;s cursors, and comment on cells — all with zero friction. Sharing is a URL, not a file attachment.\nExcel has improved significantly with real-time co-authoring in Microsoft 365, but it still requires OneDrive or SharePoint as the backend, and the experience occasionally gets out of sync in ways Google Sheets doesn\u0026rsquo;t.\nFor any team that collaborates on spreadsheets, Google Sheets remains the superior choice.\nScore: Sheets 10/10 | Excel 7/10\nFormula and Function Power Winner: Excel — significantly\nExcel\u0026rsquo;s formula library is the most comprehensive in the industry. In 2026, features like:\nDynamic Arrays (FILTER, SORT, UNIQUE, XLOOKUP) Power Query for data transformation DAX for data modeling LAMBDA functions for custom reusable logic LET function for variable assignment in formulas \u0026hellip;give Excel a depth that Google Sheets simply hasn\u0026rsquo;t matched. Sheets has added many functions over the years, but professional analysts working with complex data models almost always prefer Excel.\nScore: Excel 10/10 | Sheets 7/10\nData Analysis and Pivot Tables Winner: Excel\nExcel\u0026rsquo;s PivotTable functionality is more powerful, with better grouping options, calculated fields, and integration with Power Pivot. The Data Model feature allows you to connect multiple tables in a relational structure — something Sheets can\u0026rsquo;t do natively.\nGoogle Sheets has solid pivot tables for everyday use, but complex data analysis genuinely requires Excel.\nScore: Excel 9/10 | Sheets 6/10\nMacros and Automation Winner: Excel (for power users) / Sheets (for accessibility)\nExcel\u0026rsquo;s VBA (Visual Basic for Applications) has been the gold standard for automation for 30 years. If you need complex, custom automation — multi-step processes, interaction with other Office apps, legacy macros — Excel is the choice.\nGoogle Sheets uses Google Apps Script (JavaScript-based), which is easier to learn, more modern, and can connect to Google services and external APIs more naturally. For most automation needs, Apps Script is actually more practical.\nScore: Excel 9/10 | Sheets 8/10\nAI Features 2026 Winner: Depends on your ecosystem\nBoth platforms have integrated AI aggressively in 2026:\nMicrosoft Copilot in Excel:\nGenerate formulas from natural language descriptions Identify insights and anomalies in data automatically Create charts and summaries from plain English prompts Available with Microsoft 365 (requires add-on for some tiers) Google Gemini in Sheets:\nSimilar natural language formula creation Smart chip integration with other Google data AI-generated charts and analysis Included with Workspace plans In practical testing, Copilot\u0026rsquo;s formula generation is slightly more sophisticated, but Gemini\u0026rsquo;s integration with the broader Google ecosystem (connecting to Drive files, Gmail data) is unique.\nScore: Excel Copilot 8/10 | Gemini in Sheets 7.5/10\nPerformance with Large Data Winner: Excel — decisively\nGoogle Sheets starts to show strain at around 100,000 rows. At 500,000+ rows, it becomes sluggish or unworkable. Excel handles millions of rows natively, and with Power Query, can process datasets that would crash Sheets entirely.\nIf you work with large datasets — sales data, logs, financial data — Excel is not optional.\nScore: Excel 10/10 | Sheets 4/10\nAccessibility and Cross-Device Use Winner: Google Sheets\nSheets works identically in any browser on any device. The mobile app is solid. Changes sync instantly everywhere.\nExcel\u0026rsquo;s web version has improved but still lags the desktop app in features. The mobile app is good but limited for complex work.\nScore: Sheets 9/10 | Excel 6/10\nIntegration with Other Tools Winner: Context-dependent\nGoogle ecosystem: Sheets wins decisively — it integrates natively with Forms, Docs, Slides, Gmail, Calendar, and every Google Workspace product Microsoft ecosystem: Excel wins decisively — deep integration with Word, PowerPoint, Teams, Power BI, and the entire Microsoft 365 suite Third-party tools: About even — both have extensive integration via Zapier, Make, and direct APIs Offline Access Winner: Excel\nExcel desktop works fully offline. Google Sheets\u0026rsquo; offline mode requires setup and has limitations — and if you\u0026rsquo;re offline on a Chromebook, you\u0026rsquo;re dependent on Sheets working correctly, which it usually does but occasionally doesn\u0026rsquo;t.\nScore: Excel 9/10 | Sheets 6/10\nLearning Curve Winner: Google Sheets (for beginners)\nGoogle Sheets is faster to learn and has a less intimidating interface. The Google support ecosystem (tutorials, community) is excellent and modern.\nExcel\u0026rsquo;s depth is also its complexity. Beginners often feel overwhelmed. But this complexity pays off — professionals who master Excel become genuinely irreplaceable at many organizations.\nSee our guide: Excel Skills That Can Double Your Salary for advanced techniques worth learning.\nScore: Sheets 9/10 | Excel 6/10 for beginners\nThe Complete Scorecard Category Google Sheets Microsoft Excel Collaboration 10 7 Formulas \u0026amp; Functions 7 10 Data Analysis 6 9 Automation 8 9 AI Features 7.5 8 Large Data Performance 4 10 Cross-Device Access 9 6 Integration 8 (Google) 8 (Microsoft) Offline Access 6 9 Beginner-Friendliness 9 6 Average Score 7.5 8.2 Free Option Quality Excellent Limited Use Cases: What Each Does Best Google Sheets Is Ideal For: 1. Shared project tracking Sales pipelines, project status boards, content calendars — anything multiple people need to view and edit in real time.\n2. Form-based data collection Google Forms → Google Sheets is one of the most powerful free automation workflows available. Survey responses, applications, orders — all flow directly into your spreadsheet.\n3. Budget and expense tracking for small businesses Simple accounting, budget tracking, and expense reporting for teams that need everyone to have access.\n4. Student and academic projects Free, collaborative, accessible everywhere, easy to share with professors and teammates.\n5. Integration with Google Workspace automation Apps Script lets you connect Sheets to Gmail, Calendar, Drive, and external APIs for powerful custom workflows.\nExcel Is Ideal For: 1. Financial modeling Budgets, financial projections, valuation models, discounted cash flow analysis — Excel\u0026rsquo;s formula depth and performance handle these properly.\n2. Data analysis at scale Hundreds of thousands of rows, Power Query transformations, PivotTable analysis — this is Excel\u0026rsquo;s natural habitat.\n3. Corporate reporting Most corporate finance and accounting teams operate in Excel. If you work in these environments, Excel proficiency is non-negotiable.\n4. Complex automation with VBA Legacy business processes, multi-step automation across Office apps, interaction with external systems — VBA is still the tool for this.\n5. Advanced visualization before Power BI Excel\u0026rsquo;s chart capabilities are more customizable than Sheets. For complex, presentation-ready data visualization, Excel gives more control.\nMigration: Moving Between the Two Google Sheets → Excel Export as .xlsx directly from File \u0026gt; Download Most formulas translate, but some Google-specific functions (IMPORTRANGE, GOOGLEFINANCE) will break Formatting usually survives well Complex scripts won\u0026rsquo;t transfer Excel → Google Sheets Upload .xlsx directly to Google Drive and open with Sheets Most standard formulas translate correctly VBA macros won\u0026rsquo;t convert — you\u0026rsquo;d need to rewrite in Apps Script Some advanced formatting and features may display differently For Students: Which Should You Learn First? Learn Google Sheets first — it\u0026rsquo;s free, collaborative, and teaches spreadsheet logic without the complexity barrier.\nOnce you\u0026rsquo;ve mastered the basics, add Excel — especially if you\u0026rsquo;re heading into finance, accounting, data analysis, or any corporate career. Excel proficiency shows up in nearly every professional job description in these fields.\nKnowing both is the real competitive advantage.\nFor Small Business Owners: Our Recommendation Start with Google Sheets for collaborative, everyday tasks:\nTeam project tracking Simple budgets and cash flow Customer lists and CRM (until you need a real CRM) Shared content calendars Graduate to Excel (via Microsoft 365, $6.99/month personal or $6-12.50/month/user for business) when you:\nWork with financial models needing precision Have datasets over 50,000 rows Need Power BI integration for reporting Join an organization that uses Microsoft 365 Frequently Asked Questions Is Google Sheets as good as Excel? For collaboration and accessibility, Google Sheets is better. For data analysis, formulas, and large datasets, Excel is better. They\u0026rsquo;re genuinely different tools optimized for different use cases.\nCan I use both Google Sheets and Excel? Yes, and many professionals do. Sheets for collaboration and sharing; Excel for complex analysis and modeling.\nIs Excel worth paying for? If you work with data professionally — finance, accounting, analysis, marketing analytics — yes. The productivity gains justify the $6.99-12.50/month cost many times over.\nDoes Excel work without a subscription? Excel 2021 (one-time purchase, ~$150) gives you most Excel features without a subscription, but no Copilot AI, no cloud sync, and no mobile apps.\nWill Google Sheets ever beat Excel for advanced features? Google has been closing the gap for years, but Excel\u0026rsquo;s depth in data analysis and financial modeling is a 30-year head start. For the foreseeable future, Excel maintains the advantage for power users.\nRelated Tools Pick colors and convert between formats → Color Picker Try our free online budget planner → Budget Planner Spreadsheet Skills Are One of the Highest-ROI Career Investments Finance, analytics, marketing, and operations roles all reward candidates who can work confidently with data. Find your next career on doda — browse finance and analytics roles on Japan\u0026rsquo;s top job platform.\nBecome a Spreadsheet Power User Whether you choose Sheets, Excel, or both — mastering spreadsheet skills is one of the highest-ROI professional investments you can make.\nOur Spreadsheet Productivity Bundle includes formula cheat sheets, automation templates, and financial model blueprints for both Google Sheets and Excel.\nRelated Reading:\nExcel Skills That Can Double Your Salary How to Use AI for Excel Automation 2026 Best AI Tools for Small Business 2026 This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/google-sheets-vs-excel-2026/","summary":"\u003chr\u003e\n\u003ch2 id=\"google-sheets-vs-excel-2026-the-complete-comparison\"\u003eGoogle Sheets vs Excel 2026: The Complete Comparison\u003c/h2\u003e\n\u003cp\u003eThe spreadsheet debate has raged for decades. In 2026, both Google Sheets and Microsoft Excel have evolved dramatically — but they\u0026rsquo;ve also pulled further apart in their strengths.\u003c/p\u003e\n\u003cp\u003eWhether you\u0026rsquo;re a student, freelancer, small business owner, or enterprise analyst, picking the right spreadsheet tool affects your daily productivity in ways most people underestimate. Wrong choice, and you\u0026rsquo;re fighting your tools instead of working with them.\u003c/p\u003e","title":"Google Sheets vs Excel 2026: Which Is Better for You?"},{"content":"How to Automate Tasks with AI Step by Step — Complete Guide [2026] Imagine waking up to find that your weekly report was drafted, your client follow-ups were sent, your social media posts were scheduled, and your inbox was sorted — all while you slept. That\u0026rsquo;s not a fantasy in 2026. It\u0026rsquo;s a Wednesday morning for professionals who\u0026rsquo;ve built AI automation workflows.\nBut here\u0026rsquo;s the challenge: most guides to AI automation are written for developers. They assume you know what an API is, can write Python, and enjoy setting up webhooks for fun. This guide isn\u0026rsquo;t for them. This is for the knowledge worker, freelancer, or small business owner who wants to eliminate repetitive tasks with AI — without writing a single line of code.\nHere\u0026rsquo;s what you\u0026rsquo;ll learn:\nThe 5 types of tasks best suited for AI automation A comparison of the top no-code automation platforms in 2026 Step-by-step workflows for the most common automation use cases How to connect ChatGPT to your existing tools without coding Advanced strategies for building a fully automated personal productivity system By the end of this guide, you\u0026rsquo;ll have at least one automation running that saves you real time this week.\nWhy Automating Tasks with AI Matters in 2026 The Productivity Gap Most professionals have a list of tasks they do the same way, week after week. Monday\u0026rsquo;s report. Friday\u0026rsquo;s newsletter roundup. The new client welcome email. The meeting follow-up. The social post announcing the blog article. Each task takes 20–60 minutes. Collectively, they consume 5–15 hours every week.\nThese tasks are perfect candidates for automation — they\u0026rsquo;re repetitive, rule-based, and follow a predictable structure. Before AI, automating them required either custom code or accepting low-quality outputs from simple templates. AI changes both constraints: it can produce high-quality, context-aware outputs, and modern no-code tools make the automation accessible to non-technical users.\nHow AI Changes the Game Traditional automation (Zapier without AI) could send an email when a form was submitted. With AI in the loop, the automation can now read the form submission, analyze it, draft a personalized response, and send it — all automatically. That\u0026rsquo;s the leap. AI gives automation judgment and language, turning \u0026ldquo;if/then\u0026rdquo; workflows into genuinely intelligent processes.\nBest AI Automation Tools Compared Tool No-Code Friendly AI Integration Price Best For Zapier (with AI actions) Excellent ChatGPT, Claude, Gemini Free–$20+/month Beginners, simple workflows Make (formerly Integromat) Very good OpenAI, various LLMs Free–$9+/month Complex multi-step workflows n8n Advanced Full LLM integration Free (self-hosted), $20+/cloud Technical users wanting control Microsoft Power Automate Good Copilot AI M365 subscription Microsoft ecosystem users Notion AI + Zapier Good Notion\u0026rsquo;s built-in AI $16+/month (Notion) Notion power users Bardeen Excellent Built-in AI Free–$10/month Browser-based automations ChatGPT Actions (custom GPTs) Excellent Native ChatGPT Plus ($20/mo) Custom AI assistants with tools Try It Free Best starting point: Zapier with AI actions for pure no-code simplicity. Make for more complex workflows at a lower price. n8n if you want full control and don\u0026rsquo;t mind a steeper learning curve.\nStep-by-Step Guide: How to Automate Tasks with AI Step 1: Identify Your Best Automation Candidates Not every task should be automated. The best candidates share these traits:\nAutomation-ready tasks:\nHappen the same way every time (or nearly so) Involve moving data from one place to another Require writing that follows a consistent template Trigger based on a clear event (new email, form submission, calendar event) Don\u0026rsquo;t require complex human judgment to complete correctly Use this prompt to find your best candidates:\nHere is a list of tasks I do regularly: [LIST YOUR RECURRING TASKS] For each task, classify it as: - Fully automatable (can be done by AI 100% without my review) - Mostly automatable (AI does 80%, I review quickly) - Partially automatable (AI does the research/draft, I do the judgment call) - Not automatable (requires human judgment, relationships, or real-time info) Then rank the top 5 by time saved per week if automated. Step 2: Build Your First Automation (Zapier + ChatGPT) Let\u0026rsquo;s build a real workflow: Auto-draft client follow-up emails after meetings.\nThe workflow:\nYou end a Zoom meeting Otter.ai (or Fireflies.ai) transcribes the meeting Zapier detects the new transcript Zapier sends the transcript to ChatGPT with your prompt ChatGPT drafts a follow-up email Zapier creates a draft in your Gmail Setup in Zapier:\nGo to zapier.com and create a new Zap Trigger: \u0026ldquo;New transcript completed\u0026rdquo; in Otter.ai (or use email trigger if Otter sends you an email) Action: \u0026ldquo;ChatGPT — Create Chat Completion\u0026rdquo; In the ChatGPT action, paste this prompt: You are my executive assistant. Below is a transcript from a client meeting. Draft a professional follow-up email: 1. Thank the client for their time 2. Summarize the 3 main discussion points 3. List all action items with who owns them 4. Confirm the next meeting date/time if mentioned 5. Keep it under 250 words, warm and professional Transcript: {{transcript_content}} Final action: \u0026ldquo;Gmail — Create Draft\u0026rdquo; using the ChatGPT output as the email body Add the client\u0026rsquo;s email address from your CRM or manually Time to set up: 30–45 minutes Time saved per week: 2–5 hours depending on meeting volume\nStep 3: Automate Your Content Pipeline The workflow: New blog post draft → AI creates social media posts for all channels\nPrompt for the Automation:\nI\u0026#39;ve just published a new blog post. Here is the full text: {{blog_post_content}} Create the following content based on this post: 1. LinkedIn post (200 words, professional, end with a question) 2. Twitter/X post (under 280 chars, punchy, one key insight) 3. Instagram caption (150 words, engaging, relevant emojis okay) 4. Email newsletter intro paragraph (100 words, conversational) Tone: Match the voice of the blog post. Do not just summarize — give readers a reason to click through to read the full post. Zapier setup:\nTrigger: New published post in WordPress / Notion / your CMS Action: ChatGPT generates the social content Action: Automatically schedules to Buffer or Hootsuite Step 4: Automate Lead Nurturing The workflow: New contact form submission → Personalized follow-up email\nA new lead submitted our contact form with this information: Name: {{name}} Company: {{company}} Industry: {{industry}} Message: {{message}} Write a personalized follow-up email that: 1. References something specific from their message 2. Shows we understand their industry 3. Proposes a specific next step (15-min call, demo, resource) 4. Is warm and conversational, NOT templated-sounding 5. Under 150 words Sign it from: [YOUR NAME], [YOUR TITLE] Step 5: Build a Daily Briefing Automation Get an AI-generated daily brief every morning at 8am.\nThe automation (using Make or Zapier scheduled trigger):\nTrigger: Every day at 7:45am Pull data from: Calendar (today\u0026rsquo;s events), task manager (today\u0026rsquo;s to-dos), email (unread flagged emails) Send all to ChatGPT with this prompt: You are my morning chief of staff. Here is today\u0026#39;s data: CALENDAR EVENTS TODAY: {{calendar_events}} TOP TASKS: {{tasks}} URGENT EMAILS: {{urgent_emails}} Create my morning briefing: 1. Today\u0026#39;s priority (the single most important thing to accomplish) 2. Schedule overview (3 bullets on what today looks like) 3. Any conflicts or issues I should know about 4. My top 3 tasks in priority order 5. One motivating sentence to start the day Keep it to 150 words. Direct, actionable, no fluff. Send the briefing to your email or Slack DM at 8am Pro Tips \u0026amp; Advanced Techniques Common Mistakes to Avoid Mistake 1: Automating before you\u0026rsquo;ve standardized. If your process changes every time you do it, automation will just automate inconsistency. First, do the task the same way 3–5 times manually. Then automate the pattern.\nMistake 2: Going too complex on your first workflow. Start with a single trigger → single AI action → single output. Master that. Then add steps. Most powerful automations are built by stacking simple workflows.\nMistake 3: No error handling. What happens when the trigger data is empty or malformed? Always add a filter step that checks for required data before sending to AI, and add a fallback notification if the automation fails.\nMistake 4: Sending AI output directly without review. For any customer-facing content, build a review step (create a draft rather than send directly) until you\u0026rsquo;ve validated the quality over 20+ runs.\nMistake 5: Forgetting to test with real data. Zapier/Make test modes use fake data. Always run a test with real data before enabling an automation on live workflows.\nPower User Strategies Strategy 1: Build a \u0026ldquo;thinking partner\u0026rdquo; automation. Set up a daily Slack bot that asks you three questions: \u0026ldquo;What\u0026rsquo;s your most important work today? What\u0026rsquo;s blocking you? What decision are you avoiding?\u0026rdquo; — and uses AI to give a brief coaching response.\nStrategy 2: Use AI to generate your automation logic. Before building in Zapier, describe the workflow to ChatGPT and ask it to map out all the steps, edge cases, and potential failure points. This dramatically speeds up the build.\nStrategy 3: Connect automation to your CRM. Any automated email, note, or summary that relates to a contact should be logged in your CRM (HubSpot, Pipedrive, etc.). Zapier integrates with all major CRMs.\nStrategy 4: Build a \u0026ldquo;weekly review\u0026rdquo; automation. Every Friday at 4pm, an automation pulls your completed tasks, metrics, and calendar from the week and generates a structured weekly review document — what worked, what didn\u0026rsquo;t, what to carry forward.\nStrategy 5: Use n8n for sensitive or complex workflows. n8n can be self-hosted, meaning your data stays on your servers. For automations involving sensitive client data, this is the right choice over SaaS tools.\nRelated: Best ChatGPT Prompts for Productivity 2026 Frequently Asked Questions Q: Do I need to know how to code to automate tasks with AI? A: No. Zapier and Make are fully no-code platforms. You click, configure, and connect. The AI prompt writing is the closest thing to \u0026ldquo;technical\u0026rdquo; work, and this guide gives you templates for all of it.\nQ: What\u0026rsquo;s the difference between Zapier and Make? A: Zapier is more beginner-friendly with a larger app library. Make is more powerful and cheaper for complex workflows, with better visual design for multi-step automations. Start with Zapier; graduate to Make when you need more complexity.\nQ: How much do these automation tools cost? A: Zapier: Free for 5 Zaps/100 tasks per month; $19.99/month for more. Make: Free for 1,000 operations/month; $9/month for more. For most beginners, the free tiers are enough to get started.\nQ: Is n8n really free? A: n8n is open-source and free to self-host on your own server. The cloud version starts at $20/month. If you\u0026rsquo;re comfortable with basic server management, self-hosting is an excellent option.\nQ: What if the AI writes something wrong in my automated workflow? A: This is why you build review steps. Start every automation by creating drafts (not sending/posting directly). Review 20–30 runs. Once quality is validated, you can enable direct publishing for low-stakes content.\nQ: Can I automate WhatsApp or SMS messages with AI? A: Yes — Zapier integrates with Twilio (SMS), WhatsApp Business API, and other messaging platforms. Be thoughtful about privacy and consent rules for automated messages.\nQ: How do I handle AI hallucinations in automated workflows? A: Design prompts with strong constraints and factual grounding (provide the actual data in the prompt rather than asking AI to recall it). For factual content, always review before it goes out. Use AI for drafting and synthesis, not for generating facts from scratch.\nAutomation Skills That Open Doors Companies are hiring people who can build AI workflows and save their teams hours every week. Find your next career on doda — browse thousands of operations and tech roles on Japan\u0026rsquo;s leading job platform.\nConclusion \u0026amp; Call to Action AI automation in 2026 is genuinely accessible to non-technical professionals. With tools like Zapier and Make, you can connect your email, calendar, CRM, and content tools to AI in hours — not months. The key is starting simple: one trigger, one AI action, one output. Then build from there.\nKey takeaways:\nStart with your highest-time-cost repetitive tasks: meeting follow-ups, lead nurturing, content repurposing Use Zapier for simplicity, Make for complexity, n8n for control and privacy Always build review steps before enabling fully automated outbound communication Design prompts with full context: who you are, what the data means, what you want The best automation is built incrementally — master simple workflows first Want the complete automation prompt template library — 40+ workflows pre-built for Zapier, Make, and direct ChatGPT use? Our Complete ChatGPT Prompt Collection includes a dedicated automation section with copy-paste-ready prompts for every workflow in this guide.\nFor the complete system — from identifying your first automation to building a fully AI-automated workday — our AI Productivity Playbook is the definitive guide.\nRelated: AI Tools for Freelancers to Earn More Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nRelated Tools Format and validate JSON data → JSON Formatter Test regular expressions in real-time → Regex Tester Encode and decode Base64 strings → Base64 Encoder Calculate your ideal freelance rate → Freelance Rate Calculator Create a monthly budget → Budget Planner Generate secure passwords instantly → Password Generator Related Templates Put these techniques into practice with our ready-made templates:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Copy-paste prompts for every situation The AI Productivity Playbook 2026 — 50+ AI workflows and automation strategies ","permalink":"https://productivity-works.com/posts/how-to-automate-tasks-with-ai-step-by-step/","summary":"\u003ch1 id=\"how-to-automate-tasks-with-ai-step-by-step--complete-guide-2026\"\u003eHow to Automate Tasks with AI Step by Step — Complete Guide [2026]\u003c/h1\u003e\n\u003cp\u003eImagine waking up to find that your weekly report was drafted, your client follow-ups were sent, your social media posts were scheduled, and your inbox was sorted — all while you slept. That\u0026rsquo;s not a fantasy in 2026. It\u0026rsquo;s a Wednesday morning for professionals who\u0026rsquo;ve built AI automation workflows.\u003c/p\u003e\n\u003cp\u003eBut here\u0026rsquo;s the challenge: most guides to AI automation are written for developers. They assume you know what an API is, can write Python, and enjoy setting up webhooks for fun. This guide isn\u0026rsquo;t for them. This is for the knowledge worker, freelancer, or small business owner who wants to eliminate repetitive tasks with AI — without writing a single line of code.\u003c/p\u003e","title":"How to Automate Tasks with AI Step by Step"},{"content":"※本記事にはアフィリエイト広告が含まれています。\nHow to Build an Emergency Fund Fast — Complete Guide [2026] Job loss. A $3,000 car repair. A medical bill that insurance didn\u0026rsquo;t fully cover. An unexpected move. Life has a way of sending financial surprises — and those surprises hit harder when you\u0026rsquo;re not prepared.\nAn emergency fund is the single most important financial safety net you can build. It\u0026rsquo;s not glamorous, it doesn\u0026rsquo;t earn 15% annual returns, and financial media rarely discusses it. But study after study shows that people with emergency funds make dramatically better financial decisions, take on less debt, and experience significantly less financial stress.\nHere\u0026rsquo;s the tough truth: nearly 40% of Americans say they couldn\u0026rsquo;t cover a $400 unexpected expense without borrowing money or selling something. That means almost half the population is one bad day away from a financial spiral.\nYou don\u0026rsquo;t have to be one of them.\nIn this guide, you\u0026rsquo;ll learn:\nExactly how much you need in your emergency fund Where to keep your emergency fund to earn the most interest Proven strategies to build your fund fast — even on a tight budget How to stay motivated when progress feels slow What qualifies as a \u0026ldquo;real\u0026rdquo; emergency (and what doesn\u0026rsquo;t) What to do once your emergency fund is fully funded What Is an Emergency Fund? Definition \u0026amp; How It Works An emergency fund is a dedicated pool of cash set aside exclusively for genuine financial emergencies — unexpected expenses or income disruptions that would otherwise derail your financial life.\nThink of it as the financial equivalent of insurance: you hope you never need it, but when you do, it\u0026rsquo;s the difference between a minor setback and a full-blown crisis.\nKey characteristics:\nLiquid: Available quickly (same day or within 1–3 business days) Safe: Not subject to investment risk — don\u0026rsquo;t invest your emergency fund in stocks Accessible: Separate from daily spending money so you don\u0026rsquo;t accidentally spend it Appropriately sized: Large enough to cover real emergencies, not every small inconvenience The right amount depends on your personal situation, but the standard recommendation is 3–6 months of essential living expenses.\nPros and Cons of Having an Emergency Fund Pros:\nPrevents financial emergencies from becoming debt crises Reduces reliance on high-interest credit cards or personal loans Provides psychological security and reduces financial anxiety Allows you to make better long-term financial decisions (e.g., don\u0026rsquo;t sell investments at a loss in an emergency) Enables you to take calculated career risks (change jobs, start a business) Acts as insurance against income disruption Cons:\nCash earns less than long-term stock market investments Requires discipline to not dip into it for non-emergencies Can feel \u0026ldquo;slow\u0026rdquo; to build compared to investing Money sitting in savings while high-interest debt exists creates an opportunity cost (though emergency fund takes priority over investing) How Much Do You Need? Emergency Fund Sizing Guide Your Situation Recommended Emergency Fund Single income, stable job, no dependents 3 months of expenses Single income, variable income (freelance/commission) 6 months of expenses Dual income household, stable jobs 3 months of expenses Single income, dependents (children, elderly parents) 6 months of expenses Self-employed or business owner 6–12 months of expenses Irregular income (seasonal work, gig economy) 6–9 months of expenses High-risk industry (tech layoffs, construction) 6 months of expenses Health condition or family health needs 6 months + medical buffer How to calculate your 3–6 month target:\nAdd up your essential monthly expenses:\nRent/mortgage: $___ Utilities: $___ Groceries: $___ Transportation (car payment, gas, insurance): $___ Insurance premiums: $___ Minimum debt payments: $___ Essential phone/internet: $___ Total monthly essentials × 3 (or 6) = Your emergency fund target\nExample: If your essential monthly expenses total $3,500, a 3-month emergency fund = $10,500; a 6-month fund = $21,000.\nNote: Use essential expenses, not your full monthly spending. You don\u0026rsquo;t need to fund dining out and Netflix subscriptions during a financial emergency.\nRelated: Best High-Yield Savings Accounts 2026 Best Places to Keep Your Emergency Fund: Options Compared Account Type APY (2026 est.) Liquidity Risk Best For High-Yield Savings Account 4–5% 1–3 business days None (FDIC) Best overall emergency fund location Money Market Account (bank) 4–4.75% Same day (debit/checks) None (FDIC) Want check/debit access Money Market Fund (brokerage) 4.5–5% Same day (sell) Very low (SIPC) Already investing at a brokerage Traditional savings account 0.01–0.10% Immediate None (FDIC) Avoid — terrible return Checking account ~0% Immediate None (FDIC) Only for 1 month of immediate buffer CDs 4.5–5% (locked) Low (penalty to exit) None (FDIC) CD ladder — advanced strategy I-Bonds Inflation-adjusted Low (1-year lockup) None (US Gov) Inflation protection — advanced add-on Our recommendation: A high-yield savings account (HYSA) is the best home for most emergency funds. You earn 4–5% APY with zero risk, full FDIC insurance, and access to your money within 1–3 days. The small delay in access is actually a feature — it adds a small friction against impulsive withdrawals.\nFind the best HYSA rates today How to Choose: Key Factors What to Look For 1. Keep it separate from spending accounts Your emergency fund should be in a separate account — ideally at a different bank from your checking account. Out of sight, harder to touch. The small psychological barrier of having to initiate a transfer prevents impulsive spending.\n2. Don\u0026rsquo;t invest it The stock market can drop 30–40% in a recession — precisely when you\u0026rsquo;re most likely to need your emergency fund. Cash in a HYSA earning 4.5% is the right choice for emergency savings, even though long-term stock market returns are higher.\n3. Make it automatic The fastest way to build an emergency fund is to automate contributions. Set up a weekly or monthly automatic transfer from your checking account to your HYSA. Treat it like a non-negotiable bill.\n4. Label the account Many banks let you name savings accounts. Name yours \u0026ldquo;Emergency Fund\u0026rdquo; (or \u0026ldquo;Do Not Touch\u0026rdquo;). This psychological framing genuinely reduces impulsive spending from the account.\nCommon Mistakes to Avoid Mistake 1: Using a credit card as your \u0026ldquo;emergency fund\u0026rdquo; A credit card is not an emergency fund — it\u0026rsquo;s debt with interest. Using a credit card for emergencies means paying 20%+ interest on top of whatever crisis you\u0026rsquo;re dealing with. A real emergency fund is cash you already own.\nMistake 2: Skipping the emergency fund to invest faster Investing without an emergency fund means you\u0026rsquo;ll likely be forced to sell investments at the worst time (during market downturns when emergencies tend to cluster). Build the emergency fund first, then invest.\nMistake 3: Setting the target too low A $1,000 emergency fund is a start, but it\u0026rsquo;s not a finish line. A car repair plus an ER visit can easily exceed $1,000. Aim for the full 3–6 month target.\nMistake 4: Raiding it for non-emergencies The definition of \u0026ldquo;emergency\u0026rdquo; matters. A car breakdown is an emergency. A concert ticket, holiday shopping, or a vacation is not. Depleting your emergency fund for discretionary spending defeats the entire purpose.\nMistake 5: Never replenishing after using it After you use your emergency fund, rebuild it before resuming normal investing contributions. A depleted emergency fund is nearly as risky as no emergency fund.\nStep-by-Step Guide: Build Your Emergency Fund Fast Emergency Fund Savings Milestones Your path from $0 to full 3–6 month coverage $1K Starter Fund opened First win! 1 mo 1 Month Basic buffer Auto-transfer set 3 mo 3 Months Standard goal Most people stop here 6 mo 6 Months Fully funded Start investing! Best home for your fund: High-Yield Savings Account 4–5% APY • FDIC insured • Access in 1–3 days Step 1: Open a high-yield savings account If you don\u0026rsquo;t already have one, open a HYSA today. Top options: SoFi (4.60% APY, $0 minimum), Marcus by Goldman Sachs (4.50%, $0 minimum), or Ally Bank (4.35%, $0 minimum). This takes about 10 minutes online.\nStep 2: Calculate your target Use the formula above: essential monthly expenses × 3 (or 6). Write down your specific target number. Concrete goals are more motivating than vague ones.\nStep 3: Find your initial \u0026ldquo;seed\u0026rdquo; amount Look for money you can move immediately:\nCheck your checking account for excess funds above 1 month of expenses Sell items you don\u0026rsquo;t need (Facebook Marketplace, eBay, Decluttr) Apply a tax refund, bonus, or cash gift directly to your emergency fund Move savings from a low-interest account to your new HYSA Aim to put $500–$1,000 in your HYSA as a seed deposit. This creates momentum.\nStep 4: Set up automatic transfers Calculate how much you can consistently save per month toward your emergency fund. Even $100/month is meaningful. Set up a recurring automatic transfer from your checking account to your HYSA on the day after each paycheck arrives.\nAcceleration timeline examples (starting from $0, contributing monthly):\nMonthly Contribution Time to $5,000 Time to $10,000 $100 4.1 years 8.3 years $200 2 years 4.1 years $300 1.4 years 2.8 years $500 10 months 20 months $750 6.7 months 13.3 months $1,000 5 months 10 months Step 5: Find extra money to accelerate contributions Several strategies to speed up emergency fund growth:\nSide hustle income: Dedicate 100% of any side income to the emergency fund until it\u0026rsquo;s fully funded Expense audit: Cancel unused subscriptions; redirect the savings to your HYSA Windfalls: Apply tax refunds, work bonuses, and gifts directly to your emergency fund Cash back rewards: Redeem credit card cash back directly to your savings No-spend weeks: Commit to 1–2 no-spend weeks per month and transfer what you would have spent Step 6: Protect it from yourself\nDon\u0026rsquo;t link your HYSA as overdraft protection for your checking account Remove the app from your phone\u0026rsquo;s home screen (less visibility = less temptation) Set up account alerts for withdrawals over a threshold Tell a trusted friend or partner about your goal for accountability Step 7: Celebrate milestones $1,000 is a milestone. $2,500 is a milestone. Full funding is a major financial accomplishment. Celebrate milestones in low-cost ways to reinforce the positive behavior.\nFrequently Asked Questions Q: Should I build an emergency fund or pay off debt first? A: Build a small starter emergency fund of $1,000 first — regardless of debt. This prevents you from re-accumulating debt every time an unexpected expense hits. Then aggressively pay down high-interest debt (credit cards 20%+). Once high-interest debt is gone, fully fund your emergency fund before focusing on low-interest debt.\nQ: Is $1,000 enough for an emergency fund? A: $1,000 is a good starting point and provides meaningful protection. But it\u0026rsquo;s not a complete emergency fund — medical bills, major car repairs, or a month of unemployment typically exceed $1,000. Treat $1,000 as a \u0026ldquo;starter\u0026rdquo; emergency fund and work toward 3–6 months of expenses.\nQ: Should I keep my emergency fund invested in index funds? A: No. Emergency funds should not be invested in stocks or bonds. The stock market can drop 30–50% when your need is highest — recessions and job losses often coincide with market crashes. Keep your emergency fund in a FDIC-insured HYSA.\nQ: What counts as an emergency? A: Genuine emergencies include: sudden job loss or income reduction, unexpected medical/dental bills, essential car or home repairs (not upgrades), a family emergency requiring immediate travel, and unexpected pet emergencies. Non-emergencies include: planned vacations, holiday gifts, new electronics, clothing, or anything you could have anticipated and budgeted for.\nQ: My emergency fund is fully funded — what should I do with the extra money? A: Redirect automatic contributions to: (1) Maximize Roth IRA contributions, (2) Increase 401k contributions, (3) Pay down moderate-interest debt, (4) Save for specific goals (down payment, car, etc.) in a separate HYSA. Don\u0026rsquo;t let fully-funded status become an excuse to stop saving entirely.\nQ: Should I have a separate emergency fund and a general savings account? A: Many financial experts recommend keeping emergency funds separate from other savings goals (down payment, vacation, etc.) — even if all are at the same bank. Separate labeled accounts help you track progress and resist raiding the emergency fund for non-emergency goals.\nQ: My income is irregular — how should I size my emergency fund? A: For freelancers, self-employed individuals, or those with variable income, aim for 6–12 months of expenses rather than 3–6 months. The larger buffer protects against both unexpected expenses and income gaps between clients or contracts.\nBuild Your Savings While Growing Your Wealth Once your emergency fund is fully funded, put your surplus to work. Open a Rakuten Securities account to begin investing your extra savings in low-cost index funds and start building long-term wealth beyond the emergency fund.\nConclusion: Your Emergency Fund Is Your Financial Foundation Every sophisticated financial strategy — investing in index funds, maxing your Roth IRA, building passive income — rests on one foundation: having cash reserves to weather the unexpected without derailing your progress.\nBuilding an emergency fund isn\u0026rsquo;t exciting. It won\u0026rsquo;t go viral on financial Twitter. But it will protect every other financial goal you have from being destroyed by the first unexpected $2,000 expense.\nStart today. Open a high-yield savings account, deposit whatever you can, and set up an automatic transfer. Even $50/week is $2,600 after a year. Three years later, you\u0026rsquo;ll have a fully funded emergency fund that gives you genuine financial security — and the freedom to take risks, make career moves, and invest with confidence.\nTake action now:\nOpen a SoFi High-Yield Savings account — $0 minimum, top rates Start with Marcus by Goldman Sachs — simple, no fees Try Ally Bank — consistently excellent HYSA with great app Related: Best Budgeting Apps 2026 — Find Money to Save Faster Disclaimer: This article is for informational purposes only and does not constitute financial advice. Investment decisions should be made based on your individual circumstances. Please consult a qualified financial advisor before making investment decisions. Information is current as of the publication date — verify details on official websites.\nDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nRelated Tools \u0026amp; Articles Calculate loan payments and payoff timeline → Loan Calculator Check your BMI and healthy weight range → BMI Calculator Emergency Fund Calculator — Calculate your ideal fund size and see your savings timeline Savings Goal Calculator — Calculate how long to reach any savings target Budget Planner — Find money to save faster with a personalized budget Compound Interest Calculator — See how your savings grow over time Calculate your mortgage payment → Mortgage Calculator See how inflation affects your money → Inflation Calculator How to Pay Off Debt Fast: Best Strategies 2026 How to Start Investing with $100 in 2026 Passive Income Ideas That Actually Work 2026 This article contains affiliate links. We may earn a commission at no extra cost to you.\nRelated Templates Take control of your finances with these tools:\nSmart Budget Tracker (Excel Template) — Track income, expenses, and savings goals Side Hustle Starter Kit 2026 — Build additional income streams ","permalink":"https://productivity-works.com/posts/how-to-build-an-emergency-fund-fast/","summary":"\u003cp\u003e※本記事にはアフィリエイト広告が含まれています。\u003c/p\u003e\n\u003ch1 id=\"how-to-build-an-emergency-fund-fast--complete-guide-2026\"\u003eHow to Build an Emergency Fund Fast — Complete Guide [2026]\u003c/h1\u003e\n\u003cp\u003eJob loss. A $3,000 car repair. A medical bill that insurance didn\u0026rsquo;t fully cover. An unexpected move. Life has a way of sending financial surprises — and those surprises hit harder when you\u0026rsquo;re not prepared.\u003c/p\u003e\n\u003cp\u003eAn emergency fund is the single most important financial safety net you can build. It\u0026rsquo;s not glamorous, it doesn\u0026rsquo;t earn 15% annual returns, and financial media rarely discusses it. But study after study shows that people with emergency funds make dramatically better financial decisions, take on less debt, and experience significantly less financial stress.\u003c/p\u003e","title":"How to Build an Emergency Fund Fast (2026)"},{"content":" How to Make Money With AI in 2026 AI isn\u0026rsquo;t just changing how we work — it\u0026rsquo;s creating entirely new income streams that didn\u0026rsquo;t exist three years ago. Freelancers are charging premium rates for AI-augmented services. Entrepreneurs are building automated businesses with minimal overhead. Regular people are earning hundreds to thousands of dollars monthly using AI tools they can learn in a weekend.\nThis guide covers 15 specific, realistic ways to make money with AI in 2026 — with honest earning potential, startup requirements, and exactly how to begin.\nThe AI Economy in 2026: Why the Opportunity Is Real Here\u0026rsquo;s what\u0026rsquo;s actually happening:\nBusinesses desperately need AI help. Most companies know AI is important but don\u0026rsquo;t know how to implement it. That gap is your opportunity. AI dramatically amplifies individual output. A skilled freelancer with AI tools can produce 3-5x the work. This means more clients, higher rates, or both. The tools are increasingly cheap and accessible. Many of the strategies below require only a $20-50/month AI subscription to start. Most people aren\u0026rsquo;t doing this yet. Despite the hype, actual implementation of AI in small businesses is still early. You\u0026rsquo;re not late. Strategy 1: AI-Assisted Content Writing Earning potential: $1,000-8,000/month Startup cost: $20/month (ChatGPT Plus or Claude Pro) Time to first dollar: 1-4 weeks\nBusinesses need blog posts, newsletters, website copy, product descriptions, and social media content — constantly. With AI assistance, a capable writer can produce 3-5x more content per hour than traditional methods.\nHow it works:\nPosition yourself as a content writer (not an \u0026ldquo;AI writer\u0026rdquo; — the value is in your editorial judgment) Use AI for first drafts, research, outline generation, and variations Apply your expertise, brand knowledge, and editing to produce final-quality content Charge professional rates ($50-200/hour or $100-500 per article) The key insight: Clients aren\u0026rsquo;t paying for words — they\u0026rsquo;re paying for results (traffic, conversions, engagement). AI lets you focus more time on strategy and quality, not typing.\nGetting started: Create 3 sample pieces in your chosen niche. Post on LinkedIn and reach out to content marketing agencies.\nStrategy 2: AI Prompt Engineering and Consulting Earning potential: $50-300/hour or $500-5,000 per project Startup cost: $20-50/month in AI subscriptions Time to first dollar: 2-6 weeks\nEvery business is trying to implement AI, and most are doing it badly — using generic prompts, getting mediocre results, and concluding \u0026ldquo;AI doesn\u0026rsquo;t work for us.\u0026rdquo; Prompt engineers solve this.\nWhat you\u0026rsquo;d do:\nAudit a company\u0026rsquo;s current AI usage Design custom prompt systems and workflows for their specific needs Train their team on effective AI usage Build custom GPTs or AI assistants for specific business functions This is not a technical role. You need deep understanding of language models and prompt design — not coding skills. Many successful prompt engineers have backgrounds in writing, marketing, or operations.\nGetting started: Spend 4 weeks mastering prompts for 2-3 specific business functions (email, customer service, content). Offer free audits to 5 local businesses. Turn results into case studies.\nRelated: AI Prompt Engineering Tips for Beginners 2026 Strategy 3: AI-Generated Digital Products Earning potential: $500-10,000+/month (passive) Startup cost: $20-50/month Time to first dollar: 4-8 weeks\nAI dramatically reduces the time to create digital products: ebooks, templates, courses, printables, prompt packs. What previously took weeks now takes days or hours.\nHigh-demand AI-assisted digital products:\nNiche ebooks and guides (sold on Gumroad, Payhip, or Amazon KDP) Prompt template packs for specific industries Notion or spreadsheet templates Social media content calendars and templates Mini-courses on practical topics The income model: Create once, sell indefinitely. Platforms like Payhip, Gumroad, and Amazon KDP handle payment and delivery automatically.\nWhat AI doesn\u0026rsquo;t replace: Your expertise, market knowledge, and the ability to identify what people actually want to buy. AI is the production tool; human judgment is the product strategy.\nGetting started: Identify one specific problem in a niche you know. Use AI to write a 5,000-10,000 word guide solving that problem. Design a cover with Canva. List on Gumroad or Payhip for $9-29.\nStrategy 4: AI-Enhanced Freelance Services Earning potential: $2,000-15,000/month Startup cost: $20-100/month Time to first dollar: 1-3 weeks\nThis is the fastest path to AI income for most people: take a service you already provide (or can learn quickly) and use AI to deliver it better, faster, or at higher quality.\nServices that AI dramatically enhances:\nGraphic design — Midjourney, Adobe Firefly for asset generation Video editing — AI audio cleanup, captioning, background removal Social media management — AI content generation, scheduling, analytics Email marketing — AI copywriting, subject line optimization Research and analysis — AI-assisted data gathering and synthesis Resume and cover letter writing — AI draft, expert editing The formula: Your expertise + AI tools = premium service at sustainable volume.\nGetting started: List on Upwork, Fiverr, or Contra. Be specific about your niche (e.g., \u0026ldquo;I create 30 days of social media content for wellness brands in 48 hours\u0026rdquo;).\nStrategy 5: AI Automation Consulting Earning potential: $100-300/hour, $2,000-20,000 per project Startup cost: $50-100/month in tools Time to first dollar: 4-8 weeks\nZapier, Make.com, and n8n connect AI to business workflows. Someone who can build these automations is extremely valuable to small businesses.\nExample automations businesses pay for:\nAutomatically summarize and route customer emails Generate first-draft responses to common inquiries Create content from form submissions Sync CRM data and trigger personalized outreach Monitor competitor mentions and generate reports This requires some technical comfort — but not coding. These are no-code/low-code platforms with drag-and-drop interfaces.\nGetting started: Get certified in Zapier (free certification available). Build 3 demo automations in industries you understand. Pitch local businesses with a free audit.\nStrategy 6: YouTube with AI Assistance Earning potential: $500-15,000+/month (long-term) Startup cost: $20-50/month Time to first dollar: 3-6 months (for meaningful AdSense)\nAI doesn\u0026rsquo;t create viral YouTube channels — creativity and consistency do. But AI massively reduces the production burden.\nHow AI helps YouTube creators:\nScript writing and structure Thumbnail concepts (generate with Midjourney, finalize in Canva) Video titles and description optimization Transcription and subtitle generation Comment response drafts Repurposing video content into articles, tweets, and shorts The most realistic path: Niche tutorials or explainer content where AI can help research and script, while your on-camera delivery provides the authentic element.\nWarning: Fully AI-generated faceless channels have become saturated. The opportunity is in using AI to enhance human-led content, not replace it.\nStrategy 7: AI-Assisted Blogging and SEO Earning potential: $500-20,000+/month (long-term passive) Startup cost: $20-100/month Time to first dollar: 3-9 months\nBlogging remains one of the most reliable long-term passive income models. AI has dramatically changed the economics: what previously required 8 hours per post now takes 2-3 hours, allowing faster site growth.\nThe model:\nBuild a niche blog on a topic with commercial intent Use AI to research, outline, and draft articles Add your expertise and optimize with SEO tools Monetize via AdSense, affiliate marketing, and digital products Realistic income ranges:\n6 months: $100-500/month 12 months: $500-3,000/month 24 months: $2,000-20,000+/month (if you pick the right niche) Best niches in 2026: Personal finance, health and fitness, software tools, home improvement, career development.\nGetting started: Choose a specific niche. Buy a domain ($12/year). Set up WordPress with a free theme. Publish 3 articles per week for 6 months. Monetize from month 3 onward.\nStrategy 8: Social Media Management with AI Earning potential: $1,000-5,000/month per client Startup cost: $50-100/month in tools Time to first dollar: 2-4 weeks\nSmall businesses want a social media presence and don\u0026rsquo;t have time to maintain it. AI makes it possible for one person to manage 5-10 clients effectively.\nWhat\u0026rsquo;s included in social media management:\nContent strategy and calendar planning Writing and designing posts (AI + Canva) Scheduling (Buffer, Hootsuite, Later) Community management (AI-drafted responses) Monthly performance reporting What to charge: $500-2,000/month per client for a basic social media package. With AI tools, 5 clients at $800/month = $4,000/month.\nGetting started: Manage one account for free (friend, family, local business) for 2 months. Document results. Use as case study to get paying clients.\nStrategy 9: AI-Powered Etsy and Print-on-Demand Earning potential: $200-3,000+/month Startup cost: $25-50/month Time to first dollar: 2-6 weeks\nAI image generators (Midjourney, Adobe Firefly, Stable Diffusion) have transformed the print-on-demand market. Unique, AI-generated designs sell on:\nEtsy (prints, wall art, digital downloads) Redbubble and Society6 (t-shirts, mugs, phone cases) Amazon KDP (low-content books: journals, planners) Merch by Amazon (apparel) What sells well:\nNiche humor designs (specific professions, hobbies) Aesthetic wall art for specific aesthetics (cottagecore, dark academia) Custom occasion designs (wedding, birthday, graduation) Inspirational and motivational prints The key: Specificity wins. \u0026ldquo;Funny nurse print\u0026rdquo; competes with thousands. \u0026ldquo;Funny NICU nurse night shift coffee mug design\u0026rdquo; finds its audience.\nSee our related guide: Passive Income with Digital Products 2026 Strategy 10: AI Tutoring and Education Earning potential: $30-100/hour Startup cost: $20/month Time to first dollar: 1-2 weeks\nThe demand for people who can teach others to use AI tools effectively is exploding. Businesses, schools, and individuals all need AI training.\nWhat you could teach:\n\u0026ldquo;How to use ChatGPT for your business\u0026rdquo; workshops One-on-one AI onboarding for professionals AI productivity training for corporate teams Online courses on specific AI tools or workflows Getting started: Teach what you know. If you\u0026rsquo;ve mastered AI tools for a specific use case (real estate, law, education, marketing), you\u0026rsquo;re already qualified to teach that audience.\nStrategy 11: AI-Assisted Podcast Production Earning potential: $1,000-5,000/month per client Startup cost: $50-100/month Time to first dollar: 3-6 weeks\nPodcast production agencies using AI can deliver:\nAutomated transcription (Otter.ai, Descript) AI-generated show notes and summaries Clip selection for social media AI-cleaned audio SEO-optimized episode titles and descriptions What to charge: $500-2,000/month per podcast for full production services.\nStrategy 12: Selling AI-Generated Stock Content Earning potential: $200-2,000/month (passive) Startup cost: $30-60/month Time to first dollar: 1-3 months\nText-based stock:\nPrompt packs on Etsy or Gumroad AI-written blog post templates Swipe files and email templates Visual stock:\nAdobe Stock, Shutterstock, and iStock now accept AI-generated images with disclosure Niche illustration styles in consistent series sell well Strategy 13: AI Voiceover and Audio Production Earning potential: $500-5,000/month Startup cost: $30-100/month Time to first dollar: 2-4 weeks\nAI voice tools (ElevenLabs, Play.ht, Murf) produce natural-sounding voiceovers. Combined with audio editing skills, you can offer:\nAudiobook narration Corporate explainer video voiceover Podcast ad reads E-learning course narration Note: Disclose AI involvement. Many platforms require it, and transparency builds trust.\nStrategy 14: AI-Assisted Resume and Career Services Earning potential: $100-500 per client Startup cost: $20/month Time to first dollar: 1 week\nAI dramatically improves resume and cover letter quality. Positioning yourself as a career coach who uses AI tools to optimize job search materials is increasingly in demand.\nService bundle example:\nAI-optimized resume ($150) Cover letter template ($75) LinkedIn profile optimization ($100) Interview prep guide ($50) Total package: $375 per client. 10 clients/month = $3,750.\nRelated: AI Resume Optimization for Job Search 2026 Strategy 15: Build and Sell AI Tools and Micro-SaaS Earning potential: $1,000-50,000+/month Startup cost: $50-200/month Time to first dollar: 2-6 months\nWith no-code tools (Bubble, Glide, Webflow) combined with AI APIs (OpenAI, Anthropic, Google), non-technical entrepreneurs can build and sell simple AI-powered tools:\nNiche-specific AI assistants AI document analyzers Custom chatbots for specific industries Specialized prompt interfaces The model: Build a simple tool that solves a specific problem. Charge $15-50/month subscription. 200 subscribers = $3,000-10,000/month.\nAI Income Methods — Speed vs Potential Organized by time to first dollar and long-term earning potential → Time to First Dollar (Faster to Slower) ↑ Earning Potential 1–2 wks 1–2 mo 3–6 mo 6–24 mo AI Freelance $2K–15K/mo AI Writing $1K–8K/mo Social Media $1K–5K/mo Automation Consulting $2K–20K/proj Digital Products $500–10K+/mo AI Blogging \u0026amp; SEO $500–20K+/mo YouTube AI-Assisted $500–15K+/mo Service / Content Consulting / Products Platform-based Bubble size ≈ passivity The Honest Reality Check Not all of these will work for you. The best income stream depends on your:\nExisting skills and knowledge Available time (part-time vs full-time) Risk tolerance Technical comfort level The fastest paths to income:\nAI-enhanced freelance services (existing skills + AI tools) Content writing with AI assistance Social media management The highest long-term income potential:\nBlogging and SEO content sites Digital products Micro-SaaS tools YouTube The most unique opportunities:\nAI automation consulting Prompt engineering AI training and education Getting Started: Your First 30 Days | Days 1-7 | Learn your chosen AI tools thoroughly | | Days 8-14 | Create 3-5 portfolio pieces or product prototypes | | Days 15-21 | Set up your platform (Upwork, Gumroad, LinkedIn) | | Days 22-30 | Reach out to 20 potential clients or publish your first products |\nThe people making real money with AI in 2026 started months ago. The second-best time to start is today.\nStake Your Claim Online Before You Scale Every AI income strategy works better when you have your own website and domain. Get your domain on Onamae.com — register a professional domain today and build the online hub that turns your AI skills into a lasting business.\nResources to Accelerate Your AI Income Journey Our AI Income Starter Pack includes proven prompt templates, client pitch scripts, digital product blueprints, and workflow guides for the top 5 strategies in this article.\nRelated Reading:\nBest AI Tools for Small Business 2026 AI Tools for Freelancers to Earn More 2026 Passive Income with Digital Products 2026 Side Hustles with AI Tools 2026 Related Tools Pick colors and convert between formats → Color Picker Calculate your ideal freelance hourly rate → Freelance Rate Calculator Estimate taxes on your AI income → Side Hustle Tax Calculator See your 2026 federal tax bracket and effective rate → Tax Bracket Calculator Calculate your take-home pay → Salary Calculator See how reinvested income compounds → Compound Interest Calculator Related Articles 15 Side Hustles You Can Start with AI Tools Today AI Tools for Freelancers to Earn More 2026 7 Passive Income Ideas Using Digital Products Best AI Tools for Small Business 2026: The Complete Roundup How to Start Freelancing With No Experience 2026: Full Guide This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/how-to-make-money-with-ai-2026/","summary":"\u003chr\u003e\n\u003ch2 id=\"how-to-make-money-with-ai-in-2026\"\u003eHow to Make Money With AI in 2026\u003c/h2\u003e\n\u003cp\u003eAI isn\u0026rsquo;t just changing how we work — it\u0026rsquo;s creating entirely new income streams that didn\u0026rsquo;t exist three years ago. Freelancers are charging premium rates for AI-augmented services. Entrepreneurs are building automated businesses with minimal overhead. Regular people are earning hundreds to thousands of dollars monthly using AI tools they can learn in a weekend.\u003c/p\u003e\n\u003cp\u003eThis guide covers 15 specific, realistic ways to make money with AI in 2026 — with honest earning potential, startup requirements, and exactly how to begin.\u003c/p\u003e","title":"How to Make Money With AI in 2026: 15 Realistic Ways That Work"},{"content":"※本記事にはアフィリエイト広告が含まれています。\nHow to Pay Off Debt Fast Strategies — Complete Guide [2026] Debt is the invisible tax on your future. Every dollar you pay in interest is a dollar that doesn\u0026rsquo;t go toward your emergency fund, your retirement account, your home down payment, or simply enjoying your life. In the U.S., the average household carries over $7,000 in credit card debt alone — at interest rates that often exceed 20% APY.\nBut here\u0026rsquo;s what nobody tells you: there is a mathematically optimal way to pay off debt, and it\u0026rsquo;s not complicated. There are also psychological hacks that help people stay motivated and follow through, even when the numbers feel overwhelming. The combination of the right strategy and the right mindset has helped millions of people pay off thousands of dollars faster than they thought possible.\nIn this guide, you\u0026rsquo;ll learn:\nThe two most powerful debt payoff strategies (and when to use each) How to list and organize all your debts in one clear framework The math of how much faster aggressive payoff works vs. minimum payments Debt consolidation: when it helps and when it doesn\u0026rsquo;t Step-by-step action plan to start paying off debt this week How to stay motivated when payoff timelines feel long What to do once you\u0026rsquo;re debt-free Understanding Debt: The Basics Definition \u0026amp; How It Works Debt is borrowed money that you owe to a lender, with interest accruing over time until repaid. Different types of debt carry very different interest rates and psychological weights.\nTypes of debt by priority:\nDebt Type Typical APR Priority to Pay Off Payday loans 200–400% Extremely urgent Credit cards 18–29% Very high Personal loans 8–20% High Medical debt 0–6% Moderate (often negotiable) Auto loans 4–10% Moderate Student loans (private) 5–12% Moderate-high Student loans (federal) 4–8% Lower (income-based repayment available) Home equity loans 6–9% Lower Mortgage 5–8% Lowest (good debt) The interest rate is the critical number: debt costing 20% APR destroys wealth faster than almost any investment can create it. Paying off a 22% APR credit card is an instant, guaranteed 22% return on your money.\nPros and Cons of Different Payoff Approaches Aggressive payoff (snowball/avalanche) — Pros:\nSaves the most money in interest over time Frees up cash flow as balances are eliminated Creates financial freedom and reduced stress Psychological momentum builds over time Aggressive payoff — Cons:\nRequires reducing other spending or finding extra income Can feel slow when balances are large Requires consistent discipline over months or years Minimum payments only — the reality:\nA $5,000 credit card balance at 22% APR, paying only the minimum (~$100/month), takes 8+ years to pay off and costs over $4,700 in interest — nearly doubling the original balance. Paying $300/month on the same balance eliminates it in under 2 years and costs under $800 in interest. The Two Main Debt Payoff Strategies Compared Strategy 1: The Debt Avalanche (Best Mathematically) How it works: List all debts by interest rate, highest to lowest. Pay minimum payments on all debts except the one with the highest interest rate — put every extra dollar toward that one. Once it\u0026rsquo;s paid off, \u0026ldquo;avalanche\u0026rdquo; those payments onto the next highest-rate debt.\nExample:\nDebt Balance APR Min. Payment Credit card A $3,500 24% $70 Credit card B $1,200 19% $24 Auto loan $8,000 7% $180 Student loan $15,000 5% $150 With $500/month to put toward debt:\nPay minimums on credit card B, auto loan, and student loan: $354 Put remaining $146 toward credit card A (highest rate) Once credit card A is paid, roll that full payment ($70 + $146 = $216) onto credit card B Continue until debt-free Avalanche wins on math: Saves the maximum amount of interest paid over time. Best for disciplined people who won\u0026rsquo;t get discouraged when progress feels slow.\nStrategy 2: The Debt Snowball (Best Psychologically) How it works: List all debts by balance, smallest to largest (ignoring interest rate). Pay minimums on everything except the smallest balance — put every extra dollar toward it. When it\u0026rsquo;s paid off, roll that payment onto the next-smallest debt.\nExample (same debts as above, snowball order):\nDebt Balance APR Min. Payment Credit card B $1,200 19% $24 Credit card A $3,500 24% $70 Auto loan $8,000 7% $180 Student loan $15,000 5% $150 With $500/month:\nPay minimums on the last three: $400 Put $100 extra toward credit card B Credit card B is paid off in ~9–10 months (quick win!) Roll $124/month onto credit card A Continue building the \u0026ldquo;snowball\u0026rdquo; Snowball wins on psychology: Dave Ramsey popularized this approach. The quick wins from eliminating small balances first build momentum and confidence. Research shows that people using the snowball method are more likely to stick with their payoff plan.\nAvalanche vs Snowball: Which Path to Debt Freedom? Debt Avalanche List debts by Highest Interest Rate First Credit Card A — 24% APR ← Attack first Credit Card B — 19% APR Auto Loan — 7% APR Student Loan — 5% APR Saves the most money in interest Best for: disciplined, data-driven people Debt Snowball List debts by Smallest Balance First Credit Card B — $1,200 ← Attack first Credit Card A — $3,500 Auto Loan — $8,000 Student Loan — $15,000 Quick wins build momentum \u0026amp; motivation Best for: motivation-driven people Which Strategy Should You Choose? Choose the Avalanche if:\nYou\u0026rsquo;re highly motivated and won\u0026rsquo;t lose steam Your highest-rate debt is not your smallest balance You want to save the most money in interest overall You prefer data-driven approaches Choose the Snowball if:\nYou need psychological wins to stay motivated You have several small debts you can eliminate quickly You\u0026rsquo;ve tried aggressive payoff before and given up The emotional relief of closing accounts matters to you The truth: The best strategy is the one you\u0026rsquo;ll actually follow. A perfectly optimized Avalanche plan you abandon after 3 months saves less money than an imperfect Snowball you stick with for 3 years.\nDebt Payoff Strategies Compared: Full Options Strategy Best For Interest Saved Difficulty Notes Debt Avalanche Math-focused, disciplined Maximum Medium Pay highest APR first Debt Snowball Motivation-focused Good Medium Pay smallest balance first Debt Consolidation Loan Multiple high-rate debts High (if rate is lower) Medium Requires good credit Balance Transfer Card Credit card debt specifically Very high (0% intro period) Medium Watch transfer fees, expiry date HELOC/Home Equity Loan Homeowners with equity Very high (lowest rates) High Risk: your home is collateral Debt Management Plan Overwhelmed, need help Moderate Low (outsourced) Via nonprofit credit counseling Debt Settlement Severely delinquent debt Varies Low/risky Credit damage, tax implications Bankruptcy Truly unmanageable debt N/A N/A Last resort — major credit impact Compare debt consolidation loan rates How to Choose: Key Factors What to Look For 1. Calculate the true cost of your debt Before choosing a strategy, calculate how much each debt is actually costing you. Use an online debt payoff calculator to see the total interest you\u0026rsquo;ll pay over time — this number is often shocking and motivating.\n2. Is debt consolidation right for you? Debt consolidation makes sense if you can qualify for a personal loan or balance transfer card at a lower interest rate than your current debts. The math is simple:\nIf your credit card charges 24% APR and you can get a debt consolidation loan at 10% APR, consolidating saves 14 percentage points of interest — significant over $5,000+ of debt.\nConsolidation does NOT work if:\nYou consolidate and then rack up new credit card debt The fees eat up the interest savings The new loan term is so long that you pay more in total interest despite a lower rate 3. Evaluate your income and expense situation Before choosing how aggressively to pay debt, assess:\nIs your emergency fund funded ($500–$1,000 minimum)? Are you getting your full 401k employer match? After those two priorities, how much extra can you put toward debt? A $500/month extra debt payment makes an enormous difference. But you need that $500 to come from somewhere — which means the next step is finding it.\nCommon Mistakes to Avoid Mistake 1: Paying off low-interest debt aggressively while ignoring high-interest debt Paying extra on a 4% student loan while carrying a 22% credit card balance is mathematically irrational. Always attack highest-rate debt first (or at least simultaneously).\nMistake 2: Closing paid-off credit card accounts Closing accounts reduces your total available credit, which can temporarily hurt your credit score. Unless the card has an annual fee you want to avoid, keep paid-off accounts open with zero balance.\nMistake 3: Treating consolidation as a solution rather than a tool Debt consolidation is a rate reduction tool, not a cure for overspending. If you consolidate $10,000 of credit card debt and then spend your way back to $10,000 of credit card debt within two years, you\u0026rsquo;ve made your situation worse (now you have the consolidation loan plus the new card debt).\nMistake 4: Not negotiating with creditors Many people don\u0026rsquo;t realize that credit card issuers often negotiate interest rates with customers who call and ask. If you have a good payment history, call your card issuer and ask for a lower APR. Success rate is surprisingly high — and a 3–5% reduction is significant on a large balance.\nMistake 5: Making minimum payments and hoping for the best Minimum payments are designed to keep you in debt as long as possible. Paying just the minimum on a $7,000 credit card balance at 20% APR for years makes the card issuer tens of thousands of dollars — at your expense.\nStep-by-Step Guide: Pay Off Debt Fast Starting Today Step 1: List every debt you owe Create a complete debt inventory. Include:\nLender name Current balance Interest rate (APR) Minimum monthly payment Payoff date if paying minimums only Use a spreadsheet or app like Debt Payoff Planner, Undebt.it, or Tally.\nStep 2: Build your $1,000 starter emergency fund Before aggressively paying debt, save $1,000 in a HYSA for true emergencies. This prevents you from re-accumulating debt every time an unexpected expense hits during your payoff journey.\nStep 3: Get your 401k employer match If your employer matches 401k contributions, contribute at least enough to get the full match before putting extra toward debt. The employer match is an instant 50%–100% return — higher than almost any interest rate you\u0026rsquo;re paying.\nStep 4: Choose your payoff strategy Select Avalanche or Snowball (or a hybrid). If in doubt: try the Snowball for 90 days. The psychological momentum is real.\nStep 5: Find extra money to accelerate Every extra dollar toward debt is a guaranteed return equal to the interest rate. Strategies to find extra money:\nCancel unused subscriptions (audit with a budgeting app) Reduce dining out by 50% temporarily Sell items you don\u0026rsquo;t use (Facebook Marketplace, eBay) Pick up a side gig for a fixed-term sprint (6 months of extra income can pay off $5,000+) Apply windfalls (tax refunds, bonuses, gifts) 100% to debt Temporarily reduce retirement contributions above the employer match Step 6: Contact creditors about rate reduction Call each credit card issuer and ask: \u0026ldquo;I\u0026rsquo;ve been a loyal customer and I\u0026rsquo;d like to request a lower interest rate.\u0026rdquo; Even a 3–5% reduction helps. Also, if you have credit card debt, research 0% APR balance transfer cards.\nStep 7: Automate minimum payments on all debts Never miss a payment. Late fees and penalty APRs can be devastating. Set up autopay for minimums on all debts, then manually add extra payments on your target debt.\nStep 8: Track progress visually Create a debt payoff thermometer, use a tracking app, or simply watch your balances on a simple spreadsheet. Visual progress is motivating. Mark every $500 milestone.\nStep 9: Maintain motivation through the middle phase The \u0026ldquo;messy middle\u0026rdquo; — when initial excitement fades and payoff is still months away — is where most people quit. Strategies to maintain momentum:\nCelebrate milestones (every $1,000 paid off) Listen to personal finance podcasts (Dave Ramsey Show, ChooseFI, How To Money) Join an online community of debt payoff warriors Keep a visual reminder of your \u0026ldquo;why\u0026rdquo; (freedom, home purchase, quitting a job you hate) Frequently Asked Questions Q: Should I pay off debt or invest? A: It depends on the interest rate. The general framework:\nHigh-interest debt (15%+): Pay off before investing (beyond 401k match) Medium-interest debt (7–15%): Split between debt payoff and investing Low-interest debt (under 7%): Invest while making regular debt payments 401k employer match: Always capture this before aggressive debt payoff Q: Does paying off debt hurt your credit score? A: Paying off debt generally improves your credit score over time, as your credit utilization ratio decreases. However, closing paid-off credit card accounts can temporarily lower your score by reducing available credit. Pay off balances but keep accounts open.\nQ: What is the fastest legal way to pay off debt? A: Increase income + decrease expenses + apply every extra dollar to the highest-rate debt. The math is ruthlessly simple. A combination of a temporary side hustle, expense cuts, and putting windfalls toward debt can pay off significant balances in 1–3 years.\nQ: Is debt consolidation a good idea? A: Debt consolidation is a good idea if: (1) you can qualify for a meaningfully lower interest rate, (2) you will not accumulate new debt afterward, and (3) the fees don\u0026rsquo;t offset the interest savings. It\u0026rsquo;s a tool, not a solution. Consolidate only if you\u0026rsquo;ve addressed the spending habits that created the debt.\nQ: What are 0% APR balance transfer cards? A: Some credit cards offer 0% APR on balance transfers for an introductory period (usually 12–21 months). If you can pay off the transferred balance within the intro period, you pay zero interest. There\u0026rsquo;s typically a balance transfer fee of 3–5%. This can be a powerful tool for credit card debt specifically — but requires discipline to not use the card for new spending.\nQ: How do I deal with medical debt? A: Medical debt is often negotiable. Many hospitals have financial assistance programs for uninsured or underinsured patients. Call the billing department and ask about: financial assistance programs, a reduced lump-sum settlement, or an interest-free payment plan. Medical debt under $500 was removed from credit reports by the major bureaus in 2023 — verify current rules.\nQ: What if I can\u0026rsquo;t make minimum payments? A: Contact your creditors immediately. Most credit card companies offer hardship programs that temporarily reduce interest rates and minimum payments. Also contact a nonprofit credit counseling agency (NFCC member agencies offer free or low-cost counseling). Avoid for-profit debt settlement companies — they often charge high fees and damage your credit.\nFrom Debt-Free to Wealth-Building Once you\u0026rsquo;ve paid off high-interest debt, redirect those same payments into investments. Open a Rakuten Securities account so you\u0026rsquo;re ready to invest the moment your debt is gone — and turn your debt payments into wealth-building.\nConclusion: Debt Freedom Is Closer Than You Think The math of debt payoff is, ironically, the most motivating part once you run the numbers. The difference between making minimum payments and making aggressive extra payments isn\u0026rsquo;t just a faster payoff date — it\u0026rsquo;s thousands of dollars of interest saved and years of financial freedom gained.\nHere\u0026rsquo;s your action plan distilled:\nList all your debts today Build a $1,000 emergency buffer Get your full 401k employer match Choose Avalanche (math) or Snowball (motivation) Find $200–$500/month extra to throw at debt Automate minimums, manually attack the target debt Stay consistent — debt freedom is a marathon, not a sprint The average person who follows a structured debt payoff plan gets out of consumer debt within 2–5 years, depending on the balance and income. Every month you start earlier is compounding interest you keep in your own pocket.\nTools to accelerate your debt payoff:\nCompare balance transfer credit cards with 0% intro APR Check debt consolidation loan rates — see your offers in 2 minutes Get a free debt payoff plan from a nonprofit credit counselor Related: Best Budgeting Apps 2026 to Find Extra Money for Debt Payoff Disclaimer: This article is for informational purposes only and does not constitute financial advice. Investment decisions should be made based on your individual circumstances. Please consult a qualified financial advisor before making investment decisions. Information is current as of the publication date — verify details on official websites.\nDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nRelated Tools \u0026amp; Articles Calculate loan payments and payoff timeline → Loan Calculator Calculate percentages, discounts, and tips instantly → Percentage Calculator Debt Payoff Calculator — Compare snowball vs avalanche strategies with interactive charts Budget Planner — Create a personalized monthly budget to free up debt payments Side Hustle Tax Calculator — Estimate taxes on extra income from side gigs Savings Goal Calculator — Calculate how long to reach any savings target Calculate your mortgage payment → Mortgage Calculator How to Build an Emergency Fund Fast (2026) How to Start Investing with $100 in 2026 Passive Income Ideas That Actually Work 2026 This article contains affiliate links. We may earn a commission at no extra cost to you.\nRelated Templates Take control of your finances with these tools:\nSmart Budget Tracker (Excel Template) — Track income, expenses, and savings goals Side Hustle Starter Kit 2026 — Build additional income streams ","permalink":"https://productivity-works.com/posts/how-to-pay-off-debt-fast-strategies/","summary":"\u003cp\u003e※本記事にはアフィリエイト広告が含まれています。\u003c/p\u003e\n\u003ch1 id=\"how-to-pay-off-debt-fast-strategies--complete-guide-2026\"\u003eHow to Pay Off Debt Fast Strategies — Complete Guide [2026]\u003c/h1\u003e\n\u003cp\u003eDebt is the invisible tax on your future. Every dollar you pay in interest is a dollar that doesn\u0026rsquo;t go toward your emergency fund, your retirement account, your home down payment, or simply enjoying your life. In the U.S., the average household carries over $7,000 in credit card debt alone — at interest rates that often exceed 20% APY.\u003c/p\u003e","title":"How to Pay Off Debt Fast: Best Strategies 2026"},{"content":"This article contains affiliate links.\nFreelancing is one of the few career paths where you can go from zero income to $3,000/month in 90 days without a degree, certification, or years of experience. It\u0026rsquo;s also a path where most beginners waste their first six months on the wrong activities.\nThis guide skips the fluff and gives you the specific steps that actually work in 2026 — including how AI tools have changed the competitive landscape and what that means for newcomers.\nWhy 2026 Is a Good Time to Start The demand for freelance work has never been higher. Companies that eliminated full-time roles in recent years continue to hire contract workers for the same work. Remote work normalization means geographic barriers have largely disappeared — a designer in Birmingham competes for the same projects as one in New York.\nAI has changed the supply side too. Beginners with strong AI literacy can now produce work that previously required years of experience. The playing field has shifted in favor of people who can leverage tools intelligently — not just people with the longest CVs.\nThe combination of high demand and accessible tools creates genuine opportunity for newcomers.\nStep 1: Choose Your Freelance Service The biggest mistake beginners make is being too broad. \u0026ldquo;I do design work\u0026rdquo; is not a service. \u0026ldquo;I create LinkedIn content graphics for B2B SaaS companies\u0026rdquo; is.\nSkills That Pay Well in 2026 Service Typical Rate Difficulty to Start Copywriting / Content writing $50–$200/hr Low Web development $75–$200/hr Medium Graphic / brand design $50–$150/hr Medium SEO consulting $75–$200/hr Medium Social media management $500–$2,000/mo Low Video editing $30–$100/hr Medium AI automation consulting $100–$250/hr Low-Medium UX/UI design $75–$150/hr High Bookkeeping $30–$75/hr Low-Medium Podcast editing $75–$300/episode Low How to Choose Answer these three questions:\nWhat do you already know? Past job experience, hobbies, and side projects all count. You don\u0026rsquo;t need to be an expert — you need to be ahead of your client. What can you learn quickly with AI assistance? Tools like ChatGPT and Claude can compress the learning curve for writing, research, and strategy work significantly. Who do you want to work with? Define your ideal client industry. Working with clients you find interesting sustains motivation when early-stage rejection comes. Pick one service. Specialize. Generalists struggle to find clients; specialists get referred.\nStep 2: Set Your Pricing Most beginners underprice, then resent their clients. Price too low and you attract difficult clients who expect unlimited revisions. Price too high before you have a portfolio, and you don\u0026rsquo;t get hired.\nThe Three-Phase Pricing Strategy Phase 1 (0–2 clients): Offer your first project at a reduced rate in exchange for a testimonial and permission to feature the work in your portfolio. Not free — reduced. Even $100 establishes the relationship as professional.\nPhase 2 (3–10 clients): Charge market-average rates for your skill level. Research what similar freelancers charge on Upwork, LinkedIn, and industry forums. Don\u0026rsquo;t undercut; match.\nPhase 3 (10+ clients): Raise prices every 6 months. You should price yourself out of the cheapest projects as you gain experience.\nHourly vs. Project-Based Pricing Project rates are almost always better for both parties. Hourly rates incentivize slowness and create anxiety for clients who watch the clock. Project rates align your incentives — you\u0026rsquo;re rewarded for working efficiently.\nCalculate your project rate by estimating hours and multiplying by your target hourly rate, then add 20% for revision buffer and admin time.\nStep 3: Build a Portfolio (Without Past Clients) You need samples before you can get hired. You can get samples before you have clients.\nHow to Create Portfolio Pieces Without Clients Spec work: Create a project for a real company as if you were hired. A logo redesign for a local business, a content strategy for a brand you like, a rewritten homepage for a company whose copy is bad. Make it specific and realistic.\nVolunteer projects: Offer your skills to a nonprofit, a local community organization, or a friend\u0026rsquo;s business at no charge. Get written permission to use the work in your portfolio.\nPersonal projects: Build things for yourself that demonstrate your skills. A blog showcases writing. A side project app shows development. A personal brand shows design.\nYou need 3–5 strong samples before outreach. Quality over quantity.\nStep 4: Find Your First Client This is where most people stall. The temptation is to keep refining your portfolio or website instead of talking to potential clients. That\u0026rsquo;s avoidance dressed up as preparation.\nThe Fastest Path to First Client: Warm Outreach Tell every person in your network what you do and what kind of clients you\u0026rsquo;re looking for. Not \u0026ldquo;I\u0026rsquo;m a freelancer\u0026rdquo; — be specific. \u0026ldquo;I write email sequences for e-commerce brands under 50 employees. Do you know anyone I should talk to?\u0026rdquo;\nMost first clients come from:\nA former employer or colleague A friend\u0026rsquo;s referral A community you\u0026rsquo;re already part of (a Slack group, a subreddit, a professional association) Platforms That Work If warm outreach doesn\u0026rsquo;t yield results within 30 days, turn to platforms:\nUpwork — The largest freelance marketplace. Competitive, but higher-quality clients than Fiverr for most service categories. Requires patience building profile reviews.\nLinkedIn — The best platform for B2B service providers. Optimize your headline and About section for your service, post content consistently, and connect with potential clients directly.\nContra — A newer platform with no commission fees. Growing user base, particularly strong for design and development.\nToptal — High barrier to entry but premium rates. Suited for developers and designers with strong portfolios.\nDirect outreach — Email or LinkedIn message to companies that match your ideal client profile. A specific, personalized message explaining what you noticed about their business and how you can help. 1–3% response rate is normal.\nCold Outreach That Actually Gets Responses Most cold outreach fails because it\u0026rsquo;s about the sender, not the recipient. Structure your message like this:\nOne sentence showing you know their business specifically (not generic flattery) One sentence identifying a problem or opportunity they likely have One sentence explaining how you address it A low-commitment call to action (a 15-minute call, not \u0026ldquo;let\u0026rsquo;s work together\u0026rdquo;) Keep it under 100 words. No attachments on the first message.\nStep 5: Nail Your First Project Landing the client is step one. Delivering well enough to get a testimonial and referral is step two — and it matters more.\nClient Communication Confirm scope, timeline, and deliverables in writing before starting Provide one update midway through without being asked Deliver one day before the deadline if possible Send a summary of what was done and why with your final delivery Managing Scope Creep Scope creep — clients adding requests beyond the original agreement — is the most common profitability killer for beginners. When it happens:\n\u0026ldquo;That sounds like a great addition. It\u0026rsquo;s outside the original scope, so I\u0026rsquo;d quote that as a separate project. Want me to put together a quick proposal?\u0026rdquo;\nThis is not rude. It\u0026rsquo;s professional. Clients who have worked with experienced freelancers expect it.\nGetting Testimonials After delivery, ask directly: \u0026ldquo;If you\u0026rsquo;re happy with the work, would you be willing to write a short testimonial I can use on my website and profile?\u0026rdquo; Most clients who are satisfied will say yes if asked.\nA good testimonial is specific — it names a result, not just a feeling. \u0026ldquo;Increased our email open rate by 34%\u0026rdquo; beats \u0026ldquo;great to work with.\u0026rdquo;\nStep 6: Scale to Full-Time Income The Target Numbers A rough guideline for replacing a $60,000/year salary ($5,000/month):\nAt $75/hr: 67 hours/month of billable work (~15 hours/week) At $150/hr: 33 hours/month of billable work (~8 hours/week) Project-based: 4–6 mid-size projects per month The gap between part-time freelancing and full-time replacement income is usually 6–18 months of consistent client acquisition effort.\nThe Recurring Revenue Unlock The most significant unlock in freelancing is converting one-off clients to retainers — monthly agreements for ongoing work. Instead of finding new clients every month, you maintain and expand existing relationships.\nRetainer proposals work best when:\nYou\u0026rsquo;ve delivered excellent work on a one-off project The client has ongoing needs in your area You can articulate a specific monthly deliverable \u0026ldquo;Based on our project together, I think you\u0026rsquo;d benefit from ongoing [X]. I offer a monthly retainer that covers [specific deliverables] for $[amount]. Would you like to discuss a 3-month trial?\u0026rdquo;\nUsing AI to Increase Capacity AI tools let you take on more work without working more hours. Examples:\nAI drafts first versions of content, you edit and refine AI generates code snippets and boilerplate, you architect and customize AI creates design variations, you select and polish AI researches topics, you synthesize and strategize This productivity multiplier is why AI-literate freelancers are taking market share from those who aren\u0026rsquo;t adapting.\nThe 90-Day Freelance Launch Plan Week Focus 1–2 Choose service, build 3 portfolio samples 3–4 Set up profiles (LinkedIn, Upwork, Contra), begin warm outreach 5–6 First client project (at reduced rate), collect testimonial 7–8 Raise to market rates, expand outreach volume 9–10 Second and third clients, systemize delivery process 11–12 Evaluate: raise rates, add retainer offering, or expand services Most people who follow this plan and do the outreach have their first paying client within 30–45 days.\nWhat Not to Do Don\u0026rsquo;t build a website before you have clients. A LinkedIn profile and a Google Doc portfolio work fine for the first few months. Don\u0026rsquo;t register an LLC or worry about business structure until you\u0026rsquo;re earning consistently. Keep overhead zero until revenue exists. Don\u0026rsquo;t work with clients who \u0026ldquo;can\u0026rsquo;t pay now but will when the project succeeds.\u0026rdquo; That\u0026rsquo;s not a client relationship — it\u0026rsquo;s volunteering. Don\u0026rsquo;t work without a contract. A simple one-page agreement covering scope, timeline, payment terms, and revision limits protects both parties. [Try Bonsai for contracts and invoicing at hellobonsai.com] [Get started on Upwork at upwork.com]\nOnce you\u0026rsquo;re earning consistently, keeping your books clean becomes critical — especially at tax time. If you\u0026rsquo;re freelancing in Japan, freee makes accounting straightforward for sole proprietors: invoicing, expense tracking, and the kakuteishinkoku filing process, all in one place.\nRelated Tools \u0026amp; Articles Calculate your ideal freelance hourly rate → Freelance Rate Calculator Create professional invoices for your clients → Invoice Generator Generate a QR code for your freelance portfolio or payment link → QR Code Generator Calculate your freelance tax obligations → Side Hustle Tax Calculator See your 2026 federal tax bracket and effective rate → Tax Bracket Calculator Estimate your take-home pay → Salary Calculator Plan your budget as a freelancer → Budget Calculator Calculate how long to reach any savings target → Savings Goal Calculator Convert hourly wage to salary → Hourly to Salary Calculator How to Start Freelancing with No Experience 2026 Freelance Tax Guide 2026: Everything You Need to Know AI Tools for Freelancers to Earn More 2026 Best Accounting Software for Freelancers 2026 Side Hustles with AI Tools 2026 Related Templates Start your freelance or side hustle journey with these resources:\nSide Hustle Starter Kit 2026 — 15 proven ideas with step-by-step guides ChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Boost your productivity instantly ","permalink":"https://productivity-works.com/posts/how-to-start-freelancing-2026/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eFreelancing is one of the few career paths where you can go from zero income to $3,000/month in 90 days without a degree, certification, or years of experience. It\u0026rsquo;s also a path where most beginners waste their first six months on the wrong activities.\u003c/p\u003e\n\u003cp\u003eThis guide skips the fluff and gives you the specific steps that actually work in 2026 — including how AI tools have changed the competitive landscape and what that means for newcomers.\u003c/p\u003e","title":"How to Start Freelancing in 2026: Complete Beginner's Guide"},{"content":"This article contains affiliate links.\nHow to Start Freelancing With No Experience in 2026 Here\u0026rsquo;s the truth about freelancing no one tells you: every professional you admire started with zero clients, zero portfolio pieces, and zero confidence. Experience isn\u0026rsquo;t something you have — it\u0026rsquo;s something you build.\nIn 2026, starting a freelance career with no experience is genuinely easier than it\u0026rsquo;s ever been. AI tools help you learn faster and deliver better work. Platforms like Upwork, Fiverr, and Contra connect beginners with paying clients. And the global demand for skilled freelancers continues to outpace supply.\nThis is your complete beginner\u0026rsquo;s roadmap — from \u0026ldquo;I have no idea where to start\u0026rdquo; to \u0026ldquo;I just got paid by my first client.\u0026rdquo;\nWhy 2026 Is a Great Time to Start Freelancing 57 million Americans now freelance, contributing over $1.5 trillion to the economy The average freelancer earns $28/hour — above median full-time wages Remote work normalization means more companies are comfortable hiring freelancers globally AI tools dramatically reduce the expertise gap for beginners Platforms now offer beginner-friendly features that didn\u0026rsquo;t exist five years ago The window is open. Let\u0026rsquo;s walk through exactly how to step through it.\nStep 1: Choose the Right Freelance Niche This is the most important decision you\u0026rsquo;ll make as a beginner — and the one most people get wrong.\nCommon mistake: Choosing based on passion alone If you love pottery but there\u0026rsquo;s no market for it online, that passion won\u0026rsquo;t pay your bills. You need the intersection of something you can do (or learn quickly) and something clients will pay for.\nHigh-demand freelance niches in 2026 Niche Average Rate Difficulty to Enter AI-Assistance Level Copywriting / Content Writing $30-100/hr Low High Social Media Management $25-75/hr Low High Graphic Design $35-85/hr Medium High Web Development $50-150/hr Medium-High Medium Video Editing $40-100/hr Medium Medium Virtual Assistant $20-45/hr Low High SEO Consulting $50-150/hr Medium High Data Entry / Research $15-30/hr Very Low High Bookkeeping $30-60/hr Medium High AI Prompt Engineering $50-200/hr Low-Medium N/A How to choose your niche Ask yourself these three questions:\nWhat do I already know how to do (even imperfectly)? What can I learn to a professional level in 30-60 days? What do I find interesting enough to do for hundreds of hours? For absolute beginners: Content writing, social media management, and virtual assistant work have the lowest skill barriers and the highest demand. These are excellent starting points.\nStep 2: Learn the Skill Fast (Even If Starting from Zero) You don\u0026rsquo;t need a degree. You need demonstrable competence.\nThe 30-day skill sprint method Week 1: Foundations\nTake a free or cheap course (Coursera, Udemy, YouTube) Study the top 10 pieces of work in your niche Understand what \u0026ldquo;good\u0026rdquo; looks like before you produce anything Week 2: Imitation\nRecreate high-quality examples in your niche These practice pieces become your portfolio Get feedback from communities (Reddit, Discord, Facebook Groups) Week 3: Original Projects\nCreate original work for 3 fictional or charity clients Focus on the formats clients actually buy (blog posts, social media calendars, website pages) Refine based on feedback Week 4: Go to Market\nYou now have 6-9 portfolio pieces You understand the niche well enough to deliver value You\u0026rsquo;re ready to pitch real clients Best free learning resources Writing: HubSpot Academy, Copyblogger, Google\u0026rsquo;s Digital Garage Design: Canva Design School, Adobe Creative Cloud tutorials Social Media: Meta Blueprint, HubSpot Social Media Certification Web Development: freeCodeCamp, The Odin Project SEO: Ahrefs Academy, Google Search Central Bookkeeping: QuickBooks Training, Bench Accounting Blog How AI accelerates your learning Use AI tools to compress your learning curve:\nAsk ChatGPT or Claude to explain concepts you don\u0026rsquo;t understand Use AI to review your practice work and suggest improvements Generate examples of different approaches to the same task Research what clients in your niche typically struggle with Step 3: Build a Portfolio With No Clients The \u0026ldquo;I can\u0026rsquo;t get experience without experience\u0026rdquo; paradox is real — but entirely solvable.\nMethod 1: Spec Work Create portfolio pieces for fictional businesses. Write sample blog posts for a fictional coffee shop. Design a brand identity for an imaginary tech startup. Prospective clients care about quality, not whether the client was real.\nMethod 2: Volunteer Work Offer your services free or heavily discounted to:\nLocal nonprofits and charities Friends and family with businesses Small businesses in your community Online communities looking for help You get real work, real feedback, and real testimonials.\nMethod 3: Personal Projects Start a blog in your niche. Build a personal website showcasing your work. Run social media accounts for a passion project. These demonstrate your skills authentically.\nMethod 4: Low-Rate Paid Work Take a few low-paid gigs on Fiverr or Upwork to build reviews. Charge below market rate for your first 3-5 clients, explicitly in exchange for honest reviews. Then raise your rates.\nWhat your portfolio should include 3-6 strong examples of your work Brief descriptions of each project (client, problem, your solution, result) Testimonials if available A clear statement of what services you offer and who you help Where to host your portfolio:\nContra (free, freelance-specific) Canva (great for designers) Wix or Squarespace (quick website) Notion (surprisingly effective for simple portfolios) LinkedIn (for professional services) Step 4: Set Your Rates Most beginners underprice themselves, which attracts bad clients and leads to burnout. Here\u0026rsquo;s how to set intelligent rates.\nThe beginner pricing formula Hourly rate = (Monthly income goal ÷ Billable hours) × 1.3\nThe 1.3 multiplier accounts for taxes, unpaid admin time, and software costs.\nExample: You want to earn $3,000/month. You have 80 billable hours available.\n$3,000 ÷ 80 = $37.50 $37.50 × 1.3 = $48.75/hour Round to $45-50/hour and you have a starting rate that\u0026rsquo;s realistic.\nProject-based vs hourly pricing For beginners, project-based pricing is often better:\nClients know exactly what they\u0026rsquo;ll pay (less friction to buy) You\u0026rsquo;re not penalized for getting faster as you learn It\u0026rsquo;s easier to present as a professional service Common project rates for beginners:\n1,000-word blog post: $75-150 Social media calendar (30 posts): $200-400 Brand logo design: $150-300 Virtual assistant (monthly retainer): $400-800/month SEO audit: $200-500 Step 5: Find Your First Clients Platform approach (easiest for beginners) Upwork — The largest freelance marketplace. Competitive but high volume. Start with smaller projects to build reviews, then move to higher-value work.\nFiverr — Gig-based marketplace. Good for standardized services. Create clear, specific gig offerings rather than vague \u0026ldquo;I\u0026rsquo;ll do anything\u0026rdquo; listings.\nContra — Commission-free platform with a professional feel. Growing rapidly and less saturated than Upwork.\nToptal / Talent.com — Higher tier but requires screening. Better for experienced freelancers.\nLinkedIn — Underused by beginners. Optimize your profile, post valuable content in your niche, and reach out to potential clients directly.\nDirect outreach (higher ROI, more effort) Cold outreach to your ideal clients — done well — produces better results than any platform:\nIdentify 20 businesses that could use your service Find the decision-maker\u0026rsquo;s contact (LinkedIn, company website) Send a personalized 3-sentence email: \u0026ldquo;I noticed [specific thing about their business]\u0026rdquo; \u0026ldquo;I help [type of business] with [specific problem you solve]\u0026rdquo; \u0026ldquo;Would you be open to a 15-minute conversation?\u0026rdquo; Follow up once after 5 days Track results and refine your pitch Your network (often overlooked) Tell everyone you\u0026rsquo;re freelancing. Post on LinkedIn. Tell friends who work in relevant industries. The majority of beginner freelancers land their first clients through personal connections, not platforms.\nStep 6: Deliver Excellent Work and Get Testimonials Your first clients are not just income — they\u0026rsquo;re your growth engine. Treat every project like your professional reputation depends on it (because it does).\nOver-deliver on early projects:\nMeet every deadline — ideally deliver early Communicate proactively (send a progress update before they ask) Ask questions before starting rather than guessing Deliver one extra thing they didn\u0026rsquo;t ask for but will value Getting testimonials: After delivering great work, ask directly: \u0026ldquo;I\u0026rsquo;m building my portfolio and would really appreciate a short testimonial about working together. Would you be willing to share a few sentences about your experience?\u0026rdquo;\nMost satisfied clients are happy to do this. A few strong testimonials transform your ability to land future clients.\nStep 7: Scale Your Income Once you have 2-3 regular clients, you\u0026rsquo;re no longer a beginner. Now you focus on scaling.\nRaise your rates gradually Raise rates by 15-25% for every new client Existing clients get grandfathered for 6-12 months, then informed of new rates Better clients (higher budget, clearer briefs, more respectful communication) almost always appear when you raise rates Move from time-for-money to retainers Retainer arrangements (monthly fixed fees for ongoing work) create predictable income. Aim to convert good one-time clients into monthly retainers.\nSpecialize to command premium rates Niching deeper almost always allows higher rates. \u0026ldquo;I write content\u0026rdquo; earns less than \u0026ldquo;I write SEO content for B2B SaaS companies.\u0026rdquo; Specialize relentlessly.\nAdd AI tools to increase output With AI tools, many freelancers can produce 2-3x the volume of work in the same time. This allows you to either take more clients at the same rate or deliver faster at a premium.\nSee our guide: AI Tools for Freelancers for specific tools and workflows.\nCommon Mistakes to Avoid 1. Waiting until you\u0026rsquo;re \u0026ldquo;ready\u0026rdquo; There is no ready. Set a deadline (30 days) and launch regardless.\n2. Working without a contract Always use a written agreement — even a simple one. It protects you from scope creep and non-payment. Use HelloSign or Docusign for free basic contracts.\n3. Only relying on one client A single client isn\u0026rsquo;t freelancing — it\u0026rsquo;s a job without benefits. Aim for at least 3 active clients.\n4. Ignoring taxes Freelancers in the US pay self-employment tax (~15.3%) on top of income tax. Set aside 25-30% of every payment in a separate account.\n5. Undervaluing your work Low rates attract difficult clients who don\u0026rsquo;t respect your work. Raise rates faster than feels comfortable.\nYour 30-Day Freelance Launch Plan Week Focus Key Actions Week 1 Niche + Learning Choose niche, start course, study existing work Week 2 Portfolio Create 3 spec/practice pieces Week 3 Setup Build portfolio site, LinkedIn profile, set rates Week 4 Outreach Apply to 10 platforms/jobs, send 20 cold emails The Resources You Need to Succeed Starting freelance requires minimal investment — mostly time and some basic tools:\nPortfolio site: Contra (free), Wix ($16/month) Contracts: HelloSign (free tier), AND.CO (free tier) Time tracking: Toggl (free) Invoicing: Wave (free), FreshBooks ($15/month) AI writing assistant: ChatGPT Free or Claude Free Total startup cost: $0-30/month.\nStart Your Freelance Journey Today As your freelance income grows, staying on top of accounting becomes non-negotiable. If you\u0026rsquo;re based in Japan, freee is designed for exactly this situation — it helps sole proprietors manage invoices, track business expenses, and file taxes without needing an accounting background.\nThe perfect time to start freelancing was last year. The second-best time is today.\nOur Freelance Starter Toolkit includes a contract template, rate calculator, client pitch scripts, and 30-day action plan — everything you need to launch professionally from day one.\nRelated Tools Calculate your ideal freelance hourly rate → Freelance Rate Calculator Calculate your freelance tax obligations → Side Hustle Tax Calculator See your 2026 federal tax bracket and effective rate → Tax Bracket Calculator Create professional invoices for your first clients → Invoice Generator Generate a QR code for your freelance portfolio → QR Code Generator Plan your freelance budget → Budget Calculator Convert hourly wage to salary → Hourly to Salary Calculator Related Reading:\nAI Tools for Freelancers to Earn More 2026 Freelance Tax Guide 2026 Work From Home Tips for Beginners 2026 ","permalink":"https://productivity-works.com/posts/how-to-start-freelancing-with-no-experience-2026/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-to-start-freelancing-with-no-experience-in-2026\"\u003eHow to Start Freelancing With No Experience in 2026\u003c/h2\u003e\n\u003cp\u003eHere\u0026rsquo;s the truth about freelancing no one tells you: every professional you admire started with zero clients, zero portfolio pieces, and zero confidence. Experience isn\u0026rsquo;t something you have — it\u0026rsquo;s something you build.\u003c/p\u003e\n\u003cp\u003eIn 2026, starting a freelance career with no experience is genuinely easier than it\u0026rsquo;s ever been. AI tools help you learn faster and deliver better work. Platforms like Upwork, Fiverr, and Contra connect beginners with paying clients. And the global demand for skilled freelancers continues to outpace supply.\u003c/p\u003e","title":"How to Start Freelancing With No Experience 2026: Full Guide"},{"content":"※本記事にはアフィリエイト広告が含まれています。\nHow to Start Investing with $100 — Complete Guide [2026] Here\u0026rsquo;s the myth that stops millions of people from building wealth: \u0026ldquo;I don\u0026rsquo;t have enough money to invest.\u0026rdquo;\nThe truth? You can start investing today with $100 — or even less. Thanks to fractional shares, zero-minimum brokerage accounts, and micro-investing apps, the barriers to entry have virtually disappeared. A $100 investment made at age 25, growing at the stock market\u0026rsquo;s historical average of roughly 10% per year, becomes over $1,700 by age 65. Start that same investment a decade later at 35, and it only becomes about $675.\nThe best investment you can make today isn\u0026rsquo;t the perfect one. It\u0026rsquo;s the one you actually make.\nIn this guide, you\u0026rsquo;ll learn:\nThe best places to invest your first $100 in 2026 How fractional shares and micro-investing work Which account type gives you the most bang for your buck A step-by-step beginner investment plan you can implement today How to scale from $100 to $1,000 to $10,000 over time The mindset shifts that separate successful investors from everyone else What Does \u0026ldquo;Investing with $100\u0026rdquo; Mean? Definition \u0026amp; How It Works Investing with $100 means putting that money to work in assets that have the potential to grow in value over time — rather than sitting idle in a checking account earning next to nothing.\nIn 2026, $100 gets you meaningful access to:\nFractional shares of any stock or ETF (buy $1 worth of Amazon or Apple) Zero-minimum index funds at Fidelity, Schwab, and other brokers Robo-advisor accounts that invest automatically in diversified portfolios Micro-investing apps that round up your purchases and invest the difference Treasury bonds and I-bonds via TreasuryDirect.gov ($100 minimum for I-bonds) The key insight: the amount matters less than the habit. Investors who start with $100 and add to it consistently every month outperform those who wait to accumulate a \u0026ldquo;real\u0026rdquo; amount before getting started.\nPros and Cons of Starting Small Pros:\nZero barrier to entry with today\u0026rsquo;s platforms Build investing habits before larger amounts are at stake Learn by doing without catastrophic downside risk Take advantage of compound growth from an early age Some platforms let you invest spare change automatically (virtually effortless) Cons:\nTransaction costs can proportionally sting on tiny amounts (choose commission-free platforms) $100 won\u0026rsquo;t make you rich quickly — patience and consistency are essential With limited capital, diversification requires fractional shares or funds Temptation to gamble on high-risk assets because \u0026ldquo;it\u0026rsquo;s only $100\u0026rdquo; Best Ways to Invest $100 in 2026: Options Compared Investment Option Min. to Start Expected Return* Risk Level Best For S\u0026amp;P 500 Index Fund (e.g., FXAIX) $0 ~10%/yr (long-term avg) Medium Passive long-term growth Total Market ETF (VTI) ~$1 (fractional) ~10%/yr Medium Broad diversification Robo-advisor (Betterment) $0 ~8-10%/yr Medium Hands-off automation High-Yield Savings Account $0–$1 ~4-5% APY (2026 range) Very Low Emergency fund portion I-Bonds (TreasuryDirect) $100 Inflation + 0.5–1% Very Low Inflation protection Micro-investing app (Acorns) $0 ~7-9%/yr Medium Round-up automation Individual stocks (fractional) $1 Varies widely Medium-High Learning, not core strategy REITs (e.g., VNQ) ~$1 (fractional) ~8-9%/yr Medium Real estate exposure Target-date retirement fund $0 (Fidelity) ~8-10%/yr Medium (auto-adjusting) Set-and-forget retirement Cryptocurrency Varies Very high volatility Very High Speculation only — small % Expected returns are historical averages and are NOT guaranteed. All investments carry risk.\nOpen a $0-minimum investment account today How to Choose: Key Factors What to Look For 1. Account type first, investment second Before you pick specific investments, choose the right account:\nIf you have earned income, open a Roth IRA first. Your $100 grows tax-free — the government takes nothing when you retire. If you don\u0026rsquo;t have earned income or already max your Roth IRA, open a taxable brokerage account. If your employer offers a 401k match, contribute enough to get that free money before anything else. 2. Zero commissions and zero minimums In 2026, there\u0026rsquo;s zero reason to pay trading commissions on stocks or ETFs. Choose platforms that offer commission-free trading and no account minimums: Fidelity, Schwab, M1 Finance, or Robinhood.\n3. Fractional shares Make sure your broker offers fractional shares so your $100 can buy into any stock or ETF, regardless of share price. Fidelity, Schwab, and M1 Finance all offer this.\n4. Automation The best investing strategy is one you don\u0026rsquo;t have to think about. Set up automatic monthly contributions and let the money move without willpower being involved.\n5. Simplicity over complexity With $100, you don\u0026rsquo;t need a complicated portfolio. One total market index fund is enough. Complexity doesn\u0026rsquo;t add returns — it adds confusion and potential for mistakes.\nCommon Mistakes to Avoid Mistake 1: Putting your $100 into meme stocks or crypto It\u0026rsquo;s tempting to treat small amounts as gambling money. Resist. A $100 loss is still a 100% loss, and bad habits formed with small amounts scale into disasters with larger ones.\nMistake 2: Keeping $100 in cash \u0026ldquo;until the market looks better\u0026rdquo; There is no perfect time to invest. Missing the 10 best trading days in any given decade can cut your returns in half. Invest as soon as you have the money.\nMistake 3: Using a savings account as your only \u0026ldquo;investment\u0026rdquo; High-yield savings accounts are great for emergency funds. But earning 4-5% APY doesn\u0026rsquo;t beat long-term inflation-adjusted stock market returns of ~7% real. Savings accounts are not investment accounts.\nMistake 4: Paying attention to daily market movements If you have $100 invested and the market drops 2%, you\u0026rsquo;ve \u0026ldquo;lost\u0026rdquo; $2. That\u0026rsquo;s a coffee. Don\u0026rsquo;t let short-term noise distract you from the long-term game.\nMistake 5: Not reinvesting dividends Make sure your brokerage account is set to automatically reinvest dividends (DRIP — Dividend Reinvestment Plan). Over decades, dividend reinvestment accounts for roughly 40% of total stock market returns.\nRelated: Best Index Funds for Beginners 2026 Step-by-Step Guide: Invest Your First $100 Today 5 Steps to Start Investing with $100 1 Build Emergency Fund First 2 Open Roth IRA or Brokerage 3 Transfer Your $100 4 Buy One Index Fund 5 Auto-Invest Monthly $500–$1,000 10 min online Settles 1–3 days VTI / FZROX $25+/mo $100 invested at age 25 — power of compound growth $100 Today $452 10 yrs $1,083 20 yrs $1,745 40 yrs Assumes 10%/yr historical average. Not a guarantee. Step 1: Build a $500–$1,000 emergency fund first Before investing, make sure you have at least one month of expenses in a high-yield savings account. Investing money you might need in a few months is a recipe for being forced to sell at the worst time. If you already have an emergency fund, skip to Step 2.\nStep 2: Open a Roth IRA or brokerage account For most people under 40 with earned income, a Roth IRA is the best first investment account. Recommended providers:\nFidelity (Check current offers ) — Best overall: $0 minimum, commission-free, fractional shares, ZERO expense ratio index funds Charles Schwab — Excellent for long-term investors and customer service Betterment — Best if you want it done for you automatically M1 Finance — Best for building a custom \u0026ldquo;pie\u0026rdquo; portfolio with automation The account takes about 10 minutes to open online. You\u0026rsquo;ll need your SSN and bank account information.\nStep 3: Link your bank account and transfer $100 Transfer $100 from your checking or savings account. Most brokers make this instant with Plaid. The money typically settles in 1–3 business days.\nStep 4: Choose one simple investment With $100, you don\u0026rsquo;t need more than one fund. Choose one of these:\nFidelity ZERO Total Market Index Fund (FZROX) — 0% expense ratio, $0 minimum, total U.S. market coverage Vanguard Total Stock Market ETF (VTI) — 0.03% expense ratio, fractional shares available at Fidelity/Schwab iShares Core S\u0026amp;P 500 ETF (IVV) — 0.03% expense ratio, excellent liquidity If you want Fidelity to manage it automatically, choose a Fidelity Freedom Index Fund matching your target retirement year (e.g., Fidelity Freedom Index 2055 Fund for someone retiring around 2055).\nStep 5: Enable dividend reinvestment In your account settings, turn on automatic dividend reinvestment. This ensures every dollar of dividends gets put back to work immediately.\nStep 6: Set up automatic contributions Even $25/month added to your initial $100 creates powerful compounding momentum. Set up a recurring monthly transfer so investing happens automatically, without relying on willpower.\nStep 7: Don\u0026rsquo;t touch it — and don\u0026rsquo;t watch it obsessively Check your account quarterly at most. Your job is to contribute consistently and let compound interest work. Anything else is just noise.\nHow to Scale: From $100 to Your First $10,000 Once you\u0026rsquo;ve invested your first $100, here\u0026rsquo;s the roadmap to grow:\nMilestone Timeline (Typical) Action $100 invested Today Open account, invest in one index fund $500 invested 3–6 months Increase automatic contribution to $75–$100/month $1,000 invested 6–12 months Review allocation, ensure dividend reinvestment is on $5,000 invested 2–3 years Consider adding international diversification (VXUS) $7,000/year contributed Ongoing Max out Roth IRA annual limit $10,000+ invested 3–5 years Explore taxable brokerage for additional investing The key driver of growth isn\u0026rsquo;t brilliant stock-picking — it\u0026rsquo;s consistent contributions. Automate your savings rate, and the investment account takes care of itself.\nFrequently Asked Questions Q: Is $100 really enough to start investing? A: Absolutely. The most important thing about your first $100 is the habit it creates, not the dollar amount. Investors who start with $100 at 25 and add $200/month will have dramatically more wealth at 65 than those who wait for a \u0026ldquo;significant\u0026rdquo; amount to invest.\nQ: What\u0026rsquo;s the best app to invest $100? A: For maximum simplicity: Acorns or Betterment. For more control with low minimums: Fidelity or Schwab. For automated portfolios: M1 Finance. All offer $0 minimums.\nQ: Should I pay off debt before investing? A: It depends on the interest rate. High-interest debt (credit cards at 20%+ APR) should be paid off before investing. Low-interest debt (student loans at 4–6%, mortgages) can be carried while investing simultaneously, since stock market returns historically exceed those rates.\nQ: Can I lose my $100 entirely? A: If you invest in a single stock that goes bankrupt, yes. If you invest in a broad market index fund, an entire wipeout would require every major U.S. company to go bankrupt simultaneously — essentially impossible. Individual stocks carry much more risk than index funds.\nQ: What if I can only invest $20 or $50? A: Start with whatever you have. Many of the platforms mentioned (Fidelity, Acorns, Betterment) allow you to invest with as little as $1. The habit and momentum matter far more than the starting amount.\nQ: How do I invest if I\u0026rsquo;m a teenager (under 18)? A: You\u0026rsquo;ll need a custodial account (UGMA/UTMA) opened by a parent or guardian. Fidelity and Schwab both offer custodial accounts. Once you turn 18, the account transfers to your name.\nQ: Is it better to invest $100 all at once or spread it over time? A: Research shows that lump-sum investing (all at once) outperforms dollar-cost averaging about two-thirds of the time. However, if you\u0026rsquo;re nervous about timing, spreading it over 2–3 months is perfectly fine. The psychological comfort of DCA helps many people avoid panic-selling.\nYour First $100 Deserves a Great Home Don\u0026rsquo;t let your first investment sit in a low-yield account. Open a Rakuten Securities account and invest your first $100 in a diversified index fund — your future self will thank you for starting today.\nConclusion: Your $100 Is the Starting Line, Not the Finish Line The most powerful investment you\u0026rsquo;ll ever make isn\u0026rsquo;t measured in dollars — it\u0026rsquo;s the decision to start. Every wealthy investor started somewhere. Most started with far less than they wanted, in a moment when it felt almost too small to matter.\nYour $100 is not too small. It\u0026rsquo;s the seed. Water it with consistent contributions, protect it with patience, and let compound interest do what it\u0026rsquo;s been doing for centuries: turning small, consistent actions into remarkable results.\nOpen your account today. Buy one index fund. Set up $25/month auto-invest. That\u0026rsquo;s it. You\u0026rsquo;re an investor.\nTake action now:\nOpen a Fidelity account — $0 minimum, ZERO fee index funds Start with Betterment\u0026rsquo;s automated portfolio — $0 minimum Try Acorns — invest your spare change automatically Related: How to Build an Emergency Fund Fast Disclaimer: This article is for informational purposes only and does not constitute financial advice. Investment decisions should be made based on your individual circumstances. Please consult a qualified financial advisor before making investment decisions. Information is current as of the publication date — verify details on official websites.\nDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nRelated Tools \u0026amp; Articles Compound Interest Calculator — See how your $100 grows with compound interest Savings Goal Calculator — Calculate how long to reach any savings target Dividend Income Calculator — Calculate passive income from dividend stocks and ETFs Budget Planner — Find money to invest with a personalized budget Track your total net worth → Net Worth Calculator Plan your path to financial independence → FIRE Calculator See how inflation affects your money → Inflation Calculator ETF vs Mutual Fund: Which Should I Choose? Roth IRA vs Traditional IRA: Which Is Better? How to Build an Emergency Fund Fast (2026) This article contains affiliate links. We may earn a commission at no extra cost to you.\nRelated Templates Take control of your finances with these tools:\nSmart Budget Tracker (Excel Template) — Track income, expenses, and savings goals Side Hustle Starter Kit 2026 — Build additional income streams ","permalink":"https://productivity-works.com/posts/how-to-start-investing-with-100/","summary":"\u003cp\u003e※本記事にはアフィリエイト広告が含まれています。\u003c/p\u003e\n\u003ch1 id=\"how-to-start-investing-with-100--complete-guide-2026\"\u003eHow to Start Investing with $100 — Complete Guide [2026]\u003c/h1\u003e\n\u003cp\u003eHere\u0026rsquo;s the myth that stops millions of people from building wealth: \u0026ldquo;I don\u0026rsquo;t have enough money to invest.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eThe truth? You can start investing today with $100 — or even less. Thanks to fractional shares, zero-minimum brokerage accounts, and micro-investing apps, the barriers to entry have virtually disappeared. A $100 investment made at age 25, growing at the stock market\u0026rsquo;s historical average of roughly 10% per year, becomes over $1,700 by age 65. Start that same investment a decade later at 35, and it only becomes about $675.\u003c/p\u003e","title":"How to Start Investing with $100 in 2026"},{"content":"How to Use AI for Excel Automation — Complete Guide [2026] Let\u0026rsquo;s be honest: most of us are using Excel at maybe 20% of its capacity. We know there are faster ways to do what we\u0026rsquo;re doing — better formulas, automated reports, dynamic dashboards — but learning them takes time we don\u0026rsquo;t have. So we copy-paste data manually, wrestle with VLOOKUP at 9pm, and wonder why the spreadsheet always breaks when someone else touches it.\nAI changes this completely. With the right prompts, ChatGPT or Microsoft Copilot can write formulas you\u0026rsquo;d never figure out alone, generate VBA macros in seconds, explain what a complicated formula does in plain English, and help you clean messy data with one command.\nHere\u0026rsquo;s what you\u0026rsquo;ll learn in this guide:\nThe 5 highest-leverage AI use cases for Excel automation How to prompt AI to write any formula or macro you need A comparison of the best AI tools for Excel work Step-by-step walkthroughs with real prompt examples Advanced techniques for building fully automated workbooks No coding background required. If you can describe what you want in plain English, AI can build it for you in Excel.\nWhy AI for Excel Automation Matters in 2026 The Productivity Gap Excel users fall into two camps. Camp A spends 3 hours building a report manually every Monday morning. Camp B spends 30 minutes building an automated report once — then runs it in 5 minutes every week after that. The difference isn\u0026rsquo;t intelligence or dedication. It\u0026rsquo;s knowing how to use Excel\u0026rsquo;s automation features.\nHistorically, the barrier to automation was learning VBA, Power Query, or complex nested formulas. That barrier just disappeared. AI can write all of that for you. The only skill you need is the ability to describe what you want.\nHow AI Changes the Game Before AI, a non-technical analyst who needed a macro to auto-format a weekly report had three options: spend a weekend learning VBA, hire a developer ($50–$150/hour), or keep doing it manually. Now they describe the macro in plain English to ChatGPT and get working code in 30 seconds.\nThe same is true for formulas. \u0026ldquo;SUMPRODUCT with multiple criteria filtered by date range\u0026rdquo; used to require a tutorial. Now you describe the business logic and the formula appears. This democratization of Excel power is one of the most underrated productivity shifts of 2026.\nBest AI Tools for Excel Automation Compared Tool Excel Integration Formula Help VBA/Macros Power Query Price Microsoft Copilot (M365) Native (in-app) Excellent Excellent Excellent Included with M365 ($6+/mo) ChatGPT Plus Browser-based Excellent Excellent Very good $20/month Claude Pro Browser-based Excellent Very good Good $20/month Google Gemini (Workspace) Google Sheets focus Good Limited N/A Included with Workspace Excel\u0026rsquo;s built-in AI features Native Good Basic Basic Included with M365 Try It Free Bottom line: Microsoft Copilot is the most integrated if you\u0026rsquo;re on Microsoft 365. ChatGPT is the most versatile if you want help outside Excel too. Both are excellent. If you use Excel daily and don\u0026rsquo;t have M365, ChatGPT Plus at $20/month is the fastest path to automation.\nStep-by-Step Guide: How to Automate Excel with AI Step 1: Generate Formulas from Plain English This is the most immediate win. Instead of hunting for the right formula or stacking nested functions, describe what you want and copy the result.\nPrompt for a Complex Formula:\nI\u0026#39;m working in Excel. I have: - Column A: Product names - Column B: Sales rep names - Column C: Sale dates (formatted as dates) - Column D: Revenue amounts I want a formula in cell F2 that: - Sums all revenue in column D - Only for sales rep \u0026#34;John Smith\u0026#34; (in column B) - Only for sales in the last 30 days (compared to today) Write the formula and explain what each part does. Typical AI Output:\n=SUMPRODUCT((B2:B1000=\u0026#34;John Smith\u0026#34;)*(C2:C1000\u0026gt;=TODAY()-30)*(D2:D1000)) Paste it, done. No tutorial required.\nPrompt for XLOOKUP with Error Handling:\nWrite an Excel formula using XLOOKUP that: - Looks up the value in cell A2 within the range Sheet2!A:A - Returns the corresponding value from Sheet2!C:C - If nothing is found, returns \u0026#34;Not Found\u0026#34; instead of an error - If A2 is blank, returns an empty string Step 2: Write VBA Macros Automatically VBA macros are the backbone of Excel automation — they can format reports, move data, send emails, and run entire workflows. AI writes them from plain English.\nPrompt for a Formatting Macro:\nWrite a VBA macro for Excel that does the following: 1. Goes to the sheet named \u0026#34;Weekly Report\u0026#34; 2. Selects all cells in the used range 3. Auto-fits all column widths 4. Sets all headers in row 1 to bold, with a blue background (hex #003087) and white font 5. Adds a border around every cell in the used range 6. Freezes row 1 7. Saves the workbook Add comments to explain each section. Prompt for a Data Transfer Macro:\nWrite a VBA macro that: 1. Opens a specific file: C:\\Reports\\sales_data.xlsx 2. Copies all data from Sheet1, column A through column E, starting from row 2 3. Pastes it into the active workbook on a sheet called \u0026#34;Import\u0026#34; starting at cell A2 4. Closes the source file without saving 5. Shows a message box saying \u0026#34;Import complete\u0026#34; when done To use VBA code from AI:\nPress Alt + F11 in Excel to open the VBA editor Insert \u0026gt; Module Paste the code Press F5 to run, or assign it to a button Step 3: Clean Messy Data with AI Raw data is almost always messy. AI can write the formulas and macros to clean it in minutes.\nPrompt for Data Cleaning Formulas:\nI have a column of email addresses in column A that are messy. Some issues: - Extra spaces before/after the address - Some are in ALL CAPS - Some have \u0026#34;mailto:\u0026#34; prepended to them Write Excel formulas to: 1. Remove leading/trailing spaces 2. Convert to lowercase 3. Remove \u0026#34;mailto:\u0026#34; if present Combine all three into one formula in column B. Prompt for Duplicate Detection:\nWrite an Excel formula for column C that: - Marks the cell \u0026#34;DUPLICATE\u0026#34; if the value in column A appears more than once in the entire column A range - Marks it \u0026#34;UNIQUE\u0026#34; if it only appears once Use COUNTIF. Explain the formula. Step 4: Build Automated Reports with Power Query Power Query transforms repetitive report-building into a one-click refresh. AI can write the M code.\nPrompt for Power Query M Code:\nWrite Power Query M code that: 1. Imports a CSV file from this path: C:\\Data\\monthly_sales.csv 2. Removes any rows where the \u0026#34;Revenue\u0026#34; column is blank or zero 3. Creates a new column called \u0026#34;Quarter\u0026#34; based on the \u0026#34;Date\u0026#34; column (Q1 = Jan-Mar, Q2 = Apr-Jun, Q3 = Jul-Sep, Q4 = Oct-Dec) 4. Groups the data by \u0026#34;Quarter\u0026#34; and sums the \u0026#34;Revenue\u0026#34; column 5. Sorts the result by Quarter ascending Provide the complete M code I can paste into the Advanced Editor. Step 5: Debug Broken Formulas Paste a broken formula with a description of what it should do and let AI diagnose it.\nPrompt for Formula Debugging:\nThis Excel formula is giving me a #VALUE! error. Here it is: =IF(AND(A2\u0026gt;DATE(2025,1,1),B2=\u0026#34;Active\u0026#34;),VLOOKUP(C2,Sheet2!$A$1:$D$100,3,0),\u0026#34;N/A\u0026#34;) The intent is: - If date in A2 is after Jan 1 2025 AND B2 says \u0026#34;Active\u0026#34; - Then look up the value in C2 on Sheet2 and return column 3 - Otherwise show N/A What\u0026#39;s causing the error and how do I fix it? Pro Tips \u0026amp; Advanced Techniques Common Mistakes to Avoid Mistake 1: Not testing AI-generated VBA before using on real data. Always test macros on a copy of your file first. AI code is usually correct but occasionally has edge-case bugs.\nMistake 2: Giving AI vague descriptions. \u0026ldquo;Make my spreadsheet better\u0026rdquo; gives nothing. \u0026ldquo;Auto-format the pivot table in column A through G with alternating row colors\u0026rdquo; gives you working code.\nMistake 3: Ignoring Power Query. Most Excel users skip Power Query entirely. It\u0026rsquo;s the most powerful automation feature in Excel and AI makes it accessible even if you can\u0026rsquo;t read M code.\nMistake 4: Not asking AI to explain the code. Always ask \u0026ldquo;explain what each line does\u0026rdquo; — this teaches you the logic so you can modify it yourself next time.\nMistake 5: One-and-done. Use AI iteratively. If the first macro doesn\u0026rsquo;t handle blank rows, say \u0026ldquo;now modify it to skip blank rows in column A\u0026rdquo; and iterate until it\u0026rsquo;s perfect.\nPower User Strategies Strategy 1: Build a macro library. Save AI-generated macros in a personal macro workbook. Over time you\u0026rsquo;ll have a library of automation code you can deploy instantly.\nStrategy 2: Create a \u0026ldquo;describe your spreadsheet\u0026rdquo; system prompt. Open ChatGPT with a pasted description of your workbook structure (column names, sheet names, data types). Then ask for multiple formulas in one session without re-explaining.\nStrategy 3: Use AI to document your workbooks. Paste a complex formula and ask AI to write a plain-English comment explaining it. Add these as cell notes for your future self or colleagues.\nStrategy 4: Ask for error handling. Always add \u0026ldquo;include error handling so it doesn\u0026rsquo;t break if the data is missing or blank\u0026rdquo; to macro prompts. This saves hours of debugging.\nStrategy 5: Use Copilot\u0026rsquo;s in-app capabilities. If you have Microsoft 365, Copilot in Excel can analyze data, suggest charts, and write formulas directly in the sidebar — no copy-paste needed.\nRelated: How to Use ChatGPT for Data Analysis Frequently Asked Questions Q: Do I need to know VBA to use AI-generated macros? A: No. You only need to know how to open the VBA editor (Alt + F11), create a module, and paste code. AI handles the actual writing.\nQ: Will AI-generated Excel formulas work in Google Sheets too? A: Most standard Excel formulas (SUMIF, VLOOKUP, XLOOKUP, IF) work in Sheets. VBA macros do not — Sheets uses Google Apps Script. Tell AI \u0026ldquo;write this for Google Sheets using Apps Script\u0026rdquo; and it will adapt.\nQ: Is Microsoft Copilot worth it for Excel users? A: If you\u0026rsquo;re on Microsoft 365 Business (which costs $6–$22/month anyway), Copilot for M365 adds significant value if you use Excel heavily. The in-app integration is seamless. If you\u0026rsquo;re on a personal plan, ChatGPT Plus at $20/month is more versatile.\nQ: Can AI handle Excel files with thousands of rows? A: AI writes code that works on any size dataset — the formula or macro doesn\u0026rsquo;t \u0026ldquo;know\u0026rdquo; how big your data is. Just ensure your ranges are large enough to cover your data (e.g., A2:A100000 instead of A2:A50).\nQ: How do I handle errors in AI-generated formulas? A: Wrap the output in IFERROR: =IFERROR([your formula],\u0026quot;Error - check input\u0026quot;). Then paste back to AI and say \u0026ldquo;this formula sometimes returns an error — add error handling.\u0026rdquo;\nQ: Can AI create Excel dashboards? A: Yes — ask AI to write VBA that creates charts, sets chart types, assigns data ranges, and formats them. It can build entire dashboards programmatically. Alternatively, describe your KPIs and ask for a step-by-step guide to building the dashboard manually.\nQ: What about Python for Excel? Is AI useful there? A: Excel now has Python integration (via Anaconda). AI is extremely useful for writing Python scripts for Excel — just specify \u0026ldquo;write this as an Excel Python script using pandas.\u0026rdquo;\nExcel and Data Skills Pay Off in the Job Market Professionals who combine Excel expertise with AI automation are among the most sought-after candidates in finance, operations, and analytics. Find your next career on doda — Japan\u0026rsquo;s leading job platform with thousands of data and finance roles.\nConclusion \u0026amp; Call to Action AI has removed the technical barrier between you and a fully automated Excel workflow. Whether you need a formula you\u0026rsquo;ve never been able to figure out, a macro to eliminate a 2-hour manual task, or a Power Query pipeline to clean incoming data automatically — you can get there with a well-written prompt and 5 minutes.\nKey takeaways:\nDescribe formulas in plain English and AI writes them — no formula syntax knowledge needed VBA macros can automate almost any repetitive Excel task; AI writes the code for free Power Query is the most underused Excel feature; AI makes the M code accessible Always test macros on a copy of your data before running on production files Iterate with follow-up prompts to refine until the code is exactly right Want the exact prompt templates used in this guide — plus 80 more for Excel, Google Sheets, and data analysis? Grab our Complete ChatGPT Prompt Collection — it includes a dedicated \u0026ldquo;Excel \u0026amp; Data\u0026rdquo; section with formulas, macros, and Power Query prompts you can use immediately.\nFor a complete system to automate your work with AI, our AI Productivity Playbook covers Excel, email, reporting, and more in one end-to-end framework.\nRelated: Best ChatGPT Prompts for Productivity 2026 Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nRelated Tools Format and validate JSON data → JSON Formatter Test regular expressions in real-time → Regex Tester Create a monthly budget → Budget Planner Calculate your ideal freelance rate → Freelance Rate Calculator Related Templates Start using these techniques right away with our Excel templates:\nSmart Budget Tracker (Excel Template) — Automated calculations and visual dashboards The AI Productivity Playbook 2026 — AI-powered Excel automation workflows ","permalink":"https://productivity-works.com/posts/how-to-use-ai-for-excel-automation-2026/","summary":"\u003ch1 id=\"how-to-use-ai-for-excel-automation--complete-guide-2026\"\u003eHow to Use AI for Excel Automation — Complete Guide [2026]\u003c/h1\u003e\n\u003cp\u003eLet\u0026rsquo;s be honest: most of us are using Excel at maybe 20% of its capacity. We know there are faster ways to do what we\u0026rsquo;re doing — better formulas, automated reports, dynamic dashboards — but learning them takes time we don\u0026rsquo;t have. So we copy-paste data manually, wrestle with VLOOKUP at 9pm, and wonder why the spreadsheet always breaks when someone else touches it.\u003c/p\u003e","title":"How to Use AI for Excel Automation 2026"},{"content":"How to Use ChatGPT for Data Analysis — Complete Guide [2026] Data is everywhere in 2026, and the ability to extract meaning from it is one of the most in-demand professional skills on the planet. But here\u0026rsquo;s the thing: most data analysis work isn\u0026rsquo;t done by data scientists. It\u0026rsquo;s done by project managers staring at a messy CSV, marketing managers trying to understand campaign performance, and business owners who need to know if last month\u0026rsquo;s numbers were actually good or just okay.\nChatGPT has become a genuine data analysis partner for non-technical professionals. It can interpret your data, suggest the right analysis approach, write Python or R code, explain statistical concepts in plain English, and help you visualize results — even if you\u0026rsquo;ve never written a line of code.\nHere\u0026rsquo;s what you\u0026rsquo;ll learn:\nHow ChatGPT\u0026rsquo;s built-in data analysis tool (Advanced Data Analysis) works The exact prompts to use for common data analysis tasks How to analyze CSV files, Excel data, and raw numbers with AI A comparison of AI tools for data analysis Advanced techniques for getting publication-quality insights from messy data No data science degree required. If you have data and a question, this guide shows you how to get the answer.\nWhy Using ChatGPT for Data Analysis Matters in 2026 The Productivity Gap Traditional data analysis requires knowing SQL, Python (pandas, matplotlib), R, or at minimum, Excel pivot tables at an expert level. For the vast majority of professionals, this creates a bottleneck: they have data, they have questions, but they lack the technical means to connect the two quickly.\nThe result is either slow manual analysis, expensive analyst time ($80–$200/hour), or decisions made without data because the analysis takes too long. This gap is where AI creates enormous value.\nHow AI Changes the Game ChatGPT Plus includes a feature called Advanced Data Analysis (formerly Code Interpreter). You can literally upload a CSV file, ask a question in plain English, and ChatGPT will write Python code, run it, and give you the answer — plus the code to reproduce it. For analysts, it\u0026rsquo;s a force multiplier. For non-technical professionals, it\u0026rsquo;s a superpower.\nEven without the file upload, ChatGPT can write complete analysis scripts you run yourself, explain what statistical tests to use and why, interpret results in plain language, and help you visualize data with code or prompts for charting tools.\nBest AI Data Analysis Tools Compared Tool File Upload Code Execution Visualization SQL Price ChatGPT Plus (Advanced Data Analysis) Yes (CSV, Excel, PDF) Yes (Python) Yes (matplotlib, seaborn) Via code $20/month Claude Pro Yes (document analysis) No (generates code) No (generates code) Via code $20/month Google Gemini Advanced Yes Yes (via Colab) Yes Via BigQuery $20/month Microsoft Copilot (Excel/Power BI) Native in Excel/BI Via Excel Excellent Via Power BI M365 subscription Julius AI Yes — specialized for data Yes Excellent charts Yes $20–$49/month Noteable / Code Interpreter API Yes Yes Yes Yes API pricing Try It Free Bottom line: For most professionals, ChatGPT Plus with Advanced Data Analysis is the best starting point. Julius AI is worth considering if data analysis is your primary use case. Microsoft Copilot in Excel is ideal if you live in spreadsheets.\nStep-by-Step Guide: How to Use ChatGPT for Data Analysis Step 1: Upload Your Data (ChatGPT Advanced Data Analysis) With ChatGPT Plus, you can upload CSV, Excel, or PDF files directly.\nAfter uploading, use this prompt template:\nI\u0026#39;ve uploaded a CSV file with [DESCRIBE DATA: e.g., \u0026#34;monthly sales data for 2025, with columns: Date, Product, Region, Units Sold, Revenue\u0026#34;]. Please: 1. Show me the first 10 rows 2. Give me a summary of the dataset (number of rows, columns, data types, any missing values) 3. Describe the key statistics for numerical columns (mean, median, min, max) 4. Flag any data quality issues I should know about before analyzing Step 2: Ask Business Questions, Not Technical Questions You don\u0026rsquo;t need to know the right analysis technique — describe the business question and let AI choose the method.\nRevenue Analysis Prompt:\nUsing the uploaded sales data, answer these business questions: 1. Which product generated the most revenue in 2025? 2. Which region had the highest growth from Q1 to Q4? 3. Are there any months where revenue dropped more than 10% month-over-month? 4. What\u0026#39;s the average revenue per transaction, and how does it vary by region? Show me the results with clear numbers and a brief interpretation of what each finding means for the business. Trend Analysis Prompt:\nAnalyze the sales trend over the 12-month period in the uploaded data. - Is there a clear upward, downward, or seasonal trend? - Identify the top 3 months and bottom 3 months by revenue - Calculate the month-over-month growth rate for each month - Visualize the trend as a line chart with months on X axis and revenue on Y axis Cohort / Segmentation Prompt:\nSegment the customers in this dataset into groups based on their total purchase value (Low: under $100, Medium: $100–$500, High: over $500). - How many customers are in each segment? - What\u0026#39;s the average order value per segment? - Which segment has the highest purchase frequency? Present the results in a clear table. Step 3: Generate Python Code for Reproducible Analysis Even if you don\u0026rsquo;t run code yourself, having the code means an analyst or developer on your team can reproduce and extend your analysis.\nPrompt for Clean Python Analysis Script:\nWrite a complete Python script using pandas that: 1. Loads the CSV file at path: \u0026#34;sales_data.csv\u0026#34; 2. Cleans the data: removes duplicates, fills missing Revenue values with 0, converts Date column to datetime format 3. Creates a monthly revenue summary grouped by month and region 4. Outputs: a summary DataFrame printed to console, and a bar chart saved as \u0026#34;monthly_revenue.png\u0026#34; showing revenue by region 5. Add clear comments explaining each section Include all necessary imports at the top. Prompt for Statistical Analysis:\nI have two groups of sales data: Group A (control): [PASTE DATA or describe] Group B (new pricing): [PASTE DATA or describe] Write Python code to: 1. Run an appropriate statistical test to determine if the difference in average sales between the two groups is statistically significant 2. State which test you used and why 3. Interpret the result in plain English — is the difference real or could it be due to chance? 4. Include a visualization comparing the two distributions Step 4: Analyze Without Uploading a File If you can\u0026rsquo;t or don\u0026rsquo;t want to upload data, paste it directly into the chat or describe the numbers.\nPaste-Based Analysis Prompt:\nHere is my monthly revenue data for 2025 (in USD): Jan: $42,300 | Feb: $38,900 | Mar: $51,200 | Apr: $49,700 | May: $55,100 Jun: $61,400 | Jul: $58,200 | Aug: $63,900 | Sep: $71,100 | Oct: $68,400 Nov: $79,300 | Dec: $91,200 Analyze this data and tell me: 1. Total annual revenue 2. Average monthly revenue 3. Month-over-month growth rate for each month 4. Best and worst performing months 5. Overall trend: is the business growing, declining, or flat? 6. If the trend continues at the same rate, what would you project for January 2026? Step 5: Interpret and Communicate Results Analysis is only valuable when it becomes a decision. Use AI to turn numbers into narrative.\nExecutive Summary Prompt:\nBased on the analysis above, write a 200-word executive summary suitable for sharing with non-technical stakeholders. Include: - The main finding (1 sentence) - 3 supporting data points - What this means for the business - 1 recommended action Use plain language. No jargon. Focus on what matters most. Pro Tips \u0026amp; Advanced Techniques Common Mistakes to Avoid Mistake 1: Uploading raw messy data without describing it first. Always tell ChatGPT what the columns mean, what the data source is, and what time period it covers. Context dramatically improves analysis quality.\nMistake 2: Asking for analysis without a business question. \u0026ldquo;Analyze my sales data\u0026rdquo; gets a generic summary. \u0026ldquo;Which product line should we double down on based on margin and growth?\u0026rdquo; gets actionable insight.\nMistake 3: Trusting numbers without validating. ChatGPT can make arithmetic errors, especially on large datasets without file upload. Always sanity-check key numbers against your source data.\nMistake 4: Not asking for uncertainty. Ask \u0026ldquo;how confident are you in this analysis?\u0026rdquo; and \u0026ldquo;what data would you need to be more certain?\u0026rdquo; This surfaces limitations before you act on them.\nMistake 5: Skipping visualization. Numbers alone are hard to internalize. Always ask for a chart or visualization alongside numerical results — even if it\u0026rsquo;s just ASCII art for a quick sense-check.\nPower User Strategies Strategy 1: Use ChatGPT to design your analysis plan before executing. Ask \u0026ldquo;What analysis should I run to answer [BUSINESS QUESTION] given I have [DESCRIBE DATA]?\u0026rdquo; This ensures you run the right tests.\nStrategy 2: Chain analysis and communication. Run the analysis, then immediately say \u0026ldquo;Now write a Slack message summarizing these findings for my team\u0026rdquo; or \u0026ldquo;Write a slide title and three bullet points for a presentation.\u0026rdquo;\nStrategy 3: Ask for alternative interpretations. After getting an analysis result, ask \u0026ldquo;Is there another way to interpret this data that leads to a different conclusion?\u0026rdquo; This prevents over-anchoring on one reading.\nStrategy 4: Use AI to build your analysis templates. Once you\u0026rsquo;ve gotten a good analysis for a recurring report (monthly revenue, weekly traffic), ask ChatGPT to save the process as a reusable Python script or Excel macro.\nStrategy 5: Combine with visualization tools. Export ChatGPT\u0026rsquo;s output to Tableau, Power BI, or even Google Data Studio. Use AI to generate the underlying data transformations, then use dedicated viz tools for presentation-quality charts.\nRelated: How to Use AI for Excel Automation Frequently Asked Questions Q: Do I need a ChatGPT Plus subscription to analyze data? A: To upload files and run code directly, yes — you need ChatGPT Plus ($20/month) for Advanced Data Analysis. For text-based analysis (pasting data into the chat, getting Python code to run yourself), the free tier works fine.\nQ: Can ChatGPT analyze large datasets? A: With file upload, ChatGPT handles files up to about 25MB reliably. For very large datasets (millions of rows), it\u0026rsquo;s better to ask ChatGPT to write the Python/SQL code and run it yourself on your local machine or database.\nQ: Is my data safe when I upload it to ChatGPT? A: OpenAI\u0026rsquo;s standard consumer product may use your inputs for model training. For sensitive business data, use the ChatGPT Enterprise or API versions, which have stronger privacy controls and opt-out from training.\nQ: What types of files can ChatGPT analyze? A: With Advanced Data Analysis: CSV, Excel (.xlsx), PDF (text-based), and plain text files. For images with charts, ChatGPT can describe what it sees but can\u0026rsquo;t extract underlying data values.\nQ: Can I replace my data analyst with ChatGPT? A: For routine descriptive analytics, summarization, and basic statistical analysis — ChatGPT can handle a large portion of what entry-level analysts do. For complex modeling, causal inference, and business-specific context, human analysts remain essential.\nQ: Can ChatGPT connect to my database directly? A: Not natively in the consumer product. But you can ask it to write SQL queries that you then run in your database. Some third-party integrations and enterprise deployments do allow live database connections.\nQ: What statistical concepts should I know to work with ChatGPT for data analysis? A: Very few. Understanding \u0026ldquo;average vs. median,\u0026rdquo; \u0026ldquo;correlation vs. causation,\u0026rdquo; and \u0026ldquo;statistical significance\u0026rdquo; is helpful. For anything more advanced, describe your business question and ask ChatGPT to recommend and explain the appropriate technique.\nData Skills Are Among the Highest-Paid in 2026 Analysts who can work with AI tools to extract insights faster are in high demand. Find your next career on doda — browse data analyst, business intelligence, and analytics roles on Japan\u0026rsquo;s top job platform.\nConclusion \u0026amp; Call to Action Data analysis is no longer gated behind a technical skill set. With ChatGPT\u0026rsquo;s Advanced Data Analysis, you can upload a file, ask business questions, and get clear, actionable insights in minutes — with visualizations and reproducible code included. The key is asking the right questions and providing enough context for AI to give you genuinely useful analysis.\nKey takeaways:\nChatGPT Plus with Advanced Data Analysis can upload and execute analysis on your actual data Always frame questions as business questions, not technical analysis requests Ask for Python code to make your analysis reproducible and shareable Validate key numbers against your source data before acting on them Use AI to translate raw analysis into stakeholder-ready summaries Want 30+ ready-to-use data analysis prompt templates organized by task type (trend analysis, segmentation, statistical testing, and executive summaries)? Grab our Complete ChatGPT Prompt Collection — the data analysis section alone is worth the price.\nFor a complete AI workflow that covers data, reporting, and decision-making, our AI Productivity Playbook walks you through building a data-driven practice from scratch using AI tools.\nRelated: How to Use AI for Excel Automation Disclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nRelated Tools Format and validate JSON data → JSON Formatter Test regular expressions in real-time → Regex Tester Create a monthly budget → Budget Planner Calculate your ideal freelance rate → Freelance Rate Calculator Related Templates Put these techniques into practice with our ready-made templates:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Copy-paste prompts for every situation The AI Productivity Playbook 2026 — 50+ AI workflows and automation strategies ","permalink":"https://productivity-works.com/posts/how-to-use-chatgpt-for-data-analysis-2026/","summary":"\u003ch1 id=\"how-to-use-chatgpt-for-data-analysis--complete-guide-2026\"\u003eHow to Use ChatGPT for Data Analysis — Complete Guide [2026]\u003c/h1\u003e\n\u003cp\u003eData is everywhere in 2026, and the ability to extract meaning from it is one of the most in-demand professional skills on the planet. But here\u0026rsquo;s the thing: most data analysis work isn\u0026rsquo;t done by data scientists. It\u0026rsquo;s done by project managers staring at a messy CSV, marketing managers trying to understand campaign performance, and business owners who need to know if last month\u0026rsquo;s numbers were actually good or just okay.\u003c/p\u003e","title":"How to Use ChatGPT for Data Analysis 2026"},{"content":" How to Use ChatGPT for Studying: Everything Students Need to Know Students who use ChatGPT well don\u0026rsquo;t just get answers faster — they understand material more deeply, prepare for exams more effectively, and develop skills that will serve them for decades.\nStudents who use ChatGPT poorly get answers they don\u0026rsquo;t understand, submit work they didn\u0026rsquo;t really write, and miss the learning that makes education valuable.\nThis guide is about using ChatGPT the right way: as a tool that makes you a better learner, not a shortcut that bypasses learning.\nWhy ChatGPT Is Different From a Search Engine (and Why It Matters for Studying) Google gives you links. ChatGPT gives you a conversation.\nThat difference is profound for learning. You can:\nAsk for simpler explanations when you don\u0026rsquo;t understand something Ask follow-up questions that build on previous answers Request multiple examples until a concept clicks Get something explained \u0026ldquo;like you\u0026rsquo;re 10\u0026rdquo; or \u0026ldquo;like you have a PhD\u0026rdquo; Test your understanding by having ChatGPT quiz you This two-way interaction is closer to a one-on-one tutor than a search engine — and for many students, it\u0026rsquo;s the most personalized learning experience they\u0026rsquo;ve ever had.\nThe Cardinal Rule: Use AI to Learn, Not to Skip Learning Before the techniques: a note on academic integrity.\nMost schools and universities have AI use policies. Some prohibit AI assistance entirely. Many allow it for some purposes (brainstorming, research) but not others (submitting AI-generated essays as your own work).\nUsing ChatGPT to understand something is almost universally acceptable. Using ChatGPT to do your work and submitting it as your own is cheating in most contexts.\nBeyond policy: the students who use AI to skip learning show up unprepared for exams, interviews, and the actual application of their education. The students who use AI to learn faster show up more capable than their peers.\nThis guide focuses entirely on the first category.\nPart 1: Understanding Complex Concepts Technique 1: The \u0026ldquo;Explain It to Me\u0026rdquo; Request When you encounter a concept you don\u0026rsquo;t understand, don\u0026rsquo;t just Google it — have a conversation.\nBasic prompt:\n\u0026ldquo;Explain [concept] to me. I\u0026rsquo;m a [year] [subject] student and I\u0026rsquo;ve just encountered this for the first time.\u0026rdquo;\nExample:\n\u0026ldquo;Explain monetary policy to me. I\u0026rsquo;m a first-year economics student and I\u0026rsquo;ve just read about it for the first time but I\u0026rsquo;m confused about how interest rates affect inflation.\u0026rdquo;\nChatGPT will give you an initial explanation. Then:\nAsk \u0026ldquo;Can you use a simple real-world example?\u0026rdquo; Ask \u0026ldquo;What\u0026rsquo;s the part most students find confusing?\u0026rdquo; Ask \u0026ldquo;How does this relate to [other concept I know]?\u0026rdquo; Technique 2: The Ladder of Complexity Ask ChatGPT to explain the same concept at different levels:\nPrompt template:\n\u0026ldquo;Explain [concept] in three ways: (1) like I\u0026rsquo;m 10 years old, (2) like I\u0026rsquo;m a college student who just started studying this, and (3) like I already understand the basics and want deeper insight.\u0026rdquo;\nThis gives you entry points at multiple levels, helping you find where your understanding actually lives.\nTechnique 3: Analogies on Demand Human brains understand new things by connecting them to things already understood. Ask ChatGPT for analogies until one clicks.\nPrompt:\n\u0026ldquo;I\u0026rsquo;m trying to understand [concept]. Can you give me 5 different analogies that explain it? Make them come from different everyday contexts (sports, cooking, relationships, technology, etc.).\u0026rdquo;\nTechnique 4: The \u0026ldquo;What Am I Missing?\u0026rdquo; Check After you think you\u0026rsquo;ve understood something:\nPrompt:\n\u0026ldquo;I think I understand [concept]. Here\u0026rsquo;s what I think I know: [your explanation in your own words]. What am I getting wrong, oversimplifying, or missing?\u0026rdquo;\nThis is one of the most valuable uses of ChatGPT for studying — getting targeted feedback on your actual understanding.\nPart 2: Exam Preparation and Practice Technique 5: Generate Practice Questions Prompt template:\n\u0026ldquo;I have an exam on [subject] covering [topics]. Generate 15 practice questions at different difficulty levels — some basic recall, some application, and some that require deeper analysis. Don\u0026rsquo;t give me the answers yet.\u0026rdquo;\nStudy the questions first. Attempt your answers. Then:\n\u0026ldquo;Now give me the answers and explain where I might make mistakes.\u0026rdquo;\nTechnique 6: The Socratic Quiz Tell ChatGPT to quiz you interactively:\nPrompt:\n\u0026ldquo;Quiz me on [topic] using the Socratic method. Ask me one question at a time. After I answer, tell me if I\u0026rsquo;m right or wrong, explain any mistakes, then ask the next question. Keep going until I\u0026rsquo;ve demonstrated solid understanding of the key concepts.\u0026rdquo;\nThis simulates a one-on-one tutoring session. Don\u0026rsquo;t move to the next question until you genuinely understand your mistake on the previous one.\nTechnique 7: Create Study Guides and Summaries For long reading assignments:\n\u0026ldquo;Here\u0026rsquo;s [paste text or describe the reading]. Create a structured study guide with: (1) the 5 most important concepts, (2) key terms and definitions, (3) how these concepts connect to each other, and (4) 3 likely exam questions.\u0026rdquo;\nFor creating flashcard content:\n\u0026ldquo;Based on [topic], create 20 question-answer pairs formatted for flashcard study. Focus on the concepts most likely to appear on exams.\u0026rdquo;\nYou can import these into Anki or Quizlet for spaced repetition study.\nTechnique 8: Past Exam Practice If you have access to past exam papers:\nPrompt:\n\u0026ldquo;Here are questions from my past exam on [subject]: [paste questions]. I want to practice answering these. I\u0026rsquo;ll give you my answer for each one, and I want you to: (1) tell me if my answer is correct and complete, (2) explain what\u0026rsquo;s missing or wrong, (3) give me the ideal answer.\u0026rdquo;\nWork through questions one at a time with feedback after each.\nPart 3: Writing and Research Technique 9: Brainstorming and Thesis Development Use ChatGPT at the beginning of writing assignments — not the end.\nPrompt:\n\u0026ldquo;I need to write a [length] essay on [topic] for [class]. My initial thoughts are [brief description]. Help me brainstorm different angles and approaches. Don\u0026rsquo;t write the essay — just help me think through the possibilities.\u0026rdquo;\nAfter exploring options:\n\u0026ldquo;I want to argue [your chosen argument]. Help me identify the strongest counterarguments I\u0026rsquo;ll need to address and the best evidence types to support my position.\u0026rdquo;\nThis is the legitimate use of AI in essay writing — sharpening your thinking, not replacing it.\nTechnique 10: Understanding Research Papers Academic papers are often dense and difficult to parse. ChatGPT can help you extract the key insights:\nPrompt:\n\u0026ldquo;Here\u0026rsquo;s the abstract and introduction of a research paper I need to understand for my class: [paste text]. (1) What is the research question? (2) What method did they use? (3) What did they find? (4) Why does this matter? (5) What are the limitations of this study?\u0026rdquo;\nFor longer papers, work section by section.\nTechnique 11: Getting Feedback on Your Writing Before submitting your essay:\n\u0026ldquo;Here\u0026rsquo;s my essay draft: [paste essay]. Give me feedback on: (1) clarity of my argument, (2) logical flow between paragraphs, (3) strength of my evidence, (4) any factual claims I should double-check, and (5) the quality of my conclusion. Don\u0026rsquo;t rewrite it — just give me specific, actionable feedback.\u0026rdquo;\nThe key: don\u0026rsquo;t ask ChatGPT to rewrite your essay. Ask for feedback that helps you improve it yourself.\nTechnique 12: Citation and Source Verification Important warning: ChatGPT can hallucinate citations — inventing papers that don\u0026rsquo;t exist. Never use AI-generated citations without verifying them independently.\nSafe use:\n\u0026ldquo;I\u0026rsquo;m writing about [topic] for a [class] paper. What search terms should I use to find academic sources? What key researchers or journals should I look for?\u0026rdquo;\nUse this to direct your actual research in Google Scholar, JSTOR, or your library database — not to get citations from ChatGPT directly.\nPart 4: Subject-Specific Study Strategies Mathematics and Science For understanding concepts:\n\u0026ldquo;Explain [concept] in calculus/physics/chemistry. Include: (1) intuitive explanation, (2) formal definition, (3) a worked example, (4) the common mistakes students make.\u0026rdquo;\nFor worked problems:\n\u0026ldquo;Walk me through how to solve this type of problem step by step: [problem type or example]. Explain the reasoning behind each step, not just the mechanical process.\u0026rdquo;\nFor self-testing:\n\u0026ldquo;Give me 5 problems of increasing difficulty on [topic]. I\u0026rsquo;ll solve them and show you my work. Then tell me where my reasoning is correct or incorrect.\u0026rdquo;\nHistory and Social Sciences For understanding historical events:\n\u0026ldquo;Explain [historical event] in context. What were the root causes? What were the immediate triggers? What were the short-term and long-term consequences? What do historians disagree about regarding this event?\u0026rdquo;\nFor essay arguments:\n\u0026ldquo;I\u0026rsquo;m writing about [historical/social topic]. What are the different scholarly perspectives on this? What evidence supports each perspective?\u0026rdquo;\nLanguages and Literature For language learning:\n\u0026ldquo;I\u0026rsquo;m learning [language] at [level]. Can you act as my language tutor? I\u0026rsquo;ll write sentences in [language] and you\u0026rsquo;ll correct my grammar, explain the rule I\u0026rsquo;m applying wrong, and give me a better version.\u0026rdquo;\nFor literature analysis:\n\u0026ldquo;I\u0026rsquo;m analyzing [book/poem/work] by [author]. Help me understand: (1) the major themes, (2) what the author\u0026rsquo;s biographical context suggests about the work, (3) different critical interpretations scholars have offered.\u0026rdquo;\nLaw and Medicine (Professional Programs) For case analysis:\n\u0026ldquo;Walk me through how to analyze [type of legal/medical case]. What framework should I apply? What are the key questions to ask? What common reasoning errors should I avoid?\u0026rdquo;\nFor memorization:\n\u0026ldquo;Create mnemonics or memory aids for [list of items I need to memorize]. Make them vivid and memorable.\u0026rdquo;\nPart 5: Time Management and Study Planning Technique 13: Building a Study Schedule Prompt:\n\u0026ldquo;I have exams in [list subjects] on [dates]. I have roughly [hours] available to study per day. Help me build a study schedule that prioritizes based on difficulty, covers all material, and includes review time. My weakest subjects are [list].\u0026rdquo;\nChatGPT will generate a day-by-day plan. Adjust as needed.\nTechnique 14: Breaking Down Overwhelming Assignments Prompt:\n\u0026ldquo;I have to [describe assignment] due in [timeframe]. I feel overwhelmed and don\u0026rsquo;t know where to start. Break this down into the smallest possible steps I can take one at a time, starting with the easiest.\u0026rdquo;\nThis technique — decomposing an overwhelming task into tiny steps — is one of the most effective ways to overcome procrastination.\nPart 6: Getting Better Results from ChatGPT Always provide context Bad prompt: \u0026ldquo;Explain photosynthesis.\u0026rdquo; Better prompt: \u0026ldquo;Explain photosynthesis to a 10th-grade biology student who understands basic chemistry but hasn\u0026rsquo;t studied cellular biology yet.\u0026rdquo;\nThe more context you give, the more tailored the explanation.\nAsk for multiple versions \u0026ldquo;Give me three different explanations of [concept] — one very simple, one medium, one detailed.\u0026rdquo;\nPush back and ask follow-ups If you don\u0026rsquo;t understand an explanation: \u0026ldquo;I\u0026rsquo;m still confused. Can you try a different approach and use a more concrete example?\u0026rdquo;\nUse Claude for nuanced explanations While this guide focuses on ChatGPT, Claude (by Anthropic) is also excellent for studying — particularly for nuanced topics in humanities, social sciences, and complex reasoning. Try both for different subjects.\nSee our comparison: ChatGPT vs Claude vs Gemini 2026 What ChatGPT Cannot Reliably Do for Studying Be aware of these limitations:\n1. Provide accurate citations. Always verify any specific claims, statistics, or citations independently.\n2. Know your professor\u0026rsquo;s expectations. ChatGPT doesn\u0026rsquo;t know your professor\u0026rsquo;s grading rubric or what they emphasize. Attend class.\n3. Replace actually reading. Asking ChatGPT to summarize a book and then not reading it means you\u0026rsquo;ll lack the textual grounding to discuss it intelligently in class or on an exam.\n4. Know extremely recent events. ChatGPT\u0026rsquo;s training data has a cutoff date. For current events research, use primary sources.\n5. Be reliably accurate on specialized technical topics. In highly specialized academic fields, ChatGPT can make plausible-sounding errors. Always cross-reference with course materials and textbooks.\nA Sample Study Session Using ChatGPT Here\u0026rsquo;s how a 2-hour study session might look using these techniques:\nMinutes 1-20: Use ChatGPT to get an overview and intuitive explanation of today\u0026rsquo;s material before re-reading your notes.\nMinutes 20-50: Re-read your notes with ChatGPT available to answer questions as they come up.\nMinutes 50-80: Have ChatGPT quiz you on the material using the Socratic Quiz technique.\nMinutes 80-100: Ask ChatGPT to identify 3-5 likely exam questions and practice your answers.\nMinutes 100-120: Use ChatGPT to create a set of flashcards for the key terms from today\u0026rsquo;s material.\nThis session uses ChatGPT as a learning tool throughout — not as a replacement for studying.\nAcademic Integrity Checklist Before submitting any work where you used ChatGPT:\nDid I use AI to understand material, not just get answers? Is the submitted work genuinely my own thinking and writing? Have I checked my school\u0026rsquo;s AI use policy for this assignment? Have I disclosed AI assistance if required by my institution? Could I explain and defend everything in this work if asked? Turn Your Studies Into Career Opportunities Great study habits and AI skills are the foundation of a standout career. When you\u0026rsquo;re ready to apply them, find your next career on doda — Japan\u0026rsquo;s top job platform with thousands of entry-level and career-change opportunities.\nStudy Smarter, Not Just Harder The students who will thrive in 2026 and beyond are those who learn to work with AI tools as learning accelerators — not those who use them as shortcuts.\nOur Student Productivity Toolkit includes ChatGPT prompt templates for 12 subject areas, a weekly study planner template, and an exam preparation framework — all designed for students who want to learn faster and more effectively.\nRelated Reading:\nBest ChatGPT Prompts for Productivity 2026 ChatGPT vs Claude vs Gemini 2026 AI Prompt Engineering Tips for Beginners 2026 Related Tools Create a monthly budget → Budget Planner Set a savings goal → Savings Goal Calculator Stay focused with the Pomodoro Technique → Pomodoro Timer This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/how-to-use-chatgpt-for-studying-guide-2026/","summary":"\u003chr\u003e\n\u003ch2 id=\"how-to-use-chatgpt-for-studying-everything-students-need-to-know\"\u003eHow to Use ChatGPT for Studying: Everything Students Need to Know\u003c/h2\u003e\n\u003cp\u003eStudents who use ChatGPT well don\u0026rsquo;t just get answers faster — they understand material more deeply, prepare for exams more effectively, and develop skills that will serve them for decades.\u003c/p\u003e\n\u003cp\u003eStudents who use ChatGPT poorly get answers they don\u0026rsquo;t understand, submit work they didn\u0026rsquo;t really write, and miss the learning that makes education valuable.\u003c/p\u003e\n\u003cp\u003eThis guide is about using ChatGPT the right way: as a tool that makes you a better learner, not a shortcut that bypasses learning.\u003c/p\u003e","title":"How to Use ChatGPT for Studying: The Complete Student Guide 2026"},{"content":"This article contains affiliate links.\nThe most common reason people don\u0026rsquo;t invest is that they think they need more money to get started. They\u0026rsquo;re waiting for a windfall, a raise, or a point when everything feels financially stable. That point never comes, and the delay is expensive.\nTime in the market matters more than the amount you start with. A $100 investment made today beats a $1,000 investment made five years from now — thanks to compound growth, the mechanism that makes patient investors wealthy.\nThis guide covers everything a genuine beginner needs to start investing with $100 or less.\nWhy Starting Small Still Matters Here\u0026rsquo;s the math that makes this concrete. Assume you invest $100 today and then $100 every month, with a 7% average annual return (roughly the historical US stock market average after inflation):\nYears Total Contributed Account Value 5 $6,100 $7,261 10 $12,100 $17,309 20 $24,100 $52,093 30 $36,100 $121,997 That\u0026rsquo;s $121,997 from $36,100 in contributions. The $85,897 difference is compound growth — money your money made, without any additional effort from you.\nStart five years later, and you lose roughly $40,000 from that final figure. The cost of waiting is real and large.\nStep 1: Build a Financial Foundation First Before putting money into the stock market, three things need to be in place:\nEmergency fund: Keep 1–3 months of expenses in a high-yield savings account before investing. Money you might need in six months doesn\u0026rsquo;t belong in stocks, which can drop 30% in a bad year. (Compare high-yield savings accounts )\nHigh-interest debt: If you\u0026rsquo;re carrying credit card balances at 18–25% APR, paying those down first is a guaranteed 18–25% return. No investment reliably matches that.\nEmployer 401(k) match: If your employer matches contributions to a 401(k), contribute at least enough to capture that match before investing anywhere else. A 50% match on your contribution is an immediate 50% return.\nOnce those three are handled, every dollar you invest is genuinely working for you.\nStep 2: Choose the Right Account Type The account you use matters as much as what you invest in, because taxes can consume a significant portion of your returns.\nRoth IRA (US) Contributions made with after-tax money Growth and withdrawals in retirement are completely tax-free 2026 contribution limit: $7,000/year ($8,000 if you\u0026rsquo;re 50+) Best for: Younger investors and anyone who expects to be in a higher tax bracket in retirement Traditional IRA (US) Contributions may be tax-deductible now Withdrawals in retirement are taxed as ordinary income Best for: People in a high tax bracket today who expect to be in a lower bracket in retirement Stocks and Shares ISA (UK) Invest up to £20,000/year All growth and income is tax-free Best for: All UK investors — use this before a standard brokerage account Taxable Brokerage Account No contribution limits No special tax treatment — you pay capital gains tax on profits Best for: Investors who\u0026rsquo;ve maxed tax-advantaged accounts or need flexibility For most US beginners: Open a Roth IRA first. For UK beginners: open a Stocks and Shares ISA.\nStep 3: Pick a Brokerage You need a place to hold your investments. For beginners, low fees and simplicity matter most.\nBrokerage Country Minimum Key Feature Fidelity US $0 No fees, great index funds, excellent customer service Charles Schwab US $0 No fees, good research tools Vanguard US/UK $0–$1,000 Creator of index fund investing; excellent for long-term Robinhood US $0 Simple app, fractional shares Freetrade UK £0 Commission-free, ISA available Trading 212 UK £1 Fractional shares, commission-free ISA [Open a Fidelity brokerage account at fidelity.com — $0 minimum] [Open a Trading 212 Stocks and Shares ISA at trading212.com (UK)]\nAvoid any brokerage charging per-trade commissions. In 2026, commission-free trading is the baseline standard.\nStep 4: Understand What You\u0026rsquo;re Buying Index Funds and ETFs — The Beginner\u0026rsquo;s Best Friend An index fund tracks a market index — like the S\u0026amp;P 500, which contains the 500 largest US companies. When you buy one share of an S\u0026amp;P 500 index fund, you own a tiny piece of Apple, Microsoft, Amazon, Nvidia, and hundreds of other companies simultaneously.\nWhy index funds for beginners:\nInstant diversification — you\u0026rsquo;re not betting on one company Low fees — expense ratios as low as 0.03%/year Proven track record — the S\u0026amp;P 500 has returned roughly 10%/year on average over the past century No expertise required — you don\u0026rsquo;t need to pick stocks The Three-Fund Portfolio A simple, time-tested approach used by millions of investors:\nUS Total Market or S\u0026amp;P 500 index fund — core holding International Stock index fund — global diversification Bond index fund — stability, proportional to your age A common rule of thumb: subtract your age from 110 to get your stock allocation. A 30-year-old would hold 80% stocks, 20% bonds.\nSpecific Funds Worth Knowing Fund Type Expense Ratio VTI (Vanguard Total Stock Market ETF) US stocks 0.03% VXUS (Vanguard Total International) International stocks 0.07% BND (Vanguard Total Bond Market) Bonds 0.03% VOO (Vanguard S\u0026amp;P 500 ETF) US large-cap 0.03% VWRP (Vanguard FTSE All-World, UK) Global stocks 0.22% Step 5: Your First $100 Investment — Exactly Here\u0026rsquo;s a concrete action plan:\nIf you\u0026rsquo;re in the US:\nOpen a Roth IRA at Fidelity (free, takes 10 minutes online) Link your bank account and transfer $100 Buy FSKAX (Fidelity Total Market Index) or VOO Set up automatic monthly contributions of whatever you can manage If you\u0026rsquo;re in the UK:\nOpen a Stocks and Shares ISA at Freetrade or Trading 212 (free) Deposit £100 Buy VWRP (Vanguard FTSE All-World ETF) or a global index fund Enable automatic investing if the platform supports it That\u0026rsquo;s it. The entire process takes under an hour.\nCommon Beginner Mistakes to Avoid Checking your portfolio daily. Markets fluctuate constantly. Checking daily leads to emotional decisions — and emotional decisions cost money. Check monthly at most.\nTrying to time the market. Nobody consistently buys at the bottom and sells at the top. Studies repeatedly show that investors who stay invested through downturns outperform those who try to dodge them.\nBuying individual stocks before understanding the basics. Individual stocks are volatile. A single company can lose 80% of its value. Start with broad index funds and only add individual stocks after you\u0026rsquo;ve built a solid foundation and understand what you\u0026rsquo;re doing.\nIgnoring fees. An expense ratio of 1% versus 0.1% sounds small. Over 30 years on a $50,000 portfolio, that difference is roughly $70,000 in lost returns.\nSelling during a market crash. Markets drop — 10%, 20%, sometimes 40% in severe recessions. These drops are temporary. Selling turns paper losses into real ones and means you miss the recovery.\nWhat to Do After Your First $100 The goal is to make investing automatic so it happens without willpower:\nAutomate contributions — Set up a recurring transfer to your investment account on payday Increase contributions over time — Every raise, direct part of it to investing before you adjust your lifestyle Learn gradually — Read one investing book per year. Start with The Little Book of Common Sense Investing by John Bogle Rebalance annually — Once a year, check if your allocation has drifted from your target and adjust You don\u0026rsquo;t need to become a financial expert. You need a simple system that runs in the background while you live your life.\nThe Bottom Line Investing isn\u0026rsquo;t complicated — the financial industry profits from making it seem that way. The basics are simple:\nStart as early as possible, with whatever you have Use tax-advantaged accounts first Buy low-cost, diversified index funds Automate contributions and ignore short-term volatility Stay consistent for decades A $100 investment made today is worth more than a $1,000 investment made next year. The best time to start was ten years ago. The second-best time is today.\nInvesting from Japan? Rakuten Securities offers NISA accounts, low-cost index funds, and an interface accessible to English speakers — making it one of the most beginner-friendly brokerages for residents in Japan who want to start with a small amount.\n[Open a Roth IRA at Fidelity — $0 minimum at fidelity.com] [Open a Stocks and Shares ISA at Trading 212 (UK)]\nRelated Tools \u0026amp; Articles Calculate percentages, discounts, and tips instantly → Percentage Calculator See how $100/month grows over time → Compound Interest Calculator Estimate dividend income from your portfolio → Dividend Income Calculator Create a budget to free up investment funds → Budget Calculator Calculate how long to reach any savings target → Savings Goal Calculator See how inflation affects your money → Inflation Calculator Best Index Funds for Beginners 2026 ETF vs Mutual Fund: Which Should I Choose? How to Start Investing with $100 How to Build an Emergency Fund Fast Best Budgeting Apps 2026: Full Comparison Related Templates Take control of your finances with these tools:\nSmart Budget Tracker (Excel Template) — Track income, expenses, and savings goals Side Hustle Starter Kit 2026 — Build additional income streams ","permalink":"https://productivity-works.com/posts/investing-for-beginners-start-with-100/","summary":"\u003cp\u003e\u003cem\u003eThis article contains affiliate links.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eThe most common reason people don\u0026rsquo;t invest is that they think they need more money to get started. They\u0026rsquo;re waiting for a windfall, a raise, or a point when everything feels financially stable. That point never comes, and the delay is expensive.\u003c/p\u003e\n\u003cp\u003eTime in the market matters more than the amount you start with. A $100 investment made today beats a $1,000 investment made five years from now — thanks to compound growth, the mechanism that makes patient investors wealthy.\u003c/p\u003e","title":"Investing for Beginners: Start with $100 in 2026"},{"content":"If you\u0026rsquo;ve ever finished a book and couldn\u0026rsquo;t remember a single thing about it three months later, or if your \u0026ldquo;to-be-read\u0026rdquo; pile has become an unmanageable source of guilt rather than joy, a well-designed Notion book tracker can change your reading life.\nNotion has become the tool of choice for serious readers, book bloggers, and the BookTok and Bookstagram communities — not because it\u0026rsquo;s the only option, but because it\u0026rsquo;s flexible enough to be everything you need in one place: library, journal, stats dashboard, review system, and social content planner.\nThis guide walks you through building a complete Notion book tracker from scratch, even if you\u0026rsquo;ve never used Notion before.\nWhy Notion Is the Best Book Tracker in 2026 You could use Goodreads. You could use StoryGraph. Both are excellent services. But here\u0026rsquo;s what Notion gives you that they don\u0026rsquo;t:\nComplete customization: Design it around how you read, not how a developer thinks you should Private reviews: Write honest, personal notes you\u0026rsquo;d never want public Linked systems: Connect your book tracker to your reading goals, journal, content calendar, and note-taking system No algorithm: Your data, your way, forever Beautiful displays: Create gallery views that look stunning for content creation The Bookstagram and BookTok communities have discovered that a beautifully designed Notion database makes better content than app screenshots — because it\u0026rsquo;s completely yours.\nPart 1: Setting Up the Core Book Database Everything in your Notion book tracker is built on a single core database. Let\u0026rsquo;s build it right the first time.\nStep 1: Create Your Reading Database Open Notion and create a new page Type /database and select Database — Full Page Name it \u0026ldquo;My Reading Library\u0026rdquo; Step 2: Set Up Your Properties In Notion databases, each book is a \u0026ldquo;page\u0026rdquo; (a row in the database). Properties are the columns that store structured data about each book. Here are the properties to set up:\nBasic Properties:\nProperty Name Type Options/Notes Title Title Book title (default) Author Text Author\u0026rsquo;s full name Series Text Series name if applicable Series # Number Book number in series Genre Multi-Select See genre list below Cover Files \u0026amp; Media Upload book cover image ISBN Text For future lookups Reading Status Properties:\nProperty Name Type Options Status Select To Read, Reading Now, Finished, DNF, Re-Reading Date Started Date When you began reading Date Finished Date When you finished Reading Time (Days) Formula Auto-calculates days between Start and Finish Format Select Physical, eBook, Audiobook, Library, ARC Pages Number Total page count Rating Properties:\nProperty Name Type Options/Notes My Rating Select ⭐ 1-5 stars (use emoji stars as options) Plot Rating Select ⭐ 1-5 Characters Rating Select ⭐ 1-5 Writing Style Rating Select ⭐ 1-5 Pacing Rating Select ⭐ 1-5 Would Recommend Checkbox Simple yes/no Would Reread Checkbox Metadata Properties:\nProperty Name Type Options Publication Year Number Publisher Text Country of Author Select For reading diversity tracking Own? Checkbox Physical copy owned Source Select Purchased, Library, Gift, ARC, Borrowed Where to Buy URL Bookshop.org link (support indie!) Recommendation \u0026amp; Discovery Properties:\nProperty Name Type Options Recommended By Text Person or source Discovery Source Select BookTok, Bookstagram, Friend, Podcast, Newsletter, etc. Content Tags Multi-Select Custom tags for themes Mood Tags Multi-Select See mood list below Rep Tags Multi-Select Diversity/representation tags Content Creator Properties (optional):\nProperty Name Type Options Posted Review? Checkbox Did you post about this? Review Posted Date Date Platform Multi-Select TikTok, Instagram, Goodreads, StoryGraph, Blog Review Link URL Link to your post Photo Taken Checkbox Have a photo for this book? Step 3: Set Up Your Genre Options When creating the Genre multi-select, use these as your starting set (add more as needed):\nContemporary Fiction, Literary Fiction, Historical Fiction, Fantasy, High Fantasy, Dark Fantasy, Romantasy, Science Fiction, Dystopian, Mystery, Thriller, Psychological Thriller, Crime, Horror, Gothic, Romance, Contemporary Romance, Historical Romance, Paranormal Romance, Non-Fiction, Memoir/Biography, Self-Help, True Crime, Essay Collection, Graphic Novel/Manga, Short Stories, Young Adult, Middle Grade, Children\u0026rsquo;s, Classic\nStep 4: Set Up Mood Tags Mood tags help you find the right book for your reading mood:\nCozy, Dark and Atmospheric, Fast-Paced Page Turner, Slow Burn, Emotionally Heavy, Feel-Good, Funny/Witty, Heartbreaking, Thought-Provoking, Escapist, Character-Driven, Plot-Driven, Beautifully Written, Quick Read, Deeply Complex, Beach Read\nPart 2: Building Your Views Views are where Notion book tracking gets magical. Each view shows the same database in a different way, optimized for different purposes.\nView 1: Main Library (Table View) Your default view. Shows all books with key info at a glance.\nSetup:\nView type: Table Filter: None (shows all books) Sort: Date Finished (newest first) Visible properties: Title, Author, Status, My Rating, Genre, Format, Date Finished Name this view: \u0026ldquo;📚 Full Library\u0026rdquo;\nView 2: Currently Reading Setup:\nView type: Board (or simple Gallery) Filter: Status = \u0026ldquo;Reading Now\u0026rdquo; Group by: Format (if you read multiple books simultaneously) Show: Cover image as page cover Name this view: \u0026ldquo;📖 Currently Reading\u0026rdquo;\nView 3: TBR (To Be Read) Master List Your most important organizational view.\nSetup:\nView type: Table Filter: Status = \u0026ldquo;To Read\u0026rdquo; Sort: Create a custom sort by Mood Tags or Genre Visible properties: Title, Author, Genre, Mood Tags, Source, Recommended By Name this view: \u0026ldquo;🗂️ TBR\u0026rdquo;\nPro tip: Add a \u0026ldquo;TBR Priority\u0026rdquo; property (Select: High, Medium, Low, Someday) to manage the overwhelm of a large TBR pile.\nView 4: Books Read This Year (Reading Challenge) Setup:\nView type: Gallery Filter: Date Finished → is on or after → January 1, [current year] Sort: Date Finished (newest first) Card preview: Cover image Show: Title, Author, My Rating Name this view: \u0026ldquo;📅 2026 Reads\u0026rdquo;\nThis is the view you\u0026rsquo;ll screenshot for your reading challenge posts.\nView 5: Reading by Genre (Stats View) Setup:\nView type: Board Group by: Genre Filter: Status = Finished Sort by: Date Finished Name this view: \u0026ldquo;📊 By Genre\u0026rdquo;\nThis view instantly shows you which genres you gravitate toward and where your gaps are.\nView 6: 5-Star Reads (Favorites) Setup:\nView type: Gallery Filter: My Rating = ⭐⭐⭐⭐⭐ Card preview: Cover image Name this view: \u0026ldquo;⭐ All-Time Favorites\u0026rdquo;\nThis is your recommendation page — perfect for sharing with friends.\nView 7: DNF Shelf Setup:\nView type: Table Filter: Status = \u0026ldquo;DNF\u0026rdquo; Visible properties: Title, Author, Genre, any DNF-related notes Name this view: \u0026ldquo;🚫 DNF\u0026rdquo;\nNo shame here. DNF data is useful — patterns in your DNF list tell you what you genuinely don\u0026rsquo;t enjoy reading.\nPart 3: The Book Review Template Each book in your database is a Notion page, and you can create a template that auto-populates when you add a new book. This is where the real magic happens.\nCreating a Book Page Template In your Reading database, click the dropdown arrow next to \u0026ldquo;New\u0026rdquo; Click \u0026ldquo;+ New template\u0026rdquo; Name it \u0026ldquo;Book Review Template\u0026rdquo; Design the page content below the properties The Review Template Content Copy this template into your Notion book page template:\nBook Review Template\nFirst Impressions Write your initial thoughts after reading the first 50 pages. What\u0026rsquo;s grabbing you? What concerns do you have?\n[Your notes here]\nReading Notes / Highlights Favorite Quotes [Quote 1 — include page number]\n[Quote 2 — include page number]\n[Quote 3 — include page number]\nKey Themes I Noticed [Theme 1] [Theme 2] [Theme 3] Characters I Want to Remember Character Role Notes [Name] [Role] [Personality, arc, memorable moments] Plot Points to Remember (For series books — helps you remember what happened for the next book) My Full Review One-Sentence Summary In your own words (not the blurb):\nWhat I Loved What Didn\u0026rsquo;t Work For Me Who I\u0026rsquo;d Recommend This To Be specific: \u0026ldquo;Perfect for readers who like [X], [Y], and [Z]\u0026rdquo;\nSpoiler Section (Hidden) (Use a Notion Toggle block for spoilers)\n▶ Click to reveal spoilers\n[Spoiler content here]\nMy Ratings Breakdown Category Rating Overall ⭐ Plot ⭐ Characters ⭐ Writing Style ⭐ Pacing ⭐ World-Building (if applicable) ⭐ Final thoughts in one paragraph:\nContent Creator Notes For BookTok:\nHook idea: Key selling point to mention: Aesthetic/vibe for filming: For Bookstagram:\nCaption angle: Props/styling ideas: Photo concept: Trigger warnings to include in posts: Part 4: Reading Stats Dashboard Create a separate Notion page as your reading stats hub. Use Notion\u0026rsquo;s linked database views and rollup/formula properties to generate live statistics.\nSetting Up Reading Stats Add these calculated properties to your database:\nReading Time (Formula property):\ndateBetween(prop(\u0026#34;Date Finished\u0026#34;), prop(\u0026#34;Date Started\u0026#34;), \u0026#34;days\u0026#34;) Year Read (Formula property):\nformatDate(prop(\u0026#34;Date Finished\u0026#34;), \u0026#34;YYYY\u0026#34;) Month Read (Formula property):\nformatDate(prop(\u0026#34;Date Finished\u0026#34;), \u0026#34;MMMM\u0026#34;) Is This Year (Formula property for current year filter):\nyear(prop(\u0026#34;Date Finished\u0026#34;)) == year(now()) Stats Dashboard Page Layout Create a new Notion page titled \u0026ldquo;📊 Reading Stats [Year]\u0026rdquo; and add linked database views with filters and calculations:\nSection 1: Books Read This Year\nLinked database view filtered to current year Count of entries shown at top Section 2: Pages Read This Year\nCreate a Notion formula or use a simple running total Update manually if needed (Notion doesn\u0026rsquo;t auto-sum across filtered views easily) Section 3: Genre Breakdown\nLinked gallery view grouped by Genre, filtered to this year Section 4: Monthly Reading Log\nBoard view grouped by Month Read property Gives you a visual of your reading pace month by month Section 5: Rating Distribution\nTable view grouped by My Rating, filtered to this year Shows if you\u0026rsquo;re a harsh rater or generous one Section 6: Formats Read\nBoard view grouped by Format Section 7: Author Diversity\nBoard view grouped by Country of Author Part 5: TBR Management System A TBR (To Be Read) pile is only useful if it\u0026rsquo;s curated. Here\u0026rsquo;s a system for keeping yours manageable.\nThe TBR Triage Method Once per month (or whenever your TBR exceeds 50 books), run through it with these questions:\nWould I pick this up today? If no, move to \u0026ldquo;Someday\u0026rdquo; or remove it entirely. Why is this on my list? If you can\u0026rsquo;t remember why, you\u0026rsquo;ve probably lost interest. Has my reading taste evolved past this? It\u0026rsquo;s okay to release books you\u0026rsquo;ve outgrown. TBR Views That Help The \u0026ldquo;Pick My Next Book\u0026rdquo; View:\nFilter: Status = To Read, TBR Priority = High Sort: Random (use a Random Number property set to formula floor(random() * 1000) to shuffle) This gives you a curated list of high-priority reads to choose from when you finish a book The Seasonal TBR View:\nAdd a \u0026ldquo;Season to Read\u0026rdquo; property (Select: Spring, Summer, Autumn, Winter, Anytime) Filter by current season when choosing your next read Works great for mood readers The \u0026ldquo;Challenge Requirements\u0026rdquo; View:\nIf you\u0026rsquo;re doing a reading challenge, add a \u0026ldquo;Reading Challenge\u0026rdquo; multi-select property Create a view filtering to books tagged with specific challenge categories you still need to complete Part 6: Bookstagram and BookTok Integration Your Notion database can double as a content planning tool.\nContent Calendar Integration Create a separate Notion database (or a new view in your existing one) for content planning:\nProperties:\nLinked Book (Relation to your Reading Library) Platform (Multi-select: TikTok, Instagram, YouTube, Blog, Goodreads) Content Type (Select: Review, Recommendation, Haul, Wrap-Up, Unboxing, Discussion) Post Date (Date) Status (Select: Idea, Drafted, Filmed/Photographed, Edited, Posted) Caption Draft (Text) Hashtags (Text) Link (URL — once posted) Monthly Wrap-Up Template Use this structure for your monthly wrap-up posts:\nMonthly Wrap-Up: [Month] [Year]\nBooks read: [Link to filtered view showing this month\u0026rsquo;s reads] Favorite: [Best book] Disappointment: [Book that didn\u0026rsquo;t meet expectations] DNF: [Any you didn\u0026rsquo;t finish] Current read: [What you\u0026rsquo;re reading now] TBR for next month: [3-5 books you plan to read]\nReading Challenge Trackers If you\u0026rsquo;re participating in reading challenges (POPSUGAR, Read Harder, Goodreads Challenge), add a \u0026ldquo;Reading Challenges\u0026rdquo; database linked to your main library:\nChallenge Name Year Total Prompts Linked Books (Relation) Completion % (Formula based on count of linked books vs. total prompts) Part 7: Advanced Tips and Customization Tip 1: Use Notion AI for Book Discovery If you have Notion AI, you can ask it to analyze your reading history and make recommendations:\n\u0026ldquo;Based on the books I\u0026rsquo;ve rated 4-5 stars, what patterns do you notice in my reading preferences? Suggest 10 books I\u0026rsquo;d probably love based on these patterns.\u0026rdquo;\nPaste in a few of your highly-rated books for context.\nTip 2: Create an Author Database For voracious readers, a linked Author database is powerful:\nAuthor name, bio, nationality, active years Related to all their books in your library Lets you quickly see: \u0026ldquo;Have I read everything by this author? What\u0026rsquo;s next in their catalog?\u0026rdquo; Tip 3: Build a Series Tracker Create a Series database:\nSeries name Author Total books in series Related books in your library (Relation) Completion % (Rollup: Count of finished books / total) Status: Ongoing, Complete, Abandoned This solves the \u0026ldquo;what book is next in the series\u0026rdquo; problem forever.\nTip 4: Book Club Management Running or participating in a book club? Add a \u0026ldquo;Book Club\u0026rdquo; database:\nMeeting date Book (Relation to library) Discussion questions (generated with ChatGPT) Attendance Group rating Notes from discussion Tip 5: Wishlist vs. TBR Separation Keep your TBR (books you\u0026rsquo;re committed to reading) separate from your Wishlist (books you might want but aren\u0026rsquo;t sure about). Use the Status property: \u0026ldquo;Wishlist\u0026rdquo; is a lighter commitment than \u0026ldquo;To Read.\u0026rdquo;\nYour Next Chapter Could Be a New Career Readers who are organized, detail-oriented, and skilled with productivity tools are in demand. Find your next career on doda — Japan\u0026rsquo;s top job platform with thousands of opportunities across every industry.\nStarting Your Tracker Today You don\u0026rsquo;t need to set up every feature in this guide at once. Here\u0026rsquo;s a realistic 4-week rollout:\nWeek 1: Set up the core database with basic properties (Title, Author, Status, Rating, Genre). Add your 10 most recent reads and your current TBR.\nWeek 2: Create the main views (Full Library, Currently Reading, TBR, This Year\u0026rsquo;s Reads). Set up the Book Review Template.\nWeek 3: Add advanced properties (Mood Tags, Rep Tags, Content Creator fields). Start writing reviews using the template.\nWeek 4: Build the Stats Dashboard. Customize views for your specific reading goals and challenges.\nBy the end of month one, you\u0026rsquo;ll have a system that\u0026rsquo;s genuinely yours — not someone else\u0026rsquo;s template you\u0026rsquo;re trying to force your reading life into.\nConclusion The best book tracker is the one you actually use. Notion works because you can make it as simple or as complex as your reading life demands, and you can adjust it anytime.\nWhether you\u0026rsquo;re a casual reader who wants to remember what you\u0026rsquo;ve read, a data-driven reader chasing annual challenges, or a BookTok creator who wants to turn a genuine passion into content, this system scales to meet you where you are.\nStart with your last five books. Add them to the database this weekend. Everything else builds from there.\nLooking for the printable template version of this system? Subscribe to Productivity Works for our free Notion book tracker template download.\nRelated Tools Create a monthly budget → Budget Planner Set a savings goal → Savings Goal Calculator Related Templates Put these techniques into practice with our ready-made templates:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Copy-paste prompts for every situation The AI Productivity Playbook 2026 — 50+ AI workflows and automation strategies This article contains affiliate links. We may earn a commission at no extra cost to you.\nYou May Also Like The Ultimate Notion Setup for Maximum Productivity Best Notion AI Templates for Productivity 2026 How to Use Notion for Project Management 2026: The Complete Guide ","permalink":"https://productivity-works.com/posts/notion-book-tracker-template-guide-2026/","summary":"\u003cp\u003eIf you\u0026rsquo;ve ever finished a book and couldn\u0026rsquo;t remember a single thing about it three months later, or if your \u0026ldquo;to-be-read\u0026rdquo; pile has become an unmanageable source of guilt rather than joy, a well-designed Notion book tracker can change your reading life.\u003c/p\u003e\n\u003cp\u003eNotion has become the tool of choice for serious readers, book bloggers, and the BookTok and Bookstagram communities — not because it\u0026rsquo;s the only option, but because it\u0026rsquo;s flexible enough to be everything you need in one place: library, journal, stats dashboard, review system, and social content planner.\u003c/p\u003e","title":"How to Build a Notion Book Tracker: Complete Template Guide (2026)"},{"content":"Freelancing gives you freedom. It also gives you a financial situation that is genuinely complicated — irregular income, self-employment taxes, deductible business expenses, client invoices in various states of payment, and the quarterly tax deadline that always arrives faster than expected. Most personal budgeting apps are built for salaried employees with predictable paychecks. They fall apart the moment you have three clients paying on different schedules, a slow January, and a $4,200 Adobe software bill that is actually a tax deduction.\nNotion, when set up correctly, handles all of this in a single workspace. This guide covers everything you need to build (or use) a Notion budget template for freelancers in 2026 — including income tracking, expense categorization, quarterly estimated tax calculation, and a dashboard that shows your financial picture at a glance.\nWhy Notion Works for Freelance Budgeting (And Where It Falls Short) Notion is not accounting software. It will not file your taxes. It does not connect to your bank account the way Mint or YNAB does. What it does do is let you build a custom financial tracking system that fits the actual shape of your freelance business — something off-the-shelf tools rarely do.\nNotion\u0026rsquo;s strengths for freelancers:\nRelational databases that connect income, expenses, clients, and projects Formula fields that calculate profit margins, tax estimates, and savings rates automatically Flexible views (table, calendar, kanban, gallery) for seeing your finances from different angles Filters that let you instantly see \u0026ldquo;Q2 income only\u0026rdquo; or \u0026ldquo;unpaid invoices only\u0026rdquo; All in the same workspace as your project management, client notes, and content calendar Honest limitations:\nNo automatic bank sync (you enter data manually or copy from your bank) No native invoicing (though you can create a simple invoice template) Formulas can get complex; there is a learning curve Not a replacement for proper accounting software (QuickBooks, FreshBooks) once your business grows past ~$80K/year For freelancers in the $20K-$80K annual revenue range, Notion is often the ideal tool — more flexible than a spreadsheet, less expensive than full accounting software.\nThe Core Structure: Four Databases A well-built Notion freelance budget system uses four interconnected databases:\nIncome Database — Every payment received Expense Database — Every business expense Client Database — All clients, linked to their invoices and payments Monthly Summary — Rolled-up view for budget vs. actual comparison Let\u0026rsquo;s build each one.\nDatabase 1: Income Tracker Properties to Include Property Type Purpose Invoice/Payment Name Title e.g., \u0026ldquo;Acme Corp — April retainer\u0026rdquo; Client Relation Links to Client database Amount Number (currency) Gross payment received Date Received Date When money hit your account Payment Method Select Bank transfer, PayPal, Stripe, check Project Type Select Retainer, project, hourly, royalty Month Formula formatDate(prop(\u0026quot;Date Received\u0026quot;), \u0026quot;YYYY-MM\u0026quot;) Quarter Formula Calculated from date (see below) Net After Platform Fees Number If Upwork/Fiverr takes a cut, record net Notes Text Invoice number, late payment notes The Quarter Formula if(month(prop(\u0026#34;Date Received\u0026#34;)) \u0026lt;= 3, \u0026#34;Q1\u0026#34;, if(month(prop(\u0026#34;Date Received\u0026#34;)) \u0026lt;= 6, \u0026#34;Q2\u0026#34;, if(month(prop(\u0026#34;Date Received\u0026#34;)) \u0026lt;= 9, \u0026#34;Q3\u0026#34;, \u0026#34;Q4\u0026#34;))) This lets you filter income by quarter instantly — critical for estimated quarterly tax payments.\nViews to Create All Income — Default table view, sorted by date descending By Month — Group by Month property By Quarter — Group by Quarter property By Client — Group by Client relation This Year — Filter: Date Received is this year Database 2: Expense Tracker This is where freelancers tend to lose the most money — not tracking deductible expenses means overpaying taxes. The IRS (and equivalent tax authorities globally) allows deductions for a significant range of business expenses.\nProperties to Include Property Type Purpose Expense Name Title e.g., \u0026ldquo;Adobe Creative Cloud annual\u0026rdquo; Amount Number (currency) Amount paid Date Date When paid Category Select See categories below Deductible Checkbox Is this a business deduction? Deductible % Number 100, 50, or partial (e.g., home office %) Deductible Amount Formula prop(\u0026quot;Amount\u0026quot;) * prop(\u0026quot;Deductible %\u0026quot;) / 100 Payment Method Select Business card, personal card, cash Receipt Files Attach photo of receipt Month Formula Same as Income tracker Quarter Formula Same as Income tracker Recurring Checkbox Monthly subscription vs. one-time Vendor Text Company name for tax records Expense Categories for Freelancers Use these as your Select options — they map closely to IRS Schedule C categories:\nSoftware \u0026amp; Subscriptions (Adobe, Notion, Slack, Zoom, etc.) Hardware \u0026amp; Equipment (laptop, monitor, camera, microphone) Home Office (partial rent/mortgage, utilities — based on home office %) Internet \u0026amp; Phone (business portion) Professional Development (courses, books, conferences) Marketing \u0026amp; Advertising (website hosting, ads, design tools) Professional Services (accountant, lawyer, contractor payments) Travel \u0026amp; Transportation (client meetings, co-working commute) Meals \u0026amp; Entertainment (50% deductible; client meals) Insurance (health insurance self-employed deduction, business insurance) Office Supplies (paper, ink, notebooks, desk supplies) Banking \u0026amp; Transaction Fees (Stripe fees, PayPal fees, wire transfer fees) Other Business Expense The Home Office Deduction in Notion If you work from home (most freelancers do), you can deduct a percentage of rent/utilities based on the proportion of your home used exclusively for work. Track this by:\nSet a Home Office % formula property at the top of your workspace (e.g., 15% if your office is 15% of your home\u0026rsquo;s square footage). Tag home-related expenses with the \u0026ldquo;Home Office\u0026rdquo; category. Set Deductible % to your home office percentage. The Deductible Amount formula handles the rest. Database 3: Client Database Link your income and projects to clients so you can see at a glance which clients generate the most revenue, which invoices are outstanding, and which relationships are worth investing in.\nProperties to Include Property Type Purpose Client Name Title Company or individual name Contact Name Text Primary contact Email Email Contact email Status Select Active, Inactive, Prospect, Ended Rate Type Select Hourly, project, retainer Hourly Rate / Project Rate Number Your rate with this client Payments Relation Links to Income Database Total Paid (YTD) Rollup Sum of Amount from linked payments Contract Start Date When engagement began Notes Text Relationship notes, preferences 1099 Required Checkbox US: if you paid them, do you need to file? Invoice Tracking Within the Client Database You can also build a lightweight Invoice Tracker as a sub-database or additional view. Add these properties to your Income database:\nInvoice Status — Select: Draft, Sent, Paid, Overdue, Write-off Invoice Date — When you sent the invoice Due Date — Payment due date Days to Pay — Formula: dateBetween(prop(\u0026quot;Date Received\u0026quot;), prop(\u0026quot;Invoice Date\u0026quot;), \u0026quot;days\u0026quot;) Filter to Status = Overdue and you instantly know who owes you money.\nDatabase 4: Monthly Summary Dashboard This is your financial command center. Create a new database with one entry per month, and use rollup properties to pull totals from your Income and Expense databases.\nProperties for Monthly Summary Property Type Notes Month Title \u0026ldquo;2026-01\u0026rdquo;, \u0026ldquo;2026-02\u0026rdquo;, etc. Income (Linked) Relation Link to Income entries for this month Total Income Rollup Sum of Amount from linked income Expenses (Linked) Relation Link to Expense entries for this month Total Expenses Rollup Sum of Amount from linked expenses Total Deductible Expenses Rollup Sum of Deductible Amount Gross Profit Formula prop(\u0026quot;Total Income\u0026quot;) - prop(\u0026quot;Total Expenses\u0026quot;) Self-Employment Tax (est.) Formula prop(\u0026quot;Gross Profit\u0026quot;) * 0.153 (US 15.3%) Income Tax (est.) Formula Varies by bracket — use a simplified estimate Tax Set-Aside Formula prop(\u0026quot;Self-Employment Tax (est.)\u0026quot;) + prop(\u0026quot;Income Tax (est.)\u0026quot;) Net Take-Home Formula prop(\u0026quot;Gross Profit\u0026quot;) - prop(\u0026quot;Tax Set-Aside\u0026quot;) Savings Rate Formula prop(\u0026quot;Net Take-Home\u0026quot;) / prop(\u0026quot;Total Income\u0026quot;) Budget Target Number Your monthly income goal vs. Target Formula prop(\u0026quot;Total Income\u0026quot;) - prop(\u0026quot;Budget Target\u0026quot;) This view tells you, at a glance, whether you hit your income target, what you actually take home after estimated taxes, and whether your savings rate is healthy.\nSetting Up Quarterly Tax Estimates in Notion In the US, self-employed individuals must pay estimated taxes four times per year:\nQ1 payment due: April 15 Q2 payment due: June 16 Q3 payment due: September 15 Q4 payment due: January 15 (following year) Similar schedules exist in the UK (twice per year), Canada, Australia, and most other countries with self-employment tax obligations.\nThe Quarterly Tax Page Create a dedicated page in Notion titled \u0026ldquo;Quarterly Tax Estimates\u0026rdquo; with a table:\nQuarter Income Deductible Expenses Net Profit SE Tax (15.3%) Fed Income Tax (est.) Total Due Payment Date Paid? Q1 2026 $14,200 $2,100 $12,100 $1,851 $1,320 $3,171 Apr 15 ✓ Q2 2026 \u0026hellip; \u0026hellip; \u0026hellip; \u0026hellip; \u0026hellip; \u0026hellip; Jun 16 Link the Income and Expense totals via rollup from your other databases, so this table updates automatically as you log entries throughout the quarter.\nAdd a Notion reminder on the Due Date property — Notion will send you a notification before each deadline.\nThe 25-30% Rule A common freelancer heuristic: set aside 25-30% of every payment into a dedicated tax savings account the moment it arrives. In Notion, you can add a \u0026ldquo;Tax Set-Aside\u0026rdquo; property to your Income tracker:\nprop(\u0026#34;Amount\u0026#34;) * 0.28 Every time you log a payment, Notion tells you exactly how much to transfer to your tax savings account. This single habit eliminates most year-end tax bill shock.\nBuilding a Freelance Budget (Not Just Tracking) Tracking is reactive. Budgeting is proactive. The difference is setting income targets and expense limits before the month starts, then comparing actuals against them.\nMonthly Budget Template Add these properties to your Monthly Summary database:\nProperty Type Target Income Number Target Expenses Number Target Savings Number Actual vs. Target Income Formula Actual vs. Target Expenses Formula On Track? Formula (if/then logic) Set your targets at the beginning of each month. At month end, the formulas show you exactly how reality compared to the plan.\nThe Irregular Income Problem Freelance income is lumpy. Some months are feast; some are famine. The standard solution is to budget based on your lowest typical month, not your average — this ensures you never overextend in a good month and scramble in a slow one.\nIn Notion, track a rolling 3-month and 12-month income average:\n(prop(\u0026#34;Month -1 Income\u0026#34;) + prop(\u0026#34;Month -2 Income\u0026#34;) + prop(\u0026#34;Month -3 Income\u0026#34;)) / 3 Budget personal expenses against the 3-month rolling average. Only \u0026ldquo;upgrade\u0026rdquo; your lifestyle budget when the 12-month average rises.\nFreelance-Specific Finance Automation in Notion Notion 2026 includes several automation features that reduce manual data entry:\nRecurring expense entries: Use Notion automations to duplicate monthly subscription entries automatically. Set a trigger for the 1st of each month to create a template entry for Adobe, Notion, hosting, etc. Payment status reminders: Automate a reminder when an invoice\u0026rsquo;s Due Date passes and Status is still \u0026ldquo;Sent\u0026rdquo; — flagging it as overdue. Monthly summary creation: Automate creation of a new Monthly Summary entry on the 1st of each month with the Budget Target pre-filled from last month. These automations are available in Notion\u0026rsquo;s free and paid tiers, accessible through the database automation menu.\nIntegrating with Real Accounting Tools If you are also using QuickBooks Self-Employed, Wave, or FreshBooks, Notion is not a replacement — it is a companion planning tool. Use:\nYour accounting software for: official records, bank sync, tax filing Notion for: financial planning, goal-setting, project-level P\u0026amp;L, and the full business overview Export from your accounting software monthly, reconcile against Notion entries, and use Notion\u0026rsquo;s dashboard for strategic decisions rather than tax compliance.\nGet the Pre-Built Template Building this system from scratch takes 3-6 hours. If you want to skip straight to using it, our Budget Tracker template on Payhip includes the full freelance finance system pre-built in Notion — all four databases, formulas, views, and the quarterly tax estimator included. Duplicate it to your workspace and start logging income on day one.\nIt also pairs with our AI Productivity Playbook, which covers how to use AI tools (including ChatGPT) to automate the data-gathering and categorization that makes budget tracking feel like a chore.\nWhen Notion Isn\u0026rsquo;t Enough, freee Has You Covered Your Notion budget tracker is great for planning — but for official accounting, invoicing, and tax filing, you need dedicated software. Try freee — Japan\u0026rsquo;s #1 cloud accounting to handle everything your Notion system can\u0026rsquo;t, including bank sync, tax returns, and client invoicing.\nFinal Thoughts The freelancers who build sustainable businesses are almost always the ones who treat their finances with the same care they give their craft. You don\u0026rsquo;t need to be a numbers person. You need a system that handles the structure so you can see the signal clearly.\nNotion, set up with the four-database structure in this guide, gives you that system. Your income, expenses, clients, and tax obligations all live in one place, connected, and visible. That visibility is the foundation of confident, strategic freelancing — knowing exactly where you stand financially means you can price better, save more, and say no to bad clients without anxiety.\nStart with the Income and Expense databases this week. Add the Monthly Summary next week. By the end of the month, you will have a financial picture that most freelancers never build until they are forced to by a tax audit or a cash crisis.\nBuild it before you need it.\nOur pre-built Budget Tracker on Payhip includes the full Notion freelance finance system, ready to duplicate. Save 4+ hours of setup and start tracking today.\nRelated Tools Calculate your ideal freelance hourly rate → Freelance Rate Calculator Create a quick monthly budget → Budget Planner Estimate your freelance tax burden → Side Hustle Tax Calculator Calculate take-home from salary + freelance → Salary Calculator Plan your debt payoff strategy → Debt Payoff Calculator Related Templates Start your freelance or side hustle journey with these resources:\nSide Hustle Starter Kit 2026 — 15 proven ideas with step-by-step guides ChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Boost your productivity instantly This article contains affiliate links. We may earn a commission at no extra cost to you.\nYou May Also Like Best Budgeting Apps 2026: Full Comparison The Ultimate Notion Setup for Maximum Productivity Best Notion AI Templates for Productivity 2026 ","permalink":"https://productivity-works.com/posts/notion-budget-template-freelancers-2026/","summary":"\u003cp\u003eFreelancing gives you freedom. It also gives you a financial situation that is genuinely complicated — irregular income, self-employment taxes, deductible business expenses, client invoices in various states of payment, and the quarterly tax deadline that always arrives faster than expected. Most personal budgeting apps are built for salaried employees with predictable paychecks. They fall apart the moment you have three clients paying on different schedules, a slow January, and a $4,200 Adobe software bill that is actually a tax deduction.\u003c/p\u003e","title":"Notion Budget Template for Freelancers 2026: Track All Finances"},{"content":"If you\u0026rsquo;ve been searching for a single tool that can replace your project management software, wiki, spreadsheet, and task tracker — Notion is the closest thing to it. In 2026, Notion has evolved from a note-taking app into a full-blown work operating system used by solo freelancers, startups, and Fortune 500 teams alike.\nThis guide covers everything: setting up your first Notion workspace, building a project management system from scratch, using AI features, and avoiding the most common mistakes people make when getting started.\nWhy Notion for Project Management? Before diving in, here\u0026rsquo;s a quick comparison of Notion against the traditional PM tools:\nFeature Notion Asana Trello Monday.com Free tier Yes (generous) Limited Limited Very limited Wiki/docs Excellent Poor None Limited Database views 6 view types Board/list Board only Multiple AI built-in Yes (Notion AI) Add-on No Add-on Automation Basic Advanced Power-Ups Advanced Price (team) $10/member/mo $10.99/mo $5/mo $9/mo Custom workflows Highly flexible Moderate Low Moderate Notion\u0026rsquo;s biggest advantage is flexibility. You shape it to your workflow — not the other way around.\nSetting Up Your Notion Workspace Step 1: Create Your Account and Workspace Go to notion.so and sign up. The free plan includes unlimited pages and blocks for individuals, making it viable for solo project management.\nWhen creating a workspace, name it after your company or team, not yourself — even if you\u0026rsquo;re solo. This makes it easier to add collaborators later.\nStep 2: Understand the Core Building Blocks Notion has four key concepts to internalize:\nPages — The fundamental unit. Everything lives on a page. Pages can contain other pages (nested), creating a hierarchy.\nBlocks — Everything on a page is a block: text, images, tables, code snippets, embeds. You can drag and rearrange any block.\nDatabases — The superpower of Notion. A database is a collection of pages with shared properties (status, assignee, due date, priority). Databases can be viewed as tables, boards, calendars, timelines, galleries, or lists.\nProperties — Metadata fields on database entries: text, number, select, multi-select, date, person, checkbox, URL, formula, relation, rollup.\nStep 3: Design Your Sidebar Structure A clean sidebar is the backbone of navigable workspace. Here\u0026rsquo;s a recommended structure for project management:\nWorkspace ├── Home Dashboard ├── Projects │ ├── [Project Name 1] │ ├── [Project Name 2] │ └── Project Archive ├── Tasks (Master Task DB) ├── Team │ ├── Meeting Notes │ └── Team Directory ├── Resources │ ├── SOPs \u0026amp; Docs │ └── Brand Assets └── Personal └── My Tasks Keep the top-level list short. Buried pages get forgotten.\nBuilding a Project Management System The Core Database: Projects Create a new page called \u0026ldquo;Projects\u0026rdquo; and add a full-page database. Add these properties:\nProperty Type Purpose Name Title Project name Status Select Planning / Active / On Hold / Done Priority Select High / Medium / Low Owner Person Who\u0026rsquo;s responsible Team Multi-select Who\u0026rsquo;s involved Start Date Date Kickoff date Due Date Date Deadline Progress Number (%) Manual or formula-driven Budget Number If applicable Tags Multi-select Category labels Create a Gallery view for a visual overview of active projects, and a Table view for detailed tracking.\nThe Core Database: Tasks Create a \u0026ldquo;Tasks\u0026rdquo; database — this is the engine of your system. Key properties:\nProperty Type Purpose Task Name Title What needs doing Project Relation Links to Projects DB Assignee Person Who does it Status Select Not Started / In Progress / In Review / Done Priority Select Urgent / High / Normal / Low Due Date Date Deadline Estimate Number Hours estimated Tags Multi-select Feature / Bug / Admin / Research Blocked By Relation (self) Dependencies Once you link Tasks to Projects via a Relation property, you can add a Rollup to the Projects database to show task counts, progress percentages, and overdue items — automatically.\nCreating Filtered Views That Actually Help Raw databases are overwhelming. Filtered views make them actionable. In your Tasks database, create these saved views:\nMy Tasks Today — Filter: Assignee = Me AND Due Date = Today AND Status != Done\nThis Week — Filter: Due Date is within the next 7 days AND Status != Done\nOverdue — Filter: Due Date is before today AND Status != Done (add a red highlight)\nKanban Board — Group by Status, hide the Done column by default\nBy Project — Group by Project Relation\nEach of these views reads from the same underlying database — no data duplication.\nRunning Projects Inside Notion The Project Page Template Every project should have a dedicated page that acts as a command center. Here\u0026rsquo;s what to include:\nHeader section:\nProject name and status badge One-line description Owner, team, dates, budget at a glance (using a database sub-page or properties) Sections:\nObjectives — What success looks like (3-5 bullet points) Key Milestones — Timeline view of your Tasks DB, filtered to this project Task Board — Kanban view of Tasks, filtered to this project Meeting Notes — Linked database of meeting pages Files \u0026amp; Links — Key documents, Figma links, Google Drive embeds Decisions Log — A table tracking decisions made and rationale To create a template in Notion, open any database, click the dropdown arrow next to \u0026ldquo;New\u0026rdquo;, and select \u0026ldquo;+ New Template\u0026rdquo;. Build your project page structure once, and every new project auto-populates it.\nUsing Notion AI for Project Management Notion AI (included in the Plus plan at $10/member/month, or available as an add-on) dramatically speeds up several PM tasks:\nMeeting notes summarization — Paste raw meeting transcript, ask Notion AI to extract action items and decisions. Output is structured and ready to paste into your decisions log.\nTask breakdown — Describe a project goal in plain text, ask AI to suggest a task breakdown. Review and edit, then convert each line to a database entry.\nStatus update drafts — Ask AI to write a stakeholder update based on your project page content. Takes 30 seconds instead of 20 minutes.\nRisk identification — Describe your project scope to AI and ask \u0026ldquo;what could go wrong?\u0026rdquo; — useful for early-stage planning.\nTo use Notion AI anywhere, press the space bar on an empty line or highlight text and click \u0026ldquo;Ask AI.\u0026rdquo;\nManaging Sprints in Notion If your team runs sprints (common in software development and agency work), here\u0026rsquo;s a lightweight approach:\nAdd a Sprint property (Select or Relation) to your Tasks database Create a \u0026ldquo;Sprints\u0026rdquo; database with Sprint Number, Start Date, End Date, and Goal Relate Tasks to Sprints Create a Sprint Board view filtered to the current sprint For sprint retrospectives, create a template page with sections: What went well / What didn\u0026rsquo;t / What to change. Link it to the sprint record.\nTeam Collaboration Features Sharing and Permissions Notion\u0026rsquo;s permission model is:\nFull access — Can edit and share Can edit — Can edit, can\u0026rsquo;t change sharing settings Can comment — Comment only, no edits Can view — Read-only Share individual pages or entire workspaces. For clients or external stakeholders, create a dedicated \u0026ldquo;Client Portal\u0026rdquo; page with only the relevant views shared via \u0026ldquo;Share to web\u0026rdquo; (view-only link, no Notion account required).\nComments and Mentions Use @mention to tag a teammate anywhere on a page — they get a notification Use @date to create inline date references (auto-linked to calendar) Comment on blocks — Right-click any block and add a comment. This creates a threaded discussion anchored to that specific piece of content, keeping context intact Resolve comments when done to keep pages clean Notifications and Reminders Notion\u0026rsquo;s notification system is simpler than dedicated PM tools like Asana. To compensate:\nAdd reminders to date properties: click the date, enable \u0026ldquo;Remind\u0026rdquo; (1 day before, morning of, etc.) Use Notion\u0026rsquo;s inbox (bell icon) to track mentions and comments For critical deadlines, connect Notion to Slack via Zapier or Make.com to push notifications Advanced Notion PM Techniques Formula Properties Formulas let you calculate values automatically. Useful examples:\nDays until deadline:\ndateBetween(prop(\u0026#34;Due Date\u0026#34;), now(), \u0026#34;days\u0026#34;) Is overdue? (returns \u0026ldquo;OVERDUE\u0026rdquo; or blank):\nif(dateBetween(prop(\u0026#34;Due Date\u0026#34;), now(), \u0026#34;days\u0026#34;) \u0026lt; 0 and prop(\u0026#34;Status\u0026#34;) != \u0026#34;Done\u0026#34;, \u0026#34;OVERDUE\u0026#34;, \u0026#34;\u0026#34;) Completion percentage from subtasks: Use a Rollup property (Count checked / Count all) on a linked subtasks relation, then format as a percentage.\nRelations and Rollups Relations connect two databases. Rollups summarize related data. Practical example:\nProjects → Tasks (Relation): Link each task to a project Projects: Task Count (Rollup): Count all related tasks Projects: Done Count (Rollup): Count tasks where Status = Done Projects: Progress % (Formula): prop(\u0026quot;Done Count\u0026quot;) / prop(\u0026quot;Task Count\u0026quot;) This gives you a live progress bar on every project without manual updates.\nBuilding a Weekly Review Dashboard Create a \u0026ldquo;Home\u0026rdquo; page as your weekly command center:\nLinked database: My Tasks Due This Week — filtered to your name + this week Linked database: Projects I Own — filtered to Active projects, your name as Owner Linked database: Overdue Tasks — filtered globally, status not done, due date past Text block: Weekly Priorities — freeform notes for each Monday review Linked database: Upcoming Meetings — calendar view filtered to this week Set this page as your Notion homepage (Settings → Sidebar → Start on).\nCommon Mistakes to Avoid Over-engineering your system before using it. Many people spend hours building the perfect Notion workspace and never actually use it for work. Start simple — a task list and a project page — and add complexity only when you feel the pain of not having something.\nToo many databases. If you have separate databases for Tasks, Subtasks, Action Items, and To-Dos, you\u0026rsquo;ll constantly lose things. One master Tasks database with good filtering beats four fragmented ones.\nIgnoring templates. Notion has a template gallery with hundreds of community-built setups. Browse it before building from scratch. Good starting points: the \u0026ldquo;Simple Project Tracker\u0026rdquo; and \u0026ldquo;Engineering Wiki\u0026rdquo; templates.\nNot using keyboard shortcuts. Notion rewards keyboard users. Key shortcuts:\n/ — Open block menu Cmd/Ctrl + P — Quick search (find any page instantly) Cmd/Ctrl + Shift + L — Toggle dark mode [[ — Create inline page link @ — Mention a person or date Cmd/Ctrl + Enter — Open a page in full Not archiving old projects. Completed projects clutter your sidebar. Move them to a sub-page called \u0026ldquo;Archive\u0026rdquo; inside your Projects page. Create an Archived view in your Projects database filtered to Status = Done.\nNotion Integrations Worth Knowing Integration Use Case Slack Get Notion notifications in Slack; create Notion pages from Slack messages Google Calendar Sync date properties to calendar (via Zapier/Make) GitHub Link commits, PRs, and issues to Notion tasks Figma Embed live Figma files directly in project pages Loom Embed async video updates in project pages Zapier / Make.com Automate anything — new row in Notion triggers Slack message, email, etc. Native integrations (Slack, GitHub) are available via the Connections menu. For everything else, Zapier or Make.com are the go-to bridges.\nWhich Notion Plan Do You Need? Plan Price Best For Free $0 Solo users, personal projects Plus $10/member/mo Small teams, freelancers collaborating with clients Business $15/member/mo Teams needing advanced permissions and audit logs Enterprise Custom Large organizations For most small teams and freelancers, the Plus plan is the sweet spot. You get unlimited blocks, file uploads, 30-day version history, and Notion AI add-on eligibility.\nProject Management Skills Open Career Doors Organizations at every scale need people who can keep projects on track and teams aligned. Find your next career on doda — browse project manager and operations roles on Japan\u0026rsquo;s leading job platform.\nGetting Started: Your First Week Action Plan Day 1: Create your workspace, set up the Projects and Tasks databases with the properties listed above.\nDay 2: Import your current to-do list (you can paste directly into a database or use the CSV import). Create filtered views for \u0026ldquo;My Tasks Today\u0026rdquo; and \u0026ldquo;This Week.\u0026rdquo;\nDay 3: Build your first project page using the template structure above. Move all related tasks to link to this project.\nDay 4: Share a project page with a collaborator or client (view-only link). Test the comment and mention features.\nDay 5: Set up your weekly review dashboard as your Notion homepage.\nWeek 2+: Add formulas, rollups, and automation as you identify gaps.\nNotion has a learning curve, but it\u0026rsquo;s shallower than people expect. The biggest unlock is realizing that databases are just smart tables — once that clicks, everything else follows.\nIf you want a head start, our Notion PM Starter Kit (available on our Payhip store) includes a pre-built workspace with all the databases, views, templates, and formulas described in this guide — ready to duplicate into your account.\nRelated reads:\nBest AI Tools for Small Business 2026 Best Free Alternatives to ChatGPT 2026 Related Tools Create a monthly budget → Budget Planner Calculate your ideal freelance rate → Freelance Rate Calculator Stay focused with the Pomodoro Technique → Pomodoro Timer Count down to any date or event → Countdown Timer Related Templates Put these techniques into practice with our ready-made templates:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Copy-paste prompts for every situation The AI Productivity Playbook 2026 — 50+ AI workflows and automation strategies This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/notion-project-management-guide-2026/","summary":"\u003cp\u003eIf you\u0026rsquo;ve been searching for a single tool that can replace your project management software, wiki, spreadsheet, and task tracker — Notion is the closest thing to it. In 2026, Notion has evolved from a note-taking app into a full-blown work operating system used by solo freelancers, startups, and Fortune 500 teams alike.\u003c/p\u003e\n\u003cp\u003eThis guide covers everything: setting up your first Notion workspace, building a project management system from scratch, using AI features, and avoiding the most common mistakes people make when getting started.\u003c/p\u003e","title":"How to Use Notion for Project Management 2026: The Complete Guide"},{"content":"Passive income is a term that gets abused. Most \u0026ldquo;passive income\u0026rdquo; either requires substantial upfront work, ongoing maintenance, or both. Digital products are the closest thing to genuinely passive income that exists — and they\u0026rsquo;re more accessible than ever in 2026.\nHere\u0026rsquo;s what\u0026rsquo;s real: you do work upfront to create the product. You do some ongoing work to market it. But once a product is created and distributed, a single piece of work can sell thousands of times without additional effort. That\u0026rsquo;s the value proposition.\nThis guide covers seven digital product models, with honest timelines, realistic revenue ranges, and the tools you need.\nWhat Makes Digital Products Different Before covering specific models, it\u0026rsquo;s worth understanding why digital products are uniquely suited to passive income:\nZero marginal cost: Once created, each additional sale costs you nothing. Physical products require materials, manufacturing, and shipping. Unlimited inventory: You can sell the same file to 10,000 customers without stocking anything. Global reach: A product created in Manchester can sell to a customer in Melbourne at 3am without any action on your part. Low overhead: No warehouse, no shipping, no returns to process manually. The main challenge is discoverability — you need traffic, and building traffic takes time or money.\n1. Digital Templates and Planners What they are: Pre-made, editable files that customers download and customize. Popular formats include Canva templates, Notion templates, Google Sheets/Excel templates, and printable PDF planners.\nWhy they sell: People want the result (a beautiful social media post, an organized budget tracker, a weekly planner) but don\u0026rsquo;t want to build it from scratch. Templates provide the framework they need.\nBest categories in 2026:\nSocial media graphics templates (Canva) Business/financial planners (Google Sheets or PDF) Resume and CV templates Wedding and event planning templates Notion workspace templates Where to sell:\nEtsy — largest marketplace for digital planners and templates Gumroad — creator-friendly, good for Notion and document templates Your own website using Shopify or Lemon Squeezy Realistic timeline and revenue:\nMonth 1–2: Product creation and listing setup Month 3–6: First consistent sales (often $50–$200/month) Month 6–12: With 20–50 products and optimized listings: $500–$3,000/month Established shops with strong niches: $5,000–$15,000/month Tools: Canva Pro, Adobe Illustrator, Notion, Google Workspace\n2. E-books and PDF Guides What they are: Written guides, how-to books, playbooks, or reference documents sold as PDF downloads.\nWhy they sell: Customers pay for organized, expert knowledge in a specific domain. A 30-page PDF that solves a specific problem can sell for $15–$50, and the customer gets immediate access.\nHigh-converting topics:\nHow-to guides for professional skills (photography, bookkeeping, marketing) Specific niche tutorials (how to train a rescue dog, how to start a plant-based diet) Financial guides (how to negotiate your salary, how to build a budget) Travel guides for specific destinations or trip types Where to sell:\nGumroad — simple, low fees, good discovery Amazon KDP — reach is enormous for Kindle ebooks; royalties are lower Etsy — works for guides that fit the \u0026ldquo;digital downloads\u0026rdquo; category Your own landing page (highest margin) Pricing: $7–$47 for most how-to guides. Comprehensive playbooks or business guides: $97–$197+.\nRealistic timeline and revenue:\nA single well-positioned ebook can earn $100–$1,000/month if it solves a specific, searchable problem A library of 10+ ebooks across a niche: $2,000–$8,000/month Using AI: ChatGPT dramatically speeds up the research and drafting process. A 10,000-word ebook that previously took 40+ hours to write can be drafted in 8–12 hours with AI assistance. You still need editing, fact-checking, and expert perspective — but the blank page problem is solved.\n[Start selling on Gumroad for free at gumroad.com]\n3. Online Courses What they are: Video or text-based educational programs sold on a platform or your own website.\nWhy they sell: Courses command premium pricing because they promise transformation — a specific skill or outcome — through a structured learning path. Customers pay more for a course than an ebook covering the same material because the format implies accountability and completeness.\nHigh-demand topics in 2026:\nAI tools and automation for professionals Financial literacy and investing basics Programming and data skills Creative skills (photography, video editing, music production) Language learning for specific purposes Platforms:\nUdemy — 50+ million students; competitive but large audience; platform controls pricing Teachable / Kajabi — you own the customer relationship; higher revenue per sale Thinkific — similar to Teachable; good free tier Gumroad — simpler option for shorter courses Pricing: $27–$197 for mid-level courses on Udemy (they run aggressive discounts). $197–$997 for premium courses on your own platform.\nRealistic timeline and revenue:\nCreation: 40–120 hours of content work Launch month: $0–$500 (unless you have an existing audience) Month 3–6: $200–$2,000/month with active promotion Established courses with SEO traffic or ad spend: $2,000–$20,000/month The audience-first shortcut: Build an email list or social media following in your niche before launching a course. An audience of 2,000 engaged subscribers converts far better than relying on platform discovery alone.\n4. Stock Photos, Video, and Music What they are: Licensing original creative content for use in commercial projects, marketing materials, websites, and media.\nWhy it works: Every website, video, and marketing campaign needs licensed visual and audio assets. Creators who upload quality stock receive small royalty payments each time their work is licensed — and these add up at scale.\nWhere to sell:\nPhotos: Shutterstock, Adobe Stock, Getty Images (contributor), iStock Video: Pond5, Artgrid, Shutterstock Video Music: AudioJungle, Artlist, Musicbed, Pond5 Realistic timeline and revenue:\nSmall portfolios (under 500 items): $50–$300/month across platforms Established portfolios (2,000+ items): $500–$3,000/month Top contributors: $5,000–$20,000+/month The AI angle: AI image generators (Midjourney, Stable Diffusion) have complicated the stock photo market. Most platforms have introduced policies requiring disclosure of AI involvement. However, video stock and music licensing remain largely human-driven and less commoditized.\n5. Notion and Productivity Templates What they are: Pre-built Notion workspaces, databases, and systems sold as downloadable templates.\nWhy they sell: Notion is powerful and complex. Many users know they want a functional workspace but don\u0026rsquo;t have the time or skills to build one. A well-designed template that solves a specific workflow problem commands $15–$97 per sale.\nHigh-converting Notion templates:\nSecond Brain / personal knowledge management systems Freelancer business management dashboards Content creator planning systems Student study and semester planning Budget and finance trackers Where to sell:\nGumroad — best for Notion templates (direct link sharing works well) Etsy — growing category Your own website Twitter/X and LinkedIn — creators with audiences sell direct Pricing: $15–$97 per template. Premium comprehensive systems: $127–$197.\nRealistic timeline and revenue:\nA single popular template: $200–$800/month A library of 10–20 targeted templates: $1,000–$5,000/month [Upgrade to Gumroad premium at gumroad.com]\n6. Printables and Worksheets What they are: PDF files designed to be printed by the customer — activity sheets, art prints, worksheets, coloring pages, habit trackers, meal planners, affirmation cards.\nWhy they sell: Low price point ($2–$15) means low purchase friction. Customers get instant gratification and a physical artifact. Sellers get passive sales from Etsy search traffic.\nHigh-demand categories:\nKids\u0026rsquo; activity sheets and educational printables Wedding stationery and party decorations Motivational wall art and quote prints Fitness and health trackers Budget and meal planning worksheets Where to sell: Etsy is the dominant platform for printables. Build a focused shop in one niche rather than listing random products.\nRealistic timeline and revenue:\nShops with 50+ listings in a focused niche: $300–$1,500/month Established shops with 200+ listings and reviews: $2,000–$10,000+/month Using AI: ChatGPT generates content ideas and worksheet text. Canva designs the layouts. A basic printable can be created in 30–90 minutes once you have a template workflow established.\n7. Software Tools and Scripts What they are: Browser extensions, scripts, automation tools, Excel/Google Sheets add-ons, or small software utilities that solve specific problems.\nWhy they sell: People pay for tools that save time. A $20/year browser extension that saves a professional 30 minutes per week is obviously worth it — yet it only needs to be built once.\nExamples:\nGoogle Sheets add-ons for specific industries (real estate, accounting) Excel templates with embedded macros for repetitive tasks Browser extensions for productivity or research Notion integrations Zapier or Make automation templates Where to sell:\nGoogle Workspace Marketplace Microsoft AppSource Chrome Web Store (subscription model) Gumroad (for scripts and templates) Paddle or Lemon Squeezy (for SaaS-style products) Realistic timeline and revenue: Highly variable. A useful niche tool with good SEO can earn $500–$5,000/month passively. A well-marketed software product with a subscription model can scale significantly higher.\nUsing AI: GitHub Copilot and ChatGPT have made basic software development accessible to non-engineers. With AI assistance, a non-programmer can ship a functional script or add-on that previously required a developer.\nComparison Summary Product Type Startup Time Typical Price Monthly Potential Best Platform Templates 2–8 hrs each $9–$47 $500–$10,000+ Etsy, Gumroad E-books 20–80 hrs $15–$97 $500–$5,000 Gumroad, Amazon Online Courses 40–120 hrs $97–$497 $2,000–$20,000 Teachable, Udemy Stock Media Ongoing Per license $200–$5,000 Shutterstock, Pond5 Printables 1–3 hrs each $3–$15 $500–$8,000 Etsy Software/Scripts 10–100 hrs $20–$200/yr $500–$unlimited Various Launch Your Digital Product Business With Your Own Domain Selling on Etsy or Gumroad is a great start — but your own website builds a brand nobody can take from you. Get your domain on Onamae.com and secure the online presence your digital product business deserves.\nThe Path Forward Start with one product type. The most accessible starting points are templates and printables because they require the least technical knowledge and have the fastest time-to-market.\nThe roadmap:\nIdentify a niche where you have knowledge or interest Research what\u0026rsquo;s already selling (Etsy bestsellers, Gumroad top products) Create 5–10 products in that niche Optimize listings for search (SEO matters on every platform) Collect reviews and use them to improve Scale what\u0026rsquo;s working; discontinue what isn\u0026rsquo;t The income builds slowly, then compounds. Most creators who hit $3,000+/month in passive digital product income spent 12–24 months building there. The timeline is real, but so is the destination.\nRelated Tools \u0026amp; Articles Calculate your ideal freelance hourly rate → Freelance Rate Calculator See how passive income compounds → Compound Interest Calculator See your 2026 federal tax bracket and effective rate → Tax Bracket Calculator Calculate tax on your digital product income → Side Hustle Tax Calculator Need a markdown editor? → Markdown Preview — write and preview markdown live\nGenerate placeholder text for layouts → Lorem Ipsum Generator — instant filler content\nResize images for your digital product covers and thumbnails → Image Resizer Generate a QR code for your product landing page → QR Code Generator Side Hustles with AI Tools 2026: Best Ways to Earn How to Make Money with AI in 2026 AI Tools for Freelancers to Earn More 2026 Passive Income Ideas That Actually Work in 2026 How to Start Freelancing in 2026 Related Templates Start your freelance or side hustle journey with these resources:\nSide Hustle Starter Kit 2026 — 15 proven ideas with step-by-step guides ChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Boost your productivity instantly This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/passive-income-digital-products-2026/","summary":"\u003cp\u003ePassive income is a term that gets abused. Most \u0026ldquo;passive income\u0026rdquo; either requires substantial upfront work, ongoing maintenance, or both. Digital products are the closest thing to genuinely passive income that exists — and they\u0026rsquo;re more accessible than ever in 2026.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s what\u0026rsquo;s real: you do work upfront to create the product. You do some ongoing work to market it. But once a product is created and distributed, a single piece of work can sell thousands of times without additional effort. That\u0026rsquo;s the value proposition.\u003c/p\u003e","title":"7 Passive Income Ideas Using Digital Products"},{"content":"※本記事にはアフィリエイト広告が含まれています。\nPassive Income Ideas That Actually Work 2026 — Complete Guide \u0026ldquo;Make money while you sleep.\u0026rdquo; It\u0026rsquo;s one of the most seductive promises in personal finance. It\u0026rsquo;s also one of the most misrepresented. YouTube videos and Instagram influencers have flooded the internet with \u0026ldquo;passive income\u0026rdquo; content that\u0026rsquo;s either straight-up lying or dramatically understating the upfront work required.\nHere\u0026rsquo;s the honest truth: passive income is real, it works, and people build meaningful financial freedom from it every day. But there is almost always significant upfront effort, capital, or both required before the income becomes genuinely passive. The passive part comes after the active work.\nThe goal of this guide is not to sell you a dream — it\u0026rsquo;s to give you a realistic map of what\u0026rsquo;s actually working in 2026, what each strategy requires, and how to choose the right approach for your situation.\nIn this guide, you\u0026rsquo;ll learn:\nThe most reliable passive income streams available in 2026 What each strategy actually requires (upfront investment, time, skills) Realistic income expectations — not fantasy projections Which strategies are best for people with capital vs. time vs. skills The most common passive income mistakes and how to avoid them A step-by-step framework for building your first income stream What Is Passive Income? Definition \u0026amp; How It Works Passive income is earnings that require minimal ongoing effort to maintain after the initial setup or investment. The key word is \u0026ldquo;minimal\u0026rdquo; — it doesn\u0026rsquo;t mean \u0026ldquo;zero.\u0026rdquo;\nCategories of passive income sources:\nCapital-based passive income (requires money to invest):\nDividend stocks and index funds High-yield savings accounts and CDs Real estate rental income (including REITs) Peer-to-peer lending Treasury bonds / I-bonds Asset-based passive income (requires creating or acquiring an asset):\nDigital products (ebooks, templates, courses) Print-on-demand products Affiliate marketing websites/blogs Royalties (music, photography, software) Licensing intellectual property Platform-based passive income (leverages existing platforms):\nYouTube channel ad revenue (after initial content creation) Podcast sponsorships Stock photography and video App development Semi-passive income (requires occasional active work):\nRental real estate (with property management) Selling on Amazon FBA Renting assets (car, parking space, camera gear, storage space) Pros and Cons of Passive Income Pros:\nIncome that continues when you\u0026rsquo;re sick, on vacation, or sleeping Reduces dependence on a single income source (job loss risk) Can eventually replace or supplement employment income Some streams scale significantly without proportional effort Builds long-term wealth and financial independence Cons:\nAlmost all require significant upfront investment of time, money, or skills Many passive income \u0026ldquo;ideas\u0026rdquo; are much harder than advertised Some platforms change rules, reducing income unpredictably Tax treatment varies — many passive income streams are fully taxable Requires patience: most passive income streams take 1–3+ years to become meaningfully passive Best Passive Income Ideas 2026: Options Compared Passive Income Stream Upfront Effort Upfront Capital Time to First Income Monthly Income Potential* Passive Level Dividend index funds / ETFs Low $10,000–$50,000+ Immediate (small) $40–$200/mo per $10K Very High High-yield savings (HYSA) Very Low Any amount Immediate $40–$50/mo per $10K Very High REITs (Real Estate Investment Trusts) Low $1–$10,000+ Immediate $40–$80/mo per $10K Very High Rental real estate High $20,000–$60,000+ 1–3 months $200–$800/mo per property Medium Digital products (ebook, templates) Very High (one-time) $0–$500 1–6 months $200–$5,000+/mo High (after creation) Online course creation Very High $500–$2,000 3–12 months $500–$10,000+/mo High (after creation) Affiliate marketing blog Very High $100–$500 6–24 months $500–$10,000+/mo High (after traction) Print-on-demand Medium $0–$200 1–6 months $100–$2,000+/mo High Stock photography/video Medium $500–$2,000 (equipment) 3–12 months $100–$1,000+/mo High YouTube ad revenue Very High $500–$2,000 (equipment) 6–24 months $500–$10,000+/mo High (after growth) Peer-to-peer lending Low $1,000–$10,000 1 month 5–10% APR (risky) High Renting out a room / Airbnb Medium Existing property 1 month $500–$3,000+/mo Low-Medium I-Bonds / Treasury bonds Very Low $100+ Immediate 4–5% APR Very High Licensing a patent/software Very High (one-time) Varies 6–24 months Highly variable Very High Income ranges are illustrative and highly variable. Not a guarantee.\nFind the best investment platform for passive income Top Passive Income Types at a Glance 📈 Dividends Capital-based Very High passivity Needs: $10K+ 📦 Digital Products Asset-based High (after creation) Needs: Skills + time 🏢 REITs Capital-based Very High passivity Needs: $1+ 🔗 Affiliate Marketing Platform-based High (after traction) Needs: Content + 6–24 mo 🎨 Print-on-Demand Platform-based High passivity Needs: Designs only 🏦 HYSA / Bonds Capital-based Extremely passive Needs: Any amount The Best Passive Income Strategies: Deep Dives 1. Dividend Investing: The Most Truly Passive Option What it is: Investing in dividend-paying stocks, ETFs, or index funds that distribute a portion of company earnings to shareholders regularly (usually quarterly).\nHow passive is it? Extremely passive. You invest, you collect dividends, you reinvest. No ongoing work required beyond the initial investment decision.\nRealistic numbers:\nThe S\u0026amp;P 500 dividend yield is approximately 1.3–1.5% annually Dividend-focused ETFs (like VYM or SCHD) yield 3–4% On $50,000 invested at 3.5% yield: approximately $1,750/year or ~$146/month The challenge: You need significant capital for dividend income to be meaningful. $10,000 generates ~$300–$400/year in dividends. To replace a $4,000/month income entirely through dividends at a 3.5% yield would require approximately $1.37 million invested.\nBest approach: Don\u0026rsquo;t try to live off dividends immediately. Invest consistently, reinvest all dividends (DRIP), and let the portfolio grow. Dividend income becomes meaningful after years of compounding.\nBest dividend ETFs for 2026:\nVYM (Vanguard High Dividend Yield): 3.2% yield, 0.06% ER SCHD (Schwab U.S. Dividend Equity): 3.6% yield, 0.06% ER VIG (Vanguard Dividend Appreciation): 1.8% yield, focus on dividend growth, 0.06% ER Invest in dividend ETFs with $0 minimum at Fidelity 2. Real Estate Investment Trusts (REITs): Real Estate Without the Headaches What it is: REITs are companies that own income-producing real estate (apartments, offices, shopping centers, data centers, etc.) and are required to distribute at least 90% of taxable income to shareholders as dividends.\nHow passive is it? Extremely passive — buy shares like a stock, receive dividends automatically.\nRealistic numbers:\nREIT dividend yields: typically 3–6% VNQ (Vanguard Real Estate ETF): ~3.5% yield, 0.12% ER Individual REITs can yield 5–8% or more The appeal: REITs provide real estate exposure without being a landlord. No tenants, no maintenance calls, no property management.\nBest REIT ETFs for 2026:\nVNQ: Broad U.S. real estate exposure SCHH (Schwab U.S. REIT ETF): Lower cost alternative O (Realty Income): Individual REIT paying monthly dividends, nicknamed \u0026ldquo;The Monthly Dividend Company\u0026rdquo; Tax note: REIT dividends are often classified as ordinary income (not qualified dividends), making them less tax-efficient in taxable accounts. Consider holding REITs inside a Roth IRA for maximum tax-free growth.\n3. Digital Products: High Upfront Work, High Passive Returns What it is: Creating a digital product — ebook, spreadsheet template, Notion template, Lightroom preset, Canva template, printable, educational guide — once, then selling it repeatedly with zero additional production cost.\nHow passive is it? Highly passive once created. Uploading to a platform like Gumroad, Etsy, or your own website handles sales, delivery, and payment automatically.\nRealistic timelines and income:\nCreation time: 20–200 hours depending on complexity Time to first sale: 1–6 months (depends on marketing effort) Income range: $100–$10,000+/month depending on niche, quality, and audience The catch: You need an audience or marketing channel to drive traffic. A beautifully designed ebook with no promotion earns nothing. The most successful digital product sellers have a blog, social media following, or email list that drives buyers.\nTop platforms for selling digital products in 2026:\nGumroad: Easy setup, handles payments, 10% fee (flat $10/month eliminates per-sale fee) Etsy: Great for templates, printables, Canva designs — built-in search traffic Teachable/Thinkific: For online courses with video content Podia: All-in-one: courses, digital downloads, community Stan Store: Growing platform popular among social media creators Best niches for digital products in 2026:\nPersonal finance spreadsheets and budget templates Notion templates for productivity Canva social media templates for small businesses Professional resume and cover letter templates Meal planning and fitness tracking spreadsheets 4. Affiliate Marketing: Earn Commissions on Recommendations What it is: You create content (blog, YouTube, social media, email newsletter) and include affiliate links to products or services. When readers/viewers purchase through your link, you earn a commission — typically 3–30% depending on the product category.\nHow passive is it? Content created once can earn commissions for years. A blog post written in 2024 can still earn affiliate income in 2030 if it ranks in search engines.\nRealistic timelines and income:\nTime to meaningful income: 6–24 months (SEO takes time to compound) Investment: $100–$500 to start (domain, hosting, basic tools) Income range once established: $500–$50,000+/month (highly variable) High-commission affiliate programs relevant to personal finance:\nBrokerage referrals (Robinhood, Public, Webull): $5–$500 per referred account Credit card affiliates: $100–$500 per approved application Banking affiliates (Marcus, SoFi, Ally): $50–$200 per opened account Software/fintech (YNAB, Mint alternatives): recurring commission Online course platforms: 30–50% of course price The honest caveat: Affiliate marketing is highly competitive in personal finance. Building a site that earns significant passive income typically takes 1–2 years of consistent content creation, SEO optimization, and audience building. It\u0026rsquo;s not passive at the start — but the long tail can be highly profitable.\n5. Print-on-Demand: Sell Products Without Inventory What it is: You upload designs to a print-on-demand platform (Printful, Printify, Redbubble). When a customer buys a shirt, mug, or phone case with your design, the platform prints it and ships it — you pocket the profit margin.\nHow passive is it? Very passive once designs are uploaded and a storefront is set up. No inventory, no shipping, no customer service (the platform handles it).\nRealistic numbers:\nProfit margins: $3–$15 per item depending on product and platform Income range: $100–$5,000+/month with 20–200+ designs Time to first sale: 1–3 months Key platforms:\nRedbubble: Large built-in marketplace, great for artists Merch by Amazon: High traffic, competitive to get accepted Printify + Etsy: More control over pricing and store branding Society6: Art-focused marketplace How to Choose Your Passive Income Strategy What to Look For Match your passive income strategy to your available resources:\nIf You Have Best Strategy Capital ($10,000+) Dividend ETFs, REITs, HYSA, I-Bonds Skills (writing, design, teaching) Digital products, affiliate marketing, courses Time (10–20 hrs/week for 6–12 months) Affiliate blog, YouTube, print-on-demand Real estate equity Rental income, Airbnb Small amounts ($100–$1,000) HYSA, I-Bonds, small dividend portfolio, digital products No time, no skills, no capital Start with HYSA for emergency fund, then build from there Common Mistakes to Avoid Mistake 1: Expecting passive income to be immediately passive Every passive income stream requires significant upfront work or capital. An affiliate blog takes 12–18 months to generate meaningful SEO traffic. A dividend portfolio requires years of consistent investing to generate substantial income. Plan for a \u0026ldquo;cultivation phase\u0026rdquo; before the harvest.\nMistake 2: Spreading across too many streams at once It\u0026rsquo;s tempting to try five passive income strategies simultaneously. The result is usually five things done poorly rather than one done well. Master one stream before adding another.\nMistake 3: Investing in \u0026ldquo;passive income\u0026rdquo; courses from influencers The meta-irony of passive income: the most reliable passive income for many influencers is selling courses about passive income. Be skeptical of $497 courses promising \u0026ldquo;6-figure passive income\u0026rdquo; — your money is better invested in index funds.\nMistake 4: Ignoring taxes Passive income is taxable income. Dividends may be qualified (lower rate) or ordinary (higher rate). Rental income is ordinary income but with depreciation deductions. Affiliate income and digital product sales are typically self-employment income. Consult a tax professional as your passive income grows.\nMistake 5: Using passive income as an excuse to delay investing For most people under 40, the single highest-return \u0026ldquo;passive income\u0026rdquo; strategy is maximizing retirement account contributions and investing in broad market index funds. The guaranteed, risk-adjusted return of tax-free compounding in a Roth IRA often beats speculative passive income ventures.\nRelated: Best Index Funds for Beginners 2026 Step-by-Step Guide: Building Your First Passive Income Stream Step 1: Assess your starting point Honestly assess what you have: capital, skills, time, and existing assets. Your resources determine your best starting strategy.\nStep 2: Start with the financial foundation first Before building passive income from creative ventures, make sure:\nEmergency fund is in a high-yield savings account (already earning ~4.5% — passive income!) You\u0026rsquo;re getting your full 401k employer match You\u0026rsquo;re contributing to a Roth IRA These are the most reliable, risk-adjusted forms of \u0026ldquo;passive\u0026rdquo; financial return available.\nStep 3: Choose one strategy to focus on for 6–12 months Based on your resources, choose one strategy. Commit to it fully for at least 6 months before evaluating whether to continue or pivot.\nStep 4: Set specific, measurable goals \u0026ldquo;Make passive income\u0026rdquo; is not a goal. \u0026ldquo;Publish 12 blog posts and earn my first $100 in affiliate commissions within 90 days\u0026rdquo; is a goal. Specific targets create accountability.\nStep 5: Build your first asset or make your first investment\nCapital-based: Open a brokerage account and purchase your first dividend ETF Digital product: Outline and create your first template or ebook Affiliate: Register a domain, set up hosting, and publish your first 5 articles Print-on-demand: Create 10 designs and list them on Redbubble or Etsy Step 6: Set up systems for automation Passive income is passive because systems do the work:\nDividend reinvestment: Enable DRIP on your brokerage account Digital products: Set up automatic payment processing (Gumroad, Stripe) Affiliate marketing: Schedule content publishing and link tracking tools Rental income: Set up automatic rent collection (Zelle, Venmo, Cozy, or PropertyWare) Step 7: Track income and reinvest Monitor your passive income growth. In the early stages, reinvest all passive income into growth (more shares, more content, more designs) rather than spending it. Compounding applies to passive income just as it does to investment returns.\nRealistic Passive Income Timeline Year Capital-Based (invest $500/mo in VYM) Digital Product / Affiliate Blog Year 1 $6,000 invested, ~$200/yr in dividends 0–$500/yr (building phase) Year 2 $13,000 invested, ~$455/yr in dividends $500–$5,000/yr (gaining traction) Year 3 $21,000 invested, ~$735/yr in dividends $2,000–$20,000/yr (compounding) Year 5 $38,000 invested, ~$1,330/yr in dividends $10,000–$60,000/yr (established) Year 10 $97,000 invested, ~$3,395/yr in dividends $20,000–$100,000+/yr (legacy asset) Assumes 7% annual portfolio growth and 3.5% dividend yield for capital-based; income for digital/affiliate is highly variable and not guaranteed.\nFrequently Asked Questions Q: Is passive income really passive? A: True passive income — where money arrives with literally zero ongoing effort — is rare and usually requires significant upfront capital (dividend investing) or significant upfront work (digital products, content). Most \u0026ldquo;passive\u0026rdquo; income requires at least occasional maintenance. The goal is income that requires much less ongoing work than active employment.\nQ: How much money do I need to start generating passive income? A: You can start with as little as $1 (dividend ETF via fractional shares) or $0 (digital product created with free tools). Meaningful passive income from capital-based strategies typically requires $50,000+ to generate significant monthly income. Content/product-based strategies require time and skills more than capital.\nQ: What is the fastest passive income stream to set up? A: Opening a high-yield savings account takes 10 minutes and immediately starts earning 4–5% APY — genuinely passive interest income from day one. For larger returns: opening a brokerage account and purchasing a dividend ETF takes 15–30 minutes and generates quarterly dividend income immediately.\nQ: Is rental income truly passive? A: Rental income with a property management company handling tenant relations, maintenance, and rent collection is relatively passive (but costs 8–12% of monthly rent). Self-managed rental properties can be a significant time investment. REITs are a better choice if you want real estate exposure without active management.\nQ: How is passive income taxed? A: It depends on the source. Qualified dividends: taxed at 0%, 15%, or 20% (favorable capital gains rates). Non-qualified (ordinary) dividends and REIT dividends: taxed as ordinary income. Interest income (HYSA, bonds): ordinary income. Digital product / affiliate income: self-employment income (subject to SE tax). Rental income: ordinary income with depreciation deduction available. Consult a tax professional as your passive income grows.\nQ: Can passive income replace my job? A: Yes — this is the core of financial independence / early retirement (FIRE) philosophy. Replacing a full salary with passive income requires building substantial capital or highly profitable digital assets. Most people who achieve this do so over 5–15 years of disciplined saving, investing, and/or building digital businesses. It\u0026rsquo;s achievable but requires a realistic long-term commitment.\nQ: What are the best passive income ideas with no money? A: With no capital but time and skills: (1) Create and sell digital templates/products on Etsy or Gumroad, (2) Start an affiliate marketing blog using free blogging platforms, (3) Create print-on-demand designs on Redbubble (free to list), (4) License photography to stock sites (500px, Shutterstock — free to submit). These require significant time investment before generating income.\nBuild Your Online Home Base for Passive Income Whether you\u0026rsquo;re running a blog, selling digital products, or building an affiliate site, a professional domain is your foundation. Get your domain on Onamae.com and start building passive income assets on a platform you own.\nConclusion: Passive Income Is a System, Not a Scheme The fantasy of passive income — pressing a button and watching money flow in effortlessly — isn\u0026rsquo;t real. But the reality of passive income is still remarkable: build or buy assets that generate cash flow while you sleep, travel, or work on other things.\nThe most reliable path to meaningful passive income in 2026:\nStart with your financial foundation: HYSA for emergency fund, Roth IRA for retirement, employer 401k match for free money Invest consistently in dividend ETFs: Not exciting, extremely reliable over time Build one digital income asset: Pick a niche, create quality content or products, be patient Reinvest everything in the early years: Compounding requires time; spending early passive income kills the compounding The best time to start building passive income streams was five years ago. The second best time is today.\nStart building your passive income foundation:\nOpen a Fidelity account and invest in dividend ETFs — $0 minimum Open a high-yield savings account at SoFi — earn 4.6% APY passively Sell your first digital product on Gumroad — free to start Related: How to Start Investing with $100 Disclaimer: This article is for informational purposes only and does not constitute financial advice. Investment decisions should be made based on your individual circumstances. Please consult a qualified financial advisor before making investment decisions. Information is current as of the publication date — verify details on official websites.\nDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nRelated Tools \u0026amp; Articles Calculate your ideal freelance hourly rate → Freelance Rate Calculator Compound Interest Calculator — See how investments grow with compound interest over time Tax Bracket Calculator — See your 2026 federal tax bracket and effective rate Savings Goal Calculator — Calculate how long to reach any savings target Retirement Savings Calculator — Estimate your nest egg with 401(k), IRA, and employer match Side Hustle Tax Calculator — Estimate taxes on your passive income streams Track your total net worth → Net Worth Calculator Plan your path to financial independence → FIRE Calculator 15 Side Hustles You Can Start with AI Tools Today How to Start Investing with $100 in 2026 Best Index Funds for Beginners 2026 This article contains affiliate links. We may earn a commission at no extra cost to you.\nRelated Templates Start your freelance or side hustle journey with these resources:\nSide Hustle Starter Kit 2026 — 15 proven ideas with step-by-step guides ChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Boost your productivity instantly ","permalink":"https://productivity-works.com/posts/passive-income-ideas-that-actually-work-2026/","summary":"\u003cp\u003e※本記事にはアフィリエイト広告が含まれています。\u003c/p\u003e\n\u003ch1 id=\"passive-income-ideas-that-actually-work-2026--complete-guide\"\u003ePassive Income Ideas That Actually Work 2026 — Complete Guide\u003c/h1\u003e\n\u003cp\u003e\u0026ldquo;Make money while you sleep.\u0026rdquo; It\u0026rsquo;s one of the most seductive promises in personal finance. It\u0026rsquo;s also one of the most misrepresented. YouTube videos and Instagram influencers have flooded the internet with \u0026ldquo;passive income\u0026rdquo; content that\u0026rsquo;s either straight-up lying or dramatically understating the upfront work required.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s the honest truth: passive income is real, it works, and people build meaningful financial freedom from it every day. But there is almost always significant upfront effort, capital, or both required before the income becomes genuinely passive. The passive part comes after the active work.\u003c/p\u003e","title":"Passive Income Ideas That Actually Work 2026"},{"content":"※本記事にはアフィリエイト広告が含まれています。\nRoth IRA vs Traditional IRA Which Is Better — Complete Guide [2026] Choosing between a Roth IRA and a Traditional IRA is one of the most consequential financial decisions you can make in your 20s, 30s, and 40s. Get it right, and you could save tens of thousands — or even hundreds of thousands — of dollars in taxes over your lifetime. Get it wrong, and you\u0026rsquo;ll pay more than you have to.\nThe frustrating truth? Financial advisors and internet forums are full of conflicting advice. \u0026ldquo;Always do Roth!\u0026rdquo; some say. \u0026ldquo;Traditional is smarter!\u0026rdquo; say others. The real answer, as with most things in personal finance, is: it depends.\nIn this guide, you\u0026rsquo;ll learn:\nThe key differences between Roth and Traditional IRAs Which account wins based on your current income and expected future tax rate 2026 contribution limits, income limits, and phase-out ranges The best strategies for high earners who exceed income limits When it makes sense to have both types of accounts Common mistakes people make when choosing between them By the end, you\u0026rsquo;ll know exactly which account (or combination) is right for your specific situation. Let\u0026rsquo;s get into it.\nWhat Is an IRA? The Basics Explained Definition \u0026amp; How It Works An IRA — Individual Retirement Account — is a tax-advantaged investment account designed to help you save for retirement. Both Roth and Traditional IRAs allow you to invest in a wide range of assets: stocks, bonds, index funds, ETFs, mutual funds, and more.\nThe critical difference is when you get the tax break.\nTraditional IRA: You may deduct contributions from your taxable income now (tax break upfront), your money grows tax-deferred, and you pay ordinary income taxes when you withdraw money in retirement.\nRoth IRA: You contribute after-tax dollars (no upfront tax break), your money grows tax-free, and qualified withdrawals in retirement are completely tax-free.\nThink of it this way: Traditional IRA is \u0026ldquo;pay taxes later.\u0026rdquo; Roth IRA is \u0026ldquo;pay taxes now, never again.\u0026rdquo;\nPros and Cons Traditional IRA — Pros:\nPotential tax deduction on contributions lowers your tax bill today Reduces your current taxable income (useful if you\u0026rsquo;re in a high bracket now) No income limits on contributions (though deductibility has limits) Good if you expect to be in a lower tax bracket in retirement Traditional IRA — Cons:\nAll withdrawals in retirement are taxed as ordinary income Required Minimum Distributions (RMDs) starting at age 73 Early withdrawals (before 59½) trigger taxes + 10% penalty If tax rates rise in the future, you\u0026rsquo;ve deferred into a potentially worse situation Roth IRA — Pros:\nTax-free growth and tax-free qualified withdrawals No RMDs during your lifetime — leave it to heirs Contributions (not earnings) can be withdrawn at any time, penalty-free Ideal if you expect to be in a higher tax bracket in retirement More flexibility for early retirement or emergency access Roth IRA — Cons:\nNo upfront tax deduction Income limits restrict who can contribute directly Less beneficial if you\u0026rsquo;re currently in a very high tax bracket and expect lower income in retirement Related: Best Index Funds for Beginners 2026 Roth IRA vs Traditional IRA — Key Comparison Roth IRA CONTRIBUTIONS After-tax (no deduction) GROWTH Tax-FREE WITHDRAWALS Tax-FREE (qualified) REQUIRED MIN. DISTRIBUTIONS None during lifetime BEST FOR Young earners, lower tax now Traditional IRA CONTRIBUTIONS Pre-tax (may deduct) GROWTH Tax-deferred WITHDRAWALS Taxed as ordinary income REQUIRED MIN. DISTRIBUTIONS Yes — starting age 73 BEST FOR High earners, lower tax later VS Roth IRA vs Traditional IRA: Full Comparison Table Feature Roth IRA Traditional IRA Tax treatment on contributions After-tax (no deduction) Pre-tax (may be deductible) Tax on growth Tax-free Tax-deferred Tax on withdrawals Tax-free (qualified) Taxed as ordinary income 2026 contribution limit $7,000 ($8,000 if 50+) $7,000 ($8,000 if 50+) Income limit to contribute Yes — phases out above $150K (single) / $236K (married) No income limit on contributions Income limit for deductibility N/A Yes — phases out if covered by workplace plan Required Minimum Distributions None during your lifetime Yes — starting at age 73 Early withdrawal of contributions Anytime, no penalty Taxes + 10% penalty before 59½ Early withdrawal of earnings Taxes + 10% penalty before 59½ (some exceptions) Taxes + 10% penalty before 59½ Best for Young earners, expecting higher taxes later High earners expecting lower taxes in retirement Backdoor strategy available? Yes (backdoor Roth) N/A Compare IRA options at top brokerages How to Choose: Key Factors What to Look For The core question when choosing between Roth and Traditional IRA is: Will you be in a higher or lower tax bracket in retirement than you are today?\nIf you\u0026rsquo;ll be in a higher bracket in retirement → Roth IRA wins (pay lower taxes now) If you\u0026rsquo;ll be in a lower bracket in retirement → Traditional IRA wins (defer taxes to when rates are lower) If you\u0026rsquo;re unsure → Consider splitting contributions between both (tax diversification)\nYour age and career stage matters:\nEarly 20s to mid-30s, lower income: Roth IRA is almost always the right choice. Your income (and tax rate) will likely be higher in retirement. Pay taxes now at a low rate. Peak earning years (40s-50s), high income: Traditional IRA deduction provides real tax relief now. Retirement withdrawals may come at a lower rate. Near retirement, unsure about income: Split between Roth and Traditional for flexibility. The 2026 income phase-out ranges for Roth IRA contributions:\nSingle filers: Phase-out begins at $150,000, eliminated at $165,000 Married filing jointly: Phase-out begins at $236,000, eliminated at $246,000 Married filing separately: Phase-out begins at $0, eliminated at $10,000 The 2026 deductibility phase-out for Traditional IRA (if covered by a workplace plan):\nSingle filers: Phase-out $79,000–$89,000 Married filing jointly (contributor covered): Phase-out $126,000–$146,000 Married filing jointly (spouse covered, contributor not): Phase-out $236,000–$246,000 Common Mistakes to Avoid Mistake 1: Assuming Traditional IRA always gives you a tax deduction If you or your spouse has a 401k at work, your ability to deduct Traditional IRA contributions phases out at relatively modest income levels. Many people contribute to a Traditional IRA thinking they\u0026rsquo;ll get a deduction — and they don\u0026rsquo;t.\nMistake 2: Ignoring the Roth IRA\u0026rsquo;s flexibility The ability to withdraw contributions (not earnings) from a Roth IRA at any time without penalty makes it an excellent hybrid emergency fund + retirement account for younger savers.\nMistake 3: Forgetting about Required Minimum Distributions Traditional IRAs force you to take RMDs starting at age 73, which can push you into higher tax brackets and complicate your retirement income planning. Roth IRAs have no RMDs.\nMistake 4: Not considering a Backdoor Roth if you earn too much High earners above the Roth IRA income limits can still access Roth benefits through the \u0026ldquo;backdoor Roth\u0026rdquo; strategy: contribute to a non-deductible Traditional IRA, then convert it to a Roth IRA. Consult a tax advisor before doing this if you have other IRA balances.\nMistake 5: Waiting to choose until you have the \u0026ldquo;perfect\u0026rdquo; answer If you genuinely can\u0026rsquo;t decide, split your contributions. Half Traditional, half Roth gives you tax diversification and flexibility.\nStep-by-Step Guide: Opening and Contributing to Your IRA Step 1: Determine your eligibility To contribute to either IRA, you must have earned income (wages, salaries, self-employment income). For a Roth IRA, check whether your MAGI falls below the phase-out range. For a Traditional IRA, check whether your contributions will be deductible based on your workplace plan status and income.\nStep 2: Choose your account type Based on the framework above:\nUnder 40, income under $100K: Roth IRA is usually best Over 40, income over $150K, have a 401k at work: Traditional IRA or maximize 401k first Unsure: Split contributions Step 3: Select a brokerage Top IRA providers for 2026:\nFidelity — No minimums, excellent index fund selection, great interface Vanguard — Best for index fund purists, lower costs at scale Charles Schwab — Excellent customer service, fractional shares available Betterment — Best automated/robo-advisor option for hands-off investors M1 Finance — Great for automated investing with custom allocations Step 4: Open your account The process takes about 10–15 minutes online. You\u0026rsquo;ll need:\nSocial Security number Bank account information for funding Basic personal information Step 5: Fund your account Transfer funds from your checking/savings account. You can contribute up to $7,000 ($8,000 if 50+) for the 2026 tax year. You have until the tax filing deadline (April 15, 2027) to make 2026 contributions.\nStep 6: Invest your contributions Don\u0026rsquo;t leave your IRA in cash! Choose your investments — most beginners do well with a simple three-fund portfolio or a target-date retirement fund. Related: Best Index Funds for Beginners 2026 Step 7: Set up automatic contributions Automate monthly contributions to take advantage of dollar-cost averaging. A $583/month automatic contribution hits the $7,000 annual limit.\nFrequently Asked Questions Q: Can I have both a Roth IRA and a Traditional IRA? A: Yes. You can contribute to both in the same year, but your total contributions across both accounts cannot exceed the annual limit ($7,000 in 2026). For example, you could put $4,000 in a Roth and $3,000 in a Traditional IRA.\nQ: Can I have an IRA and a 401k at the same time? A: Absolutely. Having a 401k doesn\u0026rsquo;t prevent you from contributing to an IRA. However, it may affect the deductibility of Traditional IRA contributions if your income is above the phase-out range.\nQ: What happens if I contribute too much to my IRA? A: Excess contributions are subject to a 6% penalty per year until corrected. Contact your IRA provider immediately if you accidentally over-contribute to remove the excess before the tax filing deadline.\nQ: What\u0026rsquo;s the backdoor Roth IRA and should I use it? A: The backdoor Roth is a strategy for high earners who exceed the Roth IRA income limits. You contribute to a non-deductible Traditional IRA (no income limit on contributions), then convert it to a Roth IRA. The conversion is generally tax-free if you have no other pre-tax IRA balances. If you do have pre-tax IRA balances, the \u0026ldquo;pro-rata rule\u0026rdquo; may create a tax liability. Consult a CPA before proceeding.\nQ: Can I convert my Traditional IRA to a Roth IRA? A: Yes — this is called a Roth conversion. You\u0026rsquo;ll owe income taxes on the converted amount in the year of conversion, but all future growth is tax-free. Roth conversions can be a smart strategy during low-income years or early retirement before Social Security kicks in.\nQ: At what age should I switch from Roth to Traditional IRA contributions? A: There\u0026rsquo;s no universal answer, but many financial planners suggest shifting toward Traditional contributions when you reach your peak earning years (typically mid-40s to late 50s), when the tax deduction provides the most value.\nQ: What investments should I put inside my IRA? A: Focus on broad, low-cost index funds. Total stock market funds (like VTI or FZROX), international funds (VXUS), and bond funds (BND) are all excellent choices. Avoid keeping low-growth assets like bonds in a Roth IRA — that\u0026rsquo;s where you want your highest-growth assets to benefit from tax-free growth.\nQ: Is a Roth IRA better than a 401k? A: These serve different purposes. Generally, contribute to your 401k at least up to the employer match first (free money!), then max out your Roth IRA, then return to maximizing your 401k if you have additional savings capacity.\nReady to Open Your IRA and Start Investing? Choosing the right IRA type is only half the battle — you also need a brokerage with low fees and great fund selection. Open a Rakuten Securities account to access a broad range of index funds and ETFs perfect for both Roth and Traditional IRA strategies.\nConclusion: Making the Right Choice for Your Financial Future The Roth IRA vs. Traditional IRA debate doesn\u0026rsquo;t have one universally correct answer — but it does have a correct answer for your specific situation. Here\u0026rsquo;s the simplified framework:\nYoung, lower income, expecting higher future taxes? → Roth IRA High income today, expecting lower taxes in retirement? → Traditional IRA Not sure, or want flexibility? → Contribute to both for tax diversification The most important decision is the one you actually make. Both accounts are vastly superior to keeping money in a taxable savings account. Open one today, invest in a simple index fund portfolio, and let decades of tax-advantaged compounding do the work.\nReady to open your IRA?\nOpen a Roth IRA at Fidelity — $0 minimum Compare IRA options at Charles Schwab Start investing with Betterment\u0026rsquo;s automated IRA Related: 401k vs IRA — Differences Explained Disclaimer: This article is for informational purposes only and does not constitute financial advice. Investment decisions should be made based on your individual circumstances. Please consult a qualified financial advisor before making investment decisions. Information is current as of the publication date — verify details on official websites.\nDisclosure: This article may contain affiliate links. We may earn a commission at no additional cost to you.\nRelated Tools \u0026amp; Articles Calculate your exact age instantly → Age Calculator Retirement Savings Calculator — Estimate your retirement nest egg with IRA and 401(k) projections Tax Bracket Calculator — See your 2026 federal tax bracket and effective rate Dividend Income Calculator — Calculate tax-free dividend income in a Roth IRA Compound Interest Calculator — See the power of tax-advantaged compounding Savings Goal Calculator — Calculate how long to reach any savings target Track your total net worth → Net Worth Calculator Plan your path to financial independence → FIRE Calculator See how inflation affects your money → Inflation Calculator 401k vs IRA Differences Explained (2026) Best Index Funds for Beginners 2026 Passive Income Ideas That Actually Work 2026 This article contains affiliate links. We may earn a commission at no extra cost to you.\nRelated Templates Take control of your finances with these tools:\nSmart Budget Tracker (Excel Template) — Track income, expenses, and savings goals Side Hustle Starter Kit 2026 — Build additional income streams ","permalink":"https://productivity-works.com/posts/roth-ira-vs-traditional-ira-which-is-better/","summary":"\u003cp\u003e※本記事にはアフィリエイト広告が含まれています。\u003c/p\u003e\n\u003ch1 id=\"roth-ira-vs-traditional-ira-which-is-better--complete-guide-2026\"\u003eRoth IRA vs Traditional IRA Which Is Better — Complete Guide [2026]\u003c/h1\u003e\n\u003cp\u003eChoosing between a Roth IRA and a Traditional IRA is one of the most consequential financial decisions you can make in your 20s, 30s, and 40s. Get it right, and you could save tens of thousands — or even hundreds of thousands — of dollars in taxes over your lifetime. Get it wrong, and you\u0026rsquo;ll pay more than you have to.\u003c/p\u003e","title":"Roth IRA vs Traditional IRA: Which Is Better?"},{"content":"A side hustle no longer requires a rare skill, expensive equipment, or years of experience. AI tools have compressed the skill acquisition curve from years to weeks — and in some cases, to hours. Someone who has never written a word professionally can produce polished content with the right prompts and workflow. Someone with no design background can create client-ready graphics.\nThis doesn\u0026rsquo;t mean AI does the work for you. It means you can deliver professional-quality results faster, taking on more clients or creating more products than was possible two years ago.\nHere are 15 side hustles that genuinely work in 2026, organized by time investment and startup cost.\nLow Startup Cost, Flexible Hours 1. AI-Assisted Freelance Writing What it is: Writing blog posts, website copy, email sequences, and articles for businesses that need content but don\u0026rsquo;t have the time or staff.\nHow AI helps: ChatGPT or Claude drafts initial content based on your prompts. You edit, fact-check, add expert perspective, and ensure it matches the client\u0026rsquo;s voice. The AI handles the blank-page problem; you handle quality and accuracy.\nWhere to find clients: Upwork, ProBlogger job board, LinkedIn, cold outreach to small businesses.\nRealistic earnings: $50–$150 per article at entry level. Experienced writers with a niche earn $300–$800+.\nTools: ChatGPT Plus, Claude, Grammarly, SurferSEO (for SEO writing)\n2. Social Media Management What it is: Managing Instagram, LinkedIn, or Twitter/X accounts for local businesses, coaches, or e-commerce brands.\nHow AI helps: AI generates caption drafts, suggests hashtag strategies, repurposes one piece of content into multiple formats, and schedules posts. A task that took 4 hours takes 90 minutes.\nWhere to find clients: Local business networking events, Facebook groups for entrepreneurs, cold email to businesses with low social engagement.\nRealistic earnings: $500–$1,500/month per client for basic management. 3–4 clients = meaningful income.\nTools: ChatGPT, Buffer or Later (scheduling), Canva AI\n3. AI-Powered Resume and LinkedIn Profile Writing What it is: Rewriting resumes and LinkedIn profiles for job seekers, tailoring them to specific roles and industries.\nHow AI helps: AI drafts optimized bullet points, generates keyword-rich summaries, and suggests formatting improvements. You provide expertise in what hiring managers and ATS systems actually want.\nWhere to find clients: Reddit (r/resumes, r/jobs), Fiverr, career-focused Facebook groups, LinkedIn.\nRealistic earnings: $75–$250 per resume rewrite. $50–$150 for LinkedIn profile. Volume of 5–10 clients/month is achievable.\n4. Etsy Digital Products What it is: Creating and selling digital downloads — printable planners, wall art, budget templates, journal pages, Canva templates.\nHow AI helps: ChatGPT generates ideas based on trending searches. Canva AI or Midjourney creates design elements. AI can write product descriptions and keyword-optimize your Etsy listings.\nWhere to start: Research trending niches on Etsy using EverBee or Alura, create 10–20 products initially, optimize listings with AI-written SEO copy.\nRealistic earnings: Slow start, but passive once established. Top Etsy digital sellers earn $2,000–$10,000+/month. Expect $100–$500/month in the first 3–6 months.\nTools: Canva Pro, Midjourney, EverBee\n5. Voiceover Work with AI Script Writing What it is: Recording voiceovers for YouTube channels, explainer videos, e-learning courses, and podcast ads.\nHow AI helps: AI writes the scripts. You record with your natural voice. The combination of AI scripting and human delivery is in high demand — fully AI-generated voices still sound uncanny for long-form content.\nWhere to find clients: Voices.com, Voice123, Upwork, direct outreach to YouTubers.\nRealistic earnings: $50–$500 per project depending on length and usage rights. Starting rate is competitive.\nTools: ChatGPT for scripts, Audacity (free recording/editing), a decent USB microphone ($50–$100 investment)\nCreative and Design 6. AI-Generated Graphic Design What it is: Creating logos, social media graphics, presentation templates, and brand kits for small businesses.\nHow AI helps: Midjourney or Adobe Firefly generates visual concepts. Canva or Figma handles professional polish and client-ready exports. You curate, direct, and refine — clients pay for the output, not the tool.\nWhere to find clients: Fiverr, 99designs, local business outreach.\nRealistic earnings: $50–$500 per project. Logo packages for small businesses typically run $150–$400.\n[Subscribe to Midjourney at midjourney.com]\n7. Children\u0026rsquo;s Book Creation What it is: Writing and illustrating children\u0026rsquo;s e-books or print-on-demand books sold on Amazon KDP or Etsy.\nHow AI helps: ChatGPT writes the story. Midjourney or DALL-E generates illustrations. AI tools handle formatting for KDP requirements.\nWhere to sell: Amazon Kindle Direct Publishing (free), Etsy digital downloads, Gumroad.\nRealistic earnings: Royalties of $2–$4 per sale. With 10–20 books and steady marketing, $500–$2,000/month is achievable.\n8. Print-on-Demand Merchandise What it is: Designing T-shirts, mugs, phone cases, and home decor sold through Redbubble, Printful, or Merch by Amazon with no inventory.\nHow AI helps: AI generates design concepts and slogan ideas. Midjourney creates artwork. ChatGPT writes product descriptions. The fulfillment is entirely automated.\nRealistic earnings: Passive but slow-building. Consistent designers with strong niches earn $500–$3,000/month after 6–12 months.\nKnowledge and Consulting 9. AI Tool Consulting for Small Businesses What it is: Teaching small business owners which AI tools will save them time and money, then helping them implement those tools.\nHow AI helps: You\u0026rsquo;re selling your knowledge of AI tools. The value you provide is understanding which tools apply to which problems — and how to get them working.\nWhere to find clients: Local chamber of commerce events, LinkedIn, Alignable.\nRealistic earnings: $75–$200/hour for consulting. Workshop facilitation: $500–$2,000 per session.\n10. Online Course Creation What it is: Creating and selling video courses on platforms like Teachable, Udemy, or Gumroad covering topics where you have expertise.\nHow AI helps: ChatGPT outlines the curriculum, writes scripts for video lessons, creates workbooks and quizzes, and drafts all sales copy. What used to take months to create now takes weeks.\nWhere to sell: Udemy (large audience, lower price point), Teachable or Kajabi (your own audience, higher margin).\nRealistic earnings: Passive income once created. Udemy courses earn $200–$2,000+/month for well-reviewed courses on in-demand topics.\n11. YouTube Automation Channel What it is: Running a faceless YouTube channel where AI handles scripting, voiceover scripts, and research while you focus on editing and strategy.\nHow AI helps: ChatGPT researches topics and writes scripts. ElevenLabs generates voiceovers (or you record your own). Stock footage services provide visuals.\nRealistic earnings: Ad revenue ($2–$10 per 1,000 views) plus affiliate links. Top automation channels earn $5,000–$30,000/month. Expect 12–18 months before significant revenue.\nTools: ChatGPT, ElevenLabs, Pictory or InVideo for video creation\nTechnical and Data 12. Data Analysis and Reporting Freelancing What it is: Helping businesses understand their data — creating dashboards, cleaning datasets, or producing monthly reports.\nHow AI helps: ChatGPT writes data analysis code in Python or SQL when given a description of what you need. Claude can explain datasets and suggest analyses. You validate, interpret, and present findings.\nWhere to find clients: Upwork, Toptal, LinkedIn, direct outreach to e-commerce or SaaS businesses.\nRealistic earnings: $40–$100/hour. Experienced analysts earn $100–$200/hour.\n13. AI-Powered Bookkeeping Assistance What it is: Handling basic bookkeeping for small businesses — categorizing transactions, reconciling accounts, preparing monthly reports.\nHow AI helps: Modern accounting software (QuickBooks, Wave) already has AI categorization. ChatGPT helps you understand accounting concepts, draft client emails, and troubleshoot issues. You provide the human verification and client relationship.\nRealistic earnings: $300–$800/month per client for basic monthly bookkeeping. Certifications increase rates significantly.\n14. SEO Auditing and Optimization What it is: Auditing websites for technical SEO issues, keyword opportunities, and content gaps — then fixing them.\nHow AI helps: ChatGPT analyzes page content and suggests improvements. AI tools like SurferSEO or Clearscope score content against top-ranking pages. You interpret results and implement fixes.\nRealistic earnings: $500–$2,000 per audit report. Monthly retainers of $500–$1,500 for ongoing work.\n[Try SurferSEO at surferseo.com]\n15. Podcast Editing and Show Notes What it is: Editing audio for podcast creators and writing show notes, timestamps, and transcriptions.\nHow AI helps: Descript AI edits audio by editing a text transcript. AI generates show notes, chapter markers, and social media clips from the transcript automatically. A 1-hour episode can be processed in under an hour.\nWhere to find clients: Facebook groups for podcasters, Podcast Motor job board, direct outreach to podcasts you listen to.\nRealistic earnings: $75–$300 per episode. 4–6 clients = $1,500–$3,000/month.\nComparison by Effort and Earnings Potential Side Hustle Startup Cost Time to First Dollar Earnings Ceiling Freelance Writing Low 1–2 weeks $5,000+/mo Social Media Mgmt Low 2–4 weeks $5,000+/mo Resume Writing Low 1 week $3,000/mo Etsy Digital Products Low 1–3 months $10,000+/mo YouTube Automation Medium 6–18 months Unlimited Online Courses Medium 1–3 months $10,000+/mo AI Consulting Low 2–4 weeks $8,000+/mo Podcast Editing Low 1–2 weeks $4,000/mo Give Your AI Side Hustle a Professional Online Presence Every serious side hustle needs a home on the web. Get your domain on Onamae.com and establish your brand with a custom domain — the first step toward looking like a professional, not a hobbyist.\nHow to Choose Your First Side Hustle Three questions to narrow it down:\nWhat can you do for 10 hours a week right now? Start with what fits your current schedule. Do you want active income (time for money) or passive income (build once, earn later)? Writing and social media are active. Digital products and courses are passive but slower to build. How quickly do you need money? Freelancing pays fastest. Passive income products take months. Pick one. Not two or three — one. Work it consistently for 90 days before evaluating. The biggest mistake in side hustles is starting multiple things simultaneously and mastering none of them.\nRelated Tools Calculate your ideal freelance hourly rate → Freelance Rate Calculator Estimate taxes on side hustle income → Side Hustle Tax Calculator See your 2026 federal tax bracket and effective rate → Tax Bracket Calculator Calculate your take-home pay → Salary Calculator Create professional invoices for clients → Invoice Generator Generate a QR code for your side hustle or product link → QR Code Generator Create a monthly budget → Budget Planner Convert hourly wage to salary → Hourly to Salary Calculator Related Templates Start your freelance or side hustle journey with these resources:\nSide Hustle Starter Kit 2026 — 15 proven ideas with step-by-step guides ChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Boost your productivity instantly Related Articles How to Make Money With AI in 2026: 15 Realistic Ways That Work 7 Passive Income Ideas Using Digital Products Passive Income Ideas That Actually Work 2026 AI Tools for Freelancers to Earn More 2026 How to Start Freelancing With No Experience 2026: Full Guide This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/side-hustles-ai-tools-2026/","summary":"\u003cp\u003eA side hustle no longer requires a rare skill, expensive equipment, or years of experience. AI tools have compressed the skill acquisition curve from years to weeks — and in some cases, to hours. Someone who has never written a word professionally can produce polished content with the right prompts and workflow. Someone with no design background can create client-ready graphics.\u003c/p\u003e\n\u003cp\u003eThis doesn\u0026rsquo;t mean AI does the work for you. It means you can deliver professional-quality results faster, taking on more clients or creating more products than was possible two years ago.\u003c/p\u003e","title":"15 Side Hustles You Can Start with AI Tools Today"},{"content":" Best AI Tools for Small Business Owners in 2026 Running a small business has always meant doing the work of five people. In 2026, AI tools are finally closing that gap — letting a one- or two-person operation compete with companies ten times their size.\nBut with hundreds of AI tools on the market, knowing which ones actually move the needle is hard. This guide cuts through the noise. We\u0026rsquo;ve tested and reviewed the top AI tools across every core business function — and we\u0026rsquo;ll tell you exactly which ones are worth your budget.\nWhat you\u0026rsquo;ll find here:\nBest AI tools by business function (marketing, operations, finance, customer service) Pricing and free tier information Which tools work best together A starter stack for businesses under $500/month in software costs Why AI Tools Are Now Essential for Small Business Consider the numbers:\nSmall businesses using AI tools report saving an average of 12-15 hours per week AI-assisted marketing produces content 3-5x faster than traditional methods Businesses using AI customer service handle 40% more inquiries without adding headcount The tools have also become dramatically more affordable. Many world-class AI capabilities are now available for $20-50/month — less than a single hour of consultant time.\nCategory 1: AI Writing and Marketing Tools 1. ChatGPT Plus ($20/month) Best for: Marketing copy, email campaigns, social media content, product descriptions\nChatGPT remains the most versatile AI writing tool available. For small business owners, it excels at:\nWriting product descriptions at scale Drafting email campaigns and newsletters Creating social media content calendars Writing ad copy variations for A/B testing Generating FAQ and help center content Practical tip: Create a custom GPT with your brand voice, target audience, and product details. Then every piece of content you generate stays on-brand automatically.\nRating: 5/5 for versatility\n2. Jasper AI ($49/month) Best for: Teams with heavy content production needs\nJasper is built specifically for marketing content. It includes templates for blogs, ads, emails, and social posts, plus brand voice training. If you\u0026rsquo;re producing 10+ pieces of content per week, Jasper\u0026rsquo;s structured workflow can save significant time over general-purpose AI.\nDrawback: Pricier than ChatGPT for solo users.\nRating: 4/5 for content teams\n3. Copy.ai ($36/month) Best for: Sales copy and email sequences\nCopy.ai specializes in sales-oriented content — cold emails, LinkedIn outreach, product page copy, and landing pages. Its workflows can generate entire email nurture sequences from a single product description.\nRating: 4/5 for sales-focused businesses\nCategory 2: AI Tools for Customer Service 4. Intercom with Fin AI ($74/month starting) Best for: Businesses with significant customer support volume\nIntercom\u0026rsquo;s Fin AI handles customer inquiries using your existing help content. It resolves over 50% of questions automatically without human intervention. When it can\u0026rsquo;t answer, it hands off smoothly to a human agent.\nFor e-commerce stores, SaaS products, or service businesses fielding 100+ support tickets per week, Fin AI pays for itself quickly.\nRating: 5/5 for customer support automation\n5. Tidio with Lyro AI ($29/month) Best for: Small e-commerce and service businesses\nTidio\u0026rsquo;s Lyro is a more affordable AI chat option that handles common customer questions, qualifies leads, and collects contact information. It integrates with Shopify, WordPress, and most popular platforms.\nRating: 4/5 for small e-commerce\n6. Zendesk AI (add-on pricing) Best for: Businesses already using Zendesk\nIf you\u0026rsquo;re on Zendesk, their AI add-ons include ticket routing, suggested responses, and automated issue resolution. The integration quality is excellent for existing Zendesk customers.\nRating: 4/5 for Zendesk users\nCategory 3: AI Tools for Finance and Bookkeeping 7. QuickBooks with AI Features ($30-90/month) Best for: Comprehensive small business accounting\nQuickBooks has integrated AI deeply into its platform. It now automatically categorizes transactions, flags anomalies, predicts cash flow, and generates plain-English summaries of your financials. For most small businesses, QuickBooks remains the gold standard.\nRating: 5/5 for accounting\n8. Dext (formerly Receipt Bank) ($20-50/month) Best for: Receipt capture and expense management\nDext uses AI to extract data from receipts and invoices automatically. Point your phone at a receipt, and within seconds it\u0026rsquo;s categorized and synced to your accounting software. If you deal with lots of expenses, this is a massive time-saver.\nRating: 4.5/5 for expense management\n9. Fathom Analytics ($10-50/month) Best for: Financial reporting and KPI tracking\nFathom pulls data from your accounting software and generates beautiful, easy-to-read financial reports. The AI-powered commentary explains what the numbers mean in plain English — useful for presentations to partners, investors, or your own planning.\nRating: 4/5 for reporting\nCategory 4: AI Tools for Operations and Productivity 10. Notion AI ($10/user/month add-on) Best for: Documentation, SOPs, project management\nNotion AI turns your company wiki into a searchable, intelligent knowledge base. It summarizes meeting notes, drafts SOPs, generates project plans, and answers questions about your documented processes. For small teams, it replaces multiple specialist tools.\nSee our full guide: Notion Project Management for Small Teams Rating: 5/5 for knowledge management\n11. Zapier with AI Actions ($20-99/month) Best for: Automating repetitive workflows between apps\nZapier connects 6,000+ apps and now includes AI-powered steps that can write, classify, or transform data as part of automated workflows. A common small business use: automatically summarize new customer emails and add them to a CRM.\nRating: 5/5 for automation\n12. Otter.ai ($10-20/month) Best for: Meeting transcription and action items\nOtter records, transcribes, and summarizes meetings automatically. It identifies speakers, highlights action items, and generates shareable summaries. For any business running regular client or team meetings, this saves hours of note-taking per week.\nRating: 4.5/5 for meeting productivity\nCategory 5: AI Tools for Sales and CRM 13. HubSpot with AI Features (Free - $800+/month) Best for: Comprehensive CRM with AI assistance\nHubSpot\u0026rsquo;s free CRM now includes AI-assisted email drafting, deal forecasting, and contact enrichment. The paid tiers add AI content generation, predictive lead scoring, and conversation intelligence. For businesses serious about sales, HubSpot\u0026rsquo;s AI features are increasingly competitive.\nRating: 5/5 for sales-focused businesses\n14. Apollo.io ($49-99/month) Best for: B2B lead generation and outreach\nApollo uses AI to identify ideal prospects, verify contact information, and personalize outreach at scale. Small B2B businesses can run campaigns that previously required a full sales development team.\nRating: 4.5/5 for B2B sales\n15. Lavender ($27/month) Best for: Email outreach optimization\nLavender analyzes your sales emails in real time and suggests improvements that increase reply rates. It scores your email against patterns from millions of successful outreach messages. Sales reps using Lavender report 20-40% higher reply rates.\nRating: 4/5 for sales email\nCategory 6: AI Tools for Social Media 16. Buffer with AI Assistant ($6-12/month) Best for: Social media scheduling and content ideas\nBuffer\u0026rsquo;s AI assistant suggests post ideas, writes captions, and repurposes content across platforms. Combined with its scheduling features, you can plan a month of social content in a few hours.\nRating: 4.5/5 for social media\n17. Canva with Magic AI ($15/month) Best for: Visual content creation\nCanva\u0026rsquo;s AI features include Magic Write (copy generation), Magic Design (instant branded templates), and AI image generation. For businesses without a graphic designer, Canva AI dramatically reduces the cost and time of producing professional-looking visuals.\nRating: 5/5 for non-designers\nThe Recommended Starter Stack by Business Type E-Commerce Business ($75-120/month) ChatGPT Plus ($20) — product descriptions, email copy Tidio with Lyro ($29) — customer service chat Canva Pro ($15) — product and social visuals Buffer AI ($12) — social media scheduling Dext ($20) — receipt and expense management Service Business / Agency ($60-100/month) Claude Pro ($20) — proposals, client reports, long-form content Otter.ai ($20) — client meeting transcription Notion AI ($10+) — documentation and project management HubSpot Free CRM — sales pipeline and follow-up B2B / SaaS Business ($120-180/month) ChatGPT Plus ($20) — content and marketing Apollo.io ($49) — prospecting and outreach Intercom Fin ($74+) — customer support Zapier ($20) — workflow automation How to Evaluate Any AI Tool Before Buying Before adding any AI tool to your stack, ask:\nDoes it integrate with my existing tools? Integration quality matters more than features. What does the free trial actually include? Many tools limit AI features in trials. What\u0026rsquo;s the learning curve? A powerful tool you don\u0026rsquo;t use is worthless. Is there a per-user pricing trap? Some tools are cheap solo but expensive as a team. Does it handle my data securely? Check data processing agreements for sensitive business info. ROI Calculation: Are AI Tools Worth It? Here\u0026rsquo;s a simple formula: If an AI tool saves you 2 hours per week and your time is worth $50/hour, that\u0026rsquo;s $100/week = $400/month in value. A $20/month subscription delivers a 20x ROI.\nMost well-chosen AI tools for small businesses deliver between 5x and 20x ROI when you account for time savings alone — before considering quality improvements or revenue gains.\nFrequently Asked Questions What\u0026rsquo;s the best free AI tool for small business? ChatGPT Free, Claude Free, and HubSpot\u0026rsquo;s free CRM are all genuinely useful starting points. For most businesses, upgrading to at least one paid tool ($20/month) dramatically improves results.\nHow many AI tools does a small business actually need? Most businesses get 80% of the value from 2-3 well-chosen tools. Start with an AI writing tool and an automation tool, then add category-specific tools as needs grow.\nCan AI tools replace employees? AI tools augment employees rather than replace them — at the small business level, they most often allow one person to do the work of two, not replace anyone entirely.\nAre AI tools safe for sensitive business data? Read privacy policies carefully. Most reputable tools offer business agreements that prevent your data from training their models. Enable this option when available.\nRelated Tools Plan your business budget → Budget Planner Calculate your tax obligations → Side Hustle Tax Calculator Round Out Your Small Business Stack with Cloud Accounting AI tools handle marketing and operations — freee handles the money. Try freee — Japan\u0026rsquo;s #1 cloud accounting to automate bookkeeping, invoicing, and tax preparation so every part of your business runs efficiently.\nGet More from Your AI Tools Our Small Business AI Toolkit includes 100+ prompt templates, workflow blueprints, and setup guides optimized for small business owners. Stop starting from scratch with every AI interaction.\nRelated Reading:\nChatGPT vs Claude vs Gemini 2026 How to Automate Tasks with AI Step by Step Best Productivity Apps for Remote Workers 2026 This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/top-ai-tools-small-business-owners-2026/","summary":"\u003chr\u003e\n\u003ch2 id=\"best-ai-tools-for-small-business-owners-in-2026\"\u003eBest AI Tools for Small Business Owners in 2026\u003c/h2\u003e\n\u003cp\u003eRunning a small business has always meant doing the work of five people. In 2026, AI tools are finally closing that gap — letting a one- or two-person operation compete with companies ten times their size.\u003c/p\u003e\n\u003cp\u003eBut with hundreds of AI tools on the market, knowing which ones actually move the needle is hard. This guide cuts through the noise. We\u0026rsquo;ve tested and reviewed the top AI tools across every core business function — and we\u0026rsquo;ll tell you exactly which ones are worth your budget.\u003c/p\u003e","title":"Best AI Tools for Small Business Owners 2026: Complete Guide"},{"content":"Notion is one of the most powerful productivity tools available — and also one of the most frequently misconfigured. The most common failure mode is spending three weeks building an elaborate system and then abandoning it because maintaining the system takes more energy than doing the actual work.\nThis guide is built around a different philosophy: the best Notion setup is the one you actually use. That means starting simple, building only what you need, and adding complexity only when the need becomes obvious.\nHere\u0026rsquo;s how to build a workspace that earns its keep.\nThe Core Architecture Every effective Notion workspace rests on four pillars:\nTask Manager — What do I need to do, and when? Project Hub — What are the larger initiatives I\u0026rsquo;m working on? Knowledge Base — What do I know, and how do I find it? Daily Notes — What happened today, and what am I thinking? These four areas cover 90% of what most people need from a productivity system. Build these before anything else.\nPillar 1: Task Manager The Database You Need Create a new Notion database called \u0026ldquo;Tasks.\u0026rdquo; Add the following properties:\nProperty Type Purpose Name Title What the task is Status Select Not Started / In Progress / Done Due Date Date When it needs to be done Priority Select High / Medium / Low Project Relation Links to your Projects database Area Select Work / Personal / Health / Finance Views That Actually Help The key to a useful task manager is having the right view for the right context. Create these four views:\nToday View: Filter by Due Date = Today, sort by Priority. Open this every morning.\nThis Week: Filter by Due Date within the next 7 days. For weekly planning.\nBy Project: Group by Project. Useful when you\u0026rsquo;re in project-execution mode.\nInbox: Filter by Status = Not Started AND Due Date is empty. Your capture point for tasks that haven\u0026rsquo;t been planned yet.\nThe Daily Workflow Each morning, spend 10 minutes:\nOpen Today View — what\u0026rsquo;s due? Open Inbox — capture anything that arrived overnight Assign due dates to anything in Inbox that needs scheduling Pick your 3 Most Important Tasks (MITs) for the day Each evening, spend 5 minutes:\nMark completed tasks as Done Move any incomplete tasks to tomorrow or reschedule them intentionally Do a quick brain dump into tomorrow\u0026rsquo;s tasks Pillar 2: Project Hub Why Tasks Alone Aren\u0026rsquo;t Enough Tasks are atomic actions. Projects are collections of tasks working toward a specific outcome. Without a project layer, you lose the ability to see whether you\u0026rsquo;re making progress on the things that actually matter.\nThe Projects Database Create a database called \u0026ldquo;Projects.\u0026rdquo; Properties:\nProperty Type Purpose Name Title Project name Status Select Planning / Active / On Hold / Complete Goal Text One sentence: what does done look like? Due Date Date Target completion Area Select Work / Personal / Side Hustle / etc. Related Tasks Relation Links to Tasks database Priority Select High / Medium / Low Each project page should contain:\nGoal (repeated at the top, in large text) Next Actions — a filtered view of related tasks sorted by due date Notes — a toggle block for context, decisions, and reference material Files — linked documents, attachments, or relevant URLs The Weekly Project Review Every Sunday, spend 20–30 minutes reviewing active projects:\nIs each project moving? Is there a clear next action for every project? Should any project be paused or killed? A project without a clear next action is a project that will stall.\nPillar 3: Knowledge Base The Problem with Most Note Systems Most people take notes and never use them again. The issue isn\u0026rsquo;t the notes — it\u0026rsquo;s the retrieval. Notes are only valuable when you can find them at the moment you need them.\nNotion\u0026rsquo;s solution to this is a combination of linked databases, tags, and its powerful search. But structure still matters.\nA Simple Knowledge Base Structure Create a database called \u0026ldquo;Notes\u0026rdquo; with these properties:\nProperty Type Purpose Name Title Note title Type Select Article / Book / Meeting / Idea / Reference Tags Multi-select Topic tags (keep this list short) Source URL Link to original article/resource Created Created Time Auto-filled Related Project Relation If tied to a specific project The Evergreen Notes Approach Not all notes are equal. Distinguish between:\nCapture notes: Quick, rough notes taken in the moment. Don\u0026rsquo;t worry about quality.\nProcessed notes: Notes you\u0026rsquo;ve reviewed and extracted value from. Add a summary, key insights, and action items.\nEvergreen notes: Refined, standalone notes on a concept you\u0026rsquo;ll return to. Written as if you\u0026rsquo;re explaining it to a future version of yourself. These are the most valuable and rarest.\nMost notes will be captures. Process the ones that matter. Turn 1 in 10 of those into evergreens.\nConnecting Knowledge to Work The most powerful Notion pattern is linking notes to projects and tasks. When you\u0026rsquo;re working on a project, you should be able to see all related notes directly from the project page. Use the Relation property to create these links as you go.\nPillar 4: Daily Notes Why Daily Notes Matter Daily notes serve a different function than tasks or projects. They\u0026rsquo;re a space for:\nThinking out loud Capturing what you learned Logging wins and frustrations Daily reflection Over time, they become a searchable diary of how you work.\nSimple Daily Note Template Create a template in a \u0026ldquo;Daily Notes\u0026rdquo; database with this structure:\n--- ## [[Date]] ### Morning Check-In - Energy level (1–10): - Today\u0026#39;s MITs: 1. 2. 3. ### Notes \u0026amp; Thinking (Free writing area — anything goes here) ### Evening Review - What got done? - What got blocked? - What am I carrying to tomorrow? - One thing I\u0026#39;m grateful for: The morning and evening check-ins should take under 5 minutes each. The middle section is yours to use however is useful.\nAdvanced Features Worth Using Once the four pillars are working, these add meaningful value:\nNotion AI Notion\u0026rsquo;s built-in AI can summarize long notes, generate action items from meeting notes, fill in database entries, and draft content. For knowledge workers, the most useful application is meeting note summarization — paste raw notes and ask AI to extract decisions, action items, and open questions.\n(Try Notion AI — adds powerful summarization and generation features to your existing workspace.)\nLinked Database Views You can display a filtered view of any database on any page. This means your project pages can show only the tasks related to that project, and your daily note can show today\u0026rsquo;s tasks — all without duplicating data.\nSynced Blocks Any block can be synced to appear on multiple pages simultaneously. Useful for a \u0026ldquo;current priorities\u0026rdquo; block you want visible in multiple places.\nNotion Calendar Notion Calendar (free, separate app) connects your Notion databases to a visual calendar view. If you use date properties extensively, this turns Notion into a functional calendar — useful for seeing your week at a glance.\nTemplate Recommendations Building from scratch is optional. These templates are worth the time:\nFree:\nNotion\u0026rsquo;s own \u0026ldquo;Getting Things Done\u0026rdquo; template Thomas Frank\u0026rsquo;s Notion setup (free version available) Marie Poulin\u0026rsquo;s Notion Mastery resources (some free) Paid:\nAugust Bradley\u0026rsquo;s Pillars, Pipelines \u0026amp; Vaults system (complex but comprehensive) Any niche-specific template for your workflow (creators, students, developers) (Browse the Notion template gallery at notion.so/templates for pre-built setups across every use case.)\nThe Most Common Mistakes Over-engineering before using. Don\u0026rsquo;t build a 15-property database before you\u0026rsquo;ve captured a single task. Start with three properties and add more when you feel the gap.\nNot reviewing regularly. A task database with 200 stale tasks is useless. A weekly review is not optional — it\u0026rsquo;s the mechanism that keeps the system alive.\nDuplicating information. Notion\u0026rsquo;s relational databases exist so you don\u0026rsquo;t have to copy the same information into multiple places. Use relations; don\u0026rsquo;t copy-paste.\nUsing it as a to-do app only. Notion\u0026rsquo;s real power is connecting tasks to projects to notes to goals. If you\u0026rsquo;re using it as a list of things to do, you\u0026rsquo;re using 10% of its capability.\nProductivity Tools Are Now Career Tools Employers increasingly look for candidates who can manage their work efficiently with modern tools like Notion. Find your next career on doda — discover thousands of roles across every industry on Japan\u0026rsquo;s leading job platform.\nGetting Started This Week Day 1: Create the Tasks database with 5 properties. Capture everything on your mind into it.\nDay 2: Create the Projects database. Link your existing tasks to projects.\nDay 3: Set up your first daily note template. Use it that day.\nDay 4–7: Use the system as-is. Notice what\u0026rsquo;s missing and what\u0026rsquo;s annoying.\nWeek 2: Add the Knowledge Base. Start taking notes on things you read.\nWeek 3: Add views and filters that make your workflow faster.\nThe system will evolve as you use it. That\u0026rsquo;s not a problem — it\u0026rsquo;s evidence the system is working.\n(Notion Plus unlocks unlimited blocks, file uploads, and version history — worth it once your workspace is established.)\nRelated Tools Create a monthly budget → Budget Planner Calculate your ideal freelance rate → Freelance Rate Calculator Stay focused with the Pomodoro Technique → Pomodoro Timer Count down to any date or event → Countdown Timer Related Templates Put these techniques into practice with our ready-made templates:\nChatGPT Prompt Templates: 100 Ready-to-Use Prompts — Copy-paste prompts for every situation The AI Productivity Playbook 2026 — 50+ AI workflows and automation strategies This article contains affiliate links. We may earn a commission at no extra cost to you.\nYou May Also Like Best Notion AI Templates for Productivity 2026 How to Use Notion for Project Management 2026: The Complete Guide Best Productivity Apps for Remote Workers 2026: Tested and Ranked ","permalink":"https://productivity-works.com/posts/ultimate-notion-productivity-setup/","summary":"\u003cp\u003eNotion is one of the most powerful productivity tools available — and also one of the most frequently misconfigured. The most common failure mode is spending three weeks building an elaborate system and then abandoning it because maintaining the system takes more energy than doing the actual work.\u003c/p\u003e\n\u003cp\u003eThis guide is built around a different philosophy: the best Notion setup is the one you actually use. That means starting simple, building only what you need, and adding complexity only when the need becomes obvious.\u003c/p\u003e","title":"The Ultimate Notion Setup for Maximum Productivity"},{"content":" Work From Home Tips for Beginners 2026: The Complete Guide Working from home sounds like a dream. Then day three happens: you\u0026rsquo;re in pajamas at 2pm, haven\u0026rsquo;t moved from your couch, you\u0026rsquo;ve checked social media 30 times, and somehow produced less than you would in a full office day.\nThis is not a failure of character. It\u0026rsquo;s a failure of environment and systems.\nThe good news: working from home is a learnable skill. The people who thrive remotely have built specific habits, environments, and boundaries that make home as productive (and often more productive) than an office. This guide gives you everything they know.\nPart 1: Setting Up Your Physical Space The Home Office Foundation The single most important step for working from home effectively: create a dedicated workspace.\nNot \u0026ldquo;work at the kitchen table sometimes.\u0026rdquo; Not \u0026ldquo;mostly the couch.\u0026rdquo; A consistent, designated place where you work — and ideally, only work.\nThis doesn\u0026rsquo;t require a separate room. A desk in the corner of a bedroom, a converted closet, even a specific chair at the dining table works — as long as it\u0026rsquo;s consistent. Your brain will learn to associate this space with focus.\nThe minimum viable home office setup:\nA real desk (not the couch or bed) An ergonomic chair — non-negotiable for your back and productivity A separate monitor (even a $150 option transforms laptop-only work) A reliable internet connection (test: you need at least 25Mbps upload for video calls) Adequate lighting (natural light is best; a good desk lamp as backup) Optional but high-impact:\nNoise-canceling headphones ($50-300) — transforms noisy environments A USB webcam (better than built-in for video calls) A standing desk converter (alternating sitting and standing improves energy) A door you can close (signals to household members that you\u0026rsquo;re working) Ergonomics: Protect Your Body Remote workers report higher rates of back pain, eye strain, and wrist problems than office workers — largely because home setups are less ergonomic.\nEssential ergonomic principles:\nMonitor at eye level — top of screen should be at or slightly below eye level Chair height so feet rest flat on the floor, thighs roughly parallel to the ground Keyboard and mouse positioned so elbows are at 90 degrees Wrist support for typing (especially important for 6+ hour work days) 20-20-20 rule for eyes: every 20 minutes, look at something 20 feet away for 20 seconds Good ergonomics pay off enormously over years of remote work. A decent chair ($200-500) is one of the best investments a remote worker can make.\nManaging Your Environment Light: Natural light improves mood and productivity. Position your desk near a window if possible. Use a daylight bulb (5000-6500K) for artificial lighting.\nTemperature: Studies show 70-77°F (21-25°C) is the optimal range for cognitive performance. Too cold or too hot both impair focus.\nNoise: This varies by person. Some people work best in silence (use noise-canceling headphones). Others do better with background noise (try brain.fm, ambient sounds, or a coffee shop playlist on Spotify).\nScent: Uncommon but backed by research — certain scents (peppermint for alertness, lavender for calm) can modestly influence cognitive performance. A diffuser is a cheap experiment.\nPart 2: Building Productive Routines Why Routines Matter More at Home Than in an Office In an office, your routine is imposed by the environment: commute, arrival time, meeting schedule, lunch hour. Working from home removes these external structures. Without replacing them with internal ones, work and personal life blur into a formless, unproductive mess.\nThe solution isn\u0026rsquo;t rigid scheduling — it\u0026rsquo;s intentional routine building.\nThe Morning Routine: Create Your Own Commute The commute to an office serves an important psychological function: it creates a transition between home-brain and work-brain.\nWithout a commute, many remote workers start working in a foggy, unfocused state that takes hours to shake. Create a \u0026ldquo;commute substitute\u0026rdquo; — a consistent pre-work ritual that signals \u0026ldquo;work mode is starting.\u0026rdquo;\nGood commute substitutes:\nA 20-30 minute walk (best option — exercise, natural light, and transition time) A workout or yoga session Reading (non-work) for 20-30 minutes with coffee Meditation (even 10 minutes with Headspace or Calm) A specific playlist that you only listen to when starting work The key: it\u0026rsquo;s consistent, it\u0026rsquo;s a separate activity from work, and it ends when you sit down at your desk.\nStart Time Discipline Pick a start time and stick to it at least 4-5 days per week. This doesn\u0026rsquo;t mean you can\u0026rsquo;t occasionally start early or late — but having a consistent \u0026ldquo;work begins\u0026rdquo; time trains your brain and body.\nMorning productivity tip: Many remote workers find that doing their most important, cognitively demanding task in the first 2 hours of the workday (before email and Slack) produces dramatically better results. This is called \u0026ldquo;eat the frog\u0026rdquo; — doing the hard thing first.\nIdeal WFH Daily Schedule A balanced time-blocked day for remote workers 7:00–9:00 Morning Routine + \"Commute Substitute\" (walk, workout, read) 9:00–11:00 Deep Work Block — No meetings, no Slack, no email 11:00–11:30 Email + Slack Processing (batch, don't react constantly) 11:30–12:30 Meetings + Collaboration 12:30–1:30 Lunch Away from Desk — real break, move your body 1:30–4:00 Project Work + Admin + Email Review 4:00–5:00 Shutdown Ritual — review, write tomorrow's top 3, close tabs Time Blocking: Your Remote Work Superpower Time blocking means scheduling your calendar not just with meetings, but with blocks for specific types of work.\nExample time block structure:\n9:00-11:00am — Deep work (no meetings, no Slack) 11:00-11:30am — Email and Slack processing 11:30am-12:30pm — Meetings 12:30-1:30pm — Lunch (away from desk) 1:30-3:30pm — Project work 3:30-4:00pm — Email and Slack 4:00-5:00pm — Admin, planning, review Adjust to your natural energy rhythms. If you\u0026rsquo;re a night person, shift everything later.\nThe Shutdown Ritual Just as important as the morning routine: a consistent end-of-day shutdown.\nWithout one, remote workers often \u0026ldquo;just finish this one thing\u0026rdquo; until 7pm, then feel like they never truly left work.\nA simple shutdown ritual:\nReview what you accomplished today Write tomorrow\u0026rsquo;s top 3 priorities Close all work tabs and applications Physically leave your workspace Change clothes (optional but surprisingly effective — signals the mental shift) This ritual tells your brain: work is done. The rest of the evening is yours.\nPart 3: Managing Distractions The Two Types of Remote Work Distractions Type 1: Digital distractions — social media, news sites, email, messaging apps, YouTube. These are infinite, always available, and engineered to keep you engaged.\nType 2: Environmental distractions — family members, deliveries, household tasks you \u0026ldquo;should\u0026rdquo; do while you\u0026rsquo;re home, TV in the background.\nBoth require different solutions.\nDefeating Digital Distractions Tool: Website and app blockers\nFreedom (cross-device blocking, $3.33/month) Cold Turkey (more aggressive, Windows/Mac) Built-in Screen Time (iOS) or Digital Wellbeing (Android) for phone The principle: Remove willpower from the equation. If social media is blocked during your deep work hours, you can\u0026rsquo;t access it even if you want to — no decision required.\nEmail management: Turn off email notifications entirely. Check email 2-3 times per day at scheduled times. The email system was not designed for the speed of instant messaging — treating it that way creates constant interruption.\nSlack/messaging management: Set your status as \u0026ldquo;focused\u0026rdquo; or \u0026ldquo;in deep work\u0026rdquo; during important blocks. Turn off notifications. Most \u0026ldquo;urgent\u0026rdquo; messages are not actually urgent.\nPhone: Put your phone in another room during deep work blocks. Out of sight, out of mind is remarkably effective.\nManaging Environmental Distractions Communicating with household members: This is the most common struggle for new remote workers with families.\nClear communication is essential:\nEstablish specific \u0026ldquo;working hours\u0026rdquo; and post them visibly Create a signal for \u0026ldquo;in focus mode\u0026rdquo; (closed door, headphones on, a sign) Establish clear protocols for interruptions: emergencies only when the signal is showing Working from home with children: This is genuinely difficult and requires honest planning:\nIf children need care, care needs to be arranged — you cannot simultaneously provide primary childcare and work effectively Nap times and early mornings are productive windows School hours create natural deep work blocks The \u0026ldquo;can\u0026rsquo;t do housework during work hours\u0026rdquo; rule: One of the stealthiest productivity killers: doing laundry, dishes, cleaning, and errands during working hours. \u0026ldquo;I\u0026rsquo;m home, so I might as well\u0026hellip;\u0026rdquo; is a trap. Treat housework as off-limits during your work blocks the same way you would in an office.\nPart 4: Communication and Collaboration The Remote Work Communication Shift In an office, communication happens accidentally — bumping into someone, overhearing a conversation, reading body language in a meeting.\nRemote work requires intentional communication. The rules are different:\nOver-communicate on progress and blockers. Your manager can\u0026rsquo;t see you working. Proactively sharing what you\u0026rsquo;re working on and flagging blockers builds trust and replaces the visibility you\u0026rsquo;d naturally have in an office.\nWrite more, talk less (for non-urgent things). A well-written async message is more efficient than a meeting for most non-time-sensitive communication. It lets the recipient respond on their schedule and creates a written record.\nWhen to use which channel:\nCommunication Type Best Channel Quick question, non-urgent Slack message Detailed explanation or feedback Written email or document Real-time collaboration needed Video call Complex topic with back-and-forth Video call General update or announcement Slack channel or email Sensitive conversations Video call (never text) Video Call Best Practices Bad video calls are a specific remote work problem. These practices make you look professional and keep calls efficient:\nTest your audio/video before important calls — audio problems derail meetings Good lighting — face a window or use a ring light; don\u0026rsquo;t sit with a window behind you Clean, professional background — or use a virtual background Look at the camera when speaking, not your own face (practice makes this natural) Mute when not speaking — background noise from one participant affects everyone Use video on by default — face-to-face connection is important for team cohesion Have an agenda — meeting without agenda = waste of everyone\u0026rsquo;s time Async Communication: The Remote Work Superpower Asynchronous communication — where the sender and receiver don\u0026rsquo;t need to be available simultaneously — is underused by most remote workers.\nTools for async communication:\nLoom (video messages) — for visual explanations Notion (written documents) — for detailed information Slack (messaging) — for text communication GitHub/Jira comments (for technical teams) Async communication principles:\nBe more detailed than you think necessary — the person can\u0026rsquo;t ask \u0026ldquo;what did you mean?\u0026rdquo; instantly Set clear response time expectations (\u0026ldquo;no rush, anytime this week\u0026rdquo;) Use headers and bullet points — dense paragraphs are harder to read on screens Document decisions and outcomes — build the shared knowledge base Part 5: Health and Wellbeing The Physical Health Challenge of Remote Work Office workers walk to meeting rooms, visit colleagues\u0026rsquo; desks, walk to lunch, and commute. Remote workers can go entire days without standing up. This is a genuine health risk.\nSolutions:\n1. Set movement reminders — Stretchly (free) interrupts your computer every 20-50 minutes with a break prompt.\n2. Walking meetings — for audio-only calls, walk around your neighborhood.\n3. Scheduled exercise — block it on your calendar like a meeting. Without the commute, you have time that you didn\u0026rsquo;t before.\n4. Lunch away from your desk — eat somewhere else, even for 20 minutes. Your brain needs the break.\n5. Standing desk or converter — even 2 hours of standing per day reduces the negative effects of prolonged sitting.\nMental Health and Social Connection Remote work isolation is real. Working alone all day without social interaction leads to loneliness, reduced motivation, and burnout for many people.\nProactive strategies:\nSchedule social time. Don\u0026rsquo;t let \u0026ldquo;I should see friends\u0026rdquo; remain a vague intention. Block specific time for social connection the way you block work time.\nVirtual social rituals. Many remote teams create optional social touchpoints — virtual coffee chats, Friday end-of-week hangouts, Slack channels for non-work topics.\nCo-working spaces and coffee shops. Working from a cafe or co-working space for 1-2 days per week provides human presence and variety without compromising flexibility.\nClear work-life separation. Blurring work and personal time in both directions creates a feeling of never fully being off — which is exhausting. The shutdown ritual (see above) is crucial.\nManaging Energy, Not Just Time Productivity isn\u0026rsquo;t about working more hours — it\u0026rsquo;s about doing the right work when your energy is highest.\nTrack your energy levels for one week. Note when you\u0026rsquo;re sharp, when you\u0026rsquo;re sluggish, and when you\u0026rsquo;re somewhere in between. Then schedule accordingly:\nHigh-energy periods → deep work, complex thinking, important decisions Medium-energy periods → meetings, collaborative work, email Low-energy periods → admin, routine tasks, organizational work Most people find their peak energy in the morning (for early risers) or late morning to early afternoon. Protect this time for your most important work.\nPart 6: Common Mistakes New Remote Workers Make Mistake 1: Working in pajamas How you dress affects how you feel and perform. This doesn\u0026rsquo;t mean business attire — but changing out of sleep clothes signals to your brain that the day has started.\nMistake 2: Working from the couch or bed Your brain associates these spaces with relaxation. Working there trains your brain to associate your relaxation spaces with work stress — and erodes the mental separation you need.\nMistake 3: Always being available Responding to Slack instantly all day prevents deep work and signals to managers that you\u0026rsquo;re available for constant interruption. Set expectations, define response times, and work in focused blocks.\nMistake 4: Skipping breaks Without the natural breaks of an office (coffee machine, bathroom, chatting), remote workers often work in long uninterrupted stretches. This depletes focus faster than shorter focused sessions with real breaks.\nMistake 5: Neglecting human connection Assuming work video calls will provide sufficient social connection. They don\u0026rsquo;t. Deliberately cultivate non-work social time.\nMistake 6: Not advocating for yourself Remote workers who don\u0026rsquo;t proactively communicate their work and progress can become \u0026ldquo;out of sight, out of mind\u0026rdquo; with management. Over-communicate your contributions.\nYour First Week WFH: A Day-by-Day Plan Day Focus Monday Set up your workspace, test all technology Tuesday Establish morning routine, test start time Wednesday Implement time blocking, practice shutdown ritual Thursday Address distractions — block websites, establish household signals Friday Review the week, identify what worked and what didn\u0026rsquo;t, adjust The Essential WFH Toolkit Free tools to get started:\nGoogle Meet or Zoom (free tier) — video calls Slack (free tier) — team communication Notion (free) — notes and organization Stretchly (free) — movement reminders Cold Turkey or Freedom (free tier) — distraction blocking Worth paying for:\nNoise-canceling headphones — one-time cost, daily impact Ergonomic chair — your back will thank you ChatGPT Plus or Claude Pro ($20/month) — AI assistant for writing and research Level Up Your Remote Career Working from home successfully is just the start — finding a remote role that pays what you deserve is the real win. Find your next career on doda to browse thousands of remote and hybrid positions across industries — and land the flexible job you actually want.\nMake Remote Work Work for You Working from home successfully is a skill, not a trait. The routines, boundaries, and systems in this guide are learnable by anyone — regardless of personality type or work style.\nOur Remote Work Starter Kit includes a home office setup checklist, daily schedule templates, and a 30-day habit-building plan for new remote workers.\nRelated Tools Check your BMI and healthy weight range → BMI Calculator Plan your freelance budget → Budget Planner Calculate tax on side income → Side Hustle Tax Calculator Convert hourly wage to salary → Hourly to Salary Calculator Stay focused with the Pomodoro Technique → Pomodoro Timer Track time on tasks with lap timer support → Stopwatch Count down to any date or event → Countdown Timer Working across time zones? → Timezone Converter — convert times between any cities instantly\nRelated Reading:\nBest Productivity Apps for Remote Workers 2026 Best Remote Work Tools 2026 How to Start Freelancing with No Experience 2026 Related Articles Best Productivity Apps for Remote Workers 2026: Tested and Ranked Best Remote Work Tools in 2026: The Complete Stack Best ChatGPT Prompts for Productivity 2026 How to Automate Tasks with AI Step by Step How to Start Freelancing With No Experience 2026: Full Guide This article contains affiliate links. We may earn a commission at no extra cost to you.\n","permalink":"https://productivity-works.com/posts/work-from-home-tips-beginners-2026/","summary":"\u003chr\u003e\n\u003ch2 id=\"work-from-home-tips-for-beginners-2026-the-complete-guide\"\u003eWork From Home Tips for Beginners 2026: The Complete Guide\u003c/h2\u003e\n\u003cp\u003eWorking from home sounds like a dream. Then day three happens: you\u0026rsquo;re in pajamas at 2pm, haven\u0026rsquo;t moved from your couch, you\u0026rsquo;ve checked social media 30 times, and somehow produced less than you would in a full office day.\u003c/p\u003e\n\u003cp\u003eThis is not a failure of character. It\u0026rsquo;s a failure of environment and systems.\u003c/p\u003e\n\u003cp\u003eThe good news: working from home is a learnable skill. The people who thrive remotely have built specific habits, environments, and boundaries that make home as productive (and often more productive) than an office. This guide gives you everything they know.\u003c/p\u003e","title":"Work From Home Tips for Beginners 2026: Everything You Need to Succeed"},{"content":"Generate .htaccess files visually — no Apache expertise needed. Toggle sections, add redirect rules, configure security headers, and download a ready-to-use file in seconds.\nPresets: WordPress Static Site Force HTTPS Block Bad Bots Cache Static Assets Reset All \u0026lt;!-- Redirects --\u0026gt; \u0026lt;div class=\u0026quot;ha-section\u0026quot; id=\u0026quot;ha-sec-redirects\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ha-section-header\u0026quot; onclick=\u0026quot;haToggleSection('redirects')\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-chk-redirects\u0026quot; onclick=\u0026quot;event.stopPropagation();haRender()\u0026quot; checked\u0026gt; \u0026lt;span class=\u0026quot;ha-section-title\u0026quot;\u0026gt;Redirects\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;ha-section-badge\u0026quot;\u0026gt;301 / 302\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;ha-section-chevron\u0026quot; id=\u0026quot;ha-chev-redirects\u0026quot;\u0026gt;▼\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-section-body\u0026quot; id=\u0026quot;ha-body-redirects\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ha-field-row\u0026quot; style=\u0026quot;margin-bottom:10px;\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ha-field-label\u0026quot;\u0026gt;Default type\u0026lt;/span\u0026gt; \u0026lt;select class=\u0026quot;ha-select\u0026quot; id=\u0026quot;ha-redirect-type\u0026quot; onchange=\u0026quot;haRender()\u0026quot; style=\u0026quot;max-width:180px;\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;301\u0026quot;\u0026gt;301 Permanent\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;302\u0026quot;\u0026gt;302 Temporary\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-redirect-list\u0026quot; id=\u0026quot;ha-redirect-list\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;ha-add-btn\u0026quot; onclick=\u0026quot;haAddRedirect()\u0026quot;\u0026gt;+ Add Redirect Rule\u0026lt;/button\u0026gt; \u0026lt;div class=\u0026quot;ha-hint\u0026quot;\u0026gt;Source path (e.g. /old-page) → Destination URL\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Security Headers --\u0026gt; \u0026lt;div class=\u0026quot;ha-section\u0026quot; id=\u0026quot;ha-sec-security\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ha-section-header\u0026quot; onclick=\u0026quot;haToggleSection('security')\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-chk-security\u0026quot; onclick=\u0026quot;event.stopPropagation();haRender()\u0026quot; checked\u0026gt; \u0026lt;span class=\u0026quot;ha-section-title\u0026quot;\u0026gt;Security Headers\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;ha-section-chevron\u0026quot; id=\u0026quot;ha-chev-security\u0026quot;\u0026gt;▼\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-section-body\u0026quot; id=\u0026quot;ha-body-security\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-sec-xframe\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt; \u0026lt;span\u0026gt;X-Frame-Options: SAMEORIGIN\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-sec-xcontent\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt; \u0026lt;span\u0026gt;X-Content-Type-Options: nosniff\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-sec-xss\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt; \u0026lt;span\u0026gt;X-XSS-Protection: 1; mode=block\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-sec-referrer\u0026quot; onchange=\u0026quot;haRender()\u0026quot;\u0026gt; \u0026lt;span\u0026gt;Referrer-Policy: strict-origin-when-cross-origin\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-sec-hsts\u0026quot; onchange=\u0026quot;haRender()\u0026quot;\u0026gt; \u0026lt;span\u0026gt;Strict-Transport-Security (HSTS)\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-sec-csp\u0026quot; onchange=\u0026quot;haRender()\u0026quot;\u0026gt; \u0026lt;span\u0026gt;Content-Security-Policy: default-src 'self'\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-sec-serverhide\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt; \u0026lt;span\u0026gt;ServerTokens / hide server signature\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Caching --\u0026gt; \u0026lt;div class=\u0026quot;ha-section\u0026quot; id=\u0026quot;ha-sec-caching\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ha-section-header\u0026quot; onclick=\u0026quot;haToggleSection('caching')\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-chk-caching\u0026quot; onclick=\u0026quot;event.stopPropagation();haRender()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ha-section-title\u0026quot;\u0026gt;Browser Caching\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;ha-section-chevron\u0026quot; id=\u0026quot;ha-chev-caching\u0026quot;\u0026gt;▼\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-section-body ha-hidden\u0026quot; id=\u0026quot;ha-body-caching\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-cache-images\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt; \u0026lt;span\u0026gt;Images (jpg, png, gif, webp, svg) — 1 month\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-cache-css\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt; \u0026lt;span\u0026gt;CSS \u0026amp;amp; JavaScript — 1 week\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-cache-fonts\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt; \u0026lt;span\u0026gt;Fonts (woff, woff2, ttf) — 1 year\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-cache-html\u0026quot; onchange=\u0026quot;haRender()\u0026quot;\u0026gt; \u0026lt;span\u0026gt;HTML — 1 hour\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-cache-etag\u0026quot; onchange=\u0026quot;haRender()\u0026quot;\u0026gt; \u0026lt;span\u0026gt;Disable ETags (for CDN environments)\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- CORS --\u0026gt; \u0026lt;div class=\u0026quot;ha-section\u0026quot; id=\u0026quot;ha-sec-cors\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ha-section-header\u0026quot; onclick=\u0026quot;haToggleSection('cors')\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-chk-cors\u0026quot; onclick=\u0026quot;event.stopPropagation();haRender()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ha-section-title\u0026quot;\u0026gt;CORS\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;ha-section-chevron\u0026quot; id=\u0026quot;ha-chev-cors\u0026quot;\u0026gt;▼\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-section-body ha-hidden\u0026quot; id=\u0026quot;ha-body-cors\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ha-field-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ha-field-label\u0026quot;\u0026gt;Allow Origin\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;ha-input\u0026quot; id=\u0026quot;ha-cors-origin\u0026quot; value=\u0026quot;*\u0026quot; onchange=\u0026quot;haRender()\u0026quot; placeholder=\u0026quot;* or https://example.com\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-cors-methods\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt; \u0026lt;span\u0026gt;Allow Methods: GET, POST, OPTIONS\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-cors-headers\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt; \u0026lt;span\u0026gt;Allow Headers: Content-Type, Authorization\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-cors-fonts-only\u0026quot; onchange=\u0026quot;haRender()\u0026quot;\u0026gt; \u0026lt;span\u0026gt;CORS for fonts only (recommended)\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Gzip --\u0026gt; \u0026lt;div class=\u0026quot;ha-section\u0026quot; id=\u0026quot;ha-sec-gzip\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ha-section-header\u0026quot; onclick=\u0026quot;haToggleSection('gzip')\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-chk-gzip\u0026quot; onclick=\u0026quot;event.stopPropagation();haRender()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ha-section-title\u0026quot;\u0026gt;Gzip Compression\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;ha-section-chevron\u0026quot; id=\u0026quot;ha-chev-gzip\u0026quot;\u0026gt;▼\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-section-body ha-hidden\u0026quot; id=\u0026quot;ha-body-gzip\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-gzip-html\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt; \u0026lt;span\u0026gt;HTML, XML, Plain text\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-gzip-css\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt; \u0026lt;span\u0026gt;CSS \u0026amp;amp; JavaScript\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-gzip-fonts\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt; \u0026lt;span\u0026gt;Fonts (SVG, TTF)\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-gzip-json\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt; \u0026lt;span\u0026gt;JSON \u0026amp;amp; APIs\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Error Pages --\u0026gt; \u0026lt;div class=\u0026quot;ha-section\u0026quot; id=\u0026quot;ha-sec-errors\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ha-section-header\u0026quot; onclick=\u0026quot;haToggleSection('errors')\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-chk-errors\u0026quot; onclick=\u0026quot;event.stopPropagation();haRender()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ha-section-title\u0026quot;\u0026gt;Custom Error Pages\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;ha-section-chevron\u0026quot; id=\u0026quot;ha-chev-errors\u0026quot;\u0026gt;▼\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-section-body ha-hidden\u0026quot; id=\u0026quot;ha-body-errors\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ha-field-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ha-field-label\u0026quot;\u0026gt;404 Not Found\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;ha-input\u0026quot; id=\u0026quot;ha-err-404\u0026quot; value=\u0026quot;/404.html\u0026quot; onchange=\u0026quot;haRender()\u0026quot; placeholder=\u0026quot;/404.html\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-field-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ha-field-label\u0026quot;\u0026gt;403 Forbidden\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;ha-input\u0026quot; id=\u0026quot;ha-err-403\u0026quot; value=\u0026quot;/403.html\u0026quot; onchange=\u0026quot;haRender()\u0026quot; placeholder=\u0026quot;/403.html\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-field-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ha-field-label\u0026quot;\u0026gt;500 Server Error\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;ha-input\u0026quot; id=\u0026quot;ha-err-500\u0026quot; value=\u0026quot;/500.html\u0026quot; onchange=\u0026quot;haRender()\u0026quot; placeholder=\u0026quot;/500.html\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-field-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ha-field-label\u0026quot;\u0026gt;401 Unauthorized\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;ha-input\u0026quot; id=\u0026quot;ha-err-401\u0026quot; value=\u0026quot;\u0026quot; onchange=\u0026quot;haRender()\u0026quot; placeholder=\u0026quot;/401.html (optional)\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Directory Options --\u0026gt; \u0026lt;div class=\u0026quot;ha-section\u0026quot; id=\u0026quot;ha-sec-directory\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ha-section-header\u0026quot; onclick=\u0026quot;haToggleSection('directory')\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-chk-directory\u0026quot; onclick=\u0026quot;event.stopPropagation();haRender()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ha-section-title\u0026quot;\u0026gt;Directory Options\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;ha-section-chevron\u0026quot; id=\u0026quot;ha-chev-directory\u0026quot;\u0026gt;▼\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-section-body ha-hidden\u0026quot; id=\u0026quot;ha-body-directory\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-dir-noindex\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt; \u0026lt;span\u0026gt;Disable directory listing (Options -Indexes)\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-dir-followlinks\u0026quot; onchange=\u0026quot;haRender()\u0026quot;\u0026gt; \u0026lt;span\u0026gt;Follow symbolic links (Options +FollowSymLinks)\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-dir-blockdotfiles\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt; \u0026lt;span\u0026gt;Block access to dotfiles (.env, .git, etc.)\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-dir-blockphp\u0026quot; onchange=\u0026quot;haRender()\u0026quot;\u0026gt; \u0026lt;span\u0026gt;Block PHP execution in uploads directory\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-dir-badbots\u0026quot; onchange=\u0026quot;haRender()\u0026quot;\u0026gt; \u0026lt;span\u0026gt;Block common bad bots\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ha-field-row\u0026quot; style=\u0026quot;margin-top:8px;\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ha-field-label\u0026quot;\u0026gt;Default charset\u0026lt;/span\u0026gt; \u0026lt;select class=\u0026quot;ha-select\u0026quot; id=\u0026quot;ha-dir-charset\u0026quot; onchange=\u0026quot;haRender()\u0026quot; style=\u0026quot;max-width:180px;\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;UTF-8\u0026quot; selected\u0026gt;UTF-8\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;ISO-8859-1\u0026quot;\u0026gt;ISO-8859-1\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;\u0026quot;\u0026gt;(none)\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Preview — .htaccess Copy Download Related Tools Build robots.txt → Robots.txt Generator Generate meta tags → Meta Tag Generator Format SQL → SQL Formatter ","permalink":"https://productivity-works.com/tools/htaccess-generator/","summary":"\u003cp\u003eGenerate \u003ccode\u003e.htaccess\u003c/code\u003e files visually — no Apache expertise needed. Toggle sections, add redirect rules, configure security headers, and download a ready-to-use file in seconds.\u003c/p\u003e\n\u003cdiv id=\"htaccess-app\"\u003e\n\u003cstyle\u003e\n#htaccess-app *,\n#htaccess-app *::before,\n#htaccess-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#htaccess-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n  font-size: 15px;\n  color: #1a1a2e;\n  line-height: 1.6;\n  max-width: 900px;\n  margin: 0 auto;\n}\n#htaccess-app .ha-layout {\n  display: flex;\n  gap: 20px;\n  flex-wrap: wrap;\n}\n#htaccess-app .ha-left {\n  flex: 1 1 480px;\n  min-width: 0;\n}\n#htaccess-app .ha-right {\n  flex: 1 1 320px;\n  min-width: 0;\n}\n#htaccess-app .ha-presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 20px;\n}\n#htaccess-app .ha-preset-btn {\n  padding: 6px 14px;\n  background: #f0f4ff;\n  border: 1px solid #c3d0f5;\n  border-radius: 20px;\n  cursor: pointer;\n  font-size: 13px;\n  color: #3a56d4;\n  font-weight: 500;\n  transition: background 0.15s, border-color 0.15s;\n}\n#htaccess-app .ha-preset-btn:hover {\n  background: #dce6ff;\n  border-color: #3a56d4;\n}\n#htaccess-app .ha-section {\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n  margin-bottom: 14px;\n  overflow: hidden;\n}\n#htaccess-app .ha-section-header {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  padding: 12px 16px;\n  background: #f8fafc;\n  cursor: pointer;\n  user-select: none;\n}\n#htaccess-app .ha-section-header:hover {\n  background: #f0f4ff;\n}\n#htaccess-app .ha-section-header input[type=\"checkbox\"] {\n  width: 17px;\n  height: 17px;\n  accent-color: #3a56d4;\n  cursor: pointer;\n  flex-shrink: 0;\n}\n#htaccess-app .ha-section-title {\n  font-weight: 600;\n  font-size: 14px;\n  flex: 1;\n}\n#htaccess-app .ha-section-chevron {\n  font-size: 11px;\n  color: #94a3b8;\n  transition: transform 0.2s;\n}\n#htaccess-app .ha-section-body {\n  padding: 14px 16px;\n  border-top: 1px solid #e2e8f0;\n  background: #fff;\n}\n#htaccess-app .ha-section-body.ha-hidden {\n  display: none;\n}\n#htaccess-app .ha-field-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-bottom: 10px;\n  flex-wrap: wrap;\n}\n#htaccess-app .ha-field-label {\n  font-size: 13px;\n  color: #475569;\n  min-width: 90px;\n  flex-shrink: 0;\n}\n#htaccess-app .ha-input,\n#htaccess-app .ha-select {\n  flex: 1;\n  min-width: 120px;\n  padding: 6px 10px;\n  border: 1px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 13px;\n  color: #1a1a2e;\n  background: #fff;\n  outline: none;\n  transition: border-color 0.15s;\n}\n#htaccess-app .ha-input:focus,\n#htaccess-app .ha-select:focus {\n  border-color: #3a56d4;\n  box-shadow: 0 0 0 3px rgba(58,86,212,0.1);\n}\n#htaccess-app .ha-redirect-list {\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n  margin-bottom: 10px;\n}\n#htaccess-app .ha-redirect-row {\n  display: flex;\n  gap: 8px;\n  align-items: center;\n  flex-wrap: wrap;\n}\n#htaccess-app .ha-redirect-row .ha-input {\n  flex: 1;\n  min-width: 100px;\n}\n#htaccess-app .ha-remove-btn {\n  padding: 5px 10px;\n  background: #fee2e2;\n  border: 1px solid #fca5a5;\n  border-radius: 6px;\n  color: #dc2626;\n  cursor: pointer;\n  font-size: 12px;\n  flex-shrink: 0;\n  transition: background 0.15s;\n}\n#htaccess-app .ha-remove-btn:hover {\n  background: #fecaca;\n}\n#htaccess-app .ha-add-btn {\n  padding: 6px 14px;\n  background: #f0fdf4;\n  border: 1px solid #86efac;\n  border-radius: 6px;\n  color: #16a34a;\n  cursor: pointer;\n  font-size: 13px;\n  font-weight: 500;\n  transition: background 0.15s;\n}\n#htaccess-app .ha-add-btn:hover {\n  background: #dcfce7;\n}\n#htaccess-app .ha-checkbox-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin-bottom: 8px;\n  font-size: 13px;\n  color: #334155;\n}\n#htaccess-app .ha-checkbox-row input[type=\"checkbox\"] {\n  width: 15px;\n  height: 15px;\n  accent-color: #3a56d4;\n  cursor: pointer;\n}\n#htaccess-app .ha-hint {\n  font-size: 12px;\n  color: #94a3b8;\n  margin-top: 4px;\n}\n#htaccess-app .ha-preview-box {\n  background: #0f172a;\n  border-radius: 10px;\n  overflow: hidden;\n  position: sticky;\n  top: 80px;\n}\n#htaccess-app .ha-preview-topbar {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  padding: 10px 14px;\n  background: #1e293b;\n}\n#htaccess-app .ha-preview-label {\n  font-size: 12px;\n  font-weight: 600;\n  color: #94a3b8;\n  letter-spacing: 0.05em;\n  text-transform: uppercase;\n}\n#htaccess-app .ha-preview-actions {\n  display: flex;\n  gap: 8px;\n}\n#htaccess-app .ha-copy-btn,\n#htaccess-app .ha-download-btn {\n  padding: 5px 12px;\n  border-radius: 6px;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  border: none;\n  transition: background 0.15s, transform 0.1s;\n}\n#htaccess-app .ha-copy-btn {\n  background: #3a56d4;\n  color: #fff;\n}\n#htaccess-app .ha-copy-btn:hover {\n  background: #2d44b0;\n}\n#htaccess-app .ha-copy-btn.ha-copied {\n  background: #16a34a;\n}\n#htaccess-app .ha-download-btn {\n  background: #334155;\n  color: #e2e8f0;\n}\n#htaccess-app .ha-download-btn:hover {\n  background: #475569;\n}\n#htaccess-app .ha-preview-code {\n  padding: 16px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 12px;\n  line-height: 1.7;\n  color: #e2e8f0;\n  white-space: pre;\n  overflow-x: auto;\n  max-height: 520px;\n  overflow-y: auto;\n  min-height: 200px;\n}\n#htaccess-app .ha-preview-code .ht-comment { color: #64748b; }\n#htaccess-app .ha-preview-code .ht-directive { color: #7dd3fc; }\n#htaccess-app .ha-preview-code .ht-value { color: #86efac; }\n#htaccess-app .ha-preview-code .ht-tag { color: #f9a8d4; }\n#htaccess-app .ha-preview-code .ht-string { color: #fde68a; }\n#htaccess-app .ha-preview-code .ht-section { color: #c084fc; }\n#htaccess-app .ha-section-badge {\n  font-size: 11px;\n  background: #e0e7ff;\n  color: #3730a3;\n  border-radius: 10px;\n  padding: 1px 8px;\n  font-weight: 500;\n}\n#htaccess-app .ha-generate-btn {\n  width: 100%;\n  padding: 13px;\n  background: linear-gradient(135deg, #3a56d4, #7c3aed);\n  color: #fff;\n  border: none;\n  border-radius: 10px;\n  font-size: 15px;\n  font-weight: 700;\n  cursor: pointer;\n  margin-bottom: 16px;\n  transition: opacity 0.2s, transform 0.1s;\n  letter-spacing: 0.02em;\n}\n#htaccess-app .ha-generate-btn:hover {\n  opacity: 0.92;\n  transform: translateY(-1px);\n}\n#htaccess-app .ha-section-count {\n  font-size: 11px;\n  color: #64748b;\n  margin-top: 2px;\n}\n@media (max-width: 700px) {\n  #htaccess-app .ha-layout {\n    flex-direction: column;\n  }\n  #htaccess-app .ha-preview-box {\n    position: static;\n  }\n}\n\u003c/style\u003e\n\u003cdiv class=\"ha-presets\"\u003e\n  \u003cstrong style=\"font-size:13px;color:#64748b;align-self:center;margin-right:4px;\"\u003ePresets:\u003c/strong\u003e\n  \u003cbutton class=\"ha-preset-btn\" onclick=\"haApplyPreset('wordpress')\"\u003eWordPress\u003c/button\u003e\n  \u003cbutton class=\"ha-preset-btn\" onclick=\"haApplyPreset('static')\"\u003eStatic Site\u003c/button\u003e\n  \u003cbutton class=\"ha-preset-btn\" onclick=\"haApplyPreset('https')\"\u003eForce HTTPS\u003c/button\u003e\n  \u003cbutton class=\"ha-preset-btn\" onclick=\"haApplyPreset('bots')\"\u003eBlock Bad Bots\u003c/button\u003e\n  \u003cbutton class=\"ha-preset-btn\" onclick=\"haApplyPreset('cache')\"\u003eCache Static Assets\u003c/button\u003e\n  \u003cbutton class=\"ha-preset-btn\" onclick=\"haApplyPreset('reset')\" style=\"color:#dc2626;background:#fff5f5;border-color:#fca5a5;\"\u003eReset All\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ha-layout\"\u003e\n  \u003cdiv class=\"ha-left\"\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;!-- Redirects --\u0026gt;\n\u0026lt;div class=\u0026quot;ha-section\u0026quot; id=\u0026quot;ha-sec-redirects\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;ha-section-header\u0026quot; onclick=\u0026quot;haToggleSection('redirects')\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-chk-redirects\u0026quot; onclick=\u0026quot;event.stopPropagation();haRender()\u0026quot; checked\u0026gt;\n    \u0026lt;span class=\u0026quot;ha-section-title\u0026quot;\u0026gt;Redirects\u0026lt;/span\u0026gt;\n    \u0026lt;span class=\u0026quot;ha-section-badge\u0026quot;\u0026gt;301 / 302\u0026lt;/span\u0026gt;\n    \u0026lt;span class=\u0026quot;ha-section-chevron\u0026quot; id=\u0026quot;ha-chev-redirects\u0026quot;\u0026gt;▼\u0026lt;/span\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;ha-section-body\u0026quot; id=\u0026quot;ha-body-redirects\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-field-row\u0026quot; style=\u0026quot;margin-bottom:10px;\u0026quot;\u0026gt;\n      \u0026lt;span class=\u0026quot;ha-field-label\u0026quot;\u0026gt;Default type\u0026lt;/span\u0026gt;\n      \u0026lt;select class=\u0026quot;ha-select\u0026quot; id=\u0026quot;ha-redirect-type\u0026quot; onchange=\u0026quot;haRender()\u0026quot; style=\u0026quot;max-width:180px;\u0026quot;\u0026gt;\n        \u0026lt;option value=\u0026quot;301\u0026quot;\u0026gt;301 Permanent\u0026lt;/option\u0026gt;\n        \u0026lt;option value=\u0026quot;302\u0026quot;\u0026gt;302 Temporary\u0026lt;/option\u0026gt;\n      \u0026lt;/select\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-redirect-list\u0026quot; id=\u0026quot;ha-redirect-list\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;button class=\u0026quot;ha-add-btn\u0026quot; onclick=\u0026quot;haAddRedirect()\u0026quot;\u0026gt;+ Add Redirect Rule\u0026lt;/button\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-hint\u0026quot;\u0026gt;Source path (e.g. /old-page) → Destination URL\u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Security Headers --\u0026gt;\n\u0026lt;div class=\u0026quot;ha-section\u0026quot; id=\u0026quot;ha-sec-security\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;ha-section-header\u0026quot; onclick=\u0026quot;haToggleSection('security')\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-chk-security\u0026quot; onclick=\u0026quot;event.stopPropagation();haRender()\u0026quot; checked\u0026gt;\n    \u0026lt;span class=\u0026quot;ha-section-title\u0026quot;\u0026gt;Security Headers\u0026lt;/span\u0026gt;\n    \u0026lt;span class=\u0026quot;ha-section-chevron\u0026quot; id=\u0026quot;ha-chev-security\u0026quot;\u0026gt;▼\u0026lt;/span\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;ha-section-body\u0026quot; id=\u0026quot;ha-body-security\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-sec-xframe\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt;\n      \u0026lt;span\u0026gt;X-Frame-Options: SAMEORIGIN\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-sec-xcontent\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt;\n      \u0026lt;span\u0026gt;X-Content-Type-Options: nosniff\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-sec-xss\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt;\n      \u0026lt;span\u0026gt;X-XSS-Protection: 1; mode=block\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-sec-referrer\u0026quot; onchange=\u0026quot;haRender()\u0026quot;\u0026gt;\n      \u0026lt;span\u0026gt;Referrer-Policy: strict-origin-when-cross-origin\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-sec-hsts\u0026quot; onchange=\u0026quot;haRender()\u0026quot;\u0026gt;\n      \u0026lt;span\u0026gt;Strict-Transport-Security (HSTS)\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-sec-csp\u0026quot; onchange=\u0026quot;haRender()\u0026quot;\u0026gt;\n      \u0026lt;span\u0026gt;Content-Security-Policy: default-src 'self'\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-sec-serverhide\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt;\n      \u0026lt;span\u0026gt;ServerTokens / hide server signature\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Caching --\u0026gt;\n\u0026lt;div class=\u0026quot;ha-section\u0026quot; id=\u0026quot;ha-sec-caching\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;ha-section-header\u0026quot; onclick=\u0026quot;haToggleSection('caching')\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-chk-caching\u0026quot; onclick=\u0026quot;event.stopPropagation();haRender()\u0026quot;\u0026gt;\n    \u0026lt;span class=\u0026quot;ha-section-title\u0026quot;\u0026gt;Browser Caching\u0026lt;/span\u0026gt;\n    \u0026lt;span class=\u0026quot;ha-section-chevron\u0026quot; id=\u0026quot;ha-chev-caching\u0026quot;\u0026gt;▼\u0026lt;/span\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;ha-section-body ha-hidden\u0026quot; id=\u0026quot;ha-body-caching\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-cache-images\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt;\n      \u0026lt;span\u0026gt;Images (jpg, png, gif, webp, svg) — 1 month\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-cache-css\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt;\n      \u0026lt;span\u0026gt;CSS \u0026amp;amp; JavaScript — 1 week\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-cache-fonts\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt;\n      \u0026lt;span\u0026gt;Fonts (woff, woff2, ttf) — 1 year\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-cache-html\u0026quot; onchange=\u0026quot;haRender()\u0026quot;\u0026gt;\n      \u0026lt;span\u0026gt;HTML — 1 hour\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-cache-etag\u0026quot; onchange=\u0026quot;haRender()\u0026quot;\u0026gt;\n      \u0026lt;span\u0026gt;Disable ETags (for CDN environments)\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- CORS --\u0026gt;\n\u0026lt;div class=\u0026quot;ha-section\u0026quot; id=\u0026quot;ha-sec-cors\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;ha-section-header\u0026quot; onclick=\u0026quot;haToggleSection('cors')\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-chk-cors\u0026quot; onclick=\u0026quot;event.stopPropagation();haRender()\u0026quot;\u0026gt;\n    \u0026lt;span class=\u0026quot;ha-section-title\u0026quot;\u0026gt;CORS\u0026lt;/span\u0026gt;\n    \u0026lt;span class=\u0026quot;ha-section-chevron\u0026quot; id=\u0026quot;ha-chev-cors\u0026quot;\u0026gt;▼\u0026lt;/span\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;ha-section-body ha-hidden\u0026quot; id=\u0026quot;ha-body-cors\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-field-row\u0026quot;\u0026gt;\n      \u0026lt;span class=\u0026quot;ha-field-label\u0026quot;\u0026gt;Allow Origin\u0026lt;/span\u0026gt;\n      \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;ha-input\u0026quot; id=\u0026quot;ha-cors-origin\u0026quot; value=\u0026quot;*\u0026quot; onchange=\u0026quot;haRender()\u0026quot; placeholder=\u0026quot;* or https://example.com\u0026quot;\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-cors-methods\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt;\n      \u0026lt;span\u0026gt;Allow Methods: GET, POST, OPTIONS\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-cors-headers\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt;\n      \u0026lt;span\u0026gt;Allow Headers: Content-Type, Authorization\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-cors-fonts-only\u0026quot; onchange=\u0026quot;haRender()\u0026quot;\u0026gt;\n      \u0026lt;span\u0026gt;CORS for fonts only (recommended)\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Gzip --\u0026gt;\n\u0026lt;div class=\u0026quot;ha-section\u0026quot; id=\u0026quot;ha-sec-gzip\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;ha-section-header\u0026quot; onclick=\u0026quot;haToggleSection('gzip')\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-chk-gzip\u0026quot; onclick=\u0026quot;event.stopPropagation();haRender()\u0026quot;\u0026gt;\n    \u0026lt;span class=\u0026quot;ha-section-title\u0026quot;\u0026gt;Gzip Compression\u0026lt;/span\u0026gt;\n    \u0026lt;span class=\u0026quot;ha-section-chevron\u0026quot; id=\u0026quot;ha-chev-gzip\u0026quot;\u0026gt;▼\u0026lt;/span\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;ha-section-body ha-hidden\u0026quot; id=\u0026quot;ha-body-gzip\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-gzip-html\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt;\n      \u0026lt;span\u0026gt;HTML, XML, Plain text\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-gzip-css\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt;\n      \u0026lt;span\u0026gt;CSS \u0026amp;amp; JavaScript\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-gzip-fonts\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt;\n      \u0026lt;span\u0026gt;Fonts (SVG, TTF)\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-gzip-json\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt;\n      \u0026lt;span\u0026gt;JSON \u0026amp;amp; APIs\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Error Pages --\u0026gt;\n\u0026lt;div class=\u0026quot;ha-section\u0026quot; id=\u0026quot;ha-sec-errors\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;ha-section-header\u0026quot; onclick=\u0026quot;haToggleSection('errors')\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-chk-errors\u0026quot; onclick=\u0026quot;event.stopPropagation();haRender()\u0026quot;\u0026gt;\n    \u0026lt;span class=\u0026quot;ha-section-title\u0026quot;\u0026gt;Custom Error Pages\u0026lt;/span\u0026gt;\n    \u0026lt;span class=\u0026quot;ha-section-chevron\u0026quot; id=\u0026quot;ha-chev-errors\u0026quot;\u0026gt;▼\u0026lt;/span\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;ha-section-body ha-hidden\u0026quot; id=\u0026quot;ha-body-errors\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-field-row\u0026quot;\u0026gt;\n      \u0026lt;span class=\u0026quot;ha-field-label\u0026quot;\u0026gt;404 Not Found\u0026lt;/span\u0026gt;\n      \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;ha-input\u0026quot; id=\u0026quot;ha-err-404\u0026quot; value=\u0026quot;/404.html\u0026quot; onchange=\u0026quot;haRender()\u0026quot; placeholder=\u0026quot;/404.html\u0026quot;\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-field-row\u0026quot;\u0026gt;\n      \u0026lt;span class=\u0026quot;ha-field-label\u0026quot;\u0026gt;403 Forbidden\u0026lt;/span\u0026gt;\n      \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;ha-input\u0026quot; id=\u0026quot;ha-err-403\u0026quot; value=\u0026quot;/403.html\u0026quot; onchange=\u0026quot;haRender()\u0026quot; placeholder=\u0026quot;/403.html\u0026quot;\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-field-row\u0026quot;\u0026gt;\n      \u0026lt;span class=\u0026quot;ha-field-label\u0026quot;\u0026gt;500 Server Error\u0026lt;/span\u0026gt;\n      \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;ha-input\u0026quot; id=\u0026quot;ha-err-500\u0026quot; value=\u0026quot;/500.html\u0026quot; onchange=\u0026quot;haRender()\u0026quot; placeholder=\u0026quot;/500.html\u0026quot;\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-field-row\u0026quot;\u0026gt;\n      \u0026lt;span class=\u0026quot;ha-field-label\u0026quot;\u0026gt;401 Unauthorized\u0026lt;/span\u0026gt;\n      \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;ha-input\u0026quot; id=\u0026quot;ha-err-401\u0026quot; value=\u0026quot;\u0026quot; onchange=\u0026quot;haRender()\u0026quot; placeholder=\u0026quot;/401.html (optional)\u0026quot;\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Directory Options --\u0026gt;\n\u0026lt;div class=\u0026quot;ha-section\u0026quot; id=\u0026quot;ha-sec-directory\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;ha-section-header\u0026quot; onclick=\u0026quot;haToggleSection('directory')\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-chk-directory\u0026quot; onclick=\u0026quot;event.stopPropagation();haRender()\u0026quot;\u0026gt;\n    \u0026lt;span class=\u0026quot;ha-section-title\u0026quot;\u0026gt;Directory Options\u0026lt;/span\u0026gt;\n    \u0026lt;span class=\u0026quot;ha-section-chevron\u0026quot; id=\u0026quot;ha-chev-directory\u0026quot;\u0026gt;▼\u0026lt;/span\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;ha-section-body ha-hidden\u0026quot; id=\u0026quot;ha-body-directory\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-dir-noindex\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt;\n      \u0026lt;span\u0026gt;Disable directory listing (Options -Indexes)\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-dir-followlinks\u0026quot; onchange=\u0026quot;haRender()\u0026quot;\u0026gt;\n      \u0026lt;span\u0026gt;Follow symbolic links (Options +FollowSymLinks)\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-dir-blockdotfiles\u0026quot; onchange=\u0026quot;haRender()\u0026quot; checked\u0026gt;\n      \u0026lt;span\u0026gt;Block access to dotfiles (.env, .git, etc.)\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-dir-blockphp\u0026quot; onchange=\u0026quot;haRender()\u0026quot;\u0026gt;\n      \u0026lt;span\u0026gt;Block PHP execution in uploads directory\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ha-dir-badbots\u0026quot; onchange=\u0026quot;haRender()\u0026quot;\u0026gt;\n      \u0026lt;span\u0026gt;Block common bad bots\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;ha-field-row\u0026quot; style=\u0026quot;margin-top:8px;\u0026quot;\u0026gt;\n      \u0026lt;span class=\u0026quot;ha-field-label\u0026quot;\u0026gt;Default charset\u0026lt;/span\u0026gt;\n      \u0026lt;select class=\u0026quot;ha-select\u0026quot; id=\u0026quot;ha-dir-charset\u0026quot; onchange=\u0026quot;haRender()\u0026quot; style=\u0026quot;max-width:180px;\u0026quot;\u0026gt;\n        \u0026lt;option value=\u0026quot;UTF-8\u0026quot; selected\u0026gt;UTF-8\u0026lt;/option\u0026gt;\n        \u0026lt;option value=\u0026quot;ISO-8859-1\u0026quot;\u0026gt;ISO-8859-1\u0026lt;/option\u0026gt;\n        \u0026lt;option value=\u0026quot;\u0026quot;\u0026gt;(none)\u0026lt;/option\u0026gt;\n      \u0026lt;/select\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\u003c!-- /.ha-left --\u003e\n  \u003cdiv class=\"ha-right\"\u003e\n    \u003cdiv class=\"ha-preview-box\"\u003e\n      \u003cdiv class=\"ha-preview-topbar\"\u003e\n        \u003cspan class=\"ha-preview-label\"\u003ePreview — .htaccess\u003c/span\u003e\n        \u003cdiv class=\"ha-preview-actions\"\u003e\n          \u003cbutton class=\"ha-copy-btn\" id=\"ha-copy-btn\" onclick=\"haCopy()\"\u003eCopy\u003c/button\u003e\n          \u003cbutton class=\"ha-download-btn\" onclick=\"haDownload()\"\u003eDownload\u003c/button\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n      \u003cpre class=\"ha-preview-code\" id=\"ha-preview-code\"\u003e\u003c/pre\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\u003c!-- /.ha-layout --\u003e\n\u003cscript\u003e\n(function() {\n  var haRedirects = [{ src: '/old-page', dst: 'https://example.com/new-page' }];\n  var haOpenSections = { redirects: true, security: true, caching: false, cors: false, gzip: false, errors: false, directory: false };\n\n  function haToggleSection(id) {\n    haOpenSections[id] = !haOpenSections[id];\n    var body = document.getElementById('ha-body-' + id);\n    var chev = document.getElementById('ha-chev-' + id);\n    if (body) body.classList.toggle('ha-hidden', !haOpenSections[id]);\n    if (chev) chev.textContent = haOpenSections[id] ? '▼' : '▶';\n  }\n  window.haToggleSection = haToggleSection;\n\n  function haAddRedirect() {\n    haRedirects.push({ src: '', dst: '' });\n    haRenderRedirectList();\n    haRender();\n  }\n  window.haAddRedirect = haAddRedirect;\n\n  function haRemoveRedirect(i) {\n    haRedirects.splice(i, 1);\n    haRenderRedirectList();\n    haRender();\n  }\n  window.haRemoveRedirect = haRemoveRedirect;\n\n  function haRenderRedirectList() {\n    var list = document.getElementById('ha-redirect-list');\n    if (!list) return;\n    list.innerHTML = '';\n    haRedirects.forEach(function(r, i) {\n      var row = document.createElement('div');\n      row.className = 'ha-redirect-row';\n      row.innerHTML =\n        '\u003cinput type=\"text\" class=\"ha-input\" placeholder=\"/old-path\" value=\"' + escAttr(r.src) + '\" oninput=\"haSetRedirect(' + i + ',\\'src\\',this.value)\"\u003e' +\n        '\u003cspan style=\"color:#94a3b8;font-size:13px;\"\u003e→\u003c/span\u003e' +\n        '\u003cinput type=\"text\" class=\"ha-input\" placeholder=\"https://example.com/new\" value=\"' + escAttr(r.dst) + '\" oninput=\"haSetRedirect(' + i + ',\\'dst\\',this.value)\"\u003e' +\n        '\u003cbutton class=\"ha-remove-btn\" onclick=\"haRemoveRedirect(' + i + ')\"\u003e✕\u003c/button\u003e';\n      list.appendChild(row);\n    });\n  }\n\n  function haSetRedirect(i, key, val) {\n    haRedirects[i][key] = val;\n    haRender();\n  }\n  window.haSetRedirect = haSetRedirect;\n\n  function escAttr(s) { return (s || '').replace(/\"/g, '\u0026quot;'); }\n\n  function v(id) { var el = document.getElementById(id); return el ? el.value : ''; }\n  function c(id) { var el = document.getElementById(id); return el ? el.checked : false; }\n\n  function haRender() {\n    var lines = [];\n    lines.push('# Generated by .htaccess Generator');\n    lines.push('# https://productivity.works/tools/htaccess-generator/');\n    lines.push('');\n    lines.push('Options -MultiViews');\n\n    // Redirects\n    if (c('ha-chk-redirects')) {\n      var type = v('ha-redirect-type') || '301';\n      var hasRules = haRedirects.some(function(r) { return r.src \u0026\u0026 r.dst; });\n      if (hasRules) {\n        lines.push('');\n        lines.push('# ─── Redirects ──────────────────────────────────────────');\n        lines.push('RewriteEngine On');\n        haRedirects.forEach(function(r) {\n          if (r.src \u0026\u0026 r.dst) {\n            if (type === '301') {\n              lines.push('Redirect 301 ' + r.src + ' ' + r.dst);\n            } else {\n              lines.push('Redirect 302 ' + r.src + ' ' + r.dst);\n            }\n          }\n        });\n      }\n    }\n\n    // Security Headers\n    if (c('ha-chk-security')) {\n      lines.push('');\n      lines.push('# ─── Security Headers ───────────────────────────────────');\n      lines.push('\u003cIfModule mod_headers.c\u003e');\n      if (c('ha-sec-xframe'))      lines.push('  Header always set X-Frame-Options \"SAMEORIGIN\"');\n      if (c('ha-sec-xcontent'))    lines.push('  Header always set X-Content-Type-Options \"nosniff\"');\n      if (c('ha-sec-xss'))         lines.push('  Header always set X-XSS-Protection \"1; mode=block\"');\n      if (c('ha-sec-referrer'))    lines.push('  Header always set Referrer-Policy \"strict-origin-when-cross-origin\"');\n      if (c('ha-sec-hsts'))        lines.push('  Header always set Strict-Transport-Security \"max-age=31536000; includeSubDomains; preload\"');\n      if (c('ha-sec-csp'))         lines.push('  Header always set Content-Security-Policy \"default-src \\'self\\'\"');\n      if (c('ha-sec-serverhide')) {\n        lines.push('  Header unset Server');\n        lines.push('  Header unset X-Powered-By');\n        lines.push('  ServerTokens Prod');\n        lines.push('  ServerSignature Off');\n      }\n      lines.push('\u003c/IfModule\u003e');\n    }\n\n    // Caching\n    if (c('ha-chk-caching')) {\n      lines.push('');\n      lines.push('# ─── Browser Caching ────────────────────────────────────');\n      lines.push('\u003cIfModule mod_expires.c\u003e');\n      lines.push('  ExpiresActive On');\n      if (c('ha-cache-images')) lines.push('  ExpiresByType image/jpeg \"access plus 1 month\"');\n      if (c('ha-cache-images')) lines.push('  ExpiresByType image/png \"access plus 1 month\"');\n      if (c('ha-cache-images')) lines.push('  ExpiresByType image/gif \"access plus 1 month\"');\n      if (c('ha-cache-images')) lines.push('  ExpiresByType image/webp \"access plus 1 month\"');\n      if (c('ha-cache-images')) lines.push('  ExpiresByType image/svg+xml \"access plus 1 month\"');\n      if (c('ha-cache-css'))    lines.push('  ExpiresByType text/css \"access plus 1 week\"');\n      if (c('ha-cache-css'))    lines.push('  ExpiresByType application/javascript \"access plus 1 week\"');\n      if (c('ha-cache-fonts'))  lines.push('  ExpiresByType font/woff2 \"access plus 1 year\"');\n      if (c('ha-cache-fonts'))  lines.push('  ExpiresByType font/woff \"access plus 1 year\"');\n      if (c('ha-cache-fonts'))  lines.push('  ExpiresByType font/ttf \"access plus 1 year\"');\n      if (c('ha-cache-html'))   lines.push('  ExpiresByType text/html \"access plus 1 hour\"');\n      lines.push('\u003c/IfModule\u003e');\n      if (c('ha-cache-etag')) {\n        lines.push('FileETag None');\n        lines.push('Header unset ETag');\n      }\n    }\n\n    // CORS\n    if (c('ha-chk-cors')) {\n      var origin = v('ha-cors-origin') || '*';\n      lines.push('');\n      lines.push('# ─── CORS ───────────────────────────────────────────────');\n      lines.push('\u003cIfModule mod_headers.c\u003e');\n      if (c('ha-cors-fonts-only')) {\n        lines.push('  \u003cFilesMatch \"\\\\.(ttf|ttc|otf|eot|woff|woff2|font.css)$\"\u003e');\n        lines.push('    Header add Access-Control-Allow-Origin \"' + origin + '\"');\n        lines.push('  \u003c/FilesMatch\u003e');\n      } else {\n        lines.push('  Header add Access-Control-Allow-Origin \"' + origin + '\"');\n        if (c('ha-cors-methods')) lines.push('  Header add Access-Control-Allow-Methods \"GET, POST, OPTIONS\"');\n        if (c('ha-cors-headers')) lines.push('  Header add Access-Control-Allow-Headers \"Content-Type, Authorization\"');\n      }\n      lines.push('\u003c/IfModule\u003e');\n    }\n\n    // Gzip\n    if (c('ha-chk-gzip')) {\n      lines.push('');\n      lines.push('# ─── Gzip Compression ───────────────────────────────────');\n      lines.push('\u003cIfModule mod_deflate.c\u003e');\n      if (c('ha-gzip-html')) {\n        lines.push('  AddOutputFilterByType DEFLATE text/html');\n        lines.push('  AddOutputFilterByType DEFLATE text/plain');\n        lines.push('  AddOutputFilterByType DEFLATE text/xml');\n        lines.push('  AddOutputFilterByType DEFLATE application/xml');\n        lines.push('  AddOutputFilterByType DEFLATE application/xhtml+xml');\n      }\n      if (c('ha-gzip-css')) {\n        lines.push('  AddOutputFilterByType DEFLATE text/css');\n        lines.push('  AddOutputFilterByType DEFLATE text/javascript');\n        lines.push('  AddOutputFilterByType DEFLATE application/javascript');\n        lines.push('  AddOutputFilterByType DEFLATE application/x-javascript');\n      }\n      if (c('ha-gzip-fonts')) {\n        lines.push('  AddOutputFilterByType DEFLATE image/svg+xml');\n        lines.push('  AddOutputFilterByType DEFLATE font/ttf');\n        lines.push('  AddOutputFilterByType DEFLATE font/otf');\n      }\n      if (c('ha-gzip-json')) {\n        lines.push('  AddOutputFilterByType DEFLATE application/json');\n        lines.push('  AddOutputFilterByType DEFLATE application/ld+json');\n      }\n      lines.push('\u003c/IfModule\u003e');\n    }\n\n    // Error Pages\n    if (c('ha-chk-errors')) {\n      lines.push('');\n      lines.push('# ─── Custom Error Pages ─────────────────────────────────');\n      var e404 = v('ha-err-404'); if (e404) lines.push('ErrorDocument 404 ' + e404);\n      var e403 = v('ha-err-403'); if (e403) lines.push('ErrorDocument 403 ' + e403);\n      var e500 = v('ha-err-500'); if (e500) lines.push('ErrorDocument 500 ' + e500);\n      var e401 = v('ha-err-401'); if (e401) lines.push('ErrorDocument 401 ' + e401);\n    }\n\n    // Directory Options\n    if (c('ha-chk-directory')) {\n      lines.push('');\n      lines.push('# ─── Directory Options ──────────────────────────────────');\n      if (c('ha-dir-noindex'))     lines.push('Options -Indexes');\n      if (c('ha-dir-followlinks')) lines.push('Options +FollowSymLinks');\n      var charset = v('ha-dir-charset');\n      if (charset) lines.push('AddDefaultCharset ' + charset);\n      if (c('ha-dir-blockdotfiles')) {\n        lines.push('\u003cFilesMatch \"^\\\\.(env|git|htaccess|htpasswd|DS_Store)\"\u003e');\n        lines.push('  Order allow,deny');\n        lines.push('  Deny from all');\n        lines.push('\u003c/FilesMatch\u003e');\n      }\n      if (c('ha-dir-blockphp')) {\n        lines.push('\u003cDirectory \"/wp-content/uploads\"\u003e');\n        lines.push('  \u003cFilesMatch \"\\\\.php$\"\u003e');\n        lines.push('    Order allow,deny');\n        lines.push('    Deny from all');\n        lines.push('  \u003c/FilesMatch\u003e');\n        lines.push('\u003c/Directory\u003e');\n      }\n      if (c('ha-dir-badbots')) {\n        lines.push('\u003cIfModule mod_rewrite.c\u003e');\n        lines.push('  RewriteEngine On');\n        lines.push('  RewriteCond %{HTTP_USER_AGENT} (SemrushBot|AhrefsBot|MJ12bot|DotBot|PetalBot) [NC]');\n        lines.push('  RewriteRule .* - [F,L]');\n        lines.push('\u003c/IfModule\u003e');\n      }\n    }\n\n    // Syntax highlight\n    var code = document.getElementById('ha-preview-code');\n    if (code) {\n      var raw = lines.join('\\n');\n      code.innerHTML = raw.split('\\n').map(function(line) {\n        if (line.startsWith('#')) return '\u003cspan class=\"ht-comment\"\u003e' + hEsc(line) + '\u003c/span\u003e';\n        if (line.startsWith('\u003c') || line.startsWith('\u003c/')) return '\u003cspan class=\"ht-tag\"\u003e' + hEsc(line) + '\u003c/span\u003e';\n        var m = line.match(/^(\\s*\\S+)(.*)/);\n        if (!m) return hEsc(line);\n        return '\u003cspan class=\"ht-directive\"\u003e' + hEsc(m[1]) + '\u003c/span\u003e\u003cspan class=\"ht-value\"\u003e' + hEsc(m[2]) + '\u003c/span\u003e';\n      }).join('\\n');\n      code.setAttribute('data-raw', raw);\n    }\n  }\n  window.haRender = haRender;\n\n  function hEsc(s) {\n    return s.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n  }\n\n  function haCopy() {\n    var code = document.getElementById('ha-preview-code');\n    var raw = code ? code.getAttribute('data-raw') : '';\n    if (!raw) return;\n    var btn = document.getElementById('ha-copy-btn');\n    if (navigator.clipboard) {\n      navigator.clipboard.writeText(raw).then(function() {\n        btn.textContent = 'Copied!';\n        btn.classList.add('ha-copied');\n        setTimeout(function() { btn.textContent = 'Copy'; btn.classList.remove('ha-copied'); }, 2000);\n      });\n    } else {\n      var ta = document.createElement('textarea');\n      ta.value = raw;\n      ta.style.position = 'fixed';\n      ta.style.opacity = '0';\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      btn.textContent = 'Copied!';\n      btn.classList.add('ha-copied');\n      setTimeout(function() { btn.textContent = 'Copy'; btn.classList.remove('ha-copied'); }, 2000);\n    }\n  }\n  window.haCopy = haCopy;\n\n  function haDownload() {\n    var code = document.getElementById('ha-preview-code');\n    var raw = code ? code.getAttribute('data-raw') : '';\n    if (!raw) return;\n    var blob = new Blob([raw], { type: 'text/plain' });\n    var url = URL.createObjectURL(blob);\n    var a = document.createElement('a');\n    a.href = url;\n    a.download = '.htaccess';\n    a.click();\n    URL.revokeObjectURL(url);\n  }\n  window.haDownload = haDownload;\n\n  function haApplyPreset(name) {\n    var ids = ['ha-chk-redirects','ha-chk-security','ha-chk-caching','ha-chk-cors','ha-chk-gzip','ha-chk-errors','ha-chk-directory',\n               'ha-sec-xframe','ha-sec-xcontent','ha-sec-xss','ha-sec-referrer','ha-sec-hsts','ha-sec-csp','ha-sec-serverhide',\n               'ha-cache-images','ha-cache-css','ha-cache-fonts','ha-cache-html','ha-cache-etag',\n               'ha-cors-methods','ha-cors-headers','ha-cors-fonts-only',\n               'ha-gzip-html','ha-gzip-css','ha-gzip-fonts','ha-gzip-json',\n               'ha-dir-noindex','ha-dir-followlinks','ha-dir-blockdotfiles','ha-dir-blockphp','ha-dir-badbots'];\n    function setChk(id, val) { var el = document.getElementById(id); if (el) el.checked = val; }\n    function setVal(id, val) { var el = document.getElementById(id); if (el) el.value = val; }\n    ids.forEach(function(id) { setChk(id, false); });\n    haRedirects = [];\n    if (name === 'reset') { haRedirects = [{ src: '', dst: '' }]; haRenderRedirectList(); haRender(); return; }\n    if (name === 'wordpress') {\n      setChk('ha-chk-security', true); setChk('ha-chk-gzip', true); setChk('ha-chk-caching', true); setChk('ha-chk-directory', true);\n      setChk('ha-sec-xframe', true); setChk('ha-sec-xcontent', true); setChk('ha-sec-xss', true); setChk('ha-sec-serverhide', true);\n      setChk('ha-gzip-html', true); setChk('ha-gzip-css', true); setChk('ha-gzip-fonts', true); setChk('ha-gzip-json', true);\n      setChk('ha-cache-images', true); setChk('ha-cache-css', true); setChk('ha-cache-fonts', true);\n      setChk('ha-dir-noindex', true); setChk('ha-dir-blockdotfiles', true); setChk('ha-dir-blockphp', true);\n    } else if (name === 'static') {\n      setChk('ha-chk-security', true); setChk('ha-chk-gzip', true); setChk('ha-chk-caching', true); setChk('ha-chk-errors', true);\n      setChk('ha-sec-xframe', true); setChk('ha-sec-xcontent', true); setChk('ha-sec-xss', true); setChk('ha-sec-referrer', true); setChk('ha-sec-serverhide', true);\n      setChk('ha-gzip-html', true); setChk('ha-gzip-css', true); setChk('ha-gzip-fonts', true);\n      setChk('ha-cache-images', true); setChk('ha-cache-css', true); setChk('ha-cache-fonts', true);\n      setVal('ha-err-404', '/404.html'); setVal('ha-err-500', '/500.html');\n    } else if (name === 'https') {\n      setChk('ha-chk-redirects', true); setChk('ha-chk-security', true);\n      setChk('ha-sec-hsts', true); setChk('ha-sec-xframe', true); setChk('ha-sec-xcontent', true);\n      haRedirects = [{ src: 'http://example.com', dst: 'https://example.com' }];\n    } else if (name === 'bots') {\n      setChk('ha-chk-directory', true); setChk('ha-dir-badbots', true); setChk('ha-dir-noindex', true); setChk('ha-dir-blockdotfiles', true);\n    } else if (name === 'cache') {\n      setChk('ha-chk-caching', true); setChk('ha-chk-gzip', true);\n      setChk('ha-cache-images', true); setChk('ha-cache-css', true); setChk('ha-cache-fonts', true);\n      setChk('ha-gzip-html', true); setChk('ha-gzip-css', true); setChk('ha-gzip-fonts', true); setChk('ha-gzip-json', true);\n    }\n    // Open relevant sections\n    var secMap = { wordpress: ['security','caching','gzip','directory'], static: ['security','caching','gzip','errors'],\n                   https: ['redirects','security'], bots: ['directory'], cache: ['caching','gzip'] };\n    Object.keys(haOpenSections).forEach(function(k) { haOpenSections[k] = false; });\n    (secMap[name] || []).forEach(function(k) { haOpenSections[k] = true; });\n    Object.keys(haOpenSections).forEach(function(k) {\n      var body = document.getElementById('ha-body-' + k);\n      var chev = document.getElementById('ha-chev-' + k);\n      if (body) body.classList.toggle('ha-hidden', !haOpenSections[k]);\n      if (chev) chev.textContent = haOpenSections[k] ? '▼' : '▶';\n    });\n    haRenderRedirectList();\n    haRender();\n  }\n  window.haApplyPreset = haApplyPreset;\n\n  // Init\n  document.addEventListener('DOMContentLoaded', function() {\n    haRenderRedirectList();\n    haRender();\n  });\n  if (document.readyState !== 'loading') {\n    haRenderRedirectList();\n    haRender();\n  }\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch3 id=\"related-tools\"\u003eRelated Tools\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003eBuild robots.txt → \u003ca href=\"https://productivity-works.com/tools/robots-txt-generator/\"\u003eRobots.txt Generator\u003c/a\u003e\n\u003c/p\u003e","title":".htaccess Generator — Free Online Tool"},{"content":" Age Calculator Date Difference Date of Birth Calculate As Of Calculate Age Reset \u0026lt;!-- Stat Cards --\u0026gt; \u0026lt;div class=\u0026quot;stats-grid\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;stat-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;stat-value\u0026quot; id=\u0026quot;res-months\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;stat-label\u0026quot;\u0026gt;Total Months\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;stat-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;stat-value\u0026quot; id=\u0026quot;res-weeks\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;stat-label\u0026quot;\u0026gt;Total Weeks\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;stat-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;stat-value\u0026quot; id=\u0026quot;res-days\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;stat-label\u0026quot;\u0026gt;Total Days\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;stat-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;stat-value\u0026quot; id=\u0026quot;res-hours\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;stat-label\u0026quot;\u0026gt;Total Hours\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;stat-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;stat-value\u0026quot; id=\u0026quot;res-minutes\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;stat-label\u0026quot;\u0026gt;Total Minutes\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;stat-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;stat-value\u0026quot; id=\u0026quot;res-seconds\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;stat-label\u0026quot;\u0026gt;Total Seconds\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Info Cards Row 1 --\u0026gt; \u0026lt;div class=\u0026quot;info-grid\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;info-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;info-icon\u0026quot;\u0026gt;📅\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;info-title\u0026quot;\u0026gt;Day of Week Born\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;info-value\u0026quot; id=\u0026quot;res-dow\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;info-sub\u0026quot; id=\u0026quot;res-dob-fmt\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;info-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;info-icon\u0026quot;\u0026gt;♈\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;info-title\u0026quot;\u0026gt;Western Zodiac\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;info-value\u0026quot; id=\u0026quot;res-zodiac\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;info-sub\u0026quot; id=\u0026quot;res-zodiac-dates\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;info-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;info-icon\u0026quot;\u0026gt;🐉\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;info-title\u0026quot;\u0026gt;Chinese Zodiac\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;info-value\u0026quot; id=\u0026quot;res-chinese\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;info-sub\u0026quot; id=\u0026quot;res-chinese-year\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;info-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;info-icon\u0026quot;\u0026gt;👥\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;info-title\u0026quot;\u0026gt;Generation\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;info-value\u0026quot; id=\u0026quot;res-gen\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;info-sub\u0026quot; id=\u0026quot;res-gen-range\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Birthday Countdown --\u0026gt; \u0026lt;div class=\u0026quot;countdown-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cd-icon\u0026quot;\u0026gt;🎂\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cd-text\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cd-title\u0026quot;\u0026gt;Next Birthday\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cd-value\u0026quot; id=\u0026quot;res-countdown\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cd-sub\u0026quot; id=\u0026quot;res-next-bday\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Milestones --\u0026gt; \u0026lt;div class=\u0026quot;milestones-section\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;🏁 Life Milestones\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;milestone-list\u0026quot; id=\u0026quot;res-milestones\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Start Date End Date Calculate Difference Reset Total Months Total Weeks Total Hours Total Minutes Total Seconds Weekdays How Old Am I Exactly? Most people know their age in whole years, but your exact age is far more precise than that. Between your last birthday and today, days, hours, and even minutes have accumulated into a meaningful slice of your life. Knowing your exact age down to days and hours matters more than you might think — from legal eligibility thresholds (such as voting, retirement, or pension calculations) to health screenings that are recommended at specific age milestones. Insurance actuaries, medical researchers, and demographers all work with exact ages rather than rounded figures because the difference of even a few months can change which category you fall into.\nBeyond practicality, there is something quietly profound about seeing your life expressed in total days or seconds. 10,000 days is a milestone fewer than 30% of people have consciously noticed passing; one billion seconds occurs around age 31 years and 8 months, and almost nobody celebrates it — even though it is a far rarer number than a simple birthday. Expressing age in multiple units makes time feel tangible in a new way, transforming an abstract number of years into a vivid accumulation of lived moments.\nFun Birthday Facts The most common birthday in the United States is September 9th, likely because it falls about nine months after the winter holiday season. February 29th birthdays (leap day) occur only once every four years, meaning leap-day babies technically have a calendar birthday just once every four years — though they usually celebrate on February 28th or March 1st. You share your birthday with roughly 20 million other people alive today, based on a global population of around 8 billion spread over 365 days. The oldest verified human ever was Jeanne Calment of France, who lived to 122 years and 164 days — that is over 44,700 days of life. One billion seconds is approximately 31.69 years. If you are in your early 30s, your \u0026ldquo;billion-second birthday\u0026rdquo; may be right around the corner. Saturday is the most common day of the week to be born in many Western countries, simply because hospitals historically had more planned deliveries scheduled on weekdays — though the distribution has become more even in recent decades. Related Tools Check your BMI and healthy weight range → BMI Calculator Calculate percentages instantly → Percentage Calculator Convert between units of measurement → Unit Converter Working across time zones? → Timezone Converter — convert times between any cities instantly\n","permalink":"https://productivity-works.com/tools/age-calculator/","summary":"\u003cdiv id=\"age-app\"\u003e\n\u003cstyle\u003e\n#age-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 820px;\n  margin: 0 auto;\n  color: #1f2937;\n}\n\n/* Mode Tabs */\n#age-app .mode-tabs {\n  display: flex;\n  gap: 0;\n  margin-bottom: 24px;\n  border-radius: 10px;\n  overflow: hidden;\n  border: 2px solid #f59e0b;\n}\n#age-app .mode-tab {\n  flex: 1;\n  padding: 12px 8px;\n  background: #fff;\n  border: none;\n  cursor: pointer;\n  font-size: 0.95rem;\n  font-weight: 600;\n  color: #92400e;\n  transition: background 0.2s, color 0.2s;\n  text-align: center;\n}\n#age-app .mode-tab.active {\n  background: #f59e0b;\n  color: #fff;\n}\n#age-app .mode-tab:hover:not(.active) {\n  background: #fef3c7;\n}\n\n/* Input card */\n#age-app .input-card {\n  background: #fffbeb;\n  border: 1px solid #fde68a;\n  border-radius: 14px;\n  padding: 28px 24px;\n  margin-bottom: 24px;\n}\n#age-app .input-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 16px;\n  align-items: end;\n}\n#age-app .input-group {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n}\n#age-app .input-group label {\n  font-size: 0.85rem;\n  font-weight: 600;\n  color: #92400e;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n#age-app .input-group input[type=\"date\"] {\n  padding: 10px 14px;\n  border: 2px solid #fcd34d;\n  border-radius: 8px;\n  font-size: 1rem;\n  background: #fff;\n  color: #1f2937;\n  outline: none;\n  width: 100%;\n  box-sizing: border-box;\n  transition: border-color 0.2s;\n}\n#age-app .input-group input[type=\"date\"]:focus {\n  border-color: #f59e0b;\n}\n#age-app .calc-btn {\n  padding: 12px 28px;\n  background: #f59e0b;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 1rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.2s, transform 0.1s;\n  white-space: nowrap;\n}\n#age-app .calc-btn:hover {\n  background: #d97706;\n  transform: translateY(-1px);\n}\n#age-app .calc-btn:active {\n  transform: translateY(0);\n}\n#age-app .reset-link {\n  font-size: 0.85rem;\n  color: #b45309;\n  cursor: pointer;\n  text-decoration: underline;\n  margin-top: 4px;\n  display: inline-block;\n}\n\n/* Results */\n#age-app .results-section {\n  display: none;\n}\n#age-app .results-section.visible {\n  display: block;\n}\n\n/* Hero age display */\n#age-app .hero-age {\n  background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);\n  border-radius: 14px;\n  padding: 28px 24px;\n  text-align: center;\n  color: #fff;\n  margin-bottom: 20px;\n}\n#age-app .hero-age .age-main {\n  font-size: 3rem;\n  font-weight: 800;\n  line-height: 1.1;\n  margin-bottom: 4px;\n}\n#age-app .hero-age .age-sub {\n  font-size: 1.1rem;\n  opacity: 0.9;\n}\n\n/* Stat cards grid */\n#age-app .stats-grid {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 14px;\n  margin-bottom: 20px;\n}\n#age-app .stat-card {\n  background: #fff;\n  border: 1px solid #fde68a;\n  border-radius: 12px;\n  padding: 18px 14px;\n  text-align: center;\n  transition: box-shadow 0.2s;\n}\n#age-app .stat-card:hover {\n  box-shadow: 0 4px 16px rgba(245,158,11,0.15);\n}\n#age-app .stat-card .stat-value {\n  font-size: 1.5rem;\n  font-weight: 800;\n  color: #d97706;\n  word-break: break-all;\n  line-height: 1.2;\n}\n#age-app .stat-card .stat-label {\n  font-size: 0.8rem;\n  color: #6b7280;\n  margin-top: 4px;\n  font-weight: 500;\n}\n\n/* Info cards (birthday facts, zodiac, etc.) */\n#age-app .info-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 14px;\n  margin-bottom: 20px;\n}\n#age-app .info-card {\n  background: #fff;\n  border: 1px solid #fde68a;\n  border-radius: 12px;\n  padding: 18px 16px;\n}\n#age-app .info-card .info-icon {\n  font-size: 1.6rem;\n  margin-bottom: 8px;\n}\n#age-app .info-card .info-title {\n  font-size: 0.78rem;\n  font-weight: 700;\n  color: #92400e;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  margin-bottom: 4px;\n}\n#age-app .info-card .info-value {\n  font-size: 1.05rem;\n  font-weight: 700;\n  color: #1f2937;\n}\n#age-app .info-card .info-sub {\n  font-size: 0.82rem;\n  color: #6b7280;\n  margin-top: 2px;\n}\n\n/* Birthday countdown */\n#age-app .countdown-card {\n  background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%);\n  border: 2px solid #fcd34d;\n  border-radius: 12px;\n  padding: 18px 16px;\n  display: flex;\n  align-items: center;\n  gap: 16px;\n  margin-bottom: 20px;\n}\n#age-app .countdown-card .cd-icon {\n  font-size: 2.2rem;\n  flex-shrink: 0;\n}\n#age-app .countdown-card .cd-text .cd-title {\n  font-size: 0.82rem;\n  font-weight: 700;\n  color: #92400e;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n}\n#age-app .countdown-card .cd-text .cd-value {\n  font-size: 1.3rem;\n  font-weight: 800;\n  color: #b45309;\n}\n#age-app .countdown-card .cd-text .cd-sub {\n  font-size: 0.82rem;\n  color: #78716c;\n}\n\n/* Milestones */\n#age-app .milestones-section {\n  background: #fff;\n  border: 1px solid #fde68a;\n  border-radius: 14px;\n  padding: 22px 20px;\n  margin-bottom: 20px;\n}\n#age-app .milestones-section h3 {\n  font-size: 1rem;\n  font-weight: 700;\n  color: #92400e;\n  margin: 0 0 16px 0;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n#age-app .milestone-list {\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n}\n#age-app .milestone-item {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  padding: 10px 14px;\n  border-radius: 8px;\n  font-size: 0.9rem;\n}\n#age-app .milestone-item.passed {\n  background: #f0fdf4;\n  border: 1px solid #bbf7d0;\n}\n#age-app .milestone-item.upcoming {\n  background: #fffbeb;\n  border: 1px solid #fde68a;\n}\n#age-app .milestone-item .ms-badge {\n  font-size: 1.2rem;\n  flex-shrink: 0;\n}\n#age-app .milestone-item .ms-body {\n  flex: 1;\n}\n#age-app .milestone-item .ms-label {\n  font-weight: 700;\n  color: #1f2937;\n}\n#age-app .milestone-item .ms-date {\n  font-size: 0.78rem;\n  color: #6b7280;\n}\n#age-app .milestone-item .ms-status {\n  font-size: 0.78rem;\n  font-weight: 700;\n  flex-shrink: 0;\n}\n#age-app .milestone-item.passed .ms-status { color: #16a34a; }\n#age-app .milestone-item.upcoming .ms-status { color: #d97706; }\n\n/* Date diff mode */\n#age-app .diff-result {\n  background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);\n  border-radius: 14px;\n  padding: 28px 24px;\n  text-align: center;\n  color: #fff;\n  margin-bottom: 20px;\n}\n#age-app .diff-result .diff-main {\n  font-size: 2.4rem;\n  font-weight: 800;\n  line-height: 1.1;\n  margin-bottom: 6px;\n}\n#age-app .diff-result .diff-sub {\n  font-size: 1rem;\n  opacity: 0.9;\n}\n#age-app .diff-stats-grid {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 14px;\n  margin-bottom: 20px;\n}\n\n/* Error */\n#age-app .error-msg {\n  background: #fef2f2;\n  border: 1px solid #fecaca;\n  border-radius: 8px;\n  padding: 12px 16px;\n  color: #dc2626;\n  font-size: 0.9rem;\n  margin-top: 12px;\n  display: none;\n}\n#age-app .error-msg.visible {\n  display: block;\n}\n\n/* Responsive */\n@media (max-width: 600px) {\n  #age-app .input-grid {\n    grid-template-columns: 1fr;\n  }\n  #age-app .stats-grid {\n    grid-template-columns: repeat(2, 1fr);\n  }\n  #age-app .info-grid {\n    grid-template-columns: 1fr;\n  }\n  #age-app .diff-stats-grid {\n    grid-template-columns: repeat(2, 1fr);\n  }\n  #age-app .hero-age .age-main {\n    font-size: 2.2rem;\n  }\n}\n@media (max-width: 400px) {\n  #age-app .stats-grid {\n    grid-template-columns: 1fr 1fr;\n  }\n  #age-app .input-card {\n    padding: 18px 14px;\n  }\n}\n\u003c/style\u003e\n\u003c!-- Mode Tabs --\u003e\n\u003cdiv class=\"mode-tabs\"\u003e\n  \u003cbutton class=\"mode-tab active\" onclick=\"ageTool.setMode('age')\" id=\"tab-age\"\u003eAge Calculator\u003c/button\u003e\n  \u003cbutton class=\"mode-tab\" onclick=\"ageTool.setMode('diff')\" id=\"tab-diff\"\u003eDate Difference\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- AGE CALCULATOR MODE --\u003e\n\u003cdiv id=\"mode-age\"\u003e\n  \u003cdiv class=\"input-card\"\u003e\n    \u003cdiv class=\"input-grid\"\u003e\n      \u003cdiv class=\"input-group\"\u003e\n        \u003clabel for=\"dob-input\"\u003eDate of Birth\u003c/label\u003e\n        \u003cinput type=\"date\" id=\"dob-input\" max=\"\" /\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"input-group\"\u003e\n        \u003clabel for=\"asof-input\"\u003eCalculate As Of\u003c/label\u003e\n        \u003cinput type=\"date\" id=\"asof-input\" /\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"input-group\" style=\"align-self:end;\"\u003e\n        \u003cbutton class=\"calc-btn\" onclick=\"ageTool.calcAge()\"\u003eCalculate Age\u003c/button\u003e\n        \u003cspan class=\"reset-link\" onclick=\"ageTool.resetAge()\"\u003eReset\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"error-msg\" id=\"age-error\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"results-section\" id=\"age-results\"\u003e\n    \u003c!-- Hero --\u003e\n    \u003cdiv class=\"hero-age\"\u003e\n      \u003cdiv class=\"age-main\" id=\"res-hero-age\"\u003e\u003c/div\u003e\n      \u003cdiv class=\"age-sub\" id=\"res-hero-sub\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;!-- Stat Cards --\u0026gt;\n\u0026lt;div class=\u0026quot;stats-grid\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;stat-card\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;stat-value\u0026quot; id=\u0026quot;res-months\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;stat-label\u0026quot;\u0026gt;Total Months\u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;stat-card\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;stat-value\u0026quot; id=\u0026quot;res-weeks\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;stat-label\u0026quot;\u0026gt;Total Weeks\u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;stat-card\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;stat-value\u0026quot; id=\u0026quot;res-days\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;stat-label\u0026quot;\u0026gt;Total Days\u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;stat-card\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;stat-value\u0026quot; id=\u0026quot;res-hours\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;stat-label\u0026quot;\u0026gt;Total Hours\u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;stat-card\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;stat-value\u0026quot; id=\u0026quot;res-minutes\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;stat-label\u0026quot;\u0026gt;Total Minutes\u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;stat-card\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;stat-value\u0026quot; id=\u0026quot;res-seconds\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;stat-label\u0026quot;\u0026gt;Total Seconds\u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Info Cards Row 1 --\u0026gt;\n\u0026lt;div class=\u0026quot;info-grid\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;info-card\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;info-icon\u0026quot;\u0026gt;📅\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;info-title\u0026quot;\u0026gt;Day of Week Born\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;info-value\u0026quot; id=\u0026quot;res-dow\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;info-sub\u0026quot; id=\u0026quot;res-dob-fmt\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;info-card\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;info-icon\u0026quot;\u0026gt;♈\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;info-title\u0026quot;\u0026gt;Western Zodiac\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;info-value\u0026quot; id=\u0026quot;res-zodiac\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;info-sub\u0026quot; id=\u0026quot;res-zodiac-dates\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;info-card\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;info-icon\u0026quot;\u0026gt;🐉\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;info-title\u0026quot;\u0026gt;Chinese Zodiac\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;info-value\u0026quot; id=\u0026quot;res-chinese\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;info-sub\u0026quot; id=\u0026quot;res-chinese-year\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;info-card\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;info-icon\u0026quot;\u0026gt;👥\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;info-title\u0026quot;\u0026gt;Generation\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;info-value\u0026quot; id=\u0026quot;res-gen\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;info-sub\u0026quot; id=\u0026quot;res-gen-range\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Birthday Countdown --\u0026gt;\n\u0026lt;div class=\u0026quot;countdown-card\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;cd-icon\u0026quot;\u0026gt;🎂\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cd-text\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;cd-title\u0026quot;\u0026gt;Next Birthday\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;cd-value\u0026quot; id=\u0026quot;res-countdown\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;cd-sub\u0026quot; id=\u0026quot;res-next-bday\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Milestones --\u0026gt;\n\u0026lt;div class=\u0026quot;milestones-section\u0026quot;\u0026gt;\n  \u0026lt;h3\u0026gt;🏁 Life Milestones\u0026lt;/h3\u0026gt;\n  \u0026lt;div class=\u0026quot;milestone-list\u0026quot; id=\u0026quot;res-milestones\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- DATE DIFFERENCE MODE --\u003e\n\u003cdiv id=\"mode-diff\" style=\"display:none;\"\u003e\n  \u003cdiv class=\"input-card\"\u003e\n    \u003cdiv class=\"input-grid\"\u003e\n      \u003cdiv class=\"input-group\"\u003e\n        \u003clabel for=\"diff-start\"\u003eStart Date\u003c/label\u003e\n        \u003cinput type=\"date\" id=\"diff-start\" /\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"input-group\"\u003e\n        \u003clabel for=\"diff-end\"\u003eEnd Date\u003c/label\u003e\n        \u003cinput type=\"date\" id=\"diff-end\" /\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"input-group\" style=\"align-self:end;\"\u003e\n        \u003cbutton class=\"calc-btn\" onclick=\"ageTool.calcDiff()\"\u003eCalculate Difference\u003c/button\u003e\n        \u003cspan class=\"reset-link\" onclick=\"ageTool.resetDiff()\"\u003eReset\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"error-msg\" id=\"diff-error\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"results-section\" id=\"diff-results\"\u003e\n    \u003cdiv class=\"diff-result\"\u003e\n      \u003cdiv class=\"diff-main\" id=\"diff-hero\"\u003e\u003c/div\u003e\n      \u003cdiv class=\"diff-sub\" id=\"diff-hero-sub\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"diff-stats-grid\"\u003e\n      \u003cdiv class=\"stat-card\"\u003e\n        \u003cdiv class=\"stat-value\" id=\"diff-months\"\u003e\u003c/div\u003e\n        \u003cdiv class=\"stat-label\"\u003eTotal Months\u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"stat-card\"\u003e\n        \u003cdiv class=\"stat-value\" id=\"diff-weeks\"\u003e\u003c/div\u003e\n        \u003cdiv class=\"stat-label\"\u003eTotal Weeks\u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"stat-card\"\u003e\n        \u003cdiv class=\"stat-value\" id=\"diff-hours\"\u003e\u003c/div\u003e\n        \u003cdiv class=\"stat-label\"\u003eTotal Hours\u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"stat-card\"\u003e\n        \u003cdiv class=\"stat-value\" id=\"diff-minutes\"\u003e\u003c/div\u003e\n        \u003cdiv class=\"stat-label\"\u003eTotal Minutes\u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"stat-card\"\u003e\n        \u003cdiv class=\"stat-value\" id=\"diff-seconds\"\u003e\u003c/div\u003e\n        \u003cdiv class=\"stat-label\"\u003eTotal Seconds\u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"stat-card\"\u003e\n        \u003cdiv class=\"stat-value\" id=\"diff-workdays\"\u003e\u003c/div\u003e\n        \u003cdiv class=\"stat-label\"\u003eWeekdays\u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var ageTool = window.ageTool = {};\n\n  // ---- Helpers ----\n  function parseDate(str) {\n    if (!str) return null;\n    var p = str.split('-');\n    return new Date(+p[0], +p[1]-1, +p[2]);\n  }\n  function fmtNum(n) {\n    return n.toLocaleString();\n  }\n  function pad2(n) { return n \u003c 10 ? '0'+n : ''+n; }\n  function toDateStr(d) {\n    return d.getFullYear() + '-' + pad2(d.getMonth()+1) + '-' + pad2(d.getDate());\n  }\n\n  var DAYS = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];\n  var MONTHS_LONG = ['January','February','March','April','May','June','July','August','September','October','November','December'];\n\n  function fmtDateLong(d) {\n    return MONTHS_LONG[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear();\n  }\n\n  // ---- Zodiac ----\n  var ZODIAC = [\n    { name: 'Capricorn', symbol: '♑', start: [1,1],  end: [1,19],  dates: 'Dec 22 – Jan 19' },\n    { name: 'Aquarius',  symbol: '♒', start: [1,20], end: [2,18],  dates: 'Jan 20 – Feb 18' },\n    { name: 'Pisces',    symbol: '♓', start: [2,19], end: [3,20],  dates: 'Feb 19 – Mar 20' },\n    { name: 'Aries',     symbol: '♈', start: [3,21], end: [4,19],  dates: 'Mar 21 – Apr 19' },\n    { name: 'Taurus',    symbol: '♉', start: [4,20], end: [5,20],  dates: 'Apr 20 – May 20' },\n    { name: 'Gemini',    symbol: '♊', start: [5,21], end: [6,20],  dates: 'May 21 – Jun 20' },\n    { name: 'Cancer',    symbol: '♋', start: [6,21], end: [7,22],  dates: 'Jun 21 – Jul 22' },\n    { name: 'Leo',       symbol: '♌', start: [7,23], end: [8,22],  dates: 'Jul 23 – Aug 22' },\n    { name: 'Virgo',     symbol: '♍', start: [8,23], end: [9,22],  dates: 'Aug 23 – Sep 22' },\n    { name: 'Libra',     symbol: '♎', start: [9,23], end: [10,22], dates: 'Sep 23 – Oct 22' },\n    { name: 'Scorpio',   symbol: '♏', start: [10,23],end: [11,21], dates: 'Oct 23 – Nov 21' },\n    { name: 'Sagittarius',symbol:'♐', start: [11,22],end: [12,21], dates: 'Nov 22 – Dec 21' },\n    { name: 'Capricorn', symbol: '♑', start: [12,22],end: [12,31], dates: 'Dec 22 – Jan 19' }\n  ];\n  function getZodiac(d) {\n    var m = d.getMonth()+1, day = d.getDate();\n    for (var i=0; i\u003cZODIAC.length; i++) {\n      var z = ZODIAC[i];\n      if ((m === z.start[0] \u0026\u0026 day \u003e= z.start[1]) || (m === z.end[0] \u0026\u0026 day \u003c= z.end[1])) {\n        return z;\n      }\n    }\n    return ZODIAC[0];\n  }\n\n  // ---- Chinese Zodiac ----\n  var CZ_ANIMALS = ['Rat','Ox','Tiger','Rabbit','Dragon','Snake','Horse','Goat','Monkey','Rooster','Dog','Pig'];\n  var CZ_EMOJI   = ['🐀','🐂','🐅','🐇','🐉','🐍','🐎','🐑','🐒','🐓','🐕','🐖'];\n  function getChineseZodiac(year) {\n    var idx = (year - 1900) % 12;\n    if (idx \u003c 0) idx += 12;\n    return { animal: CZ_ANIMALS[idx], emoji: CZ_EMOJI[idx] };\n  }\n\n  // ---- Generation ----\n  var GENERATIONS = [\n    { name: 'Generation Alpha', range: '2013–present', min: 2013, max: 2030 },\n    { name: 'Generation Z',     range: '1997–2012',    min: 1997, max: 2012 },\n    { name: 'Millennial',       range: '1981–1996',    min: 1981, max: 1996 },\n    { name: 'Generation X',     range: '1965–1980',    min: 1965, max: 1980 },\n    { name: 'Baby Boomer',      range: '1946–1964',    min: 1946, max: 1964 },\n    { name: 'Silent Generation',range: '1928–1945',    min: 1928, max: 1945 },\n    { name: 'Greatest Generation',range:'1901–1927',   min: 1901, max: 1927 }\n  ];\n  function getGeneration(year) {\n    for (var i=0; i\u003cGENERATIONS.length; i++) {\n      if (year \u003e= GENERATIONS[i].min \u0026\u0026 year \u003c= GENERATIONS[i].max) return GENERATIONS[i];\n    }\n    return { name: 'Unknown', range: '' };\n  }\n\n  // ---- Milestones ----\n  function getMilestones(dob, asof) {\n    var items = [\n      { label: '1,000 days old',       days: 1000 },\n      { label: '10,000 hours lived',    days: Math.ceil(10000/24) },\n      { label: '10,000 days old',       days: 10000 },\n      { label: '100,000,000 seconds',   days: Math.ceil(100000000/86400) },\n      { label: '1 billion seconds',     days: Math.ceil(1000000000/86400) },\n      { label: '2 billion seconds',     days: Math.ceil(2000000000/86400) },\n      { label: '3 billion seconds',     days: Math.ceil(3000000000/86400) },\n      { label: '100 months old',        days: Math.ceil(100*30.4375) },\n      { label: '20,000 days old',       days: 20000 },\n      { label: '500 months old',        days: Math.ceil(500*30.4375) },\n      { label: '30,000 days old',       days: 30000 },\n    ];\n    var result = [];\n    items.forEach(function(m) {\n      var mDate = new Date(dob.getTime() + m.days * 86400000);\n      var passed = mDate \u003c= asof;\n      var diffDays = Math.round(Math.abs(mDate - asof) / 86400000);\n      result.push({\n        label: m.label,\n        date: mDate,\n        passed: passed,\n        diffDays: diffDays\n      });\n    });\n    // Sort: passed desc by date, upcoming asc\n    var passed = result.filter(function(x){ return x.passed; }).sort(function(a,b){ return b.date-a.date; });\n    var upcoming = result.filter(function(x){ return !x.passed; }).sort(function(a,b){ return a.date-b.date; });\n    return passed.concat(upcoming);\n  }\n\n  // ---- Diff between two dates (years/months/days) ----\n  function calcYMD(from, to) {\n    var y = to.getFullYear() - from.getFullYear();\n    var m = to.getMonth() - from.getMonth();\n    var d = to.getDate() - from.getDate();\n    if (d \u003c 0) { m--; var prevMonth = new Date(to.getFullYear(), to.getMonth(), 0); d += prevMonth.getDate(); }\n    if (m \u003c 0) { y--; m += 12; }\n    return { years: y, months: m, days: d };\n  }\n\n  // ---- Count weekdays ----\n  function countWeekdays(from, to) {\n    var start = new Date(Math.min(from, to));\n    var end   = new Date(Math.max(from, to));\n    var count = 0;\n    var cur = new Date(start);\n    while (cur \u003c= end) {\n      var dow = cur.getDay();\n      if (dow !== 0 \u0026\u0026 dow !== 6) count++;\n      cur.setDate(cur.getDate() + 1);\n    }\n    return count;\n  }\n\n  // ---- UI helpers ----\n  function showError(id, msg) {\n    var el = document.getElementById(id);\n    el.textContent = msg;\n    el.classList.add('visible');\n  }\n  function hideError(id) {\n    var el = document.getElementById(id);\n    el.textContent = '';\n    el.classList.remove('visible');\n  }\n  function setText(id, val) {\n    var el = document.getElementById(id);\n    if (el) el.textContent = val;\n  }\n\n  // ---- Mode switching ----\n  ageTool.setMode = function(mode) {\n    document.getElementById('mode-age').style.display = mode === 'age' ? '' : 'none';\n    document.getElementById('mode-diff').style.display = mode === 'diff' ? '' : 'none';\n    document.getElementById('tab-age').classList.toggle('active', mode === 'age');\n    document.getElementById('tab-diff').classList.toggle('active', mode === 'diff');\n  };\n\n  // ---- Age Calculator ----\n  ageTool.calcAge = function() {\n    hideError('age-error');\n    var dobStr = document.getElementById('dob-input').value;\n    var asofStr = document.getElementById('asof-input').value;\n    if (!dobStr) { showError('age-error', 'Please enter your date of birth.'); return; }\n    if (!asofStr) { showError('age-error', 'Please enter the \"as of\" date.'); return; }\n\n    var dob = parseDate(dobStr);\n    var asof = parseDate(asofStr);\n\n    if (dob \u003e asof) { showError('age-error', 'Date of birth cannot be after the \"Calculate As Of\" date.'); return; }\n\n    var ymd = calcYMD(dob, asof);\n    var totalDays = Math.floor((asof - dob) / 86400000);\n    var totalWeeks = Math.floor(totalDays / 7);\n    var totalMonths = ymd.years * 12 + ymd.months;\n    var totalHours = totalDays * 24;\n    var totalMinutes = totalHours * 60;\n    var totalSeconds = totalMinutes * 60;\n\n    // Hero\n    var heroText = ymd.years + ' years, ' + ymd.months + ' months \u0026 ' + ymd.days + ' days';\n    setText('res-hero-age', heroText);\n    setText('res-hero-sub', 'Born ' + fmtDateLong(dob) + ' — As of ' + fmtDateLong(asof));\n\n    // Stats\n    setText('res-months',  fmtNum(totalMonths));\n    setText('res-weeks',   fmtNum(totalWeeks));\n    setText('res-days',    fmtNum(totalDays));\n    setText('res-hours',   fmtNum(totalHours));\n    setText('res-minutes', fmtNum(totalMinutes));\n    setText('res-seconds', fmtNum(totalSeconds));\n\n    // Day of week\n    setText('res-dow',     'You were born on a ' + DAYS[dob.getDay()]);\n    setText('res-dob-fmt', fmtDateLong(dob));\n\n    // Zodiac\n    var z = getZodiac(dob);\n    setText('res-zodiac',       z.symbol + ' ' + z.name);\n    setText('res-zodiac-dates', z.dates);\n\n    // Chinese zodiac\n    var cz = getChineseZodiac(dob.getFullYear());\n    setText('res-chinese',      cz.emoji + ' ' + cz.animal);\n    setText('res-chinese-year', 'Year of the ' + cz.animal + ' (' + dob.getFullYear() + ')');\n\n    // Generation\n    var gen = getGeneration(dob.getFullYear());\n    setText('res-gen',       gen.name);\n    setText('res-gen-range', 'Born ' + gen.range);\n\n    // Next birthday countdown\n    var nextBday = new Date(asof.getFullYear(), dob.getMonth(), dob.getDate());\n    if (nextBday \u003c= asof) nextBday.setFullYear(nextBday.getFullYear() + 1);\n    var daysUntil = Math.ceil((nextBday - asof) / 86400000);\n    if (daysUntil === 0) {\n      setText('res-countdown', 'Today is your birthday!');\n      setText('res-next-bday', 'Happy Birthday!');\n    } else {\n      setText('res-countdown', daysUntil + (daysUntil === 1 ? ' day' : ' days') + ' until your birthday');\n      setText('res-next-bday', 'Next birthday: ' + fmtDateLong(nextBday));\n    }\n\n    // Milestones\n    var ms = getMilestones(dob, asof);\n    var msHtml = '';\n    ms.forEach(function(m) {\n      var cls = m.passed ? 'passed' : 'upcoming';\n      var badge = m.passed ? '✅' : '⏳';\n      var statusText = m.passed\n        ? (m.diffDays === 0 ? 'Today!' : m.diffDays + ' days ago')\n        : 'in ' + fmtNum(m.diffDays) + ' days';\n      msHtml += '\u003cdiv class=\"milestone-item ' + cls + '\"\u003e' +\n        '\u003cspan class=\"ms-badge\"\u003e' + badge + '\u003c/span\u003e' +\n        '\u003cdiv class=\"ms-body\"\u003e' +\n          '\u003cdiv class=\"ms-label\"\u003e' + m.label + '\u003c/div\u003e' +\n          '\u003cdiv class=\"ms-date\"\u003e' + fmtDateLong(m.date) + '\u003c/div\u003e' +\n        '\u003c/div\u003e' +\n        '\u003cspan class=\"ms-status\"\u003e' + statusText + '\u003c/span\u003e' +\n      '\u003c/div\u003e';\n    });\n    document.getElementById('res-milestones').innerHTML = msHtml;\n\n    document.getElementById('age-results').classList.add('visible');\n  };\n\n  ageTool.resetAge = function() {\n    document.getElementById('dob-input').value = '';\n    document.getElementById('asof-input').value = '';\n    document.getElementById('age-results').classList.remove('visible');\n    hideError('age-error');\n  };\n\n  // ---- Date Difference Calculator ----\n  ageTool.calcDiff = function() {\n    hideError('diff-error');\n    var startStr = document.getElementById('diff-start').value;\n    var endStr   = document.getElementById('diff-end').value;\n    if (!startStr) { showError('diff-error', 'Please select a start date.'); return; }\n    if (!endStr)   { showError('diff-error', 'Please select an end date.'); return; }\n\n    var start = parseDate(startStr);\n    var end   = parseDate(endStr);\n\n    var earlier = start \u003c= end ? start : end;\n    var later   = start \u003c= end ? end   : start;\n    var direction = start \u003c= end ? '' : '(reversed) ';\n\n    var totalDays = Math.floor((later - earlier) / 86400000);\n    var totalWeeks = Math.floor(totalDays / 7);\n    var remainDays = totalDays % 7;\n    var ymd = calcYMD(earlier, later);\n    var totalMonths = ymd.years * 12 + ymd.months;\n    var totalHours   = totalDays * 24;\n    var totalMinutes = totalHours * 60;\n    var totalSeconds = totalMinutes * 60;\n    var weekdays = countWeekdays(earlier, later);\n\n    // Hero\n    var heroParts = [];\n    if (ymd.years \u003e 0)   heroParts.push(ymd.years + (ymd.years === 1 ? ' year' : ' years'));\n    if (ymd.months \u003e 0)  heroParts.push(ymd.months + (ymd.months === 1 ? ' month' : ' months'));\n    if (ymd.days \u003e 0)    heroParts.push(ymd.days + (ymd.days === 1 ? ' day' : ' days'));\n    if (heroParts.length === 0) heroParts.push('Same day');\n\n    setText('diff-hero', heroParts.join(', '));\n    setText('diff-hero-sub', direction + fmtDateLong(earlier) + '  →  ' + fmtDateLong(later));\n\n    setText('diff-months',   fmtNum(totalMonths));\n    setText('diff-weeks',    fmtNum(totalWeeks) + (remainDays \u003e 0 ? ' w ' + remainDays + 'd' : ''));\n    setText('diff-hours',    fmtNum(totalHours));\n    setText('diff-minutes',  fmtNum(totalMinutes));\n    setText('diff-seconds',  fmtNum(totalSeconds));\n    setText('diff-workdays', fmtNum(weekdays));\n\n    document.getElementById('diff-results').classList.add('visible');\n  };\n\n  ageTool.resetDiff = function() {\n    document.getElementById('diff-start').value = '';\n    document.getElementById('diff-end').value = '';\n    document.getElementById('diff-results').classList.remove('visible');\n    hideError('diff-error');\n  };\n\n  // ---- Init: set default dates ----\n  (function init() {\n    var today = new Date();\n    var todayStr = toDateStr(today);\n    var maxEl = document.getElementById('dob-input');\n    if (maxEl) maxEl.setAttribute('max', todayStr);\n    var asofEl = document.getElementById('asof-input');\n    if (asofEl) asofEl.value = todayStr;\n    var diffEnd = document.getElementById('diff-end');\n    if (diffEnd) diffEnd.value = todayStr;\n  })();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-old-am-i-exactly\"\u003eHow Old Am I Exactly?\u003c/h2\u003e\n\u003cp\u003eMost people know their age in whole years, but your exact age is far more precise than that. Between your last birthday and today, days, hours, and even minutes have accumulated into a meaningful slice of your life. Knowing your exact age down to days and hours matters more than you might think — from legal eligibility thresholds (such as voting, retirement, or pension calculations) to health screenings that are recommended at specific age milestones. Insurance actuaries, medical researchers, and demographers all work with exact ages rather than rounded figures because the difference of even a few months can change which category you fall into.\u003c/p\u003e","title":"Age Calculator - Free Online Age \u0026 Birthday Tool"},{"content":" This tool generates code only. No actual API requests are sent from your browser. Request GET POST PUT PATCH DELETE Headers Query Params Body Auth Header Name Value + Add Header Parameter Value + Add Parameter None JSON Form Data (URL-encoded) Raw Text {\\n \"name\": \"John Doe\",\\n \"email\": \"john@example.com\"\\n} Field Value + Add Field No Auth Bearer Token Basic Auth API Key cURL JavaScript - Fetch JavaScript - Axios Python - requests PHP - cURL Generate Code cURL Copy Click \"Generate Code\" to see the output here. Related Free Tools JSON Formatter \u0026 Validator Base64 Encoder / Decoder URL Encoder / Decoder JWT Decoder Regex Tester Related Tools Dns Record Guide Http Status Codes Ip Address Calculator ","permalink":"https://productivity-works.com/tools/api-request-builder/","summary":"\u003cdiv id=\"arb-app\"\u003e\n\u003cstyle\u003e\n#arb-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 960px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#arb-app * { box-sizing: border-box; }\n#arb-app h2 {\n  font-size: 1.4rem;\n  margin: 0 0 1rem;\n  color: #0f3460;\n  border-bottom: 2px solid #e2e8f0;\n  padding-bottom: 0.5rem;\n}\n#arb-app .arb-notice {\n  background: #eff6ff;\n  border: 1px solid #bfdbfe;\n  border-radius: 8px;\n  padding: 0.75rem 1rem;\n  font-size: 0.85rem;\n  color: #1d4ed8;\n  margin-bottom: 1.5rem;\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n}\n#arb-app .arb-notice::before { content: \"ℹ\"; font-weight: bold; font-size: 1rem; }\n#arb-app .arb-card {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 1.25rem 1.5rem;\n  margin-bottom: 1.25rem;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.05);\n}\n#arb-app .arb-row {\n  display: flex;\n  gap: 0.75rem;\n  align-items: stretch;\n  margin-bottom: 0.75rem;\n}\n#arb-app .arb-method-select {\n  padding: 0.6rem 0.75rem;\n  border: 2px solid #e2e8f0;\n  border-radius: 7px;\n  font-size: 0.95rem;\n  font-weight: 700;\n  cursor: pointer;\n  background: #f8fafc;\n  color: #0f3460;\n  min-width: 110px;\n  appearance: none;\n  text-align: center;\n}\n#arb-app .arb-method-select:focus { outline: none; border-color: #3b82f6; }\n#arb-app select[data-method=\"GET\"] { color: #16a34a; border-color: #bbf7d0; background: #f0fdf4; }\n#arb-app select[data-method=\"POST\"] { color: #ca8a04; border-color: #fde68a; background: #fefce8; }\n#arb-app select[data-method=\"PUT\"] { color: #d97706; border-color: #fed7aa; background: #fff7ed; }\n#arb-app select[data-method=\"PATCH\"] { color: #7c3aed; border-color: #ddd6fe; background: #faf5ff; }\n#arb-app select[data-method=\"DELETE\"] { color: #dc2626; border-color: #fecaca; background: #fff5f5; }\n#arb-app .arb-url-input {\n  flex: 1;\n  padding: 0.6rem 0.9rem;\n  border: 2px solid #e2e8f0;\n  border-radius: 7px;\n  font-size: 0.95rem;\n  font-family: 'Courier New', monospace;\n  transition: border-color 0.2s;\n}\n#arb-app .arb-url-input:focus { outline: none; border-color: #3b82f6; }\n#arb-app .arb-btn {\n  padding: 0.6rem 1.1rem;\n  border: none;\n  border-radius: 7px;\n  font-size: 0.9rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: opacity 0.15s, transform 0.1s;\n  white-space: nowrap;\n}\n#arb-app .arb-btn:active { transform: scale(0.97); }\n#arb-app .arb-btn-primary { background: #3b82f6; color: #fff; }\n#arb-app .arb-btn-primary:hover { background: #2563eb; }\n#arb-app .arb-btn-secondary { background: #f1f5f9; color: #475569; border: 1px solid #e2e8f0; }\n#arb-app .arb-btn-secondary:hover { background: #e2e8f0; }\n#arb-app .arb-btn-danger { background: #fee2e2; color: #dc2626; font-size: 0.8rem; padding: 0.35rem 0.6rem; }\n#arb-app .arb-btn-danger:hover { background: #fecaca; }\n#arb-app .arb-btn-add { background: #f0fdf4; color: #16a34a; border: 1px dashed #86efac; font-size: 0.85rem; }\n#arb-app .arb-btn-add:hover { background: #dcfce7; }\n#arb-app .arb-tabs {\n  display: flex;\n  gap: 0;\n  border-bottom: 2px solid #e2e8f0;\n  margin-bottom: 1rem;\n  overflow-x: auto;\n}\n#arb-app .arb-tab {\n  padding: 0.5rem 1rem;\n  font-size: 0.88rem;\n  font-weight: 600;\n  cursor: pointer;\n  color: #64748b;\n  border-bottom: 3px solid transparent;\n  margin-bottom: -2px;\n  white-space: nowrap;\n  transition: color 0.15s;\n  background: none;\n  border-top: none;\n  border-left: none;\n  border-right: none;\n}\n#arb-app .arb-tab:hover { color: #3b82f6; }\n#arb-app .arb-tab.active { color: #3b82f6; border-bottom-color: #3b82f6; }\n#arb-app .arb-tab-content { display: none; }\n#arb-app .arb-tab-content.active { display: block; }\n#arb-app .arb-kv-table { width: 100%; border-collapse: collapse; }\n#arb-app .arb-kv-table th {\n  text-align: left;\n  font-size: 0.78rem;\n  font-weight: 700;\n  color: #94a3b8;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  padding: 0.3rem 0.5rem 0.6rem;\n}\n#arb-app .arb-kv-table td { padding: 0.3rem 0.4rem; vertical-align: middle; }\n#arb-app .arb-kv-input {\n  width: 100%;\n  padding: 0.45rem 0.65rem;\n  border: 1px solid #e2e8f0;\n  border-radius: 6px;\n  font-size: 0.88rem;\n  font-family: 'Courier New', monospace;\n  background: #f8fafc;\n  transition: border-color 0.2s;\n}\n#arb-app .arb-kv-input:focus { outline: none; border-color: #93c5fd; background: #fff; }\n#arb-app .arb-kv-enable {\n  width: 16px; height: 16px; cursor: pointer; accent-color: #3b82f6;\n}\n#arb-app .arb-kv-td-check { width: 28px; text-align: center; }\n#arb-app .arb-kv-td-del { width: 36px; text-align: center; }\n#arb-app .arb-kv-td-key { width: 38%; }\n#arb-app .arb-kv-td-val { }\n#arb-app .arb-body-select {\n  padding: 0.45rem 0.75rem;\n  border: 1px solid #e2e8f0;\n  border-radius: 6px;\n  font-size: 0.88rem;\n  background: #f8fafc;\n  margin-bottom: 0.75rem;\n  cursor: pointer;\n}\n#arb-app .arb-body-select:focus { outline: none; border-color: #93c5fd; }\n#arb-app .arb-body-editor {\n  width: 100%;\n  min-height: 140px;\n  padding: 0.75rem;\n  border: 1px solid #e2e8f0;\n  border-radius: 7px;\n  font-family: 'Courier New', monospace;\n  font-size: 0.875rem;\n  line-height: 1.6;\n  resize: vertical;\n  background: #1e1e2e;\n  color: #cdd6f4;\n}\n#arb-app .arb-body-editor:focus { outline: none; border-color: #93c5fd; }\n#arb-app .arb-auth-select {\n  padding: 0.45rem 0.75rem;\n  border: 1px solid #e2e8f0;\n  border-radius: 6px;\n  font-size: 0.88rem;\n  background: #f8fafc;\n  margin-bottom: 0.75rem;\n  cursor: pointer;\n  min-width: 200px;\n}\n#arb-app .arb-auth-select:focus { outline: none; border-color: #93c5fd; }\n#arb-app .arb-auth-fields { display: flex; flex-direction: column; gap: 0.6rem; }\n#arb-app .arb-auth-field-row { display: flex; align-items: center; gap: 0.75rem; }\n#arb-app .arb-auth-label { font-size: 0.85rem; font-weight: 600; color: #475569; min-width: 110px; }\n#arb-app .arb-auth-input {\n  flex: 1;\n  padding: 0.45rem 0.65rem;\n  border: 1px solid #e2e8f0;\n  border-radius: 6px;\n  font-size: 0.88rem;\n  font-family: 'Courier New', monospace;\n  background: #f8fafc;\n}\n#arb-app .arb-auth-input:focus { outline: none; border-color: #93c5fd; background: #fff; }\n#arb-app .arb-generate-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.75rem;\n  align-items: center;\n  margin-bottom: 1.25rem;\n}\n#arb-app .arb-lang-select {\n  padding: 0.6rem 0.9rem;\n  border: 2px solid #3b82f6;\n  border-radius: 7px;\n  font-size: 0.92rem;\n  font-weight: 600;\n  background: #eff6ff;\n  color: #1d4ed8;\n  cursor: pointer;\n  min-width: 200px;\n}\n#arb-app .arb-lang-select:focus { outline: none; }\n#arb-app .arb-output-wrap {\n  position: relative;\n  background: #1e1e2e;\n  border-radius: 10px;\n  overflow: hidden;\n  border: 1px solid #313244;\n}\n#arb-app .arb-output-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  padding: 0.65rem 1rem;\n  background: #181825;\n  border-bottom: 1px solid #313244;\n}\n#arb-app .arb-output-lang-badge {\n  font-size: 0.78rem;\n  font-weight: 700;\n  color: #89b4fa;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n}\n#arb-app .arb-copy-btn {\n  padding: 0.35rem 0.85rem;\n  background: #313244;\n  color: #cdd6f4;\n  border: none;\n  border-radius: 5px;\n  font-size: 0.82rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n#arb-app .arb-copy-btn:hover { background: #45475a; }\n#arb-app .arb-copy-btn.copied { background: #1e3a2f; color: #a6e3a1; }\n#arb-app .arb-code-block {\n  padding: 1.25rem 1.5rem;\n  overflow-x: auto;\n  margin: 0;\n}\n#arb-app .arb-code-block code {\n  font-family: 'Courier New', Consolas, monospace;\n  font-size: 0.875rem;\n  line-height: 1.7;\n  white-space: pre;\n  display: block;\n  color: #cdd6f4;\n}\n/* Syntax highlight tokens */\n#arb-app .t-kw { color: #cba6f7; }\n#arb-app .t-str { color: #a6e3a1; }\n#arb-app .t-num { color: #fab387; }\n#arb-app .t-fn { color: #89b4fa; }\n#arb-app .t-cm { color: #6c7086; font-style: italic; }\n#arb-app .t-op { color: #89dceb; }\n#arb-app .t-flag { color: #f38ba8; }\n#arb-app .t-url { color: #f9e2af; }\n#arb-app .t-prop { color: #89dceb; }\n#arb-app .arb-related {\n  margin-top: 2rem;\n  padding: 1.25rem 1.5rem;\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n}\n#arb-app .arb-related h3 { font-size: 1rem; color: #0f3460; margin: 0 0 0.75rem; }\n#arb-app .arb-related ul { margin: 0; padding: 0 0 0 1.25rem; }\n#arb-app .arb-related li { margin-bottom: 0.4rem; font-size: 0.9rem; }\n#arb-app .arb-related a { color: #3b82f6; text-decoration: none; }\n#arb-app .arb-related a:hover { text-decoration: underline; }\n@media (max-width: 600px) {\n  #arb-app .arb-row { flex-direction: column; }\n  #arb-app .arb-generate-row { flex-direction: column; align-items: stretch; }\n  #arb-app .arb-lang-select { min-width: unset; }\n}\n\u003c/style\u003e\n\u003cdiv class=\"arb-notice\"\u003eThis tool generates code only. No actual API requests are sent from your browser.\u003c/div\u003e\n\u003c!-- Request Line --\u003e\n\u003cdiv class=\"arb-card\"\u003e\n  \u003ch2\u003eRequest\u003c/h2\u003e\n  \u003cdiv class=\"arb-row\"\u003e\n    \u003cselect class=\"arb-method-select\" id=\"arb-method\" onchange=\"arbMethodColor()\"\u003e\n      \u003coption value=\"GET\"\u003eGET\u003c/option\u003e\n      \u003coption value=\"POST\"\u003ePOST\u003c/option\u003e\n      \u003coption value=\"PUT\"\u003ePUT\u003c/option\u003e\n      \u003coption value=\"PATCH\"\u003ePATCH\u003c/option\u003e\n      \u003coption value=\"DELETE\"\u003eDELETE\u003c/option\u003e\n    \u003c/select\u003e\n    \u003cinput type=\"url\" class=\"arb-url-input\" id=\"arb-url\" placeholder=\"https://api.example.com/endpoint\" value=\"https://api.example.com/users\" /\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Tabs: Headers / Params / Body / Auth --\u003e\n\u003cdiv class=\"arb-card\"\u003e\n  \u003cdiv class=\"arb-tabs\"\u003e\n    \u003cbutton class=\"arb-tab active\" onclick=\"arbTab('headers',this)\"\u003eHeaders\u003c/button\u003e\n    \u003cbutton class=\"arb-tab\" onclick=\"arbTab('params',this)\"\u003eQuery Params\u003c/button\u003e\n    \u003cbutton class=\"arb-tab\" onclick=\"arbTab('body',this)\"\u003eBody\u003c/button\u003e\n    \u003cbutton class=\"arb-tab\" onclick=\"arbTab('auth',this)\"\u003eAuth\u003c/button\u003e\n  \u003c/div\u003e\n  \u003c!-- Headers --\u003e\n  \u003cdiv class=\"arb-tab-content active\" id=\"arb-tab-headers\"\u003e\n    \u003ctable class=\"arb-kv-table\" id=\"arb-headers-table\"\u003e\n      \u003cthead\u003e\u003ctr\u003e\n        \u003cth class=\"arb-kv-td-check\"\u003e\u003c/th\u003e\n        \u003cth class=\"arb-kv-td-key\"\u003eHeader Name\u003c/th\u003e\n        \u003cth\u003eValue\u003c/th\u003e\n        \u003cth class=\"arb-kv-td-del\"\u003e\u003c/th\u003e\n      \u003c/tr\u003e\u003c/thead\u003e\n      \u003ctbody id=\"arb-headers-body\"\u003e\u003c/tbody\u003e\n    \u003c/table\u003e\n    \u003cbutton class=\"arb-btn arb-btn-add\" style=\"margin-top:0.6rem\" onclick=\"arbAddRow('headers')\"\u003e+ Add Header\u003c/button\u003e\n  \u003c/div\u003e\n  \u003c!-- Query Params --\u003e\n  \u003cdiv class=\"arb-tab-content\" id=\"arb-tab-params\"\u003e\n    \u003ctable class=\"arb-kv-table\" id=\"arb-params-table\"\u003e\n      \u003cthead\u003e\u003ctr\u003e\n        \u003cth class=\"arb-kv-td-check\"\u003e\u003c/th\u003e\n        \u003cth class=\"arb-kv-td-key\"\u003eParameter\u003c/th\u003e\n        \u003cth\u003eValue\u003c/th\u003e\n        \u003cth class=\"arb-kv-td-del\"\u003e\u003c/th\u003e\n      \u003c/tr\u003e\u003c/thead\u003e\n      \u003ctbody id=\"arb-params-body\"\u003e\u003c/tbody\u003e\n    \u003c/table\u003e\n    \u003cbutton class=\"arb-btn arb-btn-add\" style=\"margin-top:0.6rem\" onclick=\"arbAddRow('params')\"\u003e+ Add Parameter\u003c/button\u003e\n  \u003c/div\u003e\n  \u003c!-- Body --\u003e\n  \u003cdiv class=\"arb-tab-content\" id=\"arb-tab-body\"\u003e\n    \u003cselect class=\"arb-body-select\" id=\"arb-body-type\" onchange=\"arbBodyTypeChange()\"\u003e\n      \u003coption value=\"none\"\u003eNone\u003c/option\u003e\n      \u003coption value=\"json\" selected\u003eJSON\u003c/option\u003e\n      \u003coption value=\"form\"\u003eForm Data (URL-encoded)\u003c/option\u003e\n      \u003coption value=\"raw\"\u003eRaw Text\u003c/option\u003e\n    \u003c/select\u003e\n    \u003cdiv id=\"arb-body-editor-wrap\"\u003e\n      \u003ctextarea class=\"arb-body-editor\" id=\"arb-body-editor\" placeholder='{\\n  \"key\": \"value\"\\n}'\u003e{\\n  \"name\": \"John Doe\",\\n  \"email\": \"john@example.com\"\\n}\u003c/textarea\u003e\n    \u003c/div\u003e\n    \u003cdiv id=\"arb-body-form-wrap\" style=\"display:none\"\u003e\n      \u003ctable class=\"arb-kv-table\"\u003e\n        \u003cthead\u003e\u003ctr\u003e\n          \u003cth class=\"arb-kv-td-check\"\u003e\u003c/th\u003e\n          \u003cth class=\"arb-kv-td-key\"\u003eField\u003c/th\u003e\n          \u003cth\u003eValue\u003c/th\u003e\n          \u003cth class=\"arb-kv-td-del\"\u003e\u003c/th\u003e\n        \u003c/tr\u003e\u003c/thead\u003e\n        \u003ctbody id=\"arb-formdata-body\"\u003e\u003c/tbody\u003e\n      \u003c/table\u003e\n      \u003cbutton class=\"arb-btn arb-btn-add\" style=\"margin-top:0.6rem\" onclick=\"arbAddRow('formdata')\"\u003e+ Add Field\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Auth --\u003e\n  \u003cdiv class=\"arb-tab-content\" id=\"arb-tab-auth\"\u003e\n    \u003cselect class=\"arb-auth-select\" id=\"arb-auth-type\" onchange=\"arbAuthTypeChange()\"\u003e\n      \u003coption value=\"none\"\u003eNo Auth\u003c/option\u003e\n      \u003coption value=\"bearer\"\u003eBearer Token\u003c/option\u003e\n      \u003coption value=\"basic\"\u003eBasic Auth\u003c/option\u003e\n      \u003coption value=\"apikey\"\u003eAPI Key\u003c/option\u003e\n    \u003c/select\u003e\n    \u003cdiv class=\"arb-auth-fields\" id=\"arb-auth-fields\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Generate --\u003e\n\u003cdiv class=\"arb-generate-row\"\u003e\n  \u003cselect class=\"arb-lang-select\" id=\"arb-lang\"\u003e\n    \u003coption value=\"curl\"\u003ecURL\u003c/option\u003e\n    \u003coption value=\"fetch\"\u003eJavaScript - Fetch\u003c/option\u003e\n    \u003coption value=\"axios\"\u003eJavaScript - Axios\u003c/option\u003e\n    \u003coption value=\"python\"\u003ePython - requests\u003c/option\u003e\n    \u003coption value=\"php\"\u003ePHP - cURL\u003c/option\u003e\n  \u003c/select\u003e\n  \u003cbutton class=\"arb-btn arb-btn-primary\" onclick=\"arbGenerate()\" style=\"padding:0.6rem 1.8rem;font-size:1rem\"\u003eGenerate Code\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Output --\u003e\n\u003cdiv class=\"arb-output-wrap\" id=\"arb-output-wrap\"\u003e\n  \u003cdiv class=\"arb-output-header\"\u003e\n    \u003cspan class=\"arb-output-lang-badge\" id=\"arb-output-badge\"\u003ecURL\u003c/span\u003e\n    \u003cbutton class=\"arb-copy-btn\" id=\"arb-copy-btn\" onclick=\"arbCopy()\"\u003eCopy\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cpre class=\"arb-code-block\"\u003e\u003ccode id=\"arb-output-code\"\u003eClick \"Generate Code\" to see the output here.\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cdiv class=\"arb-related\"\u003e\n  \u003ch3\u003eRelated Free Tools\u003c/h3\u003e\n  \u003cul\u003e\n    \u003cli\u003e\u003ca href=\"/tools/json-formatter/\"\u003eJSON Formatter \u0026 Validator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/base64-encoder/\"\u003eBase64 Encoder / Decoder\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/url-encoder/\"\u003eURL Encoder / Decoder\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/jwt-decoder/\"\u003eJWT Decoder\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/regex-tester/\"\u003eRegex Tester\u003c/a\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  // ---- State ----\n  var arbRows = { headers: [], params: [], formdata: [] };\n  var arbIdCounter = 0;\n\n  function arbId() { return 'arbr' + (++arbIdCounter); }\n\n  // ---- Init ----\n  arbAddRow('headers', 'Content-Type', 'application/json');\n  arbAddRow('headers', 'Accept', 'application/json');\n  arbAddRow('params', 'limit', '20');\n  arbAddRow('formdata');\n  arbMethodColor();\n\n  // ---- Method color ----\n  window.arbMethodColor = function() {\n    var sel = document.getElementById('arb-method');\n    sel.setAttribute('data-method', sel.value);\n  };\n\n  // ---- Tab switching ----\n  window.arbTab = function(name, btn) {\n    document.querySelectorAll('#arb-app .arb-tab-content').forEach(function(el){ el.classList.remove('active'); });\n    document.querySelectorAll('#arb-app .arb-tab').forEach(function(el){ el.classList.remove('active'); });\n    document.getElementById('arb-tab-' + name).classList.add('active');\n    btn.classList.add('active');\n  };\n\n  // ---- KV rows ----\n  window.arbAddRow = function(section, key, val) {\n    var id = arbId();\n    arbRows[section].push(id);\n    var tbody = document.getElementById('arb-' + section + '-body');\n    var tr = document.createElement('tr');\n    tr.id = 'arbrow-' + id;\n    tr.innerHTML = '\u003ctd class=\"arb-kv-td-check\"\u003e\u003cinput type=\"checkbox\" class=\"arb-kv-enable\" checked id=\"arbck-' + id + '\"\u003e\u003c/td\u003e' +\n      '\u003ctd class=\"arb-kv-td-key\"\u003e\u003cinput type=\"text\" class=\"arb-kv-input\" id=\"arbk-' + id + '\" placeholder=\"Key\" value=\"' + esc(key||'') + '\"\u003e\u003c/td\u003e' +\n      '\u003ctd\u003e\u003cinput type=\"text\" class=\"arb-kv-input\" id=\"arbv-' + id + '\" placeholder=\"Value\" value=\"' + esc(val||'') + '\"\u003e\u003c/td\u003e' +\n      '\u003ctd class=\"arb-kv-td-del\"\u003e\u003cbutton class=\"arb-btn arb-btn-danger\" onclick=\"arbDelRow(\\'' + section + '\\',\\'' + id + '\\')\"\u003e✕\u003c/button\u003e\u003c/td\u003e';\n    tbody.appendChild(tr);\n  };\n\n  window.arbDelRow = function(section, id) {\n    var row = document.getElementById('arbrow-' + id);\n    if (row) row.parentNode.removeChild(row);\n    arbRows[section] = arbRows[section].filter(function(x){ return x !== id; });\n  };\n\n  function getKVRows(section) {\n    var result = [];\n    arbRows[section].forEach(function(id) {\n      var ck = document.getElementById('arbck-' + id);\n      var k = document.getElementById('arbk-' + id);\n      var v = document.getElementById('arbv-' + id);\n      if (ck \u0026\u0026 ck.checked \u0026\u0026 k \u0026\u0026 k.value.trim()) {\n        result.push([k.value.trim(), v ? v.value : '']);\n      }\n    });\n    return result;\n  }\n\n  // ---- Body type ----\n  window.arbBodyTypeChange = function() {\n    var type = document.getElementById('arb-body-type').value;\n    document.getElementById('arb-body-editor-wrap').style.display = (type === 'json' || type === 'raw') ? '' : 'none';\n    document.getElementById('arb-body-form-wrap').style.display = (type === 'form') ? '' : 'none';\n  };\n\n  // ---- Auth ----\n  window.arbAuthTypeChange = function() {\n    var type = document.getElementById('arb-auth-type').value;\n    var fields = document.getElementById('arb-auth-fields');\n    fields.innerHTML = '';\n    if (type === 'bearer') {\n      fields.innerHTML = '\u003cdiv class=\"arb-auth-field-row\"\u003e\u003cspan class=\"arb-auth-label\"\u003eToken\u003c/span\u003e\u003cinput type=\"text\" class=\"arb-auth-input\" id=\"arb-auth-token\" placeholder=\"your-bearer-token-here\"\u003e\u003c/div\u003e';\n    } else if (type === 'basic') {\n      fields.innerHTML = '\u003cdiv class=\"arb-auth-field-row\"\u003e\u003cspan class=\"arb-auth-label\"\u003eUsername\u003c/span\u003e\u003cinput type=\"text\" class=\"arb-auth-input\" id=\"arb-auth-user\" placeholder=\"username\"\u003e\u003c/div\u003e' +\n        '\u003cdiv class=\"arb-auth-field-row\"\u003e\u003cspan class=\"arb-auth-label\"\u003ePassword\u003c/span\u003e\u003cinput type=\"password\" class=\"arb-auth-input\" id=\"arb-auth-pass\" placeholder=\"password\"\u003e\u003c/div\u003e';\n    } else if (type === 'apikey') {\n      fields.innerHTML = '\u003cdiv class=\"arb-auth-field-row\"\u003e\u003cspan class=\"arb-auth-label\"\u003eKey Name\u003c/span\u003e\u003cinput type=\"text\" class=\"arb-auth-input\" id=\"arb-auth-key-name\" placeholder=\"X-API-Key\"\u003e\u003c/div\u003e' +\n        '\u003cdiv class=\"arb-auth-field-row\"\u003e\u003cspan class=\"arb-auth-label\"\u003eValue\u003c/span\u003e\u003cinput type=\"text\" class=\"arb-auth-input\" id=\"arb-auth-key-val\" placeholder=\"your-api-key-here\"\u003e\u003c/div\u003e' +\n        '\u003cdiv class=\"arb-auth-field-row\"\u003e\u003cspan class=\"arb-auth-label\"\u003eAdd To\u003c/span\u003e\u003cselect class=\"arb-auth-input\" id=\"arb-auth-key-loc\" style=\"font-family:inherit\"\u003e\u003coption value=\"header\"\u003eHeader\u003c/option\u003e\u003coption value=\"query\"\u003eQuery Param\u003c/option\u003e\u003c/select\u003e\u003c/div\u003e';\n    }\n  };\n\n  // ---- Collect request state ----\n  function arbCollect() {\n    var method = document.getElementById('arb-method').value;\n    var url = document.getElementById('arb-url').value.trim() || 'https://api.example.com/endpoint';\n    var headers = getKVRows('headers');\n    var params = getKVRows('params');\n    var bodyType = document.getElementById('arb-body-type').value;\n    var bodyRaw = document.getElementById('arb-body-editor') ? document.getElementById('arb-body-editor').value : '';\n    var formData = getKVRows('formdata');\n    var authType = document.getElementById('arb-auth-type').value;\n\n    // Auth injection\n    var extraHeaders = [];\n    var extraParams = [];\n    if (authType === 'bearer') {\n      var tok = (document.getElementById('arb-auth-token') || {}).value || 'YOUR_TOKEN';\n      extraHeaders.push(['Authorization', 'Bearer ' + tok]);\n    } else if (authType === 'basic') {\n      var user = (document.getElementById('arb-auth-user') || {}).value || 'username';\n      var pass = (document.getElementById('arb-auth-pass') || {}).value || 'password';\n      extraHeaders.push(['Authorization', 'Basic ' + btoa(user + ':' + pass)]);\n    } else if (authType === 'apikey') {\n      var kn = (document.getElementById('arb-auth-key-name') || {}).value || 'X-API-Key';\n      var kv = (document.getElementById('arb-auth-key-val') || {}).value || 'YOUR_API_KEY';\n      var loc = (document.getElementById('arb-auth-key-loc') || {}).value || 'header';\n      if (loc === 'header') extraHeaders.push([kn, kv]);\n      else extraParams.push([kn, kv]);\n    }\n\n    var allHeaders = headers.concat(extraHeaders);\n    var allParams = params.concat(extraParams);\n\n    // Build full URL with query params\n    var fullUrl = url;\n    if (allParams.length \u003e 0) {\n      var qs = allParams.map(function(p){ return encodeURIComponent(p[0]) + '=' + encodeURIComponent(p[1]); }).join('\u0026');\n      fullUrl += (url.indexOf('?') \u003e= 0 ? '\u0026' : '?') + qs;\n    }\n\n    return { method: method, url: url, fullUrl: fullUrl, headers: allHeaders, params: allParams, bodyType: bodyType, bodyRaw: bodyRaw, formData: formData };\n  }\n\n  // ---- Generators ----\n  function genCurl(r) {\n    var parts = ['curl'];\n    if (r.method !== 'GET') parts.push('-X ' + r.method);\n    r.headers.forEach(function(h) { parts.push(\"-H '\" + h[0] + ': ' + h[1] + \"'\"); });\n    if (r.bodyType === 'json' \u0026\u0026 r.bodyRaw.trim() \u0026\u0026 r.method !== 'GET') {\n      parts.push(\"--data '\" + r.bodyRaw.replace(/'/g, \"'\\\\''\") + \"'\");\n    } else if (r.bodyType === 'form' \u0026\u0026 r.formData.length \u003e 0 \u0026\u0026 r.method !== 'GET') {\n      r.formData.forEach(function(f){ parts.push(\"--data-urlencode '\" + f[0] + '=' + f[1] + \"'\"); });\n    } else if (r.bodyType === 'raw' \u0026\u0026 r.bodyRaw.trim() \u0026\u0026 r.method !== 'GET') {\n      parts.push(\"--data '\" + r.bodyRaw.replace(/'/g, \"'\\\\''\") + \"'\");\n    }\n    parts.push(\"'\" + r.fullUrl + \"'\");\n    return parts.join(' \\\\\\n  ');\n  }\n\n  function genFetch(r) {\n    var lines = [];\n    lines.push('const response = await fetch(');\n    lines.push(\"  '\" + r.fullUrl + \"',\");\n    lines.push('  {');\n    lines.push(\"    method: '\" + r.method + \"',\");\n    if (r.headers.length \u003e 0) {\n      lines.push('    headers: {');\n      r.headers.forEach(function(h, i){\n        lines.push(\"      '\" + h[0] + \"': '\" + h[1] + \"'\" + (i \u003c r.headers.length - 1 ? ',' : ''));\n      });\n      lines.push('    },');\n    }\n    if (r.method !== 'GET' \u0026\u0026 r.bodyType === 'json' \u0026\u0026 r.bodyRaw.trim()) {\n      lines.push('    body: JSON.stringify(' + r.bodyRaw + '),');\n    } else if (r.method !== 'GET' \u0026\u0026 r.bodyType === 'form' \u0026\u0026 r.formData.length \u003e 0) {\n      lines.push(\"    body: '\" + r.formData.map(function(f){ return encodeURIComponent(f[0])+'='+encodeURIComponent(f[1]); }).join('\u0026') + \"',\");\n    } else if (r.method !== 'GET' \u0026\u0026 r.bodyType === 'raw' \u0026\u0026 r.bodyRaw.trim()) {\n      lines.push('    body: `' + r.bodyRaw + '`,');\n    }\n    lines.push('  }');\n    lines.push(');');\n    lines.push('');\n    lines.push('const data = await response.json();');\n    lines.push('console.log(data);');\n    return lines.join('\\n');\n  }\n\n  function genAxios(r) {\n    var lines = [];\n    lines.push(\"import axios from 'axios';\");\n    lines.push('');\n    lines.push('const response = await axios({');\n    lines.push(\"  method: '\" + r.method.toLowerCase() + \"',\");\n    lines.push(\"  url: '\" + r.url + \"',\");\n    if (r.params.length \u003e 0) {\n      lines.push('  params: {');\n      r.params.forEach(function(p, i){\n        lines.push(\"    \" + p[0] + \": '\" + p[1] + \"'\" + (i \u003c r.params.length - 1 ? ',' : ''));\n      });\n      lines.push('  },');\n    }\n    if (r.headers.length \u003e 0) {\n      lines.push('  headers: {');\n      r.headers.forEach(function(h, i){\n        lines.push(\"    '\" + h[0] + \"': '\" + h[1] + \"'\" + (i \u003c r.headers.length - 1 ? ',' : ''));\n      });\n      lines.push('  },');\n    }\n    if (r.method !== 'GET' \u0026\u0026 r.bodyType === 'json' \u0026\u0026 r.bodyRaw.trim()) {\n      lines.push('  data: ' + r.bodyRaw + ',');\n    } else if (r.method !== 'GET' \u0026\u0026 r.bodyType === 'form' \u0026\u0026 r.formData.length \u003e 0) {\n      lines.push(\"  data: '\" + r.formData.map(function(f){ return encodeURIComponent(f[0])+'='+encodeURIComponent(f[1]); }).join('\u0026') + \"',\");\n    } else if (r.method !== 'GET' \u0026\u0026 r.bodyType === 'raw' \u0026\u0026 r.bodyRaw.trim()) {\n      lines.push(\"  data: `\" + r.bodyRaw + \"`,\");\n    }\n    lines.push('});');\n    lines.push('');\n    lines.push('console.log(response.data);');\n    return lines.join('\\n');\n  }\n\n  function genPython(r) {\n    var lines = [];\n    lines.push('import requests');\n    lines.push('');\n    if (r.headers.length \u003e 0) {\n      lines.push('headers = {');\n      r.headers.forEach(function(h, i){\n        lines.push(\"    '\" + h[0] + \"': '\" + h[1] + \"'\" + (i \u003c r.headers.length - 1 ? ',' : ''));\n      });\n      lines.push('}');\n      lines.push('');\n    }\n    if (r.params.length \u003e 0) {\n      lines.push('params = {');\n      r.params.forEach(function(p, i){\n        lines.push(\"    '\" + p[0] + \"': '\" + p[1] + \"'\" + (i \u003c r.params.length - 1 ? ',' : ''));\n      });\n      lines.push('}');\n      lines.push('');\n    }\n    var callArgs = [\"'\" + r.url + \"'\"];\n    if (r.headers.length \u003e 0) callArgs.push('headers=headers');\n    if (r.params.length \u003e 0) callArgs.push('params=params');\n    if (r.method !== 'GET' \u0026\u0026 r.bodyType === 'json' \u0026\u0026 r.bodyRaw.trim()) {\n      lines.push('payload = ' + r.bodyRaw);\n      lines.push('');\n      callArgs.push('json=payload');\n    } else if (r.method !== 'GET' \u0026\u0026 r.bodyType === 'form' \u0026\u0026 r.formData.length \u003e 0) {\n      lines.push('data = {');\n      r.formData.forEach(function(f, i){\n        lines.push(\"    '\" + f[0] + \"': '\" + f[1] + \"'\" + (i \u003c r.formData.length - 1 ? ',' : ''));\n      });\n      lines.push('}');\n      lines.push('');\n      callArgs.push('data=data');\n    }\n    lines.push('response = requests.' + r.method.toLowerCase() + '(');\n    callArgs.forEach(function(a, i){ lines.push('    ' + a + (i \u003c callArgs.length - 1 ? ',' : '')); });\n    lines.push(')');\n    lines.push('');\n    lines.push('print(response.status_code)');\n    lines.push('print(response.json())');\n    return lines.join('\\n');\n  }\n\n  function genPhp(r) {\n    var lines = [];\n    lines.push('\u003c?php');\n    lines.push('');\n    lines.push('$ch = curl_init();');\n    lines.push('');\n    lines.push(\"curl_setopt($ch, CURLOPT_URL, '\" + r.fullUrl + \"');\");\n    lines.push('curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);');\n    if (r.method !== 'GET') {\n      lines.push(\"curl_setopt($ch, CURLOPT_CUSTOMREQUEST, '\" + r.method + \"');\");\n    }\n    if (r.headers.length \u003e 0) {\n      lines.push('curl_setopt($ch, CURLOPT_HTTPHEADER, [');\n      r.headers.forEach(function(h){ lines.push(\"    '\" + h[0] + ': ' + h[1] + \"',\"); });\n      lines.push(']);');\n    }\n    if (r.method !== 'GET' \u0026\u0026 r.bodyType === 'json' \u0026\u0026 r.bodyRaw.trim()) {\n      lines.push(\"curl_setopt($ch, CURLOPT_POSTFIELDS, '\" + r.bodyRaw.replace(/'/g, \"\\\\'\") + \"');\");\n    } else if (r.method !== 'GET' \u0026\u0026 r.bodyType === 'form' \u0026\u0026 r.formData.length \u003e 0) {\n      lines.push(\"curl_setopt($ch, CURLOPT_POSTFIELDS, '\" + r.formData.map(function(f){ return encodeURIComponent(f[0])+'='+encodeURIComponent(f[1]); }).join('\u0026') + \"');\");\n    }\n    lines.push('');\n    lines.push('$response = curl_exec($ch);');\n    lines.push('$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);');\n    lines.push('curl_close($ch);');\n    lines.push('');\n    lines.push('echo \"Status: $httpCode\\\\n\";');\n    lines.push('echo $response;');\n    return lines.join('\\n');\n  }\n\n  // ---- Simple syntax highlight ----\n  function highlight(code, lang) {\n    // Escape HTML first\n    code = code.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n\n    if (lang === 'curl') {\n      code = code.replace(/(curl)/g, '\u003cspan class=\"t-kw\"\u003e$1\u003c/span\u003e');\n      code = code.replace(/(-X|-H|--data|--data-urlencode|-u|-G)/g, '\u003cspan class=\"t-flag\"\u003e$1\u003c/span\u003e');\n      code = code.replace(/'([^']*)'/g, function(m, inner){ return '\u003cspan class=\"t-str\"\u003e\\'' + inner + '\\'\u003c/span\u003e'; });\n    } else if (lang === 'python') {\n      code = code.replace(/\\b(import|def|return|if|else|print|True|False|None)\\b/g, '\u003cspan class=\"t-kw\"\u003e$1\u003c/span\u003e');\n      code = code.replace(/#[^\\n]*/g, '\u003cspan class=\"t-cm\"\u003e$\u0026\u003c/span\u003e');\n      code = code.replace(/'([^']*)'/g, '\u003cspan class=\"t-str\"\u003e\\'$1\\'\u003c/span\u003e');\n      code = code.replace(/\\b(requests)\\b/g, '\u003cspan class=\"t-fn\"\u003e$1\u003c/span\u003e');\n    } else if (lang === 'fetch' || lang === 'axios') {\n      code = code.replace(/\\b(const|let|var|await|async|import|from|new|return|true|false|null|undefined)\\b/g, '\u003cspan class=\"t-kw\"\u003e$1\u003c/span\u003e');\n      code = code.replace(/'([^']*)'/g, '\u003cspan class=\"t-str\"\u003e\\'$1\\'\u003c/span\u003e');\n      code = code.replace(/`([^`]*)`/g, '\u003cspan class=\"t-str\"\u003e`$1`\u003c/span\u003e');\n      code = code.replace(/\\b(fetch|axios|console\\.log|JSON\\.stringify|JSON\\.parse)\\b/g, '\u003cspan class=\"t-fn\"\u003e$1\u003c/span\u003e');\n      code = code.replace(/\\/\\/[^\\n]*/g, '\u003cspan class=\"t-cm\"\u003e$\u0026\u003c/span\u003e');\n    } else if (lang === 'php') {\n      code = code.replace(/\\b(echo|curl_init|curl_setopt|curl_exec|curl_close|curl_getinfo|true|false|null)\\b/g, '\u003cspan class=\"t-fn\"\u003e$1\u003c/span\u003e');\n      code = code.replace(/(\u0026lt;\\?php|\u0026lt;\\?|\\?\u0026gt;)/g, '\u003cspan class=\"t-kw\"\u003e$1\u003c/span\u003e');\n      code = code.replace(/'([^']*)'/g, '\u003cspan class=\"t-str\"\u003e\\'$1\\'\u003c/span\u003e');\n      code = code.replace(/(CURLOPT_\\w+|CURLINFO_\\w+)/g, '\u003cspan class=\"t-prop\"\u003e$1\u003c/span\u003e');\n      code = code.replace(/\\/\\/[^\\n]*/g, '\u003cspan class=\"t-cm\"\u003e$\u0026\u003c/span\u003e');\n    }\n    return code;\n  }\n\n  var langLabels = { curl: 'cURL', fetch: 'JavaScript (Fetch)', axios: 'JavaScript (Axios)', python: 'Python', php: 'PHP' };\n\n  // ---- Generate ----\n  window.arbGenerate = function() {\n    var r = arbCollect();\n    var lang = document.getElementById('arb-lang').value;\n    var code = '';\n    if (lang === 'curl') code = genCurl(r);\n    else if (lang === 'fetch') code = genFetch(r);\n    else if (lang === 'axios') code = genAxios(r);\n    else if (lang === 'python') code = genPython(r);\n    else if (lang === 'php') code = genPhp(r);\n\n    document.getElementById('arb-output-code').innerHTML = highlight(code, lang);\n    document.getElementById('arb-output-badge').textContent = langLabels[lang] || lang;\n    // Store raw for copy\n    document.getElementById('arb-output-code').setAttribute('data-raw', code);\n    // Reset copy button\n    var cb = document.getElementById('arb-copy-btn');\n    cb.textContent = 'Copy';\n    cb.classList.remove('copied');\n  };\n\n  // ---- Copy ----\n  window.arbCopy = function() {\n    var code = document.getElementById('arb-output-code').getAttribute('data-raw') ||\n               document.getElementById('arb-output-code').textContent;\n    if (!code || code.indexOf('Generate Code') \u003e= 0) return;\n    navigator.clipboard.writeText(code).then(function(){\n      var btn = document.getElementById('arb-copy-btn');\n      btn.textContent = 'Copied!';\n      btn.classList.add('copied');\n      setTimeout(function(){ btn.textContent = 'Copy'; btn.classList.remove('copied'); }, 2000);\n    }).catch(function(){\n      var ta = document.createElement('textarea');\n      ta.value = code;\n      ta.style.position = 'fixed'; ta.style.opacity = '0';\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      var btn = document.getElementById('arb-copy-btn');\n      btn.textContent = 'Copied!';\n      btn.classList.add('copied');\n      setTimeout(function(){ btn.textContent = 'Copy'; btn.classList.remove('copied'); }, 2000);\n    });\n  };\n\n  function esc(s) { return s.replace(/\"/g,'\u0026quot;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;'); }\n\n  // Auto-generate on load\n  arbGenerate();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"https://productivity-works.com/tools/dns-record-guide/\"\u003eDns Record Guide\u003c/a\u003e\n\n\u003ca href=\"https://productivity-works.com/tools/http-status-codes/\"\u003eHttp Status Codes\u003c/a\u003e\n\n\u003ca href=\"https://productivity-works.com/tools/ip-address-calculator/\"\u003eIp Address Calculator\u003c/a\u003e\n\u003c/p\u003e","title":"API Request Builder - Generate cURL, Fetch \u0026 Axios Code"},{"content":" Text to convert Font style Standard — classic # blocks Banner — = and | wide style Shadow — / and _ with depth Thin — minimalist lines Block — solid █ characters Custom fill character Character width Narrow Normal Wide Orientation Horizontal Vertical Output 0 characters Type text above to generate ASCII art... Copy to Clipboard Clear Copied to clipboard! Generate QR codes \u0026rarr; QR Code Generator\nCreate placeholder images \u0026rarr; Placeholder Image Generator\nExplore Unicode characters \u0026rarr; Unicode Character Map ","permalink":"https://productivity-works.com/tools/ascii-art-generator/","summary":"\u003cdiv id=\"aa-app\"\u003e\n\u003cstyle\u003e\n#aa-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #cbd5e1;\n}\n#aa-app .aa-section {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 10px;\n  padding: 20px 24px;\n  margin-bottom: 18px;\n}\n#aa-app label {\n  display: block;\n  font-size: 0.78rem;\n  font-weight: 600;\n  letter-spacing: 0.06em;\n  text-transform: uppercase;\n  color: #94a3b8;\n  margin-bottom: 6px;\n}\n#aa-app input[type=\"text\"],\n#aa-app select {\n  width: 100%;\n  box-sizing: border-box;\n  background: #0f172a;\n  border: 1px solid #475569;\n  border-radius: 6px;\n  color: #f1f5f9;\n  font-size: 1rem;\n  padding: 10px 12px;\n  outline: none;\n  transition: border-color 0.15s;\n}\n#aa-app input[type=\"text\"]:focus,\n#aa-app select:focus {\n  border-color: #6366f1;\n}\n#aa-app select {\n  cursor: pointer;\n  appearance: none;\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E\");\n  background-repeat: no-repeat;\n  background-position: right 12px center;\n  padding-right: 36px;\n}\n#aa-app .aa-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 16px;\n}\n#aa-app .aa-grid-3 {\n  display: grid;\n  grid-template-columns: 1fr 1fr 1fr;\n  gap: 16px;\n}\n@media (max-width: 580px) {\n  #aa-app .aa-grid,\n  #aa-app .aa-grid-3 {\n    grid-template-columns: 1fr;\n  }\n}\n#aa-app .aa-radio-group {\n  display: flex;\n  gap: 8px;\n  flex-wrap: wrap;\n}\n#aa-app .aa-radio-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  background: #0f172a;\n  border: 1px solid #475569;\n  border-radius: 6px;\n  padding: 7px 14px;\n  cursor: pointer;\n  font-size: 0.875rem;\n  color: #cbd5e1;\n  transition: border-color 0.15s, background 0.15s;\n  user-select: none;\n}\n#aa-app .aa-radio-btn input[type=\"radio\"] {\n  accent-color: #6366f1;\n  width: 14px;\n  height: 14px;\n  flex-shrink: 0;\n}\n#aa-app .aa-radio-btn.aa-selected {\n  border-color: #6366f1;\n  background: #1e1b4b;\n  color: #a5b4fc;\n}\n#aa-app .aa-output-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 12px;\n  flex-wrap: wrap;\n  gap: 8px;\n}\n#aa-app .aa-output-title {\n  font-size: 0.78rem;\n  font-weight: 600;\n  letter-spacing: 0.06em;\n  text-transform: uppercase;\n  color: #94a3b8;\n}\n#aa-app .aa-char-count {\n  font-size: 0.78rem;\n  color: #64748b;\n}\n#aa-app pre#aa-output {\n  background: #0f172a;\n  border: 1px solid #334155;\n  border-radius: 6px;\n  color: #a5b4fc;\n  font-family: \"Courier New\", Courier, monospace;\n  font-size: 0.62rem;\n  line-height: 1.15;\n  overflow-x: auto;\n  padding: 16px;\n  white-space: pre;\n  margin: 0;\n  min-height: 120px;\n  tab-size: 1;\n}\n#aa-app .aa-btn-row {\n  display: flex;\n  gap: 10px;\n  margin-top: 14px;\n  flex-wrap: wrap;\n}\n#aa-app .aa-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  border: none;\n  border-radius: 7px;\n  cursor: pointer;\n  font-size: 0.875rem;\n  font-weight: 600;\n  padding: 10px 20px;\n  transition: opacity 0.15s, transform 0.1s;\n}\n#aa-app .aa-btn:active {\n  transform: scale(0.97);\n}\n#aa-app .aa-btn-primary {\n  background: #6366f1;\n  color: #fff;\n}\n#aa-app .aa-btn-primary:hover {\n  opacity: 0.88;\n}\n#aa-app .aa-btn-secondary {\n  background: #1e293b;\n  border: 1px solid #475569;\n  color: #cbd5e1;\n}\n#aa-app .aa-btn-secondary:hover {\n  border-color: #6366f1;\n  color: #a5b4fc;\n}\n#aa-app .aa-toast {\n  display: none;\n  align-items: center;\n  gap: 8px;\n  background: #16a34a;\n  color: #fff;\n  border-radius: 7px;\n  padding: 10px 18px;\n  font-size: 0.875rem;\n  font-weight: 600;\n  margin-top: 12px;\n}\n#aa-app .aa-crosslinks {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 10px;\n  padding: 16px 24px;\n  font-size: 0.875rem;\n  color: #94a3b8;\n  line-height: 2;\n}\n#aa-app .aa-crosslinks a {\n  color: #818cf8;\n  text-decoration: none;\n}\n#aa-app .aa-crosslinks a:hover {\n  text-decoration: underline;\n}\n\u003c/style\u003e\n\u003c!-- Input Section --\u003e\n\u003cdiv class=\"aa-section\"\u003e\n  \u003clabel for=\"aa-text-input\"\u003eText to convert\u003c/label\u003e\n  \u003cinput type=\"text\" id=\"aa-text-input\" placeholder=\"Type something... e.g. HELLO\" maxlength=\"30\" autocomplete=\"off\" spellcheck=\"false\"\u003e\n\u003c/div\u003e\n\u003c!-- Font \u0026 Options --\u003e\n\u003cdiv class=\"aa-section\"\u003e\n  \u003cdiv class=\"aa-grid\" style=\"margin-bottom:16px;\"\u003e\n    \u003cdiv\u003e\n      \u003clabel for=\"aa-font-select\"\u003eFont style\u003c/label\u003e\n      \u003cselect id=\"aa-font-select\"\u003e\n        \u003coption value=\"standard\"\u003eStandard — classic # blocks\u003c/option\u003e\n        \u003coption value=\"banner\"\u003eBanner — = and | wide style\u003c/option\u003e\n        \u003coption value=\"shadow\"\u003eShadow — / and _ with depth\u003c/option\u003e\n        \u003coption value=\"thin\"\u003eThin — minimalist lines\u003c/option\u003e\n        \u003coption value=\"block\"\u003eBlock — solid █ characters\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003clabel for=\"aa-fill-char\"\u003eCustom fill character\u003c/label\u003e\n      \u003cinput type=\"text\" id=\"aa-fill-char\" placeholder=\"Leave blank to use font default\" maxlength=\"1\" autocomplete=\"off\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"aa-grid-3\"\u003e\n    \u003cdiv\u003e\n      \u003clabel\u003eCharacter width\u003c/label\u003e\n      \u003cdiv class=\"aa-radio-group\" id=\"aa-width-group\"\u003e\n        \u003clabel class=\"aa-radio-btn\"\u003e\u003cinput type=\"radio\" name=\"aa-width\" value=\"narrow\"\u003e Narrow\u003c/label\u003e\n        \u003clabel class=\"aa-radio-btn aa-selected\"\u003e\u003cinput type=\"radio\" name=\"aa-width\" value=\"normal\" checked\u003e Normal\u003c/label\u003e\n        \u003clabel class=\"aa-radio-btn\"\u003e\u003cinput type=\"radio\" name=\"aa-width\" value=\"wide\"\u003e Wide\u003c/label\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003clabel\u003eOrientation\u003c/label\u003e\n      \u003cdiv class=\"aa-radio-group\" id=\"aa-orient-group\"\u003e\n        \u003clabel class=\"aa-radio-btn aa-selected\"\u003e\u003cinput type=\"radio\" name=\"aa-orient\" value=\"horizontal\" checked\u003e Horizontal\u003c/label\u003e\n        \u003clabel class=\"aa-radio-btn\"\u003e\u003cinput type=\"radio\" name=\"aa-orient\" value=\"vertical\"\u003e Vertical\u003c/label\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Output Section --\u003e\n\u003cdiv class=\"aa-section\"\u003e\n  \u003cdiv class=\"aa-output-header\"\u003e\n    \u003cspan class=\"aa-output-title\"\u003eOutput\u003c/span\u003e\n    \u003cspan class=\"aa-char-count\" id=\"aa-char-count\"\u003e0 characters\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cpre id=\"aa-output\"\u003eType text above to generate ASCII art...\u003c/pre\u003e\n  \u003cdiv class=\"aa-btn-row\"\u003e\n    \u003cbutton class=\"aa-btn aa-btn-primary\" id=\"aa-copy-btn\"\u003eCopy to Clipboard\u003c/button\u003e\n    \u003cbutton class=\"aa-btn aa-btn-secondary\" id=\"aa-clear-btn\"\u003eClear\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"aa-toast\" id=\"aa-toast\"\u003eCopied to clipboard!\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Cross-links --\u003e\n\u003cdiv class=\"aa-crosslinks\"\u003e\n  Generate QR codes \u0026rarr; \u003ca href=\"/tools/qr-code-generator/\"\u003eQR Code Generator\u003c/a\u003e\u003cbr\u003e\n  Create placeholder images \u0026rarr; \u003ca href=\"/tools/placeholder-image/\"\u003ePlaceholder Image Generator\u003c/a\u003e\u003cbr\u003e\n  Explore Unicode characters \u0026rarr; \u003ca href=\"/tools/unicode-character-map/\"\u003eUnicode Character Map\u003c/a\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  \"use strict\";\n\n  // ─────────────────────────────────────────\n  // FONT DEFINITIONS\n  // Each glyph is an array of 6 strings (rows).\n  // Glyphs are padded to equal width within the font.\n  // ─────────────────────────────────────────\n\n  var FONTS = {};\n\n  // ── STANDARD (# blocks) ──────────────────\n  FONTS.standard = {\n    _rows: 6,\n    _fill: \"#\",\n    glyphs: {\n      \"A\": [\"  ##  \",\" #  # \",\"######\",\"#    #\",\"#    #\",\"      \"],\n      \"B\": [\"##### \",\"#    #\",\"##### \",\"#    #\",\"##### \",\"      \"],\n      \"C\": [\" #####\",\"#     \",\"#     \",\"#     \",\" #####\",\"      \"],\n      \"D\": [\"##### \",\"#    #\",\"#    #\",\"#    #\",\"##### \",\"      \"],\n      \"E\": [\"######\",\"#     \",\"####  \",\"#     \",\"######\",\"      \"],\n      \"F\": [\"######\",\"#     \",\"####  \",\"#     \",\"#     \",\"      \"],\n      \"G\": [\" #####\",\"#     \",\"#  ###\",\"#    #\",\" #####\",\"      \"],\n      \"H\": [\"#    #\",\"#    #\",\"######\",\"#    #\",\"#    #\",\"      \"],\n      \"I\": [\"######\",\"  ##  \",\"  ##  \",\"  ##  \",\"######\",\"      \"],\n      \"J\": [\"######\",\"   ## \",\"   ## \",\"#  ## \",\" ###  \",\"      \"],\n      \"K\": [\"#   # \",\"#  #  \",\"###   \",\"#  #  \",\"#   # \",\"      \"],\n      \"L\": [\"#     \",\"#     \",\"#     \",\"#     \",\"######\",\"      \"],\n      \"M\": [\"#    #\",\"##  ##\",\"# ## #\",\"#    #\",\"#    #\",\"      \"],\n      \"N\": [\"#    #\",\"##   #\",\"# #  #\",\"#  # #\",\"#   ##\",\"      \"],\n      \"O\": [\" #### \",\"#    #\",\"#    #\",\"#    #\",\" #### \",\"      \"],\n      \"P\": [\"##### \",\"#    #\",\"##### \",\"#     \",\"#     \",\"      \"],\n      \"Q\": [\" #### \",\"#    #\",\"#  # #\",\"#   ##\",\" #### \",\"      \"],\n      \"R\": [\"##### \",\"#    #\",\"##### \",\"#  #  \",\"#   # \",\"      \"],\n      \"S\": [\" #####\",\"#     \",\" #### \",\"     #\",\"##### \",\"      \"],\n      \"T\": [\"######\",\"  ##  \",\"  ##  \",\"  ##  \",\"  ##  \",\"      \"],\n      \"U\": [\"#    #\",\"#    #\",\"#    #\",\"#    #\",\" #### \",\"      \"],\n      \"V\": [\"#    #\",\"#    #\",\" #  # \",\" #  # \",\"  ##  \",\"      \"],\n      \"W\": [\"#    #\",\"#    #\",\"# ## #\",\"##  ##\",\"#    #\",\"      \"],\n      \"X\": [\"#    #\",\" #  # \",\"  ##  \",\" #  # \",\"#    #\",\"      \"],\n      \"Y\": [\"#    #\",\" #  # \",\"  ##  \",\"  ##  \",\"  ##  \",\"      \"],\n      \"Z\": [\"######\",\"    # \",\"  ##  \",\" #    \",\"######\",\"      \"],\n      \"0\": [\" #### \",\"#   ##\",\"#  # #\",\"## # #\",\" #### \",\"      \"],\n      \"1\": [\"  ##  \",\" ###  \",\"  ##  \",\"  ##  \",\"######\",\"      \"],\n      \"2\": [\" #### \",\"#    #\",\"   ## \",\"  ##  \",\"######\",\"      \"],\n      \"3\": [\"##### \",\"     #\",\" #### \",\"     #\",\"##### \",\"      \"],\n      \"4\": [\"#   # \",\"#   # \",\"######\",\"    # \",\"    # \",\"      \"],\n      \"5\": [\"######\",\"#     \",\"##### \",\"     #\",\"##### \",\"      \"],\n      \"6\": [\" #### \",\"#     \",\"##### \",\"#    #\",\" #### \",\"      \"],\n      \"7\": [\"######\",\"    # \",\"   #  \",\"  #   \",\"  #   \",\"      \"],\n      \"8\": [\" #### \",\"#    #\",\" #### \",\"#    #\",\" #### \",\"      \"],\n      \"9\": [\" #### \",\"#    #\",\" #####\",\"     #\",\" #### \",\"      \"],\n      \" \": [\"      \",\"      \",\"      \",\"      \",\"      \",\"      \"],\n      \".\": [\"      \",\"      \",\"      \",\"  ##  \",\"  ##  \",\"      \"],\n      \"!\": [\"  ##  \",\"  ##  \",\"  ##  \",\"      \",\"  ##  \",\"      \"],\n      \"?\": [\" #### \",\"#    #\",\"   ## \",\"      \",\"  ##  \",\"      \"],\n      \"-\": [\"      \",\"      \",\"######\",\"      \",\"      \",\"      \"],\n      \"_\": [\"      \",\"      \",\"      \",\"      \",\"######\",\"      \"],\n      \"+\": [\"      \",\"  ##  \",\"######\",\"  ##  \",\"      \",\"      \"],\n      \"@\": [\" #### \",\"# ## #\",\"# ## #\",\"#     \",\" #### \",\"      \"],\n      \"#\": [\" #  # \",\"######\",\" #  # \",\"######\",\" #  # \",\"      \"],\n    }\n  };\n\n  // ── BANNER (= and |) ─────────────────────\n  FONTS.banner = {\n    _rows: 6,\n    _fill: \"=\",\n    glyphs: {\n      \"A\": [\"  /\\\\  \",\" /==\\\\ \",\"|====|\",\"|    |\",\"|    |\",\"      \"],\n      \"B\": [\"|===\\\\ \",\"|    |\",\"|===\u003c \",\"|    |\",\"|===/ \",\"      \"],\n      \"C\": [\" /===\",\"|    \",\"|    \",\"|    \",\" \\\\===\",\"      \"],\n      \"D\": [\"|===\\\\ \",\"|    |\",\"|    |\",\"|    |\",\"|===/ \",\"      \"],\n      \"E\": [\"|====\",\"|    \",\"|=== \",\"|    \",\"|====\",\"      \"],\n      \"F\": [\"|====\",\"|    \",\"|=== \",\"|    \",\"|    \",\"      \"],\n      \"G\": [\" /===\",\"|    \",\"|  ==\",\"|   |\",\" \\\\===\",\"      \"],\n      \"H\": [\"|    |\",\"|    |\",\"|====|\",\"|    |\",\"|    |\",\"      \"],\n      \"I\": [\"|====\",\"  ||  \",\"  ||  \",\"  ||  \",\"|====\",\"      \"],\n      \"J\": [\"|====\",\"   || \",\"   || \",\"|  || \",\" \\\\==/ \",\"      \"],\n      \"K\": [\"|   |\",\"|  / \",\"|\u003c   \",\"|  \\\\ \",\"|   |\",\"      \"],\n      \"L\": [\"|    \",\"|    \",\"|    \",\"|    \",\"|====\",\"      \"],\n      \"M\": [\"|\\\\  /|\",\"| \\\\/ |\",\"| /\\\\ |\",\"|    |\",\"|    |\",\"      \"],\n      \"N\": [\"|\\\\   |\",\"| \\\\  |\",\"|  \\\\ |\",\"|   \\\\|\",\"|    |\",\"      \"],\n      \"O\": [\" /==\\\\ \",\"|    |\",\"|    |\",\"|    |\",\" \\\\==/ \",\"      \"],\n      \"P\": [\"|===\\\\ \",\"|    |\",\"|===/ \",\"|    \",\"| \",\"      \"],\n      \"Q\": [\" /==\\\\ \",\"|    |\",\"|    |\",\"|  \\\\/|\",\" \\\\==\\\\\",\"      \"],\n      \"R\": [\"|===\\\\ \",\"|    |\",\"|===/ \",\"|  \\\\ \",\"|   |\",\"      \"],\n      \"S\": [\" /===\",\"|    \",\" \\\\==\\\\\",\" ===|\",\"\\\\===/ \",\"      \"],\n      \"T\": [\"|====\",\"  ||  \",\"  ||  \",\"  ||  \",\"  ||  \",\"      \"],\n      \"U\": [\"|    |\",\"|    |\",\"|    |\",\"|    |\",\" \\\\==/ \",\"      \"],\n      \"V\": [\"|    |\",\"|    |\",\" \\\\  / \",\" \\\\  / \",\"  \\\\/  \",\"      \"],\n      \"W\": [\"|    |\",\"|    |\",\"| \\\\/ |\",\"| /\\\\ |\",\"|    |\",\"      \"],\n      \"X\": [\"|    |\",\" \\\\  / \",\"  \\\\/  \",\" /  \\\\ \",\"|    |\",\"      \"],\n      \"Y\": [\"|    |\",\" \\\\  / \",\"  \\\\/  \",\"  ||  \",\"  ||  \",\"      \"],\n      \"Z\": [\"|====\",\"   /  \",\"  /   \",\" /    \",\"|====\",\"      \"],\n      \"0\": [\" /==\\\\ \",\"|  / |\",\"| /  |\",\"| \\\\  |\",\" \\\\==/ \",\"      \"],\n      \"1\": [\" /|  \",\"  |  \",\"  |  \",\"  |  \",\"|====\",\"      \"],\n      \"2\": [\" /==\\\\ \",\"|    |\",\"  ==/ \",\" /    \",\"|====\",\"      \"],\n      \"3\": [\"|=== \",\"     |\",\" ===\u003c \",\"     |\",\"|=== \",\"      \"],\n      \"4\": [\"|   |\",\"|   |\",\"|===|\",\"    |\",\"    |\",\"      \"],\n      \"5\": [\"|====\",\"|    \",\"|=== \",\"    |\",\"===/ \",\"      \"],\n      \"6\": [\" /===\",\"|    \",\"|=== \",\"|   |\",\" \\\\==/ \",\"      \"],\n      \"7\": [\"|====\",\"    |\",\"   / \",\"  /  \",\" /   \",\"      \"],\n      \"8\": [\" /==\\\\ \",\"|    |\",\" \\\\==/ \",\"|    |\",\" \\\\==/ \",\"      \"],\n      \"9\": [\" /==\\\\ \",\"|    |\",\" \\\\===\",\"     |\",\" ===/ \",\"      \"],\n      \" \": [\"      \",\"      \",\"      \",\"      \",\"      \",\"      \"],\n      \".\": [\"      \",\"      \",\"      \",\" /\\\\ \",\" \\\\/ \",\"      \"],\n      \"!\": [\"  ||  \",\"  ||  \",\"  ||  \",\"      \",\" /\\\\  \",\"      \"],\n      \"?\": [\" /==\\\\ \",\"     |\",\"  ==/ \",\"      \",\"  /\\\\  \",\"      \"],\n      \"-\": [\"      \",\"      \",\"|====|\",\"      \",\"      \",\"      \"],\n      \"_\": [\"      \",\"      \",\"      \",\"      \",\"|====|\",\"      \"],\n    }\n  };\n\n  // ── SHADOW (/ _ with depth) ───────────────\n  FONTS.shadow = {\n    _rows: 6,\n    _fill: \"/\",\n    glyphs: {\n      \"A\": [\"  _   \",\" /_\\\\  \",\"/ _ \\\\ \",\"/_/ \\\\_\\\\\",\"      \",\"      \"],\n      \"B\": [\"|_)   \",\"|_) _ \",\"| |_)\",\"|_)_)\",\"      \",\"      \"],\n      \"C\": [\" ___ \",\"/ __|\",\"| (__ \",\" \\\\___|\",\"      \",\"      \"],\n      \"D\": [\"|  \\\\ \",\"| |) \",\"| |/ \",\"__/  \",\"      \",\"      \"],\n      \"E\": [\"|__ \",\"| __\",\"| _|\",\"| |_ \",\"      \",\"      \"],\n      \"F\": [\"|__ \",\"| __\",\"| _|\",\"|   \",\"      \",\"      \"],\n      \"G\": [\" ___ \",\"/ __|\",\"| (_ \",\"\\\\____|\",\"      \",\"      \"],\n      \"H\": [\"|_|_|\",\"| _ \",\"| |_|\",\"      \",\"      \",\"      \"],\n      \"I\": [\"|_|\",\" | \",\" | \",\"|_|\",\"      \",\"      \"],\n      \"J\": [\"  _| \",\"  | \",\"_\\\\ | \",\"\\\\__|\",\"      \",\"      \"],\n      \"K\": [\"|/ \",\"| \u003c\",\"|__\\\\\",\"      \",\"      \",\"      \"],\n      \"L\": [\"|   \",\"| __\",\"| |_\",\"      \",\"      \",\"      \"],\n      \"M\": [\"|\\\\/|\",\"_\\\\/_\",\"      \",\"      \",\"      \",\"      \"],\n      \"N\": [\"|\\\\ |\",\"| \\\\|\",\"      \",\"      \",\"      \",\"      \"],\n      \"O\": [\" ___ \",\"/ _ \\\\\",\"\\\\___/\",\"      \",\"      \",\"      \"],\n      \"P\": [\"|_) \",\"| .\\\\\",\"| |  \",\"      \",\"      \",\"      \"],\n      \"Q\": [\" ___ \",\"/ _ \\\\\",\"\\\\__\\\\/\",\"      \",\"      \",\"      \"],\n      \"R\": [\"|_) \",\"| \\\\ \",\"      \",\"      \",\"      \",\"      \"],\n      \"S\": [\" ___ \",\"/ __|\",\"\\\\__ \\\\\",\"\\\\___/\",\"      \",\"      \"],\n      \"T\": [\"|_ |\",\"  | \",\"  |_|\",\"      \",\"      \",\"      \"],\n      \"U\": [\"|_|_|\",\"  |  \",\"  |  \",\"  |_|\",\"      \",\"      \"],\n      \"V\": [\"\\\\/ \",\"/ \\\\ \",\"      \",\"      \",\"      \",\"      \"],\n      \"W\": [\"\\\\    /\",\"\\\\  / \",\"\\\\/ \",\"      \",\"      \",\"      \"],\n      \"X\": [\"\\\\_/\",\"/ \\\\\",\"      \",\"      \",\"      \",\"      \"],\n      \"Y\": [\"\\\\_/ \",\" | \",\"      \",\"      \",\"      \",\"      \"],\n      \"Z\": [\"|__ \",\"  / \",\"/__\\\\\",\"      \",\"      \",\"      \"],\n      \"0\": [\" ___ \",\"/ _ \\\\\",\"\\\\___/\",\"      \",\"      \",\"      \"],\n      \"1\": [\" /| \",\"  | \",\"  |_|\",\"      \",\"      \",\"      \"],\n      \"2\": [\" ___ \",\"   _/\",\"  /  \",\"/___ \",\"      \",\"      \"],\n      \"3\": [\" __  \",\"   \\\\ \",\"__  |\",\" \\\\___|\",\"      \",\"      \"],\n      \"4\": [\"|_| |\",\"  _| \",\"      \",\"      \",\"      \",\"      \"],\n      \"5\": [\"|___ \",\"/___ \",\"     |\",\"\\\\____|\",\"      \",\"      \"],\n      \"6\": [\" /_| \",\"|___ \",\"| __/\",\"\\\\___/\",\"      \",\"      \"],\n      \"7\": [\"____\",\"   /\",\"  / \",\"      \",\"      \",\"      \"],\n      \"8\": [\" ___ \",\"/ _ \\\\\",\"\\\\___/\",\"      \",\"      \",\"      \"],\n      \"9\": [\" ___ \",\"/ _ \\\\\",\"\\\\__/|\",\"      \",\"      \",\"      \"],\n      \" \": [\"  \",\"  \",\"  \",\"  \",\"  \",\"  \"],\n      \".\": [\"  \",\" _\",\"(_)\",\"      \",\"      \",\"      \"],\n      \"!\": [\" | \",\" | \",\"  \",\"      \",\"      \",\"      \"],\n      \"?\": [\" _? \",\"  _|\",\"      \",\"      \",\"      \",\"      \"],\n      \"-\": [\"    \",\"____\",\"    \",\"      \",\"      \",\"      \"],\n    }\n  };\n\n  // ── THIN (minimalist) ─────────────────────\n  FONTS.thin = {\n    _rows: 5,\n    _fill: \"|\",\n    glyphs: {\n      \"A\": [\" /\\\\ \",\" || \",\"/__\\\\\",\"|| ||\",\"      \"],\n      \"B\": [\"|--. \",\"|--\u003c \",\"|--' \",\"      \",\"      \"],\n      \"C\": [\" /--\",\"| \",\"| \",\"\\\\--\",\"      \"],\n      \"D\": [\"|--. \",\"| `|\",\"| ,|\",\"|--' \",\"      \"],\n      \"E\": [\"|--\",\"|--\",\"|\",\"| \",\"      \"],\n      \"F\": [\"|--\",\"|--\",\"|\",\"|\",\"      \"],\n      \"G\": [\" /--\",\"| \",\"| --\",\"\\\\--.\",\"      \"],\n      \"H\": [\"| |\",\"|-|\",\"| |\",\"      \",\"      \"],\n      \"I\": [\"-|-\",\" | \",\" | \",\"-|-\",\"      \"],\n      \"J\": [\" -|\",\" | \",\"\\\\|\",\"      \",\"      \"],\n      \"K\": [\"| /\",\"|\u003c \",\"|  \\\\\",\"      \",\"      \"],\n      \"L\": [\"|  \",\"| \",\"| \",\"|-\",\"      \"],\n      \"M\": [\"|V|\",\"| |\",\"      \",\"      \",\"      \"],\n      \"N\": [\"|\\\\|\",\"| \\\\\",\"| |\",\"      \",\"      \"],\n      \"O\": [\" /\\\\ \",\"|  |\",\"\\\\__/\",\"      \",\"      \"],\n      \"P\": [\"|--.\",\"|--'\",\"|   \",\"      \",\"      \"],\n      \"Q\": [\" /\\\\ \",\"|  |\",\"\\\\/\\\\ \",\"      \",\"      \"],\n      \"R\": [\"|--.\",\"|--\\\\\",\"| \\\\\",\"      \",\"      \"],\n      \"S\": [\"/--\",\"\\\\--\",\"__/\",\"      \",\"      \"],\n      \"T\": [\"-|-\",\" | \",\" | \",\" | \",\"      \"],\n      \"U\": [\"| |\",\"| |\",\"\\\\|/\",\"      \",\"      \"],\n      \"V\": [\"| |\",\" V \",\"      \",\"      \",\"      \"],\n      \"W\": [\"|   |\",\"| | |\",\" \\\\ / \",\"      \",\"      \"],\n      \"X\": [\"\\\\ /\",\"/ \\\\\",\"      \",\"      \",\"      \"],\n      \"Y\": [\"| |\",\" V \",\" | \",\"      \",\"      \"],\n      \"Z\": [\"---\",\"  /\",\"/--\",\"      \",\"      \"],\n      \"0\": [\" 0 \",\"/ \\\\\",\"\\\\_/\",\"      \",\"      \"],\n      \"1\": [\"/| \",\" | \",\" | \",\"      \",\"      \"],\n      \"2\": [\"-. \",\"  |\",\"  |\",\"'-'\",\"      \"],\n      \"3\": [\"-, \",\" \\\\\",\"_/\",\"      \",\"      \"],\n      \"4\": [\"|_|\",\" | \",\"      \",\"      \",\"      \"],\n      \"5\": [\"|--\",\"'--\",\"__/\",\"      \",\"      \"],\n      \"6\": [\" / \",\"|-. \",\"\\\\-'\",\"      \",\"      \"],\n      \"7\": [\"--/\",\"  /\",\"  \",\"      \",\"      \"],\n      \"8\": [\"/\\\\\",\"\\\\/\",\"/\\\\\",\"\\\\/\",\"      \"],\n      \"9\": [\"/\\\\\",\"\\\\_\",\" /\",\"      \",\"      \"],\n      \" \": [\"  \",\"  \",\"  \",\"  \",\"  \"],\n      \".\": [\"  \",\" .\",\"  \",\"      \",\"      \"],\n      \"!\": [\"|\",\" \",\"      \",\"      \",\"      \"],\n      \"?\": [\"-?\",\"  \",\"      \",\"      \",\"      \"],\n      \"-\": [\"   \",\"---\",\"   \",\"      \",\"      \"],\n    }\n  };\n\n  // ── BLOCK (solid █) ──────────────────────\n  FONTS.block = {\n    _rows: 5,\n    _fill: \"\\u2588\",\n    glyphs: {\n      \"A\": [\" \\u2588\\u2588\\u2588 \",\"\\u2588   \\u2588\",\"\\u2588\\u2588\\u2588\\u2588\\u2588\",\"\\u2588   \\u2588\",\"      \"],\n      \"B\": [\"\\u2588\\u2588\\u2588\\u2588 \",\"\\u2588   \\u2588\",\"\\u2588\\u2588\\u2588\\u2588 \",\"\\u2588   \\u2588\",\"\\u2588\\u2588\\u2588\\u2588 \"],\n      \"C\": [\" \\u2588\\u2588\\u2588\\u2588\",\"\\u2588    \",\"\\u2588    \",\"\\u2588    \",\" \\u2588\\u2588\\u2588\\u2588\"],\n      \"D\": [\"\\u2588\\u2588\\u2588  \",\"\\u2588  \\u2588\\u2588\",\"\\u2588  \\u2588\\u2588\",\"\\u2588  \\u2588\\u2588\",\"\\u2588\\u2588\\u2588  \"],\n      \"E\": [\"\\u2588\\u2588\\u2588\\u2588\\u2588\",\"\\u2588    \",\"\\u2588\\u2588\\u2588  \",\"\\u2588    \",\"\\u2588\\u2588\\u2588\\u2588\\u2588\"],\n      \"F\": [\"\\u2588\\u2588\\u2588\\u2588\\u2588\",\"\\u2588    \",\"\\u2588\\u2588\\u2588  \",\"\\u2588    \",\"\\u2588    \"],\n      \"G\": [\" \\u2588\\u2588\\u2588\\u2588\",\"\\u2588    \",\"\\u2588  \\u2588\\u2588\",\"\\u2588   \\u2588\",\" \\u2588\\u2588\\u2588\\u2588\"],\n      \"H\": [\"\\u2588   \\u2588\",\"\\u2588   \\u2588\",\"\\u2588\\u2588\\u2588\\u2588\\u2588\",\"\\u2588   \\u2588\",\"\\u2588   \\u2588\"],\n      \"I\": [\"\\u2588\\u2588\\u2588\\u2588\\u2588\",\"  \\u2588  \",\"  \\u2588  \",\"  \\u2588  \",\"\\u2588\\u2588\\u2588\\u2588\\u2588\"],\n      \"J\": [\"\\u2588\\u2588\\u2588\\u2588\\u2588\",\"   \\u2588 \",\"   \\u2588 \",\"\\u2588  \\u2588 \",\" \\u2588\\u2588  \"],\n      \"K\": [\"\\u2588   \\u2588\",\"\\u2588  \\u2588 \",\"\\u2588\\u2588\\u2588  \",\"\\u2588  \\u2588 \",\"\\u2588   \\u2588\"],\n      \"L\": [\"\\u2588    \",\"\\u2588    \",\"\\u2588    \",\"\\u2588    \",\"\\u2588\\u2588\\u2588\\u2588\\u2588\"],\n      \"M\": [\"\\u2588\\u2588 \\u2588\\u2588\",\"\\u2588 \\u2588 \\u2588\",\"\\u2588   \\u2588\",\"\\u2588   \\u2588\",\"\\u2588   \\u2588\"],\n      \"N\": [\"\\u2588   \\u2588\",\"\\u2588\\u2588  \\u2588\",\"\\u2588 \\u2588 \\u2588\",\"\\u2588  \\u2588\\u2588\",\"\\u2588   \\u2588\"],\n      \"O\": [\" \\u2588\\u2588\\u2588 \",\"\\u2588   \\u2588\",\"\\u2588   \\u2588\",\"\\u2588   \\u2588\",\" \\u2588\\u2588\\u2588 \"],\n      \"P\": [\"\\u2588\\u2588\\u2588\\u2588 \",\"\\u2588   \\u2588\",\"\\u2588\\u2588\\u2588\\u2588 \",\"\\u2588    \",\"\\u2588    \"],\n      \"Q\": [\" \\u2588\\u2588\\u2588 \",\"\\u2588   \\u2588\",\"\\u2588 \\u2588 \\u2588\",\"\\u2588  \\u2588\\u2588\",\" \\u2588\\u2588\\u2588\\u2588\"],\n      \"R\": [\"\\u2588\\u2588\\u2588\\u2588 \",\"\\u2588   \\u2588\",\"\\u2588\\u2588\\u2588\\u2588 \",\"\\u2588  \\u2588 \",\"\\u2588   \\u2588\"],\n      \"S\": [\" \\u2588\\u2588\\u2588\\u2588\",\"\\u2588    \",\" \\u2588\\u2588\\u2588 \",\"    \\u2588\",\"\\u2588\\u2588\\u2588\\u2588 \"],\n      \"T\": [\"\\u2588\\u2588\\u2588\\u2588\\u2588\",\"  \\u2588  \",\"  \\u2588  \",\"  \\u2588  \",\"  \\u2588  \"],\n      \"U\": [\"\\u2588   \\u2588\",\"\\u2588   \\u2588\",\"\\u2588   \\u2588\",\"\\u2588   \\u2588\",\" \\u2588\\u2588\\u2588 \"],\n      \"V\": [\"\\u2588   \\u2588\",\"\\u2588   \\u2588\",\"\\u2588   \\u2588\",\" \\u2588 \\u2588 \",\"  \\u2588  \"],\n      \"W\": [\"\\u2588   \\u2588\",\"\\u2588   \\u2588\",\"\\u2588 \\u2588 \\u2588\",\"\\u2588\\u2588 \\u2588\\u2588\",\"\\u2588   \\u2588\"],\n      \"X\": [\"\\u2588   \\u2588\",\" \\u2588 \\u2588 \",\"  \\u2588  \",\" \\u2588 \\u2588 \",\"\\u2588   \\u2588\"],\n      \"Y\": [\"\\u2588   \\u2588\",\" \\u2588 \\u2588 \",\"  \\u2588  \",\"  \\u2588  \",\"  \\u2588  \"],\n      \"Z\": [\"\\u2588\\u2588\\u2588\\u2588\\u2588\",\"   \\u2588 \",\"  \\u2588  \",\" \\u2588   \",\"\\u2588\\u2588\\u2588\\u2588\\u2588\"],\n      \"0\": [\" \\u2588\\u2588\\u2588 \",\"\\u2588  \\u2588\\u2588\",\"\\u2588 \\u2588 \\u2588\",\"\\u2588\\u2588  \\u2588\",\" \\u2588\\u2588\\u2588 \"],\n      \"1\": [\"  \\u2588  \",\" \\u2588\\u2588  \",\"  \\u2588  \",\"  \\u2588  \",\"\\u2588\\u2588\\u2588\\u2588\\u2588\"],\n      \"2\": [\" \\u2588\\u2588\\u2588 \",\"\\u2588   \\u2588\",\"  \\u2588\\u2588 \",\" \\u2588   \",\"\\u2588\\u2588\\u2588\\u2588\\u2588\"],\n      \"3\": [\"\\u2588\\u2588\\u2588\\u2588 \",\"    \\u2588\",\" \\u2588\\u2588\\u2588 \",\"    \\u2588\",\"\\u2588\\u2588\\u2588\\u2588 \"],\n      \"4\": [\"\\u2588   \\u2588\",\"\\u2588   \\u2588\",\"\\u2588\\u2588\\u2588\\u2588\\u2588\",\"    \\u2588\",\"    \\u2588\"],\n      \"5\": [\"\\u2588\\u2588\\u2588\\u2588\\u2588\",\"\\u2588    \",\"\\u2588\\u2588\\u2588\\u2588 \",\"    \\u2588\",\"\\u2588\\u2588\\u2588\\u2588 \"],\n      \"6\": [\" \\u2588\\u2588\\u2588\\u2588\",\"\\u2588    \",\"\\u2588\\u2588\\u2588\\u2588 \",\"\\u2588   \\u2588\",\" \\u2588\\u2588\\u2588 \"],\n      \"7\": [\"\\u2588\\u2588\\u2588\\u2588\\u2588\",\"   \\u2588 \",\"  \\u2588  \",\" \\u2588   \",\"\\u2588    \"],\n      \"8\": [\" \\u2588\\u2588\\u2588 \",\"\\u2588   \\u2588\",\" \\u2588\\u2588\\u2588 \",\"\\u2588   \\u2588\",\" \\u2588\\u2588\\u2588 \"],\n      \"9\": [\" \\u2588\\u2588\\u2588 \",\"\\u2588   \\u2588\",\" \\u2588\\u2588\\u2588\\u2588\",\"    \\u2588\",\" \\u2588\\u2588\\u2588\\u2588\"],\n      \" \": [\"   \",\"   \",\"   \",\"   \",\"   \"],\n      \".\": [\"   \",\"   \",\"   \",\" \\u2588 \",\"   \"],\n      \"!\": [\" \\u2588 \",\" \\u2588 \",\" \\u2588 \",\"   \",\" \\u2588 \"],\n      \"?\": [\"\\u2588\\u2588\\u2588 \",\"  \\u2588\\u2588\",\" \\u2588\\u2588 \",\"   \",\"  \\u2588 \"],\n      \"-\": [\"   \",\"\\u2588\\u2588\\u2588\",\"   \",\"   \",\"   \"],\n    }\n  };\n\n  // ─────────────────────────────────────────\n  // STATE\n  // ─────────────────────────────────────────\n  var state = {\n    text: \"\",\n    font: \"standard\",\n    fillChar: \"\",\n    width: \"normal\",\n    orientation: \"horizontal\"\n  };\n\n  // ─────────────────────────────────────────\n  // DOM REFS\n  // ─────────────────────────────────────────\n  var textInput   = document.getElementById(\"aa-text-input\");\n  var fontSelect  = document.getElementById(\"aa-font-select\");\n  var fillInput   = document.getElementById(\"aa-fill-char\");\n  var outputEl    = document.getElementById(\"aa-output\");\n  var charCount   = document.getElementById(\"aa-char-count\");\n  var copyBtn     = document.getElementById(\"aa-copy-btn\");\n  var clearBtn    = document.getElementById(\"aa-clear-btn\");\n  var toast       = document.getElementById(\"aa-toast\");\n  var widthGroup  = document.getElementById(\"aa-width-group\");\n  var orientGroup = document.getElementById(\"aa-orient-group\");\n\n  // ─────────────────────────────────────────\n  // RADIO HIGHLIGHT HELPER\n  // ─────────────────────────────────────────\n  function syncRadioHighlight(group) {\n    var labels = group.querySelectorAll(\".aa-radio-btn\");\n    labels.forEach(function (lbl) {\n      var radio = lbl.querySelector(\"input[type='radio']\");\n      if (radio \u0026\u0026 radio.checked) {\n        lbl.classList.add(\"aa-selected\");\n      } else {\n        lbl.classList.remove(\"aa-selected\");\n      }\n    });\n  }\n\n  // ─────────────────────────────────────────\n  // RENDER ENGINE\n  // ─────────────────────────────────────────\n  function getGlyph(fontDef, ch, customFill) {\n    var key = ch.toUpperCase();\n    var g = fontDef.glyphs[key] || fontDef.glyphs[\" \"] || [\"?\"];\n    // Deep copy\n    var rows = g.slice();\n\n    // Optionally replace fill character\n    if (customFill \u0026\u0026 customFill.length === 1) {\n      var defaultFill = fontDef._fill;\n      rows = rows.map(function (row) {\n        return row.split(\"\").map(function (c) {\n          // Replace the \"ink\" chars — anything that's not space\n          if (c !== \" \" \u0026\u0026 c === defaultFill) return customFill;\n          return c;\n        }).join(\"\");\n      });\n    }\n    return rows;\n  }\n\n  function applyWidth(rows, width) {\n    if (width === \"narrow\") {\n      // Remove one space of padding on each side\n      return rows.map(function (r) { return r.replace(/^ /, \"\").replace(/ $/, \"\"); });\n    } else if (width === \"wide\") {\n      return rows.map(function (r) { return \" \" + r + \" \"; });\n    }\n    return rows;\n  }\n\n  function renderHorizontal(text, fontDef, customFill, width) {\n    var numRows = fontDef._rows;\n    var lines = [];\n    for (var r = 0; r \u003c numRows; r++) lines.push(\"\");\n\n    for (var i = 0; i \u003c text.length; i++) {\n      var glyph = getGlyph(fontDef, text[i], customFill);\n      glyph = applyWidth(glyph, width);\n      // Pad to numRows\n      while (glyph.length \u003c numRows) glyph.push(\"\");\n      for (var r2 = 0; r2 \u003c numRows; r2++) {\n        lines[r2] += glyph[r2] + \" \";\n      }\n    }\n    return lines.join(\"\\n\");\n  }\n\n  function renderVertical(text, fontDef, customFill, width) {\n    var result = [];\n    for (var i = 0; i \u003c text.length; i++) {\n      var glyph = getGlyph(fontDef, text[i], customFill);\n      glyph = applyWidth(glyph, width);\n      result.push(glyph.join(\"\\n\"));\n      if (i \u003c text.length - 1) result.push(\"\"); // blank separator line\n    }\n    return result.join(\"\\n\");\n  }\n\n  function render() {\n    var text = state.text;\n    if (!text) {\n      outputEl.textContent = \"Type text above to generate ASCII art...\";\n      charCount.textContent = \"0 characters\";\n      return;\n    }\n\n    var fontDef  = FONTS[state.font] || FONTS.standard;\n    var fill     = state.fillChar.length === 1 ? state.fillChar : \"\";\n    var art;\n\n    if (state.orientation === \"vertical\") {\n      art = renderVertical(text, fontDef, fill, state.width);\n    } else {\n      art = renderHorizontal(text, fontDef, fill, state.width);\n    }\n\n    outputEl.textContent = art;\n    var len = art.length;\n    charCount.textContent = len.toLocaleString() + \" character\" + (len === 1 ? \"\" : \"s\");\n  }\n\n  // ─────────────────────────────────────────\n  // EVENT LISTENERS\n  // ─────────────────────────────────────────\n  textInput.addEventListener(\"input\", function () {\n    state.text = this.value;\n    render();\n  });\n\n  fontSelect.addEventListener(\"change\", function () {\n    state.font = this.value;\n    render();\n  });\n\n  fillInput.addEventListener(\"input\", function () {\n    state.fillChar = this.value.slice(-1); // keep only last char\n    this.value = state.fillChar;\n    render();\n  });\n\n  widthGroup.addEventListener(\"change\", function (e) {\n    if (e.target \u0026\u0026 e.target.name === \"aa-width\") {\n      state.width = e.target.value;\n      syncRadioHighlight(widthGroup);\n      render();\n    }\n  });\n\n  orientGroup.addEventListener(\"change\", function (e) {\n    if (e.target \u0026\u0026 e.target.name === \"aa-orient\") {\n      state.orientation = e.target.value;\n      syncRadioHighlight(orientGroup);\n      render();\n    }\n  });\n\n  copyBtn.addEventListener(\"click\", function () {\n    var art = outputEl.textContent;\n    if (!art || art === \"Type text above to generate ASCII art...\") return;\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(art).then(showToast).catch(fallbackCopy);\n    } else {\n      fallbackCopy();\n    }\n\n    function fallbackCopy() {\n      var ta = document.createElement(\"textarea\");\n      ta.value = art;\n      ta.style.position = \"fixed\";\n      ta.style.opacity = \"0\";\n      document.body.appendChild(ta);\n      ta.focus();\n      ta.select();\n      try { document.execCommand(\"copy\"); showToast(); } catch (e) {}\n      document.body.removeChild(ta);\n    }\n  });\n\n  function showToast() {\n    toast.style.display = \"inline-flex\";\n    setTimeout(function () { toast.style.display = \"none\"; }, 2200);\n  }\n\n  clearBtn.addEventListener(\"click\", function () {\n    textInput.value = \"\";\n    state.text = \"\";\n    fillInput.value = \"\";\n    state.fillChar = \"\";\n    render();\n    textInput.focus();\n  });\n\n  // Initial render\n  render();\n})();\n\u003c/script\u003e\n\u003c/div\u003e","title":"ASCII Art Generator"},{"content":" Dimensions \u0026amp; Ratio Width (px) Height (px) \u0026#128275; Lock Ratio Ratio unlocked 16:9 Aspect Ratio Common Ratio Presets 16:9 4:3 1:1 21:9 9:16 3:2 2:3 5:4 18:9 Resolution Presets 1080p (FHD) 720p (HD) 4K (UHD) 8K Instagram Square Instagram Portrait Twitter/X Card YouTube Thumbnail 1440p (QHD) 2K Visual Preview 16:9 Info 2,073,600 Total Pixels 2.07 MP Megapixels 1.778 Decimal Ratio Code Snippets CSS HTML Tailwind Copy Resize images → Image Resizer Social media sizes → Social Media Image Resizer ","permalink":"https://productivity-works.com/tools/aspect-ratio-calculator/","summary":"\u003cdiv id=\"ar-app\"\u003e\n\u003cstyle\u003e\n#ar-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  padding: 0 0 2rem;\n  color: #1e293b;\n}\n#ar-app * { box-sizing: border-box; }\n\n#ar-app h2.ar-section-title {\n  font-size: 1rem;\n  font-weight: 700;\n  color: #475569;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  margin: 28px 0 12px;\n  border-bottom: 2px solid #e2e8f0;\n  padding-bottom: 6px;\n}\n\n#ar-app .ar-card {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 20px 24px;\n  margin-bottom: 20px;\n}\n\n#ar-app .ar-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 16px;\n}\n@media (max-width: 600px) {\n  #ar-app .ar-grid { grid-template-columns: 1fr; }\n}\n\n#ar-app label {\n  display: block;\n  font-size: 13px;\n  font-weight: 600;\n  color: #64748b;\n  margin-bottom: 5px;\n}\n\n#ar-app input[type=\"number\"] {\n  width: 100%;\n  padding: 9px 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 15px;\n  color: #1e293b;\n  background: #fff;\n  transition: border-color 0.2s;\n  -moz-appearance: textfield;\n}\n#ar-app input[type=\"number\"]::-webkit-outer-spin-button,\n#ar-app input[type=\"number\"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }\n#ar-app input[type=\"number\"]:focus {\n  outline: none;\n  border-color: #6366f1;\n}\n\n#ar-app .ar-ratio-display {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  background: linear-gradient(135deg, #6366f1 0%, #818cf8 100%);\n  color: #fff;\n  border-radius: 12px;\n  padding: 18px 10px;\n  text-align: center;\n  flex-direction: column;\n}\n#ar-app .ar-ratio-display .ar-ratio-big {\n  font-size: 2rem;\n  font-weight: 800;\n  letter-spacing: -0.02em;\n  line-height: 1;\n}\n#ar-app .ar-ratio-display .ar-ratio-label {\n  font-size: 12px;\n  opacity: 0.8;\n  margin-top: 4px;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n}\n\n#ar-app .ar-lock-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-top: 14px;\n}\n#ar-app .ar-lock-btn {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  padding: 7px 14px;\n  border-radius: 7px;\n  border: 1.5px solid #6366f1;\n  background: #fff;\n  color: #6366f1;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.2s;\n}\n#ar-app .ar-lock-btn.locked {\n  background: #6366f1;\n  color: #fff;\n}\n#ar-app .ar-lock-btn:hover { opacity: 0.85; }\n#ar-app .ar-lock-status {\n  font-size: 13px;\n  color: #64748b;\n}\n\n#ar-app .ar-presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 4px;\n}\n#ar-app .ar-preset-btn {\n  padding: 6px 13px;\n  border-radius: 6px;\n  border: 1.5px solid #cbd5e1;\n  background: #fff;\n  color: #334155;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#ar-app .ar-preset-btn:hover,\n#ar-app .ar-preset-btn.active {\n  border-color: #6366f1;\n  background: #eef2ff;\n  color: #4338ca;\n}\n\n#ar-app .ar-res-presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 4px;\n}\n#ar-app .ar-res-btn {\n  padding: 6px 12px;\n  border-radius: 6px;\n  border: 1.5px solid #cbd5e1;\n  background: #fff;\n  color: #334155;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#ar-app .ar-res-btn:hover {\n  border-color: #6366f1;\n  background: #eef2ff;\n  color: #4338ca;\n}\n\n#ar-app .ar-info-grid {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 12px;\n  margin-top: 4px;\n}\n@media (max-width: 540px) {\n  #ar-app .ar-info-grid { grid-template-columns: repeat(2, 1fr); }\n}\n#ar-app .ar-info-box {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 8px;\n  padding: 12px 14px;\n  text-align: center;\n}\n#ar-app .ar-info-box .ar-info-val {\n  font-size: 1.15rem;\n  font-weight: 700;\n  color: #1e293b;\n}\n#ar-app .ar-info-box .ar-info-key {\n  font-size: 11px;\n  color: #94a3b8;\n  margin-top: 2px;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n\n/* Visual preview */\n#ar-app .ar-preview-wrap {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  min-height: 180px;\n  background: repeating-conic-gradient(#e2e8f0 0% 25%, #f8fafc 0% 50%) 0 0 / 20px 20px;\n  border-radius: 10px;\n  padding: 20px;\n  overflow: hidden;\n}\n#ar-app .ar-preview-rect {\n  background: linear-gradient(135deg, #6366f1 0%, #818cf8 60%, #a5b4fc 100%);\n  border-radius: 6px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: #fff;\n  font-size: 13px;\n  font-weight: 600;\n  transition: all 0.3s ease;\n  min-width: 40px;\n  min-height: 30px;\n  box-shadow: 0 4px 20px rgba(99,102,241,0.35);\n}\n\n/* Code output */\n#ar-app .ar-code-block {\n  background: #1e293b;\n  color: #a5b4fc;\n  border-radius: 10px;\n  padding: 16px 18px;\n  font-family: 'Fira Mono', 'Consolas', monospace;\n  font-size: 13px;\n  line-height: 1.7;\n  overflow-x: auto;\n  white-space: pre;\n  position: relative;\n}\n#ar-app .ar-copy-btn {\n  position: absolute;\n  top: 10px;\n  right: 12px;\n  padding: 4px 11px;\n  background: #334155;\n  color: #cbd5e1;\n  border: none;\n  border-radius: 5px;\n  font-size: 12px;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n#ar-app .ar-copy-btn:hover { background: #475569; }\n#ar-app .ar-copy-btn.copied { background: #059669; color: #fff; }\n\n#ar-app .ar-tabs {\n  display: flex;\n  gap: 4px;\n  margin-bottom: 10px;\n}\n#ar-app .ar-tab {\n  padding: 6px 14px;\n  border-radius: 6px;\n  border: 1.5px solid #cbd5e1;\n  background: #fff;\n  color: #64748b;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#ar-app .ar-tab.active {\n  border-color: #6366f1;\n  background: #6366f1;\n  color: #fff;\n}\n\u003c/style\u003e\n\u003ch2 class=\"ar-section-title\"\u003eDimensions \u0026amp; Ratio\u003c/h2\u003e\n\u003cdiv class=\"ar-card\"\u003e\n  \u003cdiv class=\"ar-grid\"\u003e\n    \u003cdiv\u003e\n      \u003cdiv class=\"ar-grid\" style=\"gap:10px;\"\u003e\n        \u003cdiv\u003e\n          \u003clabel for=\"ar-width\"\u003eWidth (px)\u003c/label\u003e\n          \u003cinput type=\"number\" id=\"ar-width\" value=\"1920\" min=\"1\" max=\"99999\"\u003e\n        \u003c/div\u003e\n        \u003cdiv\u003e\n          \u003clabel for=\"ar-height\"\u003eHeight (px)\u003c/label\u003e\n          \u003cinput type=\"number\" id=\"ar-height\" value=\"1080\" min=\"1\" max=\"99999\"\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"ar-lock-row\"\u003e\n        \u003cbutton class=\"ar-lock-btn\" id=\"ar-lock-btn\" onclick=\"arToggleLock()\"\u003e\n          \u003cspan id=\"ar-lock-icon\"\u003e\u0026#128275;\u003c/span\u003e Lock Ratio\n        \u003c/button\u003e\n        \u003cspan class=\"ar-lock-status\" id=\"ar-lock-status\"\u003eRatio unlocked\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ar-ratio-display\"\u003e\n      \u003cdiv class=\"ar-ratio-big\" id=\"ar-ratio-out\"\u003e16:9\u003c/div\u003e\n      \u003cdiv class=\"ar-ratio-label\"\u003eAspect Ratio\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003ch2 class=\"ar-section-title\"\u003eCommon Ratio Presets\u003c/h2\u003e\n\u003cdiv class=\"ar-card\"\u003e\n  \u003cdiv class=\"ar-presets\" id=\"ar-presets\"\u003e\n    \u003cbutton class=\"ar-preset-btn\" onclick=\"arSetRatio(16,9)\"\u003e16:9\u003c/button\u003e\n    \u003cbutton class=\"ar-preset-btn\" onclick=\"arSetRatio(4,3)\"\u003e4:3\u003c/button\u003e\n    \u003cbutton class=\"ar-preset-btn\" onclick=\"arSetRatio(1,1)\"\u003e1:1\u003c/button\u003e\n    \u003cbutton class=\"ar-preset-btn\" onclick=\"arSetRatio(21,9)\"\u003e21:9\u003c/button\u003e\n    \u003cbutton class=\"ar-preset-btn\" onclick=\"arSetRatio(9,16)\"\u003e9:16\u003c/button\u003e\n    \u003cbutton class=\"ar-preset-btn\" onclick=\"arSetRatio(3,2)\"\u003e3:2\u003c/button\u003e\n    \u003cbutton class=\"ar-preset-btn\" onclick=\"arSetRatio(2,3)\"\u003e2:3\u003c/button\u003e\n    \u003cbutton class=\"ar-preset-btn\" onclick=\"arSetRatio(5,4)\"\u003e5:4\u003c/button\u003e\n    \u003cbutton class=\"ar-preset-btn\" onclick=\"arSetRatio(18,9)\"\u003e18:9\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003ch2 class=\"ar-section-title\"\u003eResolution Presets\u003c/h2\u003e\n\u003cdiv class=\"ar-card\"\u003e\n  \u003cdiv class=\"ar-res-presets\"\u003e\n    \u003cbutton class=\"ar-res-btn\" onclick=\"arSetRes(1920,1080)\"\u003e1080p (FHD)\u003c/button\u003e\n    \u003cbutton class=\"ar-res-btn\" onclick=\"arSetRes(1280,720)\"\u003e720p (HD)\u003c/button\u003e\n    \u003cbutton class=\"ar-res-btn\" onclick=\"arSetRes(3840,2160)\"\u003e4K (UHD)\u003c/button\u003e\n    \u003cbutton class=\"ar-res-btn\" onclick=\"arSetRes(7680,4320)\"\u003e8K\u003c/button\u003e\n    \u003cbutton class=\"ar-res-btn\" onclick=\"arSetRes(1080,1080)\"\u003eInstagram Square\u003c/button\u003e\n    \u003cbutton class=\"ar-res-btn\" onclick=\"arSetRes(1080,1350)\"\u003eInstagram Portrait\u003c/button\u003e\n    \u003cbutton class=\"ar-res-btn\" onclick=\"arSetRes(1200,675)\"\u003eTwitter/X Card\u003c/button\u003e\n    \u003cbutton class=\"ar-res-btn\" onclick=\"arSetRes(1280,720)\"\u003eYouTube Thumbnail\u003c/button\u003e\n    \u003cbutton class=\"ar-res-btn\" onclick=\"arSetRes(2560,1440)\"\u003e1440p (QHD)\u003c/button\u003e\n    \u003cbutton class=\"ar-res-btn\" onclick=\"arSetRes(2048,1152)\"\u003e2K\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003ch2 class=\"ar-section-title\"\u003eVisual Preview\u003c/h2\u003e\n\u003cdiv class=\"ar-card\" style=\"padding:16px;\"\u003e\n  \u003cdiv class=\"ar-preview-wrap\" id=\"ar-preview-wrap\"\u003e\n    \u003cdiv class=\"ar-preview-rect\" id=\"ar-preview-rect\"\u003e16:9\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003ch2 class=\"ar-section-title\"\u003eInfo\u003c/h2\u003e\n\u003cdiv class=\"ar-card\" style=\"padding:16px 20px;\"\u003e\n  \u003cdiv class=\"ar-info-grid\"\u003e\n    \u003cdiv class=\"ar-info-box\"\u003e\n      \u003cdiv class=\"ar-info-val\" id=\"ar-pixels\"\u003e2,073,600\u003c/div\u003e\n      \u003cdiv class=\"ar-info-key\"\u003eTotal Pixels\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ar-info-box\"\u003e\n      \u003cdiv class=\"ar-info-val\" id=\"ar-mp\"\u003e2.07 MP\u003c/div\u003e\n      \u003cdiv class=\"ar-info-key\"\u003eMegapixels\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ar-info-box\"\u003e\n      \u003cdiv class=\"ar-info-val\" id=\"ar-decimal\"\u003e1.778\u003c/div\u003e\n      \u003cdiv class=\"ar-info-key\"\u003eDecimal Ratio\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003ch2 class=\"ar-section-title\"\u003eCode Snippets\u003c/h2\u003e\n\u003cdiv class=\"ar-card\" style=\"padding:16px 20px;\"\u003e\n  \u003cdiv class=\"ar-tabs\"\u003e\n    \u003cbutton class=\"ar-tab active\" id=\"ar-tab-css\" onclick=\"arShowTab('css')\"\u003eCSS\u003c/button\u003e\n    \u003cbutton class=\"ar-tab\" id=\"ar-tab-html\" onclick=\"arShowTab('html')\"\u003eHTML\u003c/button\u003e\n    \u003cbutton class=\"ar-tab\" id=\"ar-tab-tailwind\" onclick=\"arShowTab('tailwind')\"\u003eTailwind\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv style=\"position:relative;\"\u003e\n    \u003cpre class=\"ar-code-block\" id=\"ar-code-out\"\u003e\u003c/pre\u003e\n    \u003cbutton class=\"ar-copy-btn\" id=\"ar-copy-btn\" onclick=\"arCopyCode()\"\u003eCopy\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var locked = false;\n  var lockedRatioW = 16, lockedRatioH = 9;\n  var currentTab = 'css';\n\n  function gcd(a, b) {\n    a = Math.round(a); b = Math.round(b);\n    while (b) { var t = b; b = a % b; a = t; }\n    return a;\n  }\n\n  function getW() { return parseFloat(document.getElementById('ar-width').value) || 1; }\n  function getH() { return parseFloat(document.getElementById('ar-height').value) || 1; }\n\n  function update() {\n    var w = getW(), h = getH();\n    var g = gcd(w, h);\n    var rw = w / g, rh = h / g;\n    var ratioStr = rw + ':' + rh;\n    document.getElementById('ar-ratio-out').textContent = ratioStr;\n    document.getElementById('ar-pixels').textContent = (w * h).toLocaleString();\n    var mp = (w * h) / 1000000;\n    document.getElementById('ar-mp').textContent = mp.toFixed(2) + ' MP';\n    document.getElementById('ar-decimal').textContent = (w / h).toFixed(3);\n    updatePreview(w, h, ratioStr);\n    updateCode(w, h, rw, rh);\n    highlightActivePreset(rw, rh);\n  }\n\n  function updatePreview(w, h, ratioStr) {\n    var wrap = document.getElementById('ar-preview-wrap');\n    var rect = document.getElementById('ar-preview-rect');\n    var maxW = wrap.clientWidth - 40 || 300;\n    var maxH = 180;\n    var ratio = w / h;\n    var pw, ph;\n    if (ratio \u003e= 1) {\n      pw = Math.min(maxW, maxH * ratio);\n      ph = pw / ratio;\n      if (ph \u003e maxH) { ph = maxH; pw = ph * ratio; }\n    } else {\n      ph = Math.min(maxH, maxW / ratio);\n      pw = ph * ratio;\n      if (pw \u003e maxW) { pw = maxW; ph = pw / ratio; }\n    }\n    rect.style.width = Math.round(pw) + 'px';\n    rect.style.height = Math.round(ph) + 'px';\n    rect.textContent = ratioStr;\n  }\n\n  function updateCode(w, h, rw, rh) {\n    var decimalRatio = (w / h).toFixed(4);\n    var codes = {\n      css: '/* Modern CSS aspect-ratio */\\n.element {\\n  width: 100%;\\n  aspect-ratio: ' + rw + ' / ' + rh + ';\\n}\\n\\n/* Padding-bottom hack (legacy) */\\n.wrapper {\\n  position: relative;\\n  padding-bottom: ' + ((h / w) * 100).toFixed(4) + '%;\\n  height: 0;\\n  overflow: hidden;\\n}\\n.wrapper \u003e * {\\n  position: absolute;\\n  top: 0; left: 0;\\n  width: 100%; height: 100%;\\n}',\n      html: '\u003cdiv class=\"ratio-box\" style=\"\\n  width: ' + w + 'px;\\n  height: ' + h + 'px;\\n  aspect-ratio: ' + rw + ' / ' + rh + ';\\n\"\u003e\\n  \u003c!-- ' + w + ' x ' + h + ' (' + rw + ':' + rh + ') --\u003e\\n\u003c/div\u003e',\n      tailwind: '\u003c!-- Tailwind CSS --\u003e\\n\u003c!-- Note: use [aspect-ratio:' + rw + '/' + rh + '] for custom ratios --\u003e\\n\u003cdiv class=\"w-full [aspect-ratio:' + rw + '/' + rh + ']\"\u003e\\n  \u003c!-- ' + w + ' x ' + h + ' --\u003e\\n\u003c/div\u003e\\n\\n\u003c!-- Or use inline style --\u003e\\n\u003cdiv style=\"aspect-ratio:' + rw + '/' + rh + '\" class=\"w-full\"\u003e\\n  \u003c!-- content --\u003e\\n\u003c/div\u003e'\n    };\n    document.getElementById('ar-code-out').textContent = codes[currentTab];\n  }\n\n  function highlightActivePreset(rw, rh) {\n    var btns = document.querySelectorAll('#ar-app .ar-preset-btn');\n    btns.forEach(function(btn) {\n      btn.classList.remove('active');\n    });\n    var presets = [[16,9],[4,3],[1,1],[21,9],[9,16],[3,2],[2,3],[5,4],[18,9]];\n    presets.forEach(function(p, i) {\n      if (p[0] === rw \u0026\u0026 p[1] === rh) {\n        btns[i] \u0026\u0026 btns[i].classList.add('active');\n      }\n    });\n  }\n\n  window.arToggleLock = function() {\n    locked = !locked;\n    var w = getW(), h = getH();\n    if (locked) {\n      var g = gcd(w, h);\n      lockedRatioW = w / g;\n      lockedRatioH = h / g;\n    }\n    document.getElementById('ar-lock-btn').classList.toggle('locked', locked);\n    document.getElementById('ar-lock-icon').textContent = locked ? '\\uD83D\\uDD12' : '\\uD83D\\uDD13';\n    document.getElementById('ar-lock-status').textContent = locked\n      ? 'Ratio locked: ' + lockedRatioW + ':' + lockedRatioH\n      : 'Ratio unlocked';\n  };\n\n  window.arSetRatio = function(rw, rh) {\n    var w = getW();\n    var newH = Math.round(w * rh / rw);\n    document.getElementById('ar-height').value = newH;\n    if (locked) { lockedRatioW = rw; lockedRatioH = rh; }\n    update();\n  };\n\n  window.arSetRes = function(w, h) {\n    document.getElementById('ar-width').value = w;\n    document.getElementById('ar-height').value = h;\n    if (locked) {\n      var g = gcd(w, h);\n      lockedRatioW = w / g;\n      lockedRatioH = h / g;\n    }\n    update();\n  };\n\n  window.arShowTab = function(tab) {\n    currentTab = tab;\n    ['css','html','tailwind'].forEach(function(t) {\n      document.getElementById('ar-tab-' + t).classList.toggle('active', t === tab);\n    });\n    var w = getW(), h = getH();\n    var g = gcd(w, h);\n    updateCode(w, h, w/g, h/g);\n    document.getElementById('ar-copy-btn').textContent = 'Copy';\n    document.getElementById('ar-copy-btn').classList.remove('copied');\n  };\n\n  window.arCopyCode = function() {\n    var code = document.getElementById('ar-code-out').textContent;\n    navigator.clipboard.writeText(code).then(function() {\n      var btn = document.getElementById('ar-copy-btn');\n      btn.textContent = 'Copied!';\n      btn.classList.add('copied');\n      setTimeout(function() {\n        btn.textContent = 'Copy';\n        btn.classList.remove('copied');\n      }, 1800);\n    });\n  };\n\n  function onWidthChange() {\n    if (locked) {\n      var w = getW();\n      document.getElementById('ar-height').value = Math.round(w * lockedRatioH / lockedRatioW);\n    }\n    update();\n  }\n\n  function onHeightChange() {\n    if (locked) {\n      var h = getH();\n      document.getElementById('ar-width').value = Math.round(h * lockedRatioW / lockedRatioH);\n    }\n    update();\n  }\n\n  document.getElementById('ar-width').addEventListener('input', onWidthChange);\n  document.getElementById('ar-height').addEventListener('input', onHeightChange);\n\n  update();\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003eResize images → \u003ca href=\"https://productivity-works.com/tools/image-resizer/\"\u003eImage Resizer\u003c/a\u003e\n\nSocial media sizes → \u003ca href=\"https://productivity-works.com/tools/social-media-image-resizer/\"\u003eSocial Media Image Resizer\u003c/a\u003e\n\u003c/p\u003e","title":"Aspect Ratio Calculator"},{"content":" Aspect Ratio Crop Calculator Original Image Size (pixels)\nWidth Height Target Aspect Ratio\n16:9 4:3 1:1 3:2 21:9 9:16 5:4 Custom Custom ratio: : Crop Width — pixels Crop Height — pixels Crop Offset (center) — x, y from top-left Cropped Away (W) — pixels removed Cropped Away (H) — pixels removed Pixels Kept — % of original Visual Crop Preview Select a target ratio and enter image dimensions to calculate crop.\nRelated Tools Calculate aspect ratios → Aspect Ratio Calculator Resize images in the browser → Image Resizer Convert images to Base64 → Image to Base64 ","permalink":"https://productivity-works.com/tools/aspect-ratio-crop/","summary":"\u003cdiv id=\"arc-app\"\u003e\n\u003cstyle\u003e\n#arc-app {\n  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;\n  background: #0f0f13;\n  color: #e2e8f0;\n  border-radius: 12px;\n  padding: 24px;\n  margin: 0 auto;\n  max-width: 860px;\n  box-sizing: border-box;\n}\n#arc-app * { box-sizing: border-box; }\n\n#arc-app h2 {\n  font-size: 1.05rem;\n  font-weight: 600;\n  color: #f1f5f9;\n  margin: 0 0 18px;\n  letter-spacing: 0.02em;\n}\n\n/* section labels */\n.arc-label {\n  font-size: 0.72rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #64748b;\n  margin-bottom: 8px;\n  display: block;\n}\n\n/* input grid */\n.arc-row {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 12px;\n  margin-bottom: 16px;\n}\n\n@media (max-width: 500px) {\n  .arc-row { grid-template-columns: 1fr; }\n}\n\n.arc-field {\n  display: flex;\n  flex-direction: column;\n  gap: 5px;\n}\n\n.arc-field label {\n  font-size: 0.78rem;\n  color: #94a3b8;\n  font-weight: 600;\n}\n\n.arc-input {\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 7px;\n  color: #e2e8f0;\n  font-size: 1rem;\n  padding: 9px 12px;\n  font-family: inherit;\n  outline: none;\n  transition: border-color 0.2s;\n  width: 100%;\n}\n.arc-input:focus { border-color: #6366f1; }\n.arc-input::placeholder { color: #4a5568; }\n\n/* ratio button pills */\n.arc-ratio-row {\n  display: flex;\n  gap: 6px;\n  flex-wrap: wrap;\n  margin-bottom: 16px;\n}\n\n.arc-ratio-btn {\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 20px;\n  color: #94a3b8;\n  font-size: 0.8rem;\n  font-weight: 600;\n  padding: 5px 14px;\n  cursor: pointer;\n  font-family: inherit;\n  transition: all 0.15s;\n  white-space: nowrap;\n}\n.arc-ratio-btn:hover { border-color: #6366f1; color: #a5b4fc; }\n.arc-ratio-btn.arc-active { background: rgba(99,102,241,0.18); border-color: #6366f1; color: #a5b4fc; }\n\n/* custom ratio inputs */\n.arc-custom-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin-bottom: 16px;\n  flex-wrap: wrap;\n}\n.arc-custom-row label { font-size: 0.78rem; color: #94a3b8; white-space: nowrap; }\n.arc-custom-input {\n  width: 68px;\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 7px;\n  color: #e2e8f0;\n  font-size: 0.95rem;\n  padding: 7px 10px;\n  font-family: inherit;\n  outline: none;\n  transition: border-color 0.2s;\n  text-align: center;\n}\n.arc-custom-input:focus { border-color: #6366f1; }\n.arc-sep { color: #64748b; font-size: 1rem; font-weight: 700; }\n\n/* results */\n.arc-results {\n  display: grid;\n  grid-template-columns: 1fr 1fr 1fr;\n  gap: 10px;\n  margin-bottom: 20px;\n}\n@media (max-width: 500px) { .arc-results { grid-template-columns: 1fr; } }\n\n.arc-card {\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  padding: 12px 14px;\n}\n.arc-card-label {\n  font-size: 0.68rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #64748b;\n  margin-bottom: 4px;\n}\n.arc-card-value {\n  font-size: 1.1rem;\n  font-weight: 700;\n  color: #a5b4fc;\n  font-family: 'Courier New', monospace;\n}\n.arc-card-unit {\n  font-size: 0.72rem;\n  color: #475569;\n  margin-top: 2px;\n}\n.arc-card.arc-highlight { border-color: #6366f1; }\n.arc-card.arc-highlight .arc-card-label { color: #6366f1; }\n\n/* canvas preview */\n.arc-preview-wrap {\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  padding: 16px;\n  margin-bottom: 16px;\n}\n.arc-preview-title {\n  font-size: 0.72rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #64748b;\n  margin-bottom: 12px;\n}\n#arc-canvas {\n  display: block;\n  margin: 0 auto;\n  border-radius: 4px;\n  max-width: 100%;\n}\n\n/* note */\n.arc-note {\n  font-size: 0.78rem;\n  color: #4a5568;\n  line-height: 1.6;\n}\n.arc-note strong { color: #64748b; }\n\u003c/style\u003e\n\u003ch2\u003eAspect Ratio Crop Calculator\u003c/h2\u003e\n\u003cp\u003e\u003cspan class=\"arc-label\"\u003eOriginal Image Size (pixels)\u003c/span\u003e\u003c/p\u003e","title":"Aspect Ratio Crop Calculator - Free Online Crop Size Tool"},{"content":" Barcode Settings Barcode Type Code 128 (most versatile) Code 39 EAN-13 Data / Value\nBar Color Background Color Bar Width: 2px Height: 80px Generate Barcode\nDownload PNG Generate QR codes \u0026rarr; QR Code Generator\nCreate invoices \u0026rarr; Invoice Generator\nRelated Tools Qr Code Generator ","permalink":"https://productivity-works.com/tools/barcode-generator/","summary":"\u003cdiv id=\"bg-app\"\u003e\n\u003cstyle\u003e\n#bg-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 720px;\n  margin: 0 auto;\n  color: #1e293b;\n}\n#bg-app h2.bg-section-title {\n  font-size: 1.1rem;\n  font-weight: 700;\n  color: #0f172a;\n  margin: 24px 0 12px;\n  padding-bottom: 6px;\n  border-bottom: 2px solid #e2e8f0;\n}\n#bg-app .bg-card {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 20px;\n  margin-bottom: 18px;\n}\n#bg-app label.bg-label {\n  display: block;\n  font-size: 13px;\n  font-weight: 600;\n  color: #475569;\n  margin-bottom: 5px;\n}\n#bg-app select.bg-select,\n#bg-app input.bg-input {\n  width: 100%;\n  padding: 9px 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  font-size: 14px;\n  background: #fff;\n  color: #1e293b;\n  box-sizing: border-box;\n  margin-bottom: 12px;\n  transition: border-color 0.2s;\n}\n#bg-app select.bg-select:focus,\n#bg-app input.bg-input:focus {\n  outline: none;\n  border-color: #3b82f6;\n}\n#bg-app .bg-row {\n  display: flex;\n  gap: 14px;\n  flex-wrap: wrap;\n}\n#bg-app .bg-col {\n  flex: 1;\n  min-width: 140px;\n}\n#bg-app input[type=\"color\"].bg-color {\n  width: 100%;\n  height: 40px;\n  padding: 2px 4px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  background: #fff;\n  cursor: pointer;\n  margin-bottom: 12px;\n  box-sizing: border-box;\n}\n#bg-app input[type=\"range\"].bg-range {\n  width: 100%;\n  margin-bottom: 4px;\n}\n#bg-app .bg-range-val {\n  font-size: 12px;\n  color: #64748b;\n  margin-bottom: 12px;\n  display: block;\n}\n#bg-app .bg-samples {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 12px;\n}\n#bg-app button.bg-sample-btn {\n  padding: 5px 12px;\n  font-size: 12px;\n  border: 1.5px solid #3b82f6;\n  border-radius: 6px;\n  background: #eff6ff;\n  color: #1d4ed8;\n  cursor: pointer;\n  font-weight: 600;\n  transition: background 0.15s;\n}\n#bg-app button.bg-sample-btn:hover {\n  background: #dbeafe;\n}\n#bg-app button.bg-generate-btn {\n  width: 100%;\n  padding: 12px;\n  background: #2563eb;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 15px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.2s;\n  margin-top: 4px;\n}\n#bg-app button.bg-generate-btn:hover {\n  background: #1d4ed8;\n}\n#bg-app .bg-error {\n  background: #fef2f2;\n  border: 1.5px solid #fca5a5;\n  border-radius: 8px;\n  color: #b91c1c;\n  font-size: 13px;\n  padding: 10px 14px;\n  margin-top: 10px;\n  display: none;\n}\n#bg-app .bg-result {\n  display: none;\n  margin-top: 18px;\n}\n#bg-app .bg-canvas-wrap {\n  background: #fff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 20px;\n  text-align: center;\n  margin-bottom: 14px;\n}\n#bg-app canvas#bg-canvas {\n  display: block;\n  margin: 0 auto;\n  max-width: 100%;\n}\n#bg-app button.bg-dl-btn {\n  display: inline-block;\n  padding: 10px 26px;\n  background: #0f172a;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 14px;\n  font-weight: 700;\n  cursor: pointer;\n  text-decoration: none;\n  transition: background 0.2s;\n}\n#bg-app button.bg-dl-btn:hover {\n  background: #1e293b;\n}\n#bg-app .bg-info-box {\n  background: #f0fdf4;\n  border: 1px solid #bbf7d0;\n  border-radius: 8px;\n  padding: 14px 16px;\n  font-size: 13px;\n  color: #166534;\n  margin-top: 14px;\n  line-height: 1.6;\n}\n#bg-app .bg-crosslinks {\n  margin-top: 24px;\n  font-size: 14px;\n  color: #475569;\n}\n#bg-app .bg-crosslinks a {\n  color: #2563eb;\n  text-decoration: none;\n}\n#bg-app .bg-crosslinks a:hover {\n  text-decoration: underline;\n}\n\u003c/style\u003e\n\u003ch2 class=\"bg-section-title\"\u003eBarcode Settings\u003c/h2\u003e\n\u003cdiv class=\"bg-card\"\u003e\n  \u003clabel class=\"bg-label\" for=\"bg-type\"\u003eBarcode Type\u003c/label\u003e\n  \u003cselect id=\"bg-type\" class=\"bg-select\"\u003e\n    \u003coption value=\"code128\"\u003eCode 128 (most versatile)\u003c/option\u003e\n    \u003coption value=\"code39\"\u003eCode 39\u003c/option\u003e\n    \u003coption value=\"ean13\"\u003eEAN-13\u003c/option\u003e\n  \u003c/select\u003e\n\u003cp\u003e\u003clabel class=\"bg-label\" for=\"bg-data\"\u003eData / Value\u003c/label\u003e\u003c/p\u003e","title":"Barcode Generator"},{"content":" Text Encoder / Decoder Input AUTO-DETECT 0 chars Encode Decode Clear URL-safe mode (+/ → -_) Auto-detect mode Output as data URI \u0026#8645; Swap Input / Output Output 0 chars Copy Output File to Base64 \u0026#128196; Drag \u0026amp; drop a file here, or click to browse Choose File Include data URI prefix (e.g. data:image/png;base64,...) What is Base64 Encoding? Base64 is a binary-to-text encoding scheme that represents binary data using a set of 64 printable ASCII characters (A–Z, a–z, 0–9, +, and /), with = used for padding. The name comes from the 64 characters used in the alphabet. Each Base64 character encodes exactly 6 bits of binary data, meaning every 3 bytes of input become 4 Base64 characters — increasing the data size by approximately 33%. Base64 was standardized in RFC 4648 and is universally supported across programming languages, browsers, and operating systems.\nBase64 is not a form of encryption — it is purely an encoding mechanism designed to safely transport binary data over channels that were designed to handle text. A URL-safe variant replaces + with - and / with _, making the encoded string safe to embed in URLs and file names without percent-encoding. Data URIs (data:\u0026lt;mime\u0026gt;;base64,\u0026lt;data\u0026gt;) are a common application, allowing images and other binary assets to be embedded directly inside HTML, CSS, or JSON without a separate HTTP request.\nCommon Use Cases Email attachments — MIME email protocols use Base64 to encode binary files (images, PDFs) so they can travel through text-based email systems without corruption. Embedding images in HTML/CSS — Small icons and background images can be inlined as Base64 data URIs to eliminate extra HTTP requests and simplify asset bundling. API and JSON payloads — Binary data (audio, images, certificates) is Base64-encoded before being included in JSON fields or HTTP request/response bodies. JWT tokens — JSON Web Tokens use URL-safe Base64 to encode their header and payload sections for compact, URL-friendly transmission. Configuration and secrets — Kubernetes secrets, environment variables, and configuration files often store binary credentials or keys as Base64 strings for safe storage in text-based formats. Related Tools Format and validate JSON → JSON Formatter Generate secure passwords → Password Generator Count words and characters → Word Counter ","permalink":"https://productivity-works.com/tools/base64-encoder/","summary":"\u003cdiv id=\"base64-app\"\u003e\n\u003cstyle\u003e\n#base64-app {\n  font-family: ui-sans-serif, system-ui, -apple-system, sans-serif;\n  color: #e2e8f0;\n  max-width: 860px;\n  margin: 0 auto;\n  padding: 0;\n}\n\n#base64-app * {\n  box-sizing: border-box;\n}\n\n.b64-card {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 12px;\n  padding: 24px;\n  margin-bottom: 16px;\n}\n\n.b64-label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #94a3b8;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 8px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n\n.b64-count {\n  font-size: 12px;\n  font-weight: 400;\n  color: #64748b;\n  text-transform: none;\n  letter-spacing: 0;\n}\n\n.b64-textarea {\n  width: 100%;\n  min-height: 140px;\n  background: #0f172a;\n  border: 1px solid #334155;\n  border-radius: 8px;\n  color: #e2e8f0;\n  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n  font-size: 14px;\n  line-height: 1.6;\n  padding: 12px;\n  resize: vertical;\n  transition: border-color 0.2s;\n  outline: none;\n}\n\n.b64-textarea:focus {\n  border-color: #06b6d4;\n}\n\n.b64-textarea.b64-error-border {\n  border-color: #f87171;\n}\n\n.b64-textarea::placeholder {\n  color: #475569;\n}\n\n.b64-btn-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  margin: 16px 0;\n  align-items: center;\n}\n\n.b64-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  padding: 9px 20px;\n  border-radius: 8px;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  border: none;\n  transition: background 0.15s, transform 0.1s, opacity 0.15s;\n  white-space: nowrap;\n  outline: none;\n}\n\n.b64-btn:active {\n  transform: scale(0.97);\n}\n\n.b64-btn-primary {\n  background: #06b6d4;\n  color: #0f172a;\n}\n\n.b64-btn-primary:hover {\n  background: #22d3ee;\n}\n\n.b64-btn-secondary {\n  background: #475569;\n  color: #e2e8f0;\n}\n\n.b64-btn-secondary:hover {\n  background: #64748b;\n}\n\n.b64-btn-ghost {\n  background: transparent;\n  color: #94a3b8;\n  border: 1px solid #334155;\n}\n\n.b64-btn-ghost:hover {\n  background: #1e293b;\n  color: #e2e8f0;\n  border-color: #475569;\n}\n\n.b64-btn-success {\n  background: #10b981 !important;\n  color: #fff !important;\n}\n\n.b64-btn-danger {\n  background: #ef4444;\n  color: #fff;\n}\n\n.b64-btn-danger:hover {\n  background: #f87171;\n}\n\n.b64-divider-row {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  margin: 4px 0;\n}\n\n.b64-swap-btn {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  padding: 7px 16px;\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 20px;\n  color: #94a3b8;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n\n.b64-swap-btn:hover {\n  background: #334155;\n  color: #e2e8f0;\n  border-color: #475569;\n}\n\n.b64-toggles {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 16px;\n  margin: 12px 0 0;\n  padding-top: 12px;\n  border-top: 1px solid #1e293b;\n}\n\n.b64-toggle-item {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  cursor: pointer;\n  user-select: none;\n}\n\n.b64-toggle-item input[type=\"checkbox\"] {\n  appearance: none;\n  -webkit-appearance: none;\n  width: 36px;\n  height: 20px;\n  background: #334155;\n  border-radius: 10px;\n  position: relative;\n  cursor: pointer;\n  transition: background 0.2s;\n  flex-shrink: 0;\n}\n\n.b64-toggle-item input[type=\"checkbox\"]:checked {\n  background: #06b6d4;\n}\n\n.b64-toggle-item input[type=\"checkbox\"]::after {\n  content: '';\n  position: absolute;\n  width: 14px;\n  height: 14px;\n  background: #fff;\n  border-radius: 50%;\n  top: 3px;\n  left: 3px;\n  transition: left 0.2s;\n}\n\n.b64-toggle-item input[type=\"checkbox\"]:checked::after {\n  left: 19px;\n}\n\n.b64-toggle-label {\n  font-size: 13px;\n  color: #94a3b8;\n}\n\n.b64-error {\n  background: #450a0a;\n  border: 1px solid #f87171;\n  border-radius: 8px;\n  color: #fca5a5;\n  font-size: 13px;\n  padding: 10px 14px;\n  margin-top: 8px;\n  display: none;\n}\n\n.b64-error.visible {\n  display: block;\n}\n\n.b64-auto-badge {\n  display: inline-block;\n  background: #164e63;\n  color: #67e8f9;\n  font-size: 11px;\n  font-weight: 700;\n  padding: 2px 8px;\n  border-radius: 10px;\n  margin-left: 6px;\n  vertical-align: middle;\n  letter-spacing: 0.03em;\n}\n\n/* File drop zone */\n.b64-drop-zone {\n  border: 2px dashed #334155;\n  border-radius: 10px;\n  padding: 28px 20px;\n  text-align: center;\n  cursor: pointer;\n  transition: all 0.2s;\n  background: #0f172a;\n}\n\n.b64-drop-zone:hover,\n.b64-drop-zone.b64-drag-over {\n  border-color: #06b6d4;\n  background: #0c1a2e;\n}\n\n.b64-drop-zone-icon {\n  font-size: 32px;\n  margin-bottom: 8px;\n  line-height: 1;\n}\n\n.b64-drop-zone-text {\n  font-size: 14px;\n  color: #64748b;\n  margin-bottom: 10px;\n}\n\n.b64-drop-zone-text strong {\n  color: #94a3b8;\n}\n\n.b64-file-input {\n  display: none;\n}\n\n.b64-file-info {\n  background: #0f172a;\n  border: 1px solid #334155;\n  border-radius: 8px;\n  padding: 10px 14px;\n  font-size: 13px;\n  color: #94a3b8;\n  margin-top: 12px;\n  display: none;\n}\n\n.b64-file-info.visible {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  flex-wrap: wrap;\n}\n\n.b64-file-name {\n  color: #e2e8f0;\n  font-weight: 600;\n  word-break: break-all;\n}\n\n.b64-file-size {\n  color: #64748b;\n  font-size: 12px;\n}\n\n.b64-section-title {\n  font-size: 15px;\n  font-weight: 700;\n  color: #cbd5e1;\n  margin-bottom: 14px;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n\n.b64-section-title::before {\n  content: '';\n  display: block;\n  width: 3px;\n  height: 16px;\n  background: #06b6d4;\n  border-radius: 2px;\n}\n\n@media (max-width: 600px) {\n  .b64-card {\n    padding: 16px;\n  }\n  .b64-btn {\n    padding: 8px 14px;\n    font-size: 13px;\n  }\n  .b64-textarea {\n    min-height: 110px;\n    font-size: 13px;\n  }\n  .b64-btn-row {\n    gap: 8px;\n  }\n}\n\u003c/style\u003e\n\u003c!-- INPUT SECTION --\u003e\n\u003cdiv class=\"b64-card\"\u003e\n  \u003cdiv class=\"b64-section-title\"\u003eText Encoder / Decoder\u003c/div\u003e\n  \u003cdiv class=\"b64-label\"\u003e\n    \u003cspan\u003eInput \u003cspan id=\"b64-auto-indicator\" class=\"b64-auto-badge\" style=\"display:none;\"\u003eAUTO-DETECT\u003c/span\u003e\u003c/span\u003e\n    \u003cspan class=\"b64-count\" id=\"b64-input-count\"\u003e0 chars\u003c/span\u003e\n  \u003c/div\u003e\n  \u003ctextarea\n    class=\"b64-textarea\"\n    id=\"b64-input\"\n    placeholder=\"Type or paste text / Base64 string here...\"\n    spellcheck=\"false\"\n  \u003e\u003c/textarea\u003e\n  \u003cdiv class=\"b64-btn-row\"\u003e\n    \u003cbutton class=\"b64-btn b64-btn-primary\" id=\"b64-encode-btn\" onclick=\"b64Encode()\"\u003eEncode\u003c/button\u003e\n    \u003cbutton class=\"b64-btn b64-btn-primary\" id=\"b64-decode-btn\" onclick=\"b64Decode()\"\u003eDecode\u003c/button\u003e\n    \u003cbutton class=\"b64-btn b64-btn-ghost\" id=\"b64-clear-btn\" onclick=\"b64Clear()\"\u003eClear\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"b64-toggles\"\u003e\n    \u003clabel class=\"b64-toggle-item\" title=\"Replace + with - and / with _ for URL-safe Base64\"\u003e\n      \u003cinput type=\"checkbox\" id=\"b64-url-safe\" onchange=\"b64OnOptionChange()\"\u003e\n      \u003cspan class=\"b64-toggle-label\"\u003eURL-safe mode (\u003ccode\u003e+/\u003c/code\u003e → \u003ccode\u003e-_\u003c/code\u003e)\u003c/span\u003e\n    \u003c/label\u003e\n    \u003clabel class=\"b64-toggle-item\" title=\"Automatically detect if input looks like Base64 and switch mode\"\u003e\n      \u003cinput type=\"checkbox\" id=\"b64-auto-detect\" onchange=\"b64OnAutoDetectChange()\"\u003e\n      \u003cspan class=\"b64-toggle-label\"\u003eAuto-detect mode\u003c/span\u003e\n    \u003c/label\u003e\n    \u003clabel class=\"b64-toggle-item\" title=\"Prepend data URI prefix to encoded output\"\u003e\n      \u003cinput type=\"checkbox\" id=\"b64-data-uri\" onchange=\"b64OnOptionChange()\"\u003e\n      \u003cspan class=\"b64-toggle-label\"\u003eOutput as data URI\u003c/span\u003e\n    \u003c/label\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- SWAP ROW --\u003e\n\u003cdiv class=\"b64-divider-row\"\u003e\n  \u003cbutton class=\"b64-swap-btn\" onclick=\"b64Swap()\" title=\"Swap input and output\"\u003e\n    \u0026#8645; Swap Input / Output\n  \u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- OUTPUT SECTION --\u003e\n\u003cdiv class=\"b64-card\"\u003e\n  \u003cdiv class=\"b64-label\"\u003e\n    \u003cspan\u003eOutput\u003c/span\u003e\n    \u003cspan class=\"b64-count\" id=\"b64-output-count\"\u003e0 chars\u003c/span\u003e\n  \u003c/div\u003e\n  \u003ctextarea\n    class=\"b64-textarea\"\n    id=\"b64-output\"\n    placeholder=\"Result will appear here...\"\n    spellcheck=\"false\"\n    readonly\n  \u003e\u003c/textarea\u003e\n  \u003cdiv class=\"b64-error\" id=\"b64-error-msg\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"b64-btn-row\" style=\"margin-bottom:0;\"\u003e\n    \u003cbutton class=\"b64-btn b64-btn-secondary\" id=\"b64-copy-btn\" onclick=\"b64Copy()\"\u003eCopy Output\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- FILE TO BASE64 SECTION --\u003e\n\u003cdiv class=\"b64-card\"\u003e\n  \u003cdiv class=\"b64-section-title\"\u003eFile to Base64\u003c/div\u003e\n  \u003cdiv\n    class=\"b64-drop-zone\"\n    id=\"b64-drop-zone\"\n    onclick=\"document.getElementById('b64-file-input').click()\"\n    ondragover=\"b64DragOver(event)\"\n    ondragleave=\"b64DragLeave(event)\"\n    ondrop=\"b64Drop(event)\"\n  \u003e\n    \u003cdiv class=\"b64-drop-zone-icon\"\u003e\u0026#128196;\u003c/div\u003e\n    \u003cdiv class=\"b64-drop-zone-text\"\u003e\n      \u003cstrong\u003eDrag \u0026amp; drop a file here\u003c/strong\u003e, or click to browse\n    \u003c/div\u003e\n    \u003cbutton class=\"b64-btn b64-btn-ghost\" style=\"font-size:13px;padding:6px 14px;\" onclick=\"event.stopPropagation(); document.getElementById('b64-file-input').click()\"\u003e\n      Choose File\n    \u003c/button\u003e\n  \u003c/div\u003e\n  \u003cinput type=\"file\" class=\"b64-file-input\" id=\"b64-file-input\" onchange=\"b64FileSelected(this)\"\u003e\n  \u003cdiv class=\"b64-file-info\" id=\"b64-file-info\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"b64-btn-row\" style=\"margin-top:12px; margin-bottom:0;\"\u003e\n    \u003clabel class=\"b64-toggle-item\"\u003e\n      \u003cinput type=\"checkbox\" id=\"b64-file-data-uri\" checked\u003e\n      \u003cspan class=\"b64-toggle-label\"\u003eInclude data URI prefix (e.g. \u003ccode\u003edata:image/png;base64,...\u003c/code\u003e)\u003c/span\u003e\n    \u003c/label\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  // ---- Utility ----\n  function isBase64Like(str) {\n    var s = str.trim();\n    // URL-safe or standard base64\n    return /^[A-Za-z0-9+/\\-_]+=*$/.test(s) \u0026\u0026 s.length \u003e 0 \u0026\u0026 s.length % 4 === 0;\n  }\n\n  function updateCount(id, text) {\n    document.getElementById(id).textContent = text.length.toLocaleString() + ' chars';\n  }\n\n  function showError(msg) {\n    var el = document.getElementById('b64-error-msg');\n    el.textContent = msg;\n    el.classList.add('visible');\n    document.getElementById('b64-output').classList.add('b64-error-border');\n  }\n\n  function clearError() {\n    var el = document.getElementById('b64-error-msg');\n    el.textContent = '';\n    el.classList.remove('visible');\n    document.getElementById('b64-output').classList.remove('b64-error-border');\n  }\n\n  function setOutput(val) {\n    document.getElementById('b64-output').value = val;\n    updateCount('b64-output-count', val);\n  }\n\n  function getInput() {\n    return document.getElementById('b64-input').value;\n  }\n\n  function isUrlSafe() {\n    return document.getElementById('b64-url-safe').checked;\n  }\n\n  function isDataUri() {\n    return document.getElementById('b64-data-uri').checked;\n  }\n\n  // ---- Encode ----\n  window.b64Encode = function() {\n    clearError();\n    var input = getInput();\n    if (!input) { setOutput(''); return; }\n    try {\n      var encoded = btoa(unescape(encodeURIComponent(input)));\n      if (isUrlSafe()) {\n        encoded = encoded.replace(/\\+/g, '-').replace(/\\//g, '_');\n      }\n      if (isDataUri()) {\n        encoded = 'data:text/plain;base64,' + encoded;\n      }\n      setOutput(encoded);\n    } catch(e) {\n      showError('Encoding failed: ' + e.message);\n      setOutput('');\n    }\n  };\n\n  // ---- Decode ----\n  window.b64Decode = function() {\n    clearError();\n    var input = getInput().trim();\n    if (!input) { setOutput(''); return; }\n\n    // Strip data URI prefix if present\n    var raw = input.replace(/^data:[^;]+;base64,/, '');\n\n    // Convert URL-safe chars back\n    raw = raw.replace(/-/g, '+').replace(/_/g, '/');\n\n    // Pad if needed\n    while (raw.length % 4 !== 0) { raw += '='; }\n\n    try {\n      var decoded = decodeURIComponent(escape(atob(raw)));\n      setOutput(decoded);\n    } catch(e) {\n      showError('Decode failed: Input does not appear to be valid Base64. ' + e.message);\n      setOutput('');\n    }\n  };\n\n  // ---- Clear ----\n  window.b64Clear = function() {\n    document.getElementById('b64-input').value = '';\n    setOutput('');\n    clearError();\n    updateCount('b64-input-count', '');\n    document.getElementById('b64-auto-indicator').style.display = 'none';\n  };\n\n  // ---- Swap ----\n  window.b64Swap = function() {\n    var inputEl = document.getElementById('b64-input');\n    var outputEl = document.getElementById('b64-output');\n    var tmp = inputEl.value;\n    inputEl.value = outputEl.value;\n    outputEl.value = tmp;\n    clearError();\n    updateCount('b64-input-count', inputEl.value);\n    updateCount('b64-output-count', outputEl.value);\n  };\n\n  // ---- Copy ----\n  window.b64Copy = function() {\n    var val = document.getElementById('b64-output').value;\n    if (!val) return;\n    var btn = document.getElementById('b64-copy-btn');\n    navigator.clipboard.writeText(val).then(function() {\n      btn.textContent = 'Copied!';\n      btn.classList.add('b64-btn-success');\n      setTimeout(function() {\n        btn.textContent = 'Copy Output';\n        btn.classList.remove('b64-btn-success');\n      }, 1800);\n    }).catch(function() {\n      // Fallback\n      var ta = document.createElement('textarea');\n      ta.value = val;\n      ta.style.position = 'fixed';\n      ta.style.opacity = '0';\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      btn.textContent = 'Copied!';\n      btn.classList.add('b64-btn-success');\n      setTimeout(function() {\n        btn.textContent = 'Copy Output';\n        btn.classList.remove('b64-btn-success');\n      }, 1800);\n    });\n  };\n\n  // ---- Option change (re-run last operation) ----\n  window.b64OnOptionChange = function() {\n    // Do nothing on toggle alone; user clicks Encode/Decode explicitly\n  };\n\n  // ---- Auto-detect ----\n  window.b64OnAutoDetectChange = function() {\n    var on = document.getElementById('b64-auto-detect').checked;\n    var indicator = document.getElementById('b64-auto-indicator');\n    indicator.style.display = on ? 'inline-block' : 'none';\n    if (on) b64AutoRun();\n  };\n\n  function b64AutoRun() {\n    var input = getInput().trim();\n    if (!input) return;\n    if (isBase64Like(input)) {\n      b64Decode();\n    } else {\n      b64Encode();\n    }\n  }\n\n  // ---- Input listener ----\n  document.getElementById('b64-input').addEventListener('input', function() {\n    updateCount('b64-input-count', this.value);\n    clearError();\n    if (document.getElementById('b64-auto-detect').checked) {\n      b64AutoRun();\n    }\n  });\n\n  // ---- File to Base64 ----\n  window.b64DragOver = function(e) {\n    e.preventDefault();\n    document.getElementById('b64-drop-zone').classList.add('b64-drag-over');\n  };\n\n  window.b64DragLeave = function(e) {\n    document.getElementById('b64-drop-zone').classList.remove('b64-drag-over');\n  };\n\n  window.b64Drop = function(e) {\n    e.preventDefault();\n    document.getElementById('b64-drop-zone').classList.remove('b64-drag-over');\n    var files = e.dataTransfer.files;\n    if (files \u0026\u0026 files.length \u003e 0) {\n      b64ProcessFile(files[0]);\n    }\n  };\n\n  window.b64FileSelected = function(input) {\n    if (input.files \u0026\u0026 input.files.length \u003e 0) {\n      b64ProcessFile(input.files[0]);\n    }\n  };\n\n  function formatBytes(bytes) {\n    if (bytes \u003c 1024) return bytes + ' B';\n    if (bytes \u003c 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';\n    return (bytes / (1024 * 1024)).toFixed(2) + ' MB';\n  }\n\n  function b64ProcessFile(file) {\n    var infoEl = document.getElementById('b64-file-info');\n    var reader = new FileReader();\n    reader.onload = function(e) {\n      var result = e.target.result; // data URI: data:\u003cmime\u003e;base64,\u003cdata\u003e\n      var useDataUri = document.getElementById('b64-file-data-uri').checked;\n      var outputEl = document.getElementById('b64-output');\n\n      var outputVal;\n      if (useDataUri) {\n        outputVal = result;\n      } else {\n        // Strip the data URI prefix\n        var commaIdx = result.indexOf(',');\n        outputVal = commaIdx \u003e= 0 ? result.substring(commaIdx + 1) : result;\n      }\n\n      outputEl.value = outputVal;\n      outputEl.removeAttribute('readonly');\n      updateCount('b64-output-count', outputVal);\n\n      infoEl.innerHTML =\n        '\u003cspan\u003e\u0026#128196;\u003c/span\u003e' +\n        '\u003cspan class=\"b64-file-name\"\u003e' + file.name + '\u003c/span\u003e' +\n        '\u003cspan class=\"b64-file-size\"\u003e(' + formatBytes(file.size) + ')\u003c/span\u003e' +\n        '\u003cspan style=\"color:#10b981;font-weight:600;\"\u003e\u0026#10003; Encoded\u003c/span\u003e';\n      infoEl.classList.add('visible');\n    };\n    reader.onerror = function() {\n      infoEl.innerHTML = '\u003cspan style=\"color:#f87171;\"\u003eFailed to read file.\u003c/span\u003e';\n      infoEl.classList.add('visible');\n    };\n    reader.readAsDataURL(file);\n  }\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"what-is-base64-encoding\"\u003eWhat is Base64 Encoding?\u003c/h2\u003e\n\u003cp\u003eBase64 is a binary-to-text encoding scheme that represents binary data using a set of 64 printable ASCII characters (A–Z, a–z, 0–9, \u003ccode\u003e+\u003c/code\u003e, and \u003ccode\u003e/\u003c/code\u003e), with \u003ccode\u003e=\u003c/code\u003e used for padding. The name comes from the 64 characters used in the alphabet. Each Base64 character encodes exactly 6 bits of binary data, meaning every 3 bytes of input become 4 Base64 characters — increasing the data size by approximately 33%. Base64 was standardized in RFC 4648 and is universally supported across programming languages, browsers, and operating systems.\u003c/p\u003e","title":"Base64 Encoder \u0026 Decoder - Free Online Tool"},{"content":"Free, in-browser Base64 to image decoder. Paste any Base64 string — with or without a data URI prefix — and instantly preview, inspect, and download the decoded image. No upload, no server, no sign-up required.\nPaste Base64 String \u0026#9654; Decode \u0026amp; Preview Load Sample \u0026#10005; Clear Format: — Dimensions: — Decoded size: — \u0026#8595; Download Image \u0026#128203; Copy as Data URI How it works Paste a Base64-encoded string into the text area above. The tool automatically strips any data:image/...;base64, prefix and removes whitespace/newlines. It decodes the string in your browser, detects the image format (PNG, JPEG, GIF, WebP, SVG…), and renders a live preview. Inspect the detected format, pixel dimensions, and decoded file size. Download the image file or copy the full data URI to your clipboard. \u0026#8592; Image to Base64 Base64 Encoder Related Tools Base64 Encoder Encoder Decoder Hash Generator ","permalink":"https://productivity-works.com/tools/base64-to-image/","summary":"\u003cp\u003eFree, in-browser Base64 to image decoder. Paste any Base64 string — with or without a data URI prefix — and instantly preview, inspect, and download the decoded image. No upload, no server, no sign-up required.\u003c/p\u003e\n\u003cdiv id=\"bd-app\"\u003e\n\u003cstyle\u003e\n#bd-app *,\n#bd-app *::before,\n#bd-app *::after {\n  box-sizing: border-box;\n}\n#bd-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 820px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#bd-app h2 {\n  font-size: 1.15rem;\n  font-weight: 600;\n  margin: 1.6rem 0 0.5rem;\n  color: #1a1a2e;\n}\n#bd-app .bd-row {\n  display: flex;\n  gap: 0.75rem;\n  flex-wrap: wrap;\n  align-items: center;\n  margin-bottom: 0.75rem;\n}\n#bd-app textarea {\n  width: 100%;\n  min-height: 160px;\n  padding: 0.75rem;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 0.8rem;\n  line-height: 1.5;\n  border: 1.5px solid #c8ccd4;\n  border-radius: 8px;\n  resize: vertical;\n  background: #f8f9fb;\n  color: #1a1a2e;\n  transition: border-color 0.2s;\n}\n#bd-app textarea:focus {\n  outline: none;\n  border-color: #4f6ef7;\n  background: #fff;\n}\n#bd-app textarea::placeholder {\n  color: #9aa0ad;\n}\n#bd-app button {\n  display: inline-flex;\n  align-items: center;\n  gap: 0.4rem;\n  padding: 0.55rem 1.15rem;\n  font-size: 0.9rem;\n  font-weight: 600;\n  border: none;\n  border-radius: 7px;\n  cursor: pointer;\n  transition: background 0.18s, opacity 0.18s;\n}\n#bd-app button:active { opacity: 0.82; }\n#bd-app .bd-btn-primary {\n  background: #4f6ef7;\n  color: #fff;\n}\n#bd-app .bd-btn-primary:hover { background: #3a58e0; }\n#bd-app .bd-btn-secondary {\n  background: #eef0f8;\n  color: #4f6ef7;\n  border: 1.5px solid #c8ccd4;\n}\n#bd-app .bd-btn-secondary:hover { background: #dde1f5; }\n#bd-app .bd-btn-danger {\n  background: #fff0f0;\n  color: #d9534f;\n  border: 1.5px solid #f5c6c6;\n}\n#bd-app .bd-btn-danger:hover { background: #ffe0e0; }\n#bd-app .bd-btn-green {\n  background: #1db954;\n  color: #fff;\n}\n#bd-app .bd-btn-green:hover { background: #17a348; }\n#bd-app .bd-status {\n  padding: 0.55rem 0.85rem;\n  border-radius: 7px;\n  font-size: 0.875rem;\n  font-weight: 500;\n  margin-bottom: 0.75rem;\n  display: none;\n}\n#bd-app .bd-status.info  { display: block; background: #eef3ff; color: #3a58e0; border: 1px solid #c5d1fb; }\n#bd-app .bd-status.error { display: block; background: #fff0f0; color: #c0392b; border: 1px solid #f5c6c6; }\n#bd-app .bd-status.ok    { display: block; background: #eafaf1; color: #1a7a43; border: 1px solid #a9dfbf; }\n#bd-app .bd-preview-box {\n  display: none;\n  border: 1.5px solid #c8ccd4;\n  border-radius: 10px;\n  overflow: hidden;\n  background: repeating-conic-gradient(#e0e0e0 0% 25%, #f8f8f8 0% 50%) 0 0 / 16px 16px;\n  text-align: center;\n  padding: 12px;\n  margin-bottom: 0.75rem;\n  min-height: 80px;\n}\n#bd-app .bd-preview-box img {\n  max-width: 100%;\n  max-height: 480px;\n  display: block;\n  margin: 0 auto;\n  border-radius: 4px;\n  box-shadow: 0 2px 12px rgba(0,0,0,0.12);\n}\n#bd-app .bd-meta {\n  display: none;\n  background: #f4f6fb;\n  border: 1.5px solid #dde2ef;\n  border-radius: 8px;\n  padding: 0.75rem 1rem;\n  font-size: 0.85rem;\n  margin-bottom: 0.75rem;\n  line-height: 1.7;\n}\n#bd-app .bd-meta span {\n  display: inline-block;\n  margin-right: 1.5rem;\n}\n#bd-app .bd-meta strong { color: #4f6ef7; }\n#bd-app .bd-copy-uri {\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  font-size: 0.78rem;\n  background: #f4f6fb;\n  border: 1.5px solid #dde2ef;\n  border-radius: 7px;\n  padding: 0.55rem 0.8rem;\n  width: 100%;\n  resize: none;\n  height: 3.8rem;\n  color: #444;\n  display: none;\n  margin-bottom: 0.4rem;\n}\n#bd-app .bd-divider {\n  border: none;\n  border-top: 1.5px solid #eaecf2;\n  margin: 1.4rem 0;\n}\n#bd-app .bd-how {\n  background: #f8f9fb;\n  border-radius: 10px;\n  padding: 1rem 1.2rem;\n  font-size: 0.88rem;\n  line-height: 1.75;\n  color: #444;\n}\n#bd-app .bd-how ol { margin: 0.4rem 0 0 1.2rem; padding: 0; }\n#bd-app .bd-how li { margin-bottom: 0.25rem; }\n#bd-app .bd-links {\n  display: flex;\n  gap: 0.75rem;\n  flex-wrap: wrap;\n  margin-top: 1.2rem;\n}\n#bd-app .bd-links a {\n  padding: 0.45rem 1rem;\n  background: #eef0f8;\n  color: #4f6ef7;\n  border-radius: 6px;\n  text-decoration: none;\n  font-size: 0.875rem;\n  font-weight: 500;\n  border: 1.5px solid #c8ccd4;\n  transition: background 0.15s;\n}\n#bd-app .bd-links a:hover { background: #dde1f5; }\n\u003c/style\u003e\n\u003ch2\u003ePaste Base64 String\u003c/h2\u003e\n\u003ctextarea id=\"bd-input\" placeholder=\"Paste your Base64 string here — data URI prefix (data:image/png;base64,...) is auto-detected and stripped\"\u003e\u003c/textarea\u003e\n\u003cdiv class=\"bd-row\"\u003e\n  \u003cbutton class=\"bd-btn-primary\" onclick=\"bdDecode()\"\u003e\u0026#9654; Decode \u0026amp; Preview\u003c/button\u003e\n  \u003cbutton class=\"bd-btn-secondary\" onclick=\"bdPasteSample()\"\u003eLoad Sample\u003c/button\u003e\n  \u003cbutton class=\"bd-btn-danger\" onclick=\"bdClear()\"\u003e\u0026#10005; Clear\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv id=\"bd-status\" class=\"bd-status\"\u003e\u003c/div\u003e\n\u003cdiv id=\"bd-preview-box\" class=\"bd-preview-box\"\u003e\n  \u003cimg id=\"bd-img\" src=\"\" alt=\"Decoded image preview\" /\u003e\n\u003c/div\u003e\n\u003cdiv id=\"bd-meta\" class=\"bd-meta\"\u003e\n  \u003cspan\u003eFormat: \u003cstrong id=\"bd-fmt\"\u003e—\u003c/strong\u003e\u003c/span\u003e\n  \u003cspan\u003eDimensions: \u003cstrong id=\"bd-dims\"\u003e—\u003c/strong\u003e\u003c/span\u003e\n  \u003cspan\u003eDecoded size: \u003cstrong id=\"bd-size\"\u003e—\u003c/strong\u003e\u003c/span\u003e\n\u003c/div\u003e\n\u003cdiv id=\"bd-action-row\" class=\"bd-row\" style=\"display:none\"\u003e\n  \u003cbutton class=\"bd-btn-green\" onclick=\"bdDownload()\"\u003e\u0026#8595; Download Image\u003c/button\u003e\n  \u003cbutton class=\"bd-btn-secondary\" onclick=\"bdCopyUri()\"\u003e\u0026#128203; Copy as Data URI\u003c/button\u003e\n\u003c/div\u003e\n\u003ctextarea id=\"bd-uri-out\" class=\"bd-copy-uri\" readonly\u003e\u003c/textarea\u003e\n\u003chr class=\"bd-divider\" /\u003e\n\u003ch2\u003eHow it works\u003c/h2\u003e\n\u003cdiv class=\"bd-how\"\u003e\n  \u003col\u003e\n    \u003cli\u003ePaste a Base64-encoded string into the text area above.\u003c/li\u003e\n    \u003cli\u003eThe tool automatically strips any \u003ccode\u003edata:image/...;base64,\u003c/code\u003e prefix and removes whitespace/newlines.\u003c/li\u003e\n    \u003cli\u003eIt decodes the string in your browser, detects the image format (PNG, JPEG, GIF, WebP, SVG…), and renders a live preview.\u003c/li\u003e\n    \u003cli\u003eInspect the detected format, pixel dimensions, and decoded file size.\u003c/li\u003e\n    \u003cli\u003eDownload the image file or copy the full data URI to your clipboard.\u003c/li\u003e\n  \u003c/ol\u003e\n\u003c/div\u003e\n\u003cdiv class=\"bd-links\"\u003e\n  \u003ca href=\"/tools/image-to-base64/\"\u003e\u0026#8592; Image to Base64\u003c/a\u003e\n  \u003ca href=\"/tools/base64-encoder/\"\u003eBase64 Encoder\u003c/a\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  'use strict';\n\n  // Minimal 1×1 red PNG for the sample\n  var SAMPLE = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwADhQGAWjR9awAAAABJRU5ErkJggg==';\n\n  function showStatus(msg, type) {\n    var el = document.getElementById('bd-status');\n    el.textContent = msg;\n    el.className = 'bd-status ' + type;\n  }\n\n  function hideStatus() {\n    var el = document.getElementById('bd-status');\n    el.className = 'bd-status';\n  }\n\n  function formatBytes(n) {\n    if (n \u003c 1024) return n + ' B';\n    if (n \u003c 1024 * 1024) return (n / 1024).toFixed(1) + ' KB';\n    return (n / (1024 * 1024)).toFixed(2) + ' MB';\n  }\n\n  function detectMime(b64clean) {\n    // Decode first few bytes to detect magic\n    try {\n      var raw = atob(b64clean.substring(0, 16));\n      var bytes = [];\n      for (var i = 0; i \u003c raw.length; i++) bytes.push(raw.charCodeAt(i));\n      if (bytes[0] === 0x89 \u0026\u0026 bytes[1] === 0x50) return 'image/png';\n      if (bytes[0] === 0xFF \u0026\u0026 bytes[1] === 0xD8) return 'image/jpeg';\n      if (bytes[0] === 0x47 \u0026\u0026 bytes[1] === 0x49) return 'image/gif';\n      if (bytes[0] === 0x52 \u0026\u0026 bytes[1] === 0x49 \u0026\u0026 bytes[8] === 0x57) return 'image/webp';\n      if (bytes[0] === 0x42 \u0026\u0026 bytes[1] === 0x4D) return 'image/bmp';\n      if (bytes[0] === 0x00 \u0026\u0026 bytes[1] === 0x00 \u0026\u0026 bytes[2] === 0x01) return 'image/x-icon';\n    } catch (e) {}\n    // Fallback: check if it looks like SVG text\n    var preview = atob(b64clean.substring(0, 64));\n    if (preview.indexOf('\u003csvg') !== -1 || preview.indexOf('\u003c?xml') !== -1) return 'image/svg+xml';\n    return 'image/png'; // safe default\n  }\n\n  function mimeToExt(mime) {\n    var map = {\n      'image/png': 'png',\n      'image/jpeg': 'jpg',\n      'image/gif': 'gif',\n      'image/webp': 'webp',\n      'image/bmp': 'bmp',\n      'image/svg+xml': 'svg',\n      'image/x-icon': 'ico'\n    };\n    return map[mime] || 'png';\n  }\n\n  function mimeToLabel(mime) {\n    var map = {\n      'image/png': 'PNG',\n      'image/jpeg': 'JPEG',\n      'image/gif': 'GIF',\n      'image/webp': 'WebP',\n      'image/bmp': 'BMP',\n      'image/svg+xml': 'SVG',\n      'image/x-icon': 'ICO'\n    };\n    return map[mime] || mime;\n  }\n\n  window.bdDecode = function () {\n    var raw = document.getElementById('bd-input').value.trim();\n    if (!raw) { showStatus('Please paste a Base64 string first.', 'info'); return; }\n\n    // Strip data URI prefix\n    var b64 = raw;\n    var detectedMime = null;\n    var prefixMatch = raw.match(/^data:([^;]+);base64,(.+)$/s);\n    if (prefixMatch) {\n      detectedMime = prefixMatch[1].trim();\n      b64 = prefixMatch[2].trim();\n    }\n\n    // Strip all whitespace and newlines\n    b64 = b64.replace(/\\s+/g, '');\n\n    // Basic validation\n    if (!/^[A-Za-z0-9+/]+=*$/.test(b64)) {\n      showStatus('Invalid Base64 — unexpected characters found. Check your input.', 'error');\n      return;\n    }\n\n    var mime = detectedMime || detectMime(b64);\n    var dataUri = 'data:' + mime + ';base64,' + b64;\n\n    var img = document.getElementById('bd-img');\n    img.onload = function () {\n      // Decoded byte count: each Base64 char = 6 bits; padding = 0 bytes\n      var pad = (b64.slice(-2) === '==' ? 2 : b64.slice(-1) === '=' ? 1 : 0);\n      var byteLen = (b64.length * 3 / 4) - pad;\n\n      document.getElementById('bd-fmt').textContent = mimeToLabel(mime);\n      document.getElementById('bd-dims').textContent = img.naturalWidth + ' × ' + img.naturalHeight + ' px';\n      document.getElementById('bd-size').textContent = formatBytes(byteLen);\n\n      document.getElementById('bd-meta').style.display = 'block';\n      document.getElementById('bd-preview-box').style.display = 'block';\n      document.getElementById('bd-action-row').style.display = 'flex';\n      document.getElementById('bd-uri-out').style.display = 'none';\n\n      showStatus('Decoded successfully — format: ' + mimeToLabel(mime) + ', ' + img.naturalWidth + '×' + img.naturalHeight + ' px.', 'ok');\n    };\n    img.onerror = function () {\n      document.getElementById('bd-preview-box').style.display = 'none';\n      document.getElementById('bd-meta').style.display = 'none';\n      document.getElementById('bd-action-row').style.display = 'none';\n      showStatus('Could not render image. The Base64 data may be corrupted or an unsupported format.', 'error');\n    };\n    img.src = dataUri;\n    hideStatus();\n  };\n\n  window.bdDownload = function () {\n    var img = document.getElementById('bd-img');\n    if (!img.src) return;\n    var mimeMatch = img.src.match(/^data:([^;]+);/);\n    var mime = mimeMatch ? mimeMatch[1] : 'image/png';\n    var ext = mimeToExt(mime);\n    var a = document.createElement('a');\n    a.href = img.src;\n    a.download = 'decoded-image.' + ext;\n    document.body.appendChild(a);\n    a.click();\n    document.body.removeChild(a);\n  };\n\n  window.bdCopyUri = function () {\n    var img = document.getElementById('bd-img');\n    if (!img.src) return;\n    var out = document.getElementById('bd-uri-out');\n    out.value = img.src;\n    out.style.display = 'block';\n    if (navigator.clipboard) {\n      navigator.clipboard.writeText(img.src).then(function () {\n        showStatus('Data URI copied to clipboard.', 'ok');\n      }).catch(function () {\n        out.select();\n        showStatus('Select the text above and copy manually.', 'info');\n      });\n    } else {\n      out.select();\n      try { document.execCommand('copy'); showStatus('Data URI copied to clipboard.', 'ok'); }\n      catch (e) { showStatus('Select the text above and copy manually.', 'info'); }\n    }\n  };\n\n  window.bdPasteSample = function () {\n    document.getElementById('bd-input').value = SAMPLE;\n    showStatus('Sample Base64 loaded (1×1 red PNG). Click Decode \u0026 Preview.', 'info');\n  };\n\n  window.bdClear = function () {\n    document.getElementById('bd-input').value = '';\n    document.getElementById('bd-img').src = '';\n    document.getElementById('bd-preview-box').style.display = 'none';\n    document.getElementById('bd-meta').style.display = 'none';\n    document.getElementById('bd-action-row').style.display = 'none';\n    document.getElementById('bd-uri-out').style.display = 'none';\n    document.getElementById('bd-uri-out').value = '';\n    hideStatus();\n  };\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"https://productivity-works.com/tools/base64-encoder/\"\u003eBase64 Encoder\u003c/a\u003e\n\n\u003ca href=\"https://productivity-works.com/tools/encoder-decoder/\"\u003eEncoder Decoder\u003c/a\u003e\n\n\u003ca href=\"https://productivity-works.com/tools/hash-generator/\"\u003eHash Generator\u003c/a\u003e\n\u003c/p\u003e","title":"Base64 to Image Converter"},{"content":" Battery Life Calculator Instantly estimate how long your device will run on a single charge — or find out what battery capacity you need for a target runtime. Works for smartphones, laptops, tablets, IoT sensors, drones, and more.\nRuntime Estimator Reverse: Capacity Needed Charging Time Device Presets Smartphone Tablet Laptop IoT Sensor Drone Earbuds Smartwatch E-Bike \u0026lt;h3\u0026gt;Battery Capacity\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;field-row\u0026quot;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;label\u0026gt;Capacity (mAh)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;bl-mah\u0026quot; value=\u0026quot;4000\u0026quot; min=\u0026quot;1\u0026quot; step=\u0026quot;100\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div\u0026gt; \u0026lt;label\u0026gt;Battery Voltage (V)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;bl-voltage\u0026quot; value=\u0026quot;3.7\u0026quot; min=\u0026quot;0.1\u0026quot; step=\u0026quot;0.1\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;h3\u0026gt;Power Consumption\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;power-toggle\u0026quot;\u0026gt; \u0026lt;label\u0026gt;\u0026lt;input type=\u0026quot;radio\u0026quot; name=\u0026quot;bl-power-mode\u0026quot; value=\u0026quot;ma\u0026quot; checked onchange=\u0026quot;blTogglePowerMode()\u0026quot;\u0026gt; Current draw (mA)\u0026lt;/label\u0026gt; \u0026lt;label\u0026gt;\u0026lt;input type=\u0026quot;radio\u0026quot; name=\u0026quot;bl-power-mode\u0026quot; value=\u0026quot;w\u0026quot; onchange=\u0026quot;blTogglePowerMode()\u0026quot;\u0026gt; Wattage (W)\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;field-row\u0026quot;\u0026gt; \u0026lt;div id=\u0026quot;bl-ma-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Current Draw (mA)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;bl-ma\u0026quot; value=\u0026quot;150\u0026quot; min=\u0026quot;0.1\u0026quot; step=\u0026quot;10\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;bl-w-field\u0026quot; style=\u0026quot;display:none;\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Power (W)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;bl-w\u0026quot; value=\u0026quot;0.555\u0026quot; min=\u0026quot;0.001\u0026quot; step=\u0026quot;0.1\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;slider-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Efficiency Factor: \u0026lt;strong id=\u0026quot;bl-eff-label\u0026quot;\u0026gt;85%\u0026lt;/strong\u0026gt; \u0026lt;span class=\u0026quot;badge\u0026quot;\u0026gt;Real-world loss\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bl-efficiency\u0026quot; min=\u0026quot;70\u0026quot; max=\u0026quot;95\u0026quot; value=\u0026quot;85\u0026quot; step=\u0026quot;1\u0026quot; oninput=\u0026quot;document.getElementById('bl-eff-label').textContent=this.value+'%'\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;slider-labels\u0026quot;\u0026gt;\u0026lt;span\u0026gt;70% (Poor)\u0026lt;/span\u0026gt;\u0026lt;span\u0026gt;80%\u0026lt;/span\u0026gt;\u0026lt;span\u0026gt;90%\u0026lt;/span\u0026gt;\u0026lt;span\u0026gt;95% (Ideal)\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;calc-btn\u0026quot; onclick=\u0026quot;blCalculateRuntime()\u0026quot;\u0026gt;Calculate Runtime\u0026lt;/button\u0026gt; -- Battery Energy -- Effective Power Draw -- Efficiency Applied -- Runtime (decimal) -- Target Runtime Hours needed Minutes needed Voltage (V) Current Draw (mA) Efficiency Factor (%) Calculate Needed Capacity -- Minimum mAh -- Recommended (+20% buffer) -- Energy Required (Wh) -- Efficiency Factor -- Charging Time Estimator Battery Capacity (mAh) Charger Output (W) Battery Voltage (V) Current charge level (%) Charging Efficiency: 90% 75%85%95%98% Calculate Charging Time -- To 80% (Fast Charge) -- To 100% (Full) -- Charge Rate (mA) -- Energy to Add (Wh) -- Tips for Extending Battery Life Avoid full discharges. Lithium batteries last longer when kept between 20–80% charge. Deep cycling accelerates capacity loss. Reduce screen brightness. The display is the single largest power consumer in most mobile devices — cutting brightness 30% can extend runtime 15–20%. Disable background refresh. Apps syncing in the background can consume 10–25% of battery without you noticing. Enable it only for critical apps. Mind the temperature. Batteries discharge faster in cold and degrade faster in heat. Ideal operating range is 15–25°C (59–77°F). Use low-power or airplane mode. Radio transmitters (Wi-Fi, LTE, Bluetooth) are heavy consumers. Disable unused radios for a significant runtime boost. Slow-charge when possible. Fast charging generates heat, which degrades battery cells over time. Overnight slow-charging extends overall battery lifespan. Match charger to device. Using an underpowered or overpowered charger reduces efficiency and can stress cells. Always use the manufacturer-recommended output. Store at 50% for long-term. If storing a device unused for weeks or months, keep it at around 50% charge in a cool, dry place. How Battery Life Is Calculated The core formula is straightforward:\nRuntime (hours) = (Capacity in mAh × Efficiency) ÷ Current Draw (mA)\nOr, using watt-hours:\nRuntime (hours) = (Capacity in mAh × Voltage × Efficiency) ÷ Power (W) ÷ 1000\nThe efficiency factor accounts for real-world losses: heat generation in battery cells, voltage conversion losses in the device's power management IC, and the fact that batteries cannot fully discharge to rated capacity under load. A value of 85% is a good general estimate for lithium-ion cells; older or colder batteries may warrant 70–75%.\nRelated Tools Watt-Hour Calculator Solar Panel Output Calculator Device Power Consumption Calculator Electronics CO2 Footprint ","permalink":"https://productivity-works.com/tools/battery-life-calculator/","summary":"\u003cdiv id=\"bl-app\"\u003e\n\u003cstyle\u003e\n#bl-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 780px;\n  margin: 0 auto;\n  color: #1a1a2e;\n  line-height: 1.6;\n}\n#bl-app h2 {\n  font-size: 1.4rem;\n  font-weight: 700;\n  margin: 1.6rem 0 0.8rem;\n  color: #16213e;\n}\n#bl-app h3 {\n  font-size: 1.1rem;\n  font-weight: 600;\n  margin: 1.2rem 0 0.5rem;\n  color: #0f3460;\n}\n#bl-app .card {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 1.4rem 1.6rem;\n  margin-bottom: 1.2rem;\n}\n#bl-app .card-blue {\n  background: #eff6ff;\n  border-color: #bfdbfe;\n}\n#bl-app .card-green {\n  background: #f0fdf4;\n  border-color: #bbf7d0;\n}\n#bl-app .card-amber {\n  background: #fffbeb;\n  border-color: #fde68a;\n}\n#bl-app .tabs {\n  display: flex;\n  gap: 0;\n  border-radius: 8px;\n  overflow: hidden;\n  border: 1px solid #cbd5e1;\n  margin-bottom: 1.2rem;\n}\n#bl-app .tab-btn {\n  flex: 1;\n  padding: 0.6rem 1rem;\n  background: #f1f5f9;\n  border: none;\n  cursor: pointer;\n  font-size: 0.9rem;\n  font-weight: 500;\n  color: #475569;\n  transition: background 0.15s, color 0.15s;\n}\n#bl-app .tab-btn.active {\n  background: #2563eb;\n  color: #fff;\n}\n#bl-app .tab-btn:hover:not(.active) {\n  background: #e2e8f0;\n}\n#bl-app .field-row {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1rem;\n  margin-bottom: 0.8rem;\n}\n#bl-app .field-row.three {\n  grid-template-columns: 1fr 1fr 1fr;\n}\n@media (max-width: 540px) {\n  #bl-app .field-row,\n  #bl-app .field-row.three {\n    grid-template-columns: 1fr;\n  }\n}\n#bl-app label {\n  display: block;\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #374151;\n  margin-bottom: 0.3rem;\n  text-transform: uppercase;\n  letter-spacing: 0.03em;\n}\n#bl-app input[type=\"number\"],\n#bl-app select {\n  width: 100%;\n  padding: 0.5rem 0.7rem;\n  border: 1px solid #cbd5e1;\n  border-radius: 7px;\n  font-size: 1rem;\n  background: #fff;\n  color: #1a1a2e;\n  box-sizing: border-box;\n  transition: border-color 0.15s;\n}\n#bl-app input[type=\"number\"]:focus,\n#bl-app select:focus {\n  outline: none;\n  border-color: #2563eb;\n  box-shadow: 0 0 0 3px rgba(37,99,235,0.12);\n}\n#bl-app .slider-row {\n  margin-bottom: 0.8rem;\n}\n#bl-app input[type=\"range\"] {\n  width: 100%;\n  accent-color: #2563eb;\n  margin-top: 0.3rem;\n}\n#bl-app .slider-labels {\n  display: flex;\n  justify-content: space-between;\n  font-size: 0.78rem;\n  color: #64748b;\n  margin-top: 0.1rem;\n}\n#bl-app .presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem;\n  margin-bottom: 1rem;\n}\n#bl-app .preset-btn {\n  padding: 0.35rem 0.85rem;\n  border: 1px solid #93c5fd;\n  border-radius: 20px;\n  background: #eff6ff;\n  color: #1d4ed8;\n  font-size: 0.82rem;\n  font-weight: 500;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n#bl-app .preset-btn:hover {\n  background: #dbeafe;\n}\n#bl-app .calc-btn {\n  display: inline-block;\n  padding: 0.65rem 2rem;\n  background: #2563eb;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 1rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s;\n  margin-top: 0.4rem;\n}\n#bl-app .calc-btn:hover {\n  background: #1d4ed8;\n}\n#bl-app .result-box {\n  background: #fff;\n  border: 2px solid #2563eb;\n  border-radius: 12px;\n  padding: 1.2rem 1.4rem;\n  margin-top: 1rem;\n  display: none;\n}\n#bl-app .result-box.visible {\n  display: block;\n}\n#bl-app .result-main {\n  font-size: 2rem;\n  font-weight: 800;\n  color: #2563eb;\n  margin-bottom: 0.3rem;\n}\n#bl-app .result-sub {\n  color: #475569;\n  font-size: 0.95rem;\n}\n#bl-app .result-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 0.8rem;\n  margin-top: 0.8rem;\n}\n#bl-app .result-item {\n  background: #f1f5f9;\n  border-radius: 8px;\n  padding: 0.7rem 1rem;\n}\n#bl-app .result-item .label {\n  font-size: 0.78rem;\n  color: #64748b;\n  text-transform: uppercase;\n  font-weight: 600;\n  letter-spacing: 0.03em;\n}\n#bl-app .result-item .value {\n  font-size: 1.1rem;\n  font-weight: 700;\n  color: #1e293b;\n  margin-top: 0.1rem;\n}\n#bl-app .tips-list {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n}\n#bl-app .tips-list li {\n  padding: 0.45rem 0 0.45rem 1.6rem;\n  position: relative;\n  font-size: 0.95rem;\n  border-bottom: 1px solid #e2e8f0;\n}\n#bl-app .tips-list li:last-child {\n  border-bottom: none;\n}\n#bl-app .tips-list li::before {\n  content: \"✓\";\n  position: absolute;\n  left: 0;\n  color: #16a34a;\n  font-weight: 700;\n}\n#bl-app .related-links {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.6rem;\n  margin-top: 0.6rem;\n}\n#bl-app .related-links a {\n  color: #2563eb;\n  text-decoration: none;\n  font-size: 0.9rem;\n  border: 1px solid #bfdbfe;\n  border-radius: 6px;\n  padding: 0.3rem 0.75rem;\n  transition: background 0.15s;\n}\n#bl-app .related-links a:hover {\n  background: #eff6ff;\n}\n#bl-app .section-divider {\n  border: none;\n  border-top: 2px solid #e2e8f0;\n  margin: 1.6rem 0;\n}\n#bl-app .power-toggle {\n  display: flex;\n  gap: 0.5rem;\n  margin-bottom: 0.8rem;\n}\n#bl-app .power-toggle label {\n  display: flex;\n  align-items: center;\n  gap: 0.3rem;\n  text-transform: none;\n  letter-spacing: 0;\n  font-size: 0.9rem;\n  font-weight: 500;\n  cursor: pointer;\n}\n#bl-app .badge {\n  display: inline-block;\n  background: #dcfce7;\n  color: #15803d;\n  font-size: 0.75rem;\n  font-weight: 700;\n  padding: 0.15rem 0.5rem;\n  border-radius: 10px;\n  vertical-align: middle;\n  margin-left: 0.3rem;\n}\n\u003c/style\u003e\n\u003ch2\u003eBattery Life Calculator\u003c/h2\u003e\n\u003cp\u003eInstantly estimate how long your device will run on a single charge — or find out what battery capacity you need for a target runtime. Works for smartphones, laptops, tablets, IoT sensors, drones, and more.\u003c/p\u003e","title":"Battery Life Calculator - Estimate Device Runtime"},{"content":" Input\nOutput mode: Binary 01 Hex 0x Octal 0o Spaces Text input 0 chars \u0026#x21D3; Text → Encoded Binary output Copy Decode\nBinary / Hex / Octal input Copy result \u0026#x21D1; Encoded → Text Decoded text Character-by-character breakdown\nChar Code point Decimal Binary (UTF-8 bytes) Hex Octal Type some text above to see the breakdown. Related Tools Convert number bases → Number Base Converter Encode Base64 → Base64 Encoder Translate Morse code → Morse Code Translator ","permalink":"https://productivity-works.com/tools/binary-text-converter/","summary":"\u003cdiv id=\"btc-app\"\u003e\n\u003cstyle\u003e\n#btc-app {\n  font-family: ui-sans-serif, system-ui, -apple-system, sans-serif;\n  color: #e2e8f0;\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 0;\n}\n#btc-app * { box-sizing: border-box; }\n\n.btc-card {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 12px;\n  padding: 24px;\n  margin-bottom: 16px;\n}\n\n.btc-row {\n  display: flex;\n  gap: 12px;\n  flex-wrap: wrap;\n  align-items: center;\n  margin-bottom: 16px;\n}\n\n.btc-label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #94a3b8;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 8px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n\n.btc-label-text { color: #94a3b8; }\n\n.btc-char-count {\n  font-size: 12px;\n  font-weight: 400;\n  color: #64748b;\n  text-transform: none;\n  letter-spacing: 0;\n}\n\n.btc-textarea {\n  width: 100%;\n  min-height: 120px;\n  background: #0f172a;\n  border: 1px solid #334155;\n  border-radius: 8px;\n  color: #e2e8f0;\n  font-family: 'Courier New', Courier, monospace;\n  font-size: 14px;\n  padding: 12px;\n  resize: vertical;\n  outline: none;\n  transition: border-color 0.2s;\n  line-height: 1.6;\n}\n.btc-textarea:focus { border-color: #6366f1; }\n.btc-textarea::placeholder { color: #475569; }\n\n.btc-btn {\n  padding: 9px 18px;\n  border-radius: 7px;\n  border: none;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.2s, transform 0.1s;\n  white-space: nowrap;\n}\n.btc-btn:active { transform: scale(0.97); }\n\n.btc-btn-primary {\n  background: #6366f1;\n  color: #fff;\n}\n.btc-btn-primary:hover { background: #4f46e5; }\n\n.btc-btn-secondary {\n  background: #334155;\n  color: #cbd5e1;\n}\n.btc-btn-secondary:hover { background: #475569; }\n\n.btc-btn-copy {\n  background: #0f4c81;\n  color: #93c5fd;\n  padding: 5px 12px;\n  font-size: 12px;\n  border-radius: 6px;\n}\n.btc-btn-copy:hover { background: #1d4ed8; color: #fff; }\n.btc-btn-copy.copied { background: #065f46; color: #6ee7b7; }\n\n.btc-mode-group {\n  display: flex;\n  gap: 6px;\n  flex-wrap: wrap;\n}\n\n.btc-mode-btn {\n  padding: 7px 14px;\n  border-radius: 6px;\n  border: 1.5px solid #334155;\n  background: #0f172a;\n  color: #94a3b8;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n.btc-mode-btn.active {\n  border-color: #6366f1;\n  background: #312e81;\n  color: #a5b4fc;\n}\n.btc-mode-btn:hover:not(.active) { border-color: #6366f1; color: #e2e8f0; }\n\n.btc-toggle-group {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  font-size: 13px;\n  color: #94a3b8;\n}\n\n.btc-toggle {\n  position: relative;\n  width: 40px;\n  height: 22px;\n  flex-shrink: 0;\n}\n.btc-toggle input { opacity: 0; width: 0; height: 0; }\n.btc-toggle-slider {\n  position: absolute;\n  inset: 0;\n  background: #334155;\n  border-radius: 22px;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n.btc-toggle-slider::before {\n  content: '';\n  position: absolute;\n  width: 16px; height: 16px;\n  left: 3px; top: 3px;\n  background: #94a3b8;\n  border-radius: 50%;\n  transition: transform 0.2s, background 0.2s;\n}\n.btc-toggle input:checked + .btc-toggle-slider { background: #4f46e5; }\n.btc-toggle input:checked + .btc-toggle-slider::before {\n  transform: translateX(18px);\n  background: #fff;\n}\n\n.btc-output-box {\n  position: relative;\n  background: #0f172a;\n  border: 1px solid #334155;\n  border-radius: 8px;\n  padding: 14px;\n  min-height: 100px;\n  font-family: 'Courier New', Courier, monospace;\n  font-size: 13px;\n  color: #a5b4fc;\n  word-break: break-all;\n  line-height: 1.7;\n  white-space: pre-wrap;\n}\n\n.btc-error {\n  color: #f87171;\n  font-size: 13px;\n  margin-top: 6px;\n  min-height: 18px;\n}\n\n.btc-section-title {\n  font-size: 15px;\n  font-weight: 700;\n  color: #c7d2fe;\n  margin: 0 0 14px 0;\n}\n\n.btc-dir-arrows {\n  display: flex;\n  justify-content: center;\n  gap: 10px;\n  margin: 4px 0 4px;\n}\n.btc-dir-btn {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  padding: 10px 22px;\n  border-radius: 8px;\n  border: none;\n  font-size: 14px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.2s, transform 0.1s;\n}\n.btc-dir-btn:active { transform: scale(0.97); }\n.btc-dir-btn-encode {\n  background: #6366f1;\n  color: #fff;\n}\n.btc-dir-btn-encode:hover { background: #4f46e5; }\n.btc-dir-btn-decode {\n  background: #0891b2;\n  color: #fff;\n}\n.btc-dir-btn-decode:hover { background: #0e7490; }\n\n/* Breakdown table */\n.btc-table-wrap {\n  overflow-x: auto;\n  margin-top: 8px;\n}\n.btc-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 13px;\n}\n.btc-table th {\n  background: #0f172a;\n  color: #94a3b8;\n  font-size: 11px;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  padding: 8px 10px;\n  text-align: left;\n  border-bottom: 1px solid #334155;\n}\n.btc-table td {\n  padding: 7px 10px;\n  border-bottom: 1px solid #1e293b;\n  font-family: 'Courier New', Courier, monospace;\n  color: #e2e8f0;\n  vertical-align: middle;\n}\n.btc-table tr:hover td { background: #1e2d3d; }\n.btc-table .td-char {\n  font-size: 15px;\n  font-weight: 700;\n  color: #fbbf24;\n  min-width: 32px;\n}\n.btc-table .td-cp { color: #94a3b8; font-size: 12px; }\n.btc-table .td-bin { color: #a5b4fc; }\n.btc-table .td-hex { color: #34d399; }\n.btc-table .td-oct { color: #fb923c; }\n.btc-table .td-dec { color: #e2e8f0; }\n\n.btc-empty-msg {\n  text-align: center;\n  color: #475569;\n  font-size: 13px;\n  padding: 20px 0;\n}\n\n.btc-options-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n  gap: 10px;\n  margin-bottom: 16px;\n}\n\n.btc-badge {\n  display: inline-block;\n  padding: 2px 8px;\n  border-radius: 4px;\n  font-size: 11px;\n  font-weight: 700;\n  margin-left: 6px;\n  vertical-align: middle;\n}\n.btc-badge-bin { background: #312e81; color: #a5b4fc; }\n.btc-badge-hex { background: #064e3b; color: #34d399; }\n.btc-badge-oct { background: #431407; color: #fb923c; }\n\n@media (max-width: 600px) {\n  .btc-dir-arrows { flex-direction: column; align-items: stretch; }\n  .btc-dir-btn { justify-content: center; }\n  .btc-row { flex-direction: column; align-items: flex-start; }\n  .btc-options-grid { grid-template-columns: 1fr; }\n}\n\u003c/style\u003e\n\u003c!-- ===== INPUT CARD ===== --\u003e\n\u003cdiv class=\"btc-card\"\u003e\n  \u003cp class=\"btc-section-title\"\u003eInput\u003c/p\u003e","title":"Binary ↔ Text Converter - Free Online Tool"},{"content":" Number Converter Text → Binary Binary → Text Number Base Converter Binary (Base 2) Octal (Base 8) Decimal (Base 10) Hexadecimal (Base 16) Binary (Base 2) — Copy Octal (Base 8) — Copy Decimal (Base 10) — Copy Hexadecimal (Base 16) — Copy Bit Manipulation Display 8-bit 16-bit 32-bit Two's Complement (Negative Number) Text → Binary Input Text Binary Output (8-bit per character) Copy Binary Clear Binary → Text Input Binary (space-separated 8-bit groups) Text Output Copy Text Clear Related: Hash your data with Hash Generator ","permalink":"https://productivity-works.com/tools/binary-converter/","summary":"\u003cdiv id=\"bin-app\"\u003e\n\u003cstyle\u003e\n#bin-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  background: #0f172a;\n  color: #e2e8f0;\n  border-radius: 12px;\n  padding: 24px;\n  max-width: 860px;\n  margin: 0 auto;\n  box-sizing: border-box;\n}\n#bin-app * { box-sizing: border-box; }\n\n#bin-app h2 {\n  color: #06b6d4;\n  font-size: 1.1rem;\n  margin: 0 0 16px;\n  font-weight: 600;\n  letter-spacing: 0.03em;\n  text-transform: uppercase;\n}\n\n#bin-app .mode-tabs {\n  display: flex;\n  gap: 8px;\n  margin-bottom: 20px;\n  flex-wrap: wrap;\n}\n#bin-app .mode-tab {\n  padding: 8px 18px;\n  border-radius: 8px;\n  border: 1.5px solid #334155;\n  background: #1e293b;\n  color: #94a3b8;\n  cursor: pointer;\n  font-size: 0.9rem;\n  font-weight: 500;\n  transition: all 0.15s;\n}\n#bin-app .mode-tab.active,\n#bin-app .mode-tab:hover {\n  border-color: #06b6d4;\n  background: #0e4a58;\n  color: #06b6d4;\n}\n\n#bin-app .input-row {\n  display: flex;\n  gap: 10px;\n  margin-bottom: 18px;\n  align-items: stretch;\n  flex-wrap: wrap;\n}\n#bin-app .input-row select {\n  background: #1e293b;\n  border: 1.5px solid #334155;\n  color: #e2e8f0;\n  border-radius: 8px;\n  padding: 10px 12px;\n  font-size: 0.9rem;\n  cursor: pointer;\n  min-width: 150px;\n}\n#bin-app .input-row select:focus { outline: none; border-color: #06b6d4; }\n#bin-app #main-input {\n  flex: 1;\n  background: #1e293b;\n  border: 1.5px solid #334155;\n  color: #e2e8f0;\n  border-radius: 8px;\n  padding: 10px 14px;\n  font-size: 1rem;\n  font-family: 'Courier New', monospace;\n  min-width: 0;\n  transition: border-color 0.15s;\n}\n#bin-app #main-input:focus { outline: none; border-color: #06b6d4; }\n#bin-app #main-input.error { border-color: #ef4444; }\n\n#bin-app .error-msg {\n  color: #ef4444;\n  font-size: 0.82rem;\n  margin-top: -12px;\n  margin-bottom: 14px;\n  min-height: 16px;\n}\n\n#bin-app .outputs-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 12px;\n  margin-bottom: 20px;\n}\n@media (max-width: 560px) {\n  #bin-app .outputs-grid { grid-template-columns: 1fr; }\n}\n\n#bin-app .output-card {\n  background: #1e293b;\n  border: 1.5px solid #334155;\n  border-radius: 10px;\n  padding: 14px;\n}\n#bin-app .output-card .label {\n  font-size: 0.75rem;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  margin-bottom: 6px;\n  font-weight: 600;\n}\n#bin-app .output-card .value {\n  font-family: 'Courier New', monospace;\n  font-size: 0.95rem;\n  color: #06b6d4;\n  word-break: break-all;\n  min-height: 22px;\n  margin-bottom: 10px;\n  line-height: 1.5;\n}\n#bin-app .output-card .copy-btn {\n  background: #0e4a58;\n  border: 1px solid #06b6d4;\n  color: #06b6d4;\n  border-radius: 6px;\n  padding: 5px 12px;\n  font-size: 0.78rem;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#bin-app .output-card .copy-btn:hover { background: #06b6d4; color: #0f172a; }\n#bin-app .output-card .copy-btn.copied { background: #06b6d4; color: #0f172a; }\n\n#bin-app .bit-section {\n  background: #1e293b;\n  border: 1.5px solid #334155;\n  border-radius: 10px;\n  padding: 16px;\n  margin-bottom: 20px;\n}\n#bin-app .bit-toggle-row {\n  display: flex;\n  gap: 8px;\n  margin-bottom: 14px;\n  flex-wrap: wrap;\n}\n#bin-app .bit-toggle {\n  padding: 5px 14px;\n  border-radius: 6px;\n  border: 1px solid #334155;\n  background: #0f172a;\n  color: #94a3b8;\n  cursor: pointer;\n  font-size: 0.82rem;\n  font-weight: 500;\n  transition: all 0.15s;\n}\n#bin-app .bit-toggle.active,\n#bin-app .bit-toggle:hover {\n  border-color: #06b6d4;\n  color: #06b6d4;\n  background: #0e4a58;\n}\n\n#bin-app .bit-grid {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 4px;\n}\n#bin-app .bit-cell {\n  width: 32px;\n  height: 32px;\n  border-radius: 5px;\n  border: 1px solid #334155;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-family: 'Courier New', monospace;\n  font-size: 0.85rem;\n  font-weight: 700;\n  color: #94a3b8;\n  background: #0f172a;\n}\n#bin-app .bit-cell.one {\n  background: #0e4a58;\n  border-color: #06b6d4;\n  color: #06b6d4;\n}\n#bin-app .bit-cell .bit-idx {\n  font-size: 0.5rem;\n  color: #475569;\n  font-weight: 400;\n  margin-top: 1px;\n}\n\n#bin-app .twos-section {\n  background: #1e293b;\n  border: 1.5px solid #334155;\n  border-radius: 10px;\n  padding: 16px;\n  margin-bottom: 20px;\n}\n#bin-app .twos-row {\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n}\n#bin-app .twos-line {\n  display: flex;\n  gap: 12px;\n  align-items: center;\n  flex-wrap: wrap;\n}\n#bin-app .twos-line .t-label {\n  font-size: 0.78rem;\n  color: #64748b;\n  min-width: 120px;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n#bin-app .twos-line .t-val {\n  font-family: 'Courier New', monospace;\n  font-size: 0.92rem;\n  color: #a5f3fc;\n  word-break: break-all;\n}\n\n#bin-app .text-section {\n  background: #1e293b;\n  border: 1.5px solid #334155;\n  border-radius: 10px;\n  padding: 16px;\n  margin-bottom: 12px;\n}\n#bin-app .text-section textarea {\n  width: 100%;\n  background: #0f172a;\n  border: 1.5px solid #334155;\n  color: #e2e8f0;\n  border-radius: 8px;\n  padding: 10px 12px;\n  font-family: 'Courier New', monospace;\n  font-size: 0.9rem;\n  resize: vertical;\n  min-height: 90px;\n  transition: border-color 0.15s;\n}\n#bin-app .text-section textarea:focus { outline: none; border-color: #06b6d4; }\n#bin-app .text-out {\n  margin-top: 12px;\n  padding: 10px 12px;\n  background: #0f172a;\n  border-radius: 8px;\n  font-family: 'Courier New', monospace;\n  font-size: 0.85rem;\n  color: #06b6d4;\n  word-break: break-all;\n  min-height: 44px;\n  line-height: 1.7;\n}\n#bin-app .text-actions {\n  display: flex;\n  gap: 8px;\n  margin-top: 10px;\n  flex-wrap: wrap;\n}\n#bin-app .text-actions button {\n  background: #0e4a58;\n  border: 1px solid #06b6d4;\n  color: #06b6d4;\n  border-radius: 6px;\n  padding: 6px 14px;\n  font-size: 0.82rem;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#bin-app .text-actions button:hover { background: #06b6d4; color: #0f172a; }\n#bin-app .text-actions button.copied { background: #06b6d4; color: #0f172a; }\n\n#bin-app .section-title {\n  font-size: 0.78rem;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  font-weight: 600;\n  margin-bottom: 10px;\n}\n\u003c/style\u003e\n\u003c!-- Mode Tabs --\u003e\n\u003cdiv class=\"mode-tabs\"\u003e\n  \u003cbutton class=\"mode-tab active\" onclick=\"binSetMode('number')\" id=\"tab-number\"\u003eNumber Converter\u003c/button\u003e\n  \u003cbutton class=\"mode-tab\" onclick=\"binSetMode('text2bin')\" id=\"tab-text2bin\"\u003eText → Binary\u003c/button\u003e\n  \u003cbutton class=\"mode-tab\" onclick=\"binSetMode('bin2text')\" id=\"tab-bin2text\"\u003eBinary → Text\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- NUMBER CONVERTER MODE --\u003e\n\u003cdiv id=\"mode-number\"\u003e\n  \u003ch2\u003eNumber Base Converter\u003c/h2\u003e\n  \u003cdiv class=\"input-row\"\u003e\n    \u003cselect id=\"base-select\" onchange=\"binConvert()\"\u003e\n      \u003coption value=\"2\"\u003eBinary (Base 2)\u003c/option\u003e\n      \u003coption value=\"8\"\u003eOctal (Base 8)\u003c/option\u003e\n      \u003coption value=\"10\" selected\u003eDecimal (Base 10)\u003c/option\u003e\n      \u003coption value=\"16\"\u003eHexadecimal (Base 16)\u003c/option\u003e\n    \u003c/select\u003e\n    \u003cinput type=\"text\" id=\"main-input\" placeholder=\"Enter a number...\" oninput=\"binConvert()\" autocomplete=\"off\" spellcheck=\"false\"\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"error-msg\" id=\"error-msg\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"outputs-grid\"\u003e\n    \u003cdiv class=\"output-card\"\u003e\n      \u003cdiv class=\"label\"\u003eBinary (Base 2)\u003c/div\u003e\n      \u003cdiv class=\"value\" id=\"out-bin\"\u003e—\u003c/div\u003e\n      \u003cbutton class=\"copy-btn\" onclick=\"binCopy('out-bin', this)\"\u003eCopy\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"output-card\"\u003e\n      \u003cdiv class=\"label\"\u003eOctal (Base 8)\u003c/div\u003e\n      \u003cdiv class=\"value\" id=\"out-oct\"\u003e—\u003c/div\u003e\n      \u003cbutton class=\"copy-btn\" onclick=\"binCopy('out-oct', this)\"\u003eCopy\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"output-card\"\u003e\n      \u003cdiv class=\"label\"\u003eDecimal (Base 10)\u003c/div\u003e\n      \u003cdiv class=\"value\" id=\"out-dec\"\u003e—\u003c/div\u003e\n      \u003cbutton class=\"copy-btn\" onclick=\"binCopy('out-dec', this)\"\u003eCopy\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"output-card\"\u003e\n      \u003cdiv class=\"label\"\u003eHexadecimal (Base 16)\u003c/div\u003e\n      \u003cdiv class=\"value\" id=\"out-hex\"\u003e—\u003c/div\u003e\n      \u003cbutton class=\"copy-btn\" onclick=\"binCopy('out-hex', this)\"\u003eCopy\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Bit Manipulation Display --\u003e\n  \u003cdiv class=\"bit-section\"\u003e\n    \u003cdiv class=\"section-title\"\u003eBit Manipulation Display\u003c/div\u003e\n    \u003cdiv class=\"bit-toggle-row\"\u003e\n      \u003cbutton class=\"bit-toggle active\" onclick=\"binSetBits(8, this)\"\u003e8-bit\u003c/button\u003e\n      \u003cbutton class=\"bit-toggle\" onclick=\"binSetBits(16, this)\"\u003e16-bit\u003c/button\u003e\n      \u003cbutton class=\"bit-toggle\" onclick=\"binSetBits(32, this)\"\u003e32-bit\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"bit-grid\" id=\"bit-grid\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Two's Complement --\u003e\n  \u003cdiv class=\"twos-section\" id=\"twos-section\" style=\"display:none\"\u003e\n    \u003cdiv class=\"section-title\"\u003eTwo's Complement (Negative Number)\u003c/div\u003e\n    \u003cdiv class=\"twos-row\" id=\"twos-rows\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- TEXT TO BINARY MODE --\u003e\n\u003cdiv id=\"mode-text2bin\" style=\"display:none\"\u003e\n  \u003ch2\u003eText → Binary\u003c/h2\u003e\n  \u003cdiv class=\"text-section\"\u003e\n    \u003cdiv class=\"section-title\"\u003eInput Text\u003c/div\u003e\n    \u003ctextarea id=\"t2b-input\" placeholder=\"Type or paste text here...\" oninput=\"binText2Bin()\"\u003e\u003c/textarea\u003e\n    \u003cdiv class=\"section-title\" style=\"margin-top:14px\"\u003eBinary Output (8-bit per character)\u003c/div\u003e\n    \u003cdiv class=\"text-out\" id=\"t2b-output\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"text-actions\"\u003e\n      \u003cbutton onclick=\"binCopyEl('t2b-output', this)\"\u003eCopy Binary\u003c/button\u003e\n      \u003cbutton onclick=\"binClearText('t2b-input','t2b-output')\"\u003eClear\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- BINARY TO TEXT MODE --\u003e\n\u003cdiv id=\"mode-bin2text\" style=\"display:none\"\u003e\n  \u003ch2\u003eBinary → Text\u003c/h2\u003e\n  \u003cdiv class=\"text-section\"\u003e\n    \u003cdiv class=\"section-title\"\u003eInput Binary (space-separated 8-bit groups)\u003c/div\u003e\n    \u003ctextarea id=\"b2t-input\" placeholder=\"01001000 01100101 01101100 01101100 01101111\" oninput=\"binBin2Text()\"\u003e\u003c/textarea\u003e\n    \u003cdiv class=\"section-title\" style=\"margin-top:14px\"\u003eText Output\u003c/div\u003e\n    \u003cdiv class=\"text-out\" id=\"b2t-output\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"text-actions\"\u003e\n      \u003cbutton onclick=\"binCopyEl('b2t-output', this)\"\u003eCopy Text\u003c/button\u003e\n      \u003cbutton onclick=\"binClearText('b2t-input','b2t-output')\"\u003eClear\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var _bitWidth = 8;\n  var _currentVal = null;\n\n  window.binSetMode = function(mode) {\n    ['number','text2bin','bin2text'].forEach(function(m) {\n      document.getElementById('mode-'+m).style.display = (m === mode) ? '' : 'none';\n      var tab = document.getElementById('tab-'+m);\n      if (tab) tab.classList.toggle('active', m === mode);\n    });\n  };\n\n  window.binConvert = function() {\n    var inp = document.getElementById('main-input').value.trim();\n    var base = parseInt(document.getElementById('base-select').value);\n    var errEl = document.getElementById('error-msg');\n    var mainInp = document.getElementById('main-input');\n\n    if (!inp) {\n      ['out-bin','out-oct','out-dec','out-hex'].forEach(function(id) {\n        document.getElementById(id).textContent = '—';\n      });\n      document.getElementById('twos-section').style.display = 'none';\n      mainInp.classList.remove('error');\n      errEl.textContent = '';\n      _currentVal = null;\n      binRenderBits(null);\n      return;\n    }\n\n    // Validate\n    var patterns = { 2: /^-?[01]+$/, 8: /^-?[0-7]+$/, 10: /^-?\\d+$/, 16: /^-?[0-9a-fA-F]+$/ };\n    if (!patterns[base].test(inp)) {\n      mainInp.classList.add('error');\n      errEl.textContent = 'Invalid characters for selected base.';\n      return;\n    }\n    mainInp.classList.remove('error');\n    errEl.textContent = '';\n\n    var isNeg = inp.startsWith('-');\n    var absStr = isNeg ? inp.slice(1) : inp;\n    // Use BigInt for large numbers\n    var n;\n    try {\n      if (base === 10) { n = BigInt(inp); }\n      else {\n        var pos = BigInt('0x' + (base === 16 ? absStr : parseInt(absStr, base).toString(16)));\n        n = isNeg ? -pos : pos;\n      }\n    } catch(e) {\n      mainInp.classList.add('error');\n      errEl.textContent = 'Number too large or invalid.';\n      return;\n    }\n\n    _currentVal = n;\n\n    var absN = n \u003c 0n ? -n : n;\n    document.getElementById('out-bin').textContent = (n \u003c 0n ? '-' : '') + absN.toString(2);\n    document.getElementById('out-oct').textContent = (n \u003c 0n ? '-' : '') + absN.toString(8);\n    document.getElementById('out-dec').textContent = n.toString(10);\n    document.getElementById('out-hex').textContent = (n \u003c 0n ? '-' : '') + absN.toString(16).toUpperCase();\n\n    binRenderBits(n);\n    binRenderTwos(n);\n  };\n\n  window.binSetBits = function(w, btn) {\n    _bitWidth = w;\n    document.querySelectorAll('#bin-app .bit-toggle').forEach(function(b) { b.classList.remove('active'); });\n    btn.classList.add('active');\n    binRenderBits(_currentVal);\n  };\n\n  function binRenderBits(n) {\n    var grid = document.getElementById('bit-grid');\n    grid.innerHTML = '';\n    var bits = [];\n    if (n === null || n === undefined) {\n      for (var i = 0; i \u003c _bitWidth; i++) bits.push(0);\n    } else {\n      var absN = n \u003c 0n ? -n : n;\n      var binStr = absN.toString(2);\n      // If negative, use two's complement representation\n      if (n \u003c 0n) {\n        binStr = twosComplementStr(n, _bitWidth);\n      } else {\n        binStr = binStr.slice(-_bitWidth).padStart(_bitWidth, '0');\n      }\n      for (var j = 0; j \u003c binStr.length; j++) bits.push(parseInt(binStr[j]));\n    }\n    for (var k = 0; k \u003c bits.length; k++) {\n      var cell = document.createElement('div');\n      cell.className = 'bit-cell' + (bits[k] ? ' one' : '');\n      var bval = document.createElement('div');\n      bval.textContent = bits[k];\n      var bidx = document.createElement('div');\n      bidx.className = 'bit-idx';\n      bidx.textContent = (bits.length - 1 - k);\n      cell.appendChild(bval);\n      cell.appendChild(bidx);\n      grid.appendChild(cell);\n    }\n  }\n\n  function twosComplementStr(n, width) {\n    // n is negative BigInt\n    var mask = (1n \u003c\u003c BigInt(width)) - 1n;\n    var tc = (((-n) ^ mask) + 1n) \u0026 mask;\n    return tc.toString(2).padStart(width, '0');\n  }\n\n  function binRenderTwos(n) {\n    var sec = document.getElementById('twos-section');\n    var rows = document.getElementById('twos-rows');\n    if (n === null || n \u003e= 0n) { sec.style.display = 'none'; return; }\n    sec.style.display = '';\n    var absN = -n;\n    var w = _bitWidth;\n    if (absN \u003e= (1n \u003c\u003c BigInt(w - 1))) w = 16;\n    if (absN \u003e= (1n \u003c\u003c BigInt(w - 1))) w = 32;\n    if (absN \u003e= (1n \u003c\u003c BigInt(w - 1))) w = 64;\n\n    var original = absN.toString(2).padStart(w, '0');\n    var inverted = '';\n    for (var i = 0; i \u003c original.length; i++) inverted += original[i] === '0' ? '1' : '0';\n    var tc = twosComplementStr(n, w);\n    var decVal = n.toString(10);\n\n    rows.innerHTML = '';\n    [\n      ['Original (magnitude)', original],\n      ['Inverted (one\\'s complement)', inverted],\n      [\"Two's complement (\" + w + \"-bit)\", tc],\n      ['Decimal value', decVal]\n    ].forEach(function(pair) {\n      var row = document.createElement('div');\n      row.className = 'twos-line';\n      row.innerHTML = '\u003cspan class=\"t-label\"\u003e' + pair[0] + '\u003c/span\u003e\u003cspan class=\"t-val\"\u003e' + pair[1] + '\u003c/span\u003e';\n      rows.appendChild(row);\n    });\n  }\n\n  window.binCopy = function(id, btn) {\n    var text = document.getElementById(id).textContent;\n    if (text === '—' || !text) return;\n    navigator.clipboard.writeText(text).then(function() {\n      btn.textContent = 'Copied!';\n      btn.classList.add('copied');\n      setTimeout(function() { btn.textContent = 'Copy'; btn.classList.remove('copied'); }, 1500);\n    });\n  };\n\n  window.binCopyEl = function(id, btn) {\n    var text = document.getElementById(id).textContent;\n    if (!text) return;\n    navigator.clipboard.writeText(text).then(function() {\n      var orig = btn.textContent;\n      btn.textContent = 'Copied!';\n      btn.classList.add('copied');\n      setTimeout(function() { btn.textContent = orig; btn.classList.remove('copied'); }, 1500);\n    });\n  };\n\n  window.binText2Bin = function() {\n    var text = document.getElementById('t2b-input').value;\n    var out = document.getElementById('t2b-output');\n    if (!text) { out.textContent = ''; return; }\n    var result = Array.from(text).map(function(ch) {\n      return ch.charCodeAt(0).toString(2).padStart(8, '0');\n    }).join(' ');\n    out.textContent = result;\n  };\n\n  window.binBin2Text = function() {\n    var raw = document.getElementById('b2t-input').value.trim();\n    var out = document.getElementById('b2t-output');\n    if (!raw) { out.textContent = ''; return; }\n    var groups = raw.split(/\\s+/);\n    var result = '';\n    var valid = true;\n    groups.forEach(function(g) {\n      if (/^[01]{1,8}$/.test(g)) {\n        result += String.fromCharCode(parseInt(g, 2));\n      } else {\n        valid = false;\n      }\n    });\n    out.textContent = valid ? result : result + ' [some groups invalid]';\n  };\n\n  window.binClearText = function(inId, outId) {\n    document.getElementById(inId).value = '';\n    document.getElementById(outId).textContent = '';\n  };\n\n  // Init\n  binRenderBits(null);\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cstrong\u003eRelated:\u003c/strong\u003e Hash your data with \u003ca href=\"https://productivity-works.com/tools/hash-generator/\"\u003eHash Generator\u003c/a\u003e\n\u003c/p\u003e","title":"Binary/Hex/Decimal Converter - Free Online Number Base Converter"},{"content":" Body Fat Calculator Male Female Height cm Weight kg Waist cm Neck cm Hip cm Age yrs Calculate Body Fat\n--% -- Essential Athletic Fitness Average Obese --% US Navy Method --% BMI Method -- kg Lean Mass -- kg Fat Mass --% Body Fat --% Lean Mass (muscle, bone, water) How it works: The US Navy method uses circumference measurements (waist, neck, hip for women) with a logarithmic formula validated by the US military. The BMI-based method uses the Deurenberg formula: BF% = (1.20 × BMI) + (0.23 × Age) − (10.8 × sex) − 5.4. Results are averaged for the displayed figure. These are estimates — DEXA scan is the gold standard. Body Fat Categories CategoryMenWomen Essential Fat2–5%10–13% Athletes6–13%14–20% Fitness14–17%21–24% Average18–24%25–31% Obese25%+32%+ Related Free Tools BMI Calculator — Check your body mass index Calorie Calculator — Daily calorie needs based on your stats TDEE Calculator — Total daily energy expenditure Ideal Weight Calculator — Find your target weight range Related Tools Bmi Calculator Calorie Calculator Water Intake Calculator ","permalink":"https://productivity-works.com/tools/body-fat-calculator/","summary":"\u003cdiv id=\"bf-app\"\u003e\n\u003cstyle\u003e\n#bf-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 720px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#bf-app * { box-sizing: border-box; }\n#bf-app h2 {\n  font-size: 1.4rem;\n  font-weight: 700;\n  margin: 1.5rem 0 0.75rem;\n  color: #0f3460;\n  border-left: 4px solid #e94560;\n  padding-left: 0.6rem;\n}\n#bf-app .gender-row {\n  display: flex;\n  gap: 12px;\n  margin-bottom: 1.2rem;\n}\n#bf-app .gender-btn {\n  flex: 1;\n  padding: 12px;\n  border: 2px solid #dde3f0;\n  border-radius: 10px;\n  background: #f8faff;\n  cursor: pointer;\n  font-size: 1rem;\n  font-weight: 600;\n  color: #555;\n  transition: all 0.2s;\n}\n#bf-app .gender-btn.active {\n  border-color: #e94560;\n  background: #fff0f3;\n  color: #e94560;\n}\n#bf-app .input-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 14px;\n  margin-bottom: 1.2rem;\n}\n#bf-app .input-group {\n  display: flex;\n  flex-direction: column;\n  gap: 4px;\n}\n#bf-app .input-group label {\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #555;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#bf-app .input-group .input-wrap {\n  position: relative;\n}\n#bf-app .input-group input {\n  width: 100%;\n  padding: 10px 42px 10px 12px;\n  border: 2px solid #dde3f0;\n  border-radius: 8px;\n  font-size: 1rem;\n  background: #f8faff;\n  transition: border-color 0.2s;\n  outline: none;\n}\n#bf-app .input-group input:focus {\n  border-color: #0f3460;\n  background: #fff;\n}\n#bf-app .input-group .unit {\n  position: absolute;\n  right: 10px;\n  top: 50%;\n  transform: translateY(-50%);\n  font-size: 0.8rem;\n  color: #888;\n  font-weight: 600;\n  pointer-events: none;\n}\n#bf-app .calc-btn {\n  width: 100%;\n  padding: 14px;\n  background: linear-gradient(135deg, #0f3460, #e94560);\n  color: #fff;\n  border: none;\n  border-radius: 10px;\n  font-size: 1.1rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: opacity 0.2s;\n  margin-bottom: 1.5rem;\n}\n#bf-app .calc-btn:hover { opacity: 0.9; }\n#bf-app .results {\n  display: none;\n  animation: bfFadeIn 0.4s ease;\n}\n@keyframes bfFadeIn {\n  from { opacity: 0; transform: translateY(12px); }\n  to   { opacity: 1; transform: translateY(0); }\n}\n#bf-app .result-hero {\n  text-align: center;\n  background: linear-gradient(135deg, #0f3460 0%, #16213e 100%);\n  border-radius: 14px;\n  padding: 2rem 1rem 1.5rem;\n  color: #fff;\n  margin-bottom: 1.2rem;\n}\n#bf-app .result-hero .pct {\n  font-size: 3.5rem;\n  font-weight: 800;\n  line-height: 1;\n  margin-bottom: 0.3rem;\n}\n#bf-app .result-hero .cat-badge {\n  display: inline-block;\n  padding: 4px 18px;\n  border-radius: 20px;\n  font-size: 0.95rem;\n  font-weight: 700;\n  margin-bottom: 1rem;\n}\n#bf-app .gauge-container {\n  margin: 0.8rem auto 0;\n  max-width: 420px;\n}\n#bf-app .gauge-bar {\n  height: 18px;\n  border-radius: 9px;\n  background: linear-gradient(to right, #4caf50, #8bc34a, #ffeb3b, #ff9800, #f44336);\n  position: relative;\n  overflow: visible;\n}\n#bf-app .gauge-needle {\n  position: absolute;\n  top: -6px;\n  width: 4px;\n  height: 30px;\n  background: #fff;\n  border-radius: 2px;\n  transform: translateX(-50%);\n  box-shadow: 0 0 0 2px rgba(0,0,0,0.4);\n  transition: left 0.6s cubic-bezier(0.34,1.56,0.64,1);\n}\n#bf-app .gauge-labels {\n  display: flex;\n  justify-content: space-between;\n  margin-top: 6px;\n  font-size: 0.68rem;\n  color: rgba(255,255,255,0.7);\n}\n#bf-app .stats-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 12px;\n  margin-bottom: 1.2rem;\n}\n#bf-app .stat-card {\n  background: #f8faff;\n  border: 2px solid #dde3f0;\n  border-radius: 12px;\n  padding: 1rem;\n  text-align: center;\n}\n#bf-app .stat-card .stat-val {\n  font-size: 1.8rem;\n  font-weight: 800;\n  color: #0f3460;\n}\n#bf-app .stat-card .stat-lbl {\n  font-size: 0.78rem;\n  color: #777;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.03em;\n}\n#bf-app .chart-wrap {\n  display: flex;\n  align-items: center;\n  gap: 1.5rem;\n  background: #f8faff;\n  border: 2px solid #dde3f0;\n  border-radius: 12px;\n  padding: 1.2rem 1.5rem;\n  margin-bottom: 1.2rem;\n}\n#bf-app canvas#bfPie {\n  flex-shrink: 0;\n}\n#bf-app .legend {\n  flex: 1;\n}\n#bf-app .legend-item {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-bottom: 10px;\n  font-size: 0.9rem;\n}\n#bf-app .legend-dot {\n  width: 14px;\n  height: 14px;\n  border-radius: 4px;\n  flex-shrink: 0;\n}\n#bf-app .legend-val {\n  font-weight: 700;\n  font-size: 1rem;\n  color: #0f3460;\n}\n#bf-app .methods-note {\n  background: #fffbf0;\n  border: 1px solid #ffe082;\n  border-radius: 10px;\n  padding: 1rem 1.2rem;\n  font-size: 0.84rem;\n  color: #7a6000;\n  line-height: 1.6;\n  margin-bottom: 1.2rem;\n}\n#bf-app .category-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.85rem;\n  margin-bottom: 1.5rem;\n}\n#bf-app .category-table th {\n  background: #0f3460;\n  color: #fff;\n  padding: 8px 12px;\n  text-align: left;\n  font-weight: 600;\n}\n#bf-app .category-table td {\n  padding: 7px 12px;\n  border-bottom: 1px solid #eee;\n}\n#bf-app .category-table tr:nth-child(even) td { background: #f8faff; }\n#bf-app .category-table tr.highlight td {\n  background: #fff0f3 !important;\n  font-weight: 700;\n  color: #e94560;\n}\n#bf-app .related-tools {\n  background: #f0f4ff;\n  border-radius: 12px;\n  padding: 1.2rem 1.5rem;\n  margin-top: 1.5rem;\n}\n#bf-app .related-tools h3 {\n  margin: 0 0 0.8rem;\n  font-size: 1rem;\n  color: #0f3460;\n  font-weight: 700;\n}\n#bf-app .related-tools ul {\n  margin: 0;\n  padding-left: 1.2rem;\n}\n#bf-app .related-tools li {\n  margin-bottom: 6px;\n  font-size: 0.9rem;\n}\n#bf-app .related-tools a {\n  color: #0f3460;\n  text-decoration: none;\n  font-weight: 600;\n}\n#bf-app .related-tools a:hover { color: #e94560; text-decoration: underline; }\n#bf-app .error-msg {\n  color: #e94560;\n  font-size: 0.88rem;\n  font-weight: 600;\n  margin-bottom: 0.8rem;\n  display: none;\n  padding: 8px 12px;\n  background: #fff0f3;\n  border-radius: 8px;\n  border: 1px solid #ffc0cb;\n}\n@media (max-width: 500px) {\n  #bf-app .input-grid { grid-template-columns: 1fr; }\n  #bf-app .stats-grid { grid-template-columns: 1fr 1fr; }\n  #bf-app .chart-wrap { flex-direction: column; }\n}\n\u003c/style\u003e\n\u003ch2\u003eBody Fat Calculator\u003c/h2\u003e\n\u003cdiv class=\"gender-row\"\u003e\n  \u003cbutton class=\"gender-btn active\" id=\"btnMale\" onclick=\"bfSetGender('male')\"\u003eMale\u003c/button\u003e\n  \u003cbutton class=\"gender-btn\" id=\"btnFemale\" onclick=\"bfSetGender('female')\"\u003eFemale\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"input-grid\"\u003e\n  \u003cdiv class=\"input-group\"\u003e\n    \u003clabel\u003eHeight\u003c/label\u003e\n    \u003cdiv class=\"input-wrap\"\u003e\n      \u003cinput type=\"number\" id=\"bfHeight\" placeholder=\"175\" min=\"100\" max=\"250\" value=\"\"\u003e\n      \u003cspan class=\"unit\"\u003ecm\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"input-group\"\u003e\n    \u003clabel\u003eWeight\u003c/label\u003e\n    \u003cdiv class=\"input-wrap\"\u003e\n      \u003cinput type=\"number\" id=\"bfWeight\" placeholder=\"75\" min=\"30\" max=\"300\" value=\"\"\u003e\n      \u003cspan class=\"unit\"\u003ekg\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"input-group\"\u003e\n    \u003clabel\u003eWaist\u003c/label\u003e\n    \u003cdiv class=\"input-wrap\"\u003e\n      \u003cinput type=\"number\" id=\"bfWaist\" placeholder=\"85\" min=\"40\" max=\"200\" value=\"\"\u003e\n      \u003cspan class=\"unit\"\u003ecm\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"input-group\"\u003e\n    \u003clabel\u003eNeck\u003c/label\u003e\n    \u003cdiv class=\"input-wrap\"\u003e\n      \u003cinput type=\"number\" id=\"bfNeck\" placeholder=\"38\" min=\"20\" max=\"80\" value=\"\"\u003e\n      \u003cspan class=\"unit\"\u003ecm\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"input-group\" id=\"hipGroup\" style=\"display:none;\"\u003e\n    \u003clabel\u003eHip\u003c/label\u003e\n    \u003cdiv class=\"input-wrap\"\u003e\n      \u003cinput type=\"number\" id=\"bfHip\" placeholder=\"95\" min=\"50\" max=\"200\" value=\"\"\u003e\n      \u003cspan class=\"unit\"\u003ecm\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"input-group\"\u003e\n    \u003clabel\u003eAge\u003c/label\u003e\n    \u003cdiv class=\"input-wrap\"\u003e\n      \u003cinput type=\"number\" id=\"bfAge\" placeholder=\"30\" min=\"10\" max=\"120\" value=\"\"\u003e\n      \u003cspan class=\"unit\"\u003eyrs\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"error-msg\" id=\"bfError\"\u003e\u003c/div\u003e\n\u003cp\u003e\u003cbutton class=\"calc-btn\" onclick=\"bfCalculate()\"\u003eCalculate Body Fat\u003c/button\u003e\u003c/p\u003e","title":"Body Fat Calculator - Estimate Your Body Fat Percentage"},{"content":"A bookmarklet is a bookmark that runs JavaScript on any page. Paste your JS code below, click Convert, drag it to your bookmark bar, and it works instantly — no extensions needed.\nJavaScript Code Paste your JavaScript here Minify (remove comments \u0026amp; whitespace) URI encode Wrap in IIFE (void function(){...}()) Convert to Bookmarklet Clear Test in Console Bookmarklet Output Copy Characters: 0 Size: 0 B Status: — Drag this button to your bookmark bar: Bookmarklet (Show your bookmark bar: Ctrl+Shift+B / Cmd+Shift+B) Ready-to-Use Examples Highlight Links Dark Mode Toggle Font Size Changer Print Page Scroll to Top Word Count Copy Page URL+Title Related Tools: Code Minifier URL Encoder How to Use a Bookmarklet Paste your JavaScript into the code area above (or pick an example). Click Convert — the tool wraps your code in a javascript: URI. Drag the generated button to your browser\u0026rsquo;s bookmark bar. Visit any page and click the bookmark — your script runs instantly. Tips Minify strips comments and whitespace, keeping the URL short. IIFE wrap prevents your variables from polluting the page\u0026rsquo;s global scope. URI encode is required for special characters like spaces, quotes, and ampersands. Most browsers cap bookmarklet URLs at ~2 KB — keep scripts lean. Use alert() or console.log() to debug output during testing. Related Tools Code Minifier — Minify JS, CSS, and HTML files. URL Encoder — Encode/decode URL components manually. ","permalink":"https://productivity-works.com/tools/bookmarklet-maker/","summary":"\u003cp\u003eA \u003cstrong\u003ebookmarklet\u003c/strong\u003e is a bookmark that runs JavaScript on any page. Paste your JS code below, click \u003cstrong\u003eConvert\u003c/strong\u003e, drag it to your bookmark bar, and it works instantly — no extensions needed.\u003c/p\u003e\n\u003cdiv id=\"bm-app\"\u003e\n\u003cstyle\u003e\n#bm-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#bm-app * { box-sizing: border-box; }\n#bm-app h2 {\n  font-size: 1.15rem;\n  font-weight: 700;\n  margin: 1.6rem 0 0.5rem;\n  color: #0f3460;\n  border-left: 4px solid #e94560;\n  padding-left: 0.6rem;\n}\n#bm-app .bm-section {\n  background: #f8f9fc;\n  border: 1px solid #dde3f0;\n  border-radius: 10px;\n  padding: 1.2rem 1.4rem;\n  margin-bottom: 1.2rem;\n}\n#bm-app label {\n  display: block;\n  font-weight: 600;\n  font-size: 0.85rem;\n  margin-bottom: 0.35rem;\n  color: #333;\n}\n#bm-app textarea {\n  width: 100%;\n  min-height: 180px;\n  font-family: 'Courier New', Courier, monospace;\n  font-size: 0.88rem;\n  line-height: 1.55;\n  padding: 0.8rem 1rem;\n  border: 1.5px solid #c8d0e0;\n  border-radius: 7px;\n  background: #fff;\n  color: #1a1a2e;\n  resize: vertical;\n  transition: border-color 0.2s;\n}\n#bm-app textarea:focus {\n  outline: none;\n  border-color: #0f3460;\n}\n#bm-app .bm-options {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 1rem;\n  align-items: center;\n  margin-top: 0.8rem;\n}\n#bm-app .bm-check {\n  display: flex;\n  align-items: center;\n  gap: 0.4rem;\n  font-size: 0.88rem;\n  cursor: pointer;\n  user-select: none;\n}\n#bm-app .bm-check input { cursor: pointer; width: 16px; height: 16px; }\n#bm-app .bm-btn-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.6rem;\n  margin-top: 1rem;\n}\n#bm-app .bm-btn {\n  padding: 0.55rem 1.2rem;\n  border: none;\n  border-radius: 6px;\n  font-size: 0.9rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: opacity 0.15s, transform 0.1s;\n}\n#bm-app .bm-btn:hover { opacity: 0.88; transform: translateY(-1px); }\n#bm-app .bm-btn:active { transform: translateY(0); }\n#bm-app .bm-btn-primary { background: #e94560; color: #fff; }\n#bm-app .bm-btn-secondary { background: #0f3460; color: #fff; }\n#bm-app .bm-btn-ghost {\n  background: transparent;\n  color: #0f3460;\n  border: 1.5px solid #0f3460;\n}\n#bm-app .bm-btn-sm {\n  padding: 0.35rem 0.75rem;\n  font-size: 0.8rem;\n}\n#bm-app .bm-output-wrap {\n  position: relative;\n  margin-top: 0.5rem;\n}\n#bm-app .bm-output {\n  width: 100%;\n  min-height: 80px;\n  font-family: 'Courier New', Courier, monospace;\n  font-size: 0.82rem;\n  line-height: 1.5;\n  padding: 0.75rem 3.5rem 0.75rem 1rem;\n  border: 1.5px solid #c8d0e0;\n  border-radius: 7px;\n  background: #fff;\n  color: #1a1a2e;\n  word-break: break-all;\n  white-space: pre-wrap;\n  resize: vertical;\n}\n#bm-app .bm-copy-btn {\n  position: absolute;\n  top: 0.5rem;\n  right: 0.5rem;\n  padding: 0.3rem 0.6rem;\n  font-size: 0.75rem;\n  font-weight: 600;\n  background: #0f3460;\n  color: #fff;\n  border: none;\n  border-radius: 5px;\n  cursor: pointer;\n  transition: opacity 0.15s;\n}\n#bm-app .bm-copy-btn:hover { opacity: 0.8; }\n#bm-app .bm-stats {\n  display: flex;\n  gap: 1.5rem;\n  margin-top: 0.5rem;\n  font-size: 0.8rem;\n  color: #666;\n}\n#bm-app .bm-stats span strong { color: #0f3460; }\n#bm-app .bm-drag-box {\n  margin-top: 0.8rem;\n  padding: 0.9rem 1.2rem;\n  background: #eaf0ff;\n  border: 2px dashed #7fa7e0;\n  border-radius: 8px;\n  font-size: 0.88rem;\n  color: #2a4a7f;\n  text-align: center;\n}\n#bm-app .bm-drag-link {\n  display: inline-block;\n  margin-top: 0.5rem;\n  padding: 0.4rem 1rem;\n  background: #fff;\n  border: 1.5px solid #7fa7e0;\n  border-radius: 5px;\n  font-weight: 700;\n  color: #0f3460;\n  text-decoration: none;\n  cursor: grab;\n}\n#bm-app .bm-drag-link:hover { background: #ddeaff; }\n#bm-app .bm-presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem;\n  margin-top: 0.3rem;\n}\n#bm-app .bm-preset-btn {\n  padding: 0.38rem 0.85rem;\n  background: #fff;\n  border: 1.5px solid #c8d0e0;\n  border-radius: 20px;\n  font-size: 0.82rem;\n  cursor: pointer;\n  transition: border-color 0.15s, background 0.15s;\n  color: #333;\n}\n#bm-app .bm-preset-btn:hover {\n  border-color: #e94560;\n  background: #fff0f3;\n  color: #e94560;\n}\n#bm-app .bm-alert {\n  padding: 0.65rem 1rem;\n  border-radius: 6px;\n  font-size: 0.85rem;\n  margin-top: 0.6rem;\n  display: none;\n}\n#bm-app .bm-alert.error { background: #fff0f0; border: 1px solid #ffbaba; color: #c00; }\n#bm-app .bm-alert.success { background: #f0fff4; border: 1px solid #a0d9b0; color: #1a7a3a; }\n#bm-app .bm-divider { border: none; border-top: 1px solid #dde3f0; margin: 1.5rem 0; }\n#bm-app .bm-crosslinks {\n  background: #f0f4ff;\n  border-radius: 8px;\n  padding: 1rem 1.2rem;\n  font-size: 0.88rem;\n}\n#bm-app .bm-crosslinks a {\n  color: #0f3460;\n  text-decoration: underline;\n  margin-right: 1rem;\n}\n\u003c/style\u003e\n\u003cdiv class=\"bm-section\"\u003e\n  \u003ch2\u003eJavaScript Code\u003c/h2\u003e\n  \u003clabel for=\"bm-code\"\u003ePaste your JavaScript here\u003c/label\u003e\n  \u003ctextarea id=\"bm-code\" placeholder=\"// Example: scroll to top\u0026#10;window.scrollTo({top: 0, behavior: 'smooth'});\"\u003e\u003c/textarea\u003e\n  \u003cdiv class=\"bm-options\"\u003e\n    \u003clabel class=\"bm-check\"\u003e\n      \u003cinput type=\"checkbox\" id=\"bm-minify\" checked\u003e\n      Minify (remove comments \u0026amp; whitespace)\n    \u003c/label\u003e\n    \u003clabel class=\"bm-check\"\u003e\n      \u003cinput type=\"checkbox\" id=\"bm-encode\" checked\u003e\n      URI encode\n    \u003c/label\u003e\n    \u003clabel class=\"bm-check\"\u003e\n      \u003cinput type=\"checkbox\" id=\"bm-wrap-iife\" checked\u003e\n      Wrap in IIFE (void function(){...}())\n    \u003c/label\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"bm-btn-row\"\u003e\n    \u003cbutton class=\"bm-btn bm-btn-primary\" id=\"bm-convert-btn\"\u003eConvert to Bookmarklet\u003c/button\u003e\n    \u003cbutton class=\"bm-btn bm-btn-ghost\" id=\"bm-clear-btn\"\u003eClear\u003c/button\u003e\n    \u003cbutton class=\"bm-btn bm-btn-secondary\" id=\"bm-test-btn\"\u003eTest in Console\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"bm-alert\" id=\"bm-alert\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"bm-section\"\u003e\n  \u003ch2\u003eBookmarklet Output\u003c/h2\u003e\n  \u003cdiv class=\"bm-output-wrap\"\u003e\n    \u003ctextarea class=\"bm-output\" id=\"bm-result\" readonly placeholder=\"Your bookmarklet URL will appear here...\"\u003e\u003c/textarea\u003e\n    \u003cbutton class=\"bm-copy-btn\" id=\"bm-copy-btn\"\u003eCopy\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"bm-stats\"\u003e\n    \u003cspan\u003eCharacters: \u003cstrong id=\"bm-char-count\"\u003e0\u003c/strong\u003e\u003c/span\u003e\n    \u003cspan\u003eSize: \u003cstrong id=\"bm-size\"\u003e0 B\u003c/strong\u003e\u003c/span\u003e\n    \u003cspan\u003eStatus: \u003cstrong id=\"bm-status\"\u003e—\u003c/strong\u003e\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"bm-drag-box\" id=\"bm-drag-box\" style=\"display:none;\"\u003e\n    \u003cdiv\u003eDrag this button to your bookmark bar:\u003c/div\u003e\n    \u003ca class=\"bm-drag-link\" id=\"bm-drag-link\" href=\"#\"\u003eBookmarklet\u003c/a\u003e\n    \u003cdiv style=\"margin-top:0.5rem;font-size:0.8rem;color:#5a7aaf;\"\u003e(Show your bookmark bar: Ctrl+Shift+B / Cmd+Shift+B)\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"bm-section\"\u003e\n  \u003ch2\u003eReady-to-Use Examples\u003c/h2\u003e\n  \u003cdiv class=\"bm-presets\" id=\"bm-presets\"\u003e\n    \u003cbutton class=\"bm-preset-btn\" data-preset=\"highlight-links\"\u003eHighlight Links\u003c/button\u003e\n    \u003cbutton class=\"bm-preset-btn\" data-preset=\"dark-mode\"\u003eDark Mode Toggle\u003c/button\u003e\n    \u003cbutton class=\"bm-preset-btn\" data-preset=\"font-size\"\u003eFont Size Changer\u003c/button\u003e\n    \u003cbutton class=\"bm-preset-btn\" data-preset=\"print-page\"\u003ePrint Page\u003c/button\u003e\n    \u003cbutton class=\"bm-preset-btn\" data-preset=\"scroll-top\"\u003eScroll to Top\u003c/button\u003e\n    \u003cbutton class=\"bm-preset-btn\" data-preset=\"word-count\"\u003eWord Count\u003c/button\u003e\n    \u003cbutton class=\"bm-preset-btn\" data-preset=\"copy-url\"\u003eCopy Page URL+Title\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003chr class=\"bm-divider\"\u003e\n\u003cdiv class=\"bm-crosslinks\"\u003e\n  \u003cstrong\u003eRelated Tools:\u003c/strong\u003e\n  \u003ca href=\"/tools/code-minifier/\"\u003eCode Minifier\u003c/a\u003e\n  \u003ca href=\"/tools/url-encoder/\"\u003eURL Encoder\u003c/a\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  'use strict';\n\n  var PRESETS = {\n    'highlight-links': {\n      name: 'Highlight Links',\n      code: [\n        '// Highlight all links on the page with a colored outline',\n        'var links = document.querySelectorAll(\"a\");',\n        'var colors = [\"#e94560\",\"#0f3460\",\"#f5a623\",\"#2ecc71\",\"#9b59b6\"];',\n        'links.forEach(function(el, i) {',\n        '  el.style.outline = \"3px solid \" + colors[i % colors.length];',\n        '  el.style.outlineOffset = \"2px\";',\n        '});',\n        'alert(\"Highlighted \" + links.length + \" links.\");'\n      ].join('\\n')\n    },\n    'dark-mode': {\n      name: 'Dark Mode Toggle',\n      code: [\n        '// Toggle a simple dark mode overlay on any site',\n        'var id = \"__bm_dark__\";',\n        'var existing = document.getElementById(id);',\n        'if (existing) {',\n        '  existing.remove();',\n        '} else {',\n        '  var s = document.createElement(\"style\");',\n        '  s.id = id;',\n        '  s.textContent = \"html{filter:invert(1) hue-rotate(180deg)!important} img,video,canvas{filter:invert(1) hue-rotate(180deg)!important}\";',\n        '  document.head.appendChild(s);',\n        '}'\n      ].join('\\n')\n    },\n    'font-size': {\n      name: 'Font Size Changer',\n      code: [\n        '// Cycle through font sizes: 100% → 120% → 140% → 100%',\n        'var sizes = [\"100%\",\"120%\",\"140%\"];',\n        'var cur = document.documentElement.style.fontSize || \"100%\";',\n        'var idx = sizes.indexOf(cur);',\n        'var next = sizes[(idx + 1) % sizes.length];',\n        'document.documentElement.style.fontSize = next;'\n      ].join('\\n')\n    },\n    'print-page': {\n      name: 'Print Page',\n      code: '// Print the current page\\nwindow.print();'\n    },\n    'scroll-top': {\n      name: 'Scroll to Top',\n      code: [\n        '// Smooth scroll to the top of the page',\n        'window.scrollTo({ top: 0, behavior: \"smooth\" });'\n      ].join('\\n')\n    },\n    'word-count': {\n      name: 'Word Count',\n      code: [\n        '// Count words in the visible body text',\n        'var text = document.body.innerText || document.body.textContent || \"\";',\n        'var words = text.trim().split(/\\\\s+/).filter(function(w){ return w.length \u003e 0; });',\n        'alert(\"Words on this page: \" + words.length);'\n      ].join('\\n')\n    },\n    'copy-url': {\n      name: 'Copy Page URL+Title',\n      code: [\n        '// Copy the page title and URL to clipboard',\n        'var txt = document.title + \"\\\\n\" + location.href;',\n        'navigator.clipboard.writeText(txt).then(function(){',\n        '  alert(\"Copied!\\\\n\" + txt);',\n        '}).catch(function(){',\n        '  prompt(\"Copy this:\", txt);',\n        '});'\n      ].join('\\n')\n    }\n  };\n\n  function minifyJS(code) {\n    // Remove single-line comments (but not URLs)\n    code = code.replace(/(?\u003c![:'\"])\\/\\/[^\\n]*/g, '');\n    // Remove multi-line comments\n    code = code.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '');\n    // Collapse whitespace\n    code = code.replace(/\\s+/g, ' ').trim();\n    // Remove spaces around operators/punctuation (safe subset)\n    code = code.replace(/\\s*([{};,=+\\-*\\/\u003c\u003e!\u0026|?:])\\s*/g, '$1');\n    // Fix: don't collapse spaces inside string literals — basic pass only\n    return code;\n  }\n\n  function buildBookmarklet(rawCode, doMinify, doEncode, doIife) {\n    var code = rawCode.trim();\n    if (!code) return null;\n\n    if (doMinify) {\n      code = minifyJS(code);\n    }\n    if (doIife) {\n      code = 'void function(){' + code + '}()';\n    }\n    if (doEncode) {\n      return 'javascript:' + encodeURIComponent(code);\n    } else {\n      return 'javascript:' + code;\n    }\n  }\n\n  function byteSize(str) {\n    return new Blob([str]).size;\n  }\n\n  function formatBytes(n) {\n    if (n \u003c 1024) return n + ' B';\n    return (n / 1024).toFixed(1) + ' KB';\n  }\n\n  function showAlert(msg, type) {\n    var el = document.getElementById('bm-alert');\n    el.textContent = msg;\n    el.className = 'bm-alert ' + type;\n    el.style.display = 'block';\n    setTimeout(function () { el.style.display = 'none'; }, 4000);\n  }\n\n  function updateStats(url) {\n    var chars = url ? url.length : 0;\n    var bytes = url ? byteSize(url) : 0;\n    document.getElementById('bm-char-count').textContent = chars.toLocaleString();\n    document.getElementById('bm-size').textContent = formatBytes(bytes);\n  }\n\n  function convert() {\n    var code = document.getElementById('bm-code').value;\n    if (!code.trim()) {\n      showAlert('Please enter some JavaScript code first.', 'error');\n      return;\n    }\n\n    var doMinify = document.getElementById('bm-minify').checked;\n    var doEncode = document.getElementById('bm-encode').checked;\n    var doIife = document.getElementById('bm-wrap-iife').checked;\n\n    try {\n      var url = buildBookmarklet(code, doMinify, doEncode, doIife);\n      if (!url) { showAlert('Could not build bookmarklet.', 'error'); return; }\n\n      document.getElementById('bm-result').value = url;\n      updateStats(url);\n      document.getElementById('bm-status').textContent = 'Ready';\n\n      // Update drag link\n      var dragBox = document.getElementById('bm-drag-box');\n      var dragLink = document.getElementById('bm-drag-link');\n      dragLink.href = url;\n      dragBox.style.display = 'block';\n\n      showAlert('Bookmarklet ready! Drag it to your bookmark bar or click Copy.', 'success');\n    } catch (e) {\n      showAlert('Error: ' + e.message, 'error');\n    }\n  }\n\n  function copyResult() {\n    var val = document.getElementById('bm-result').value;\n    if (!val) { showAlert('Nothing to copy yet. Convert first.', 'error'); return; }\n    navigator.clipboard.writeText(val).then(function () {\n      var btn = document.getElementById('bm-copy-btn');\n      btn.textContent = 'Copied!';\n      setTimeout(function () { btn.textContent = 'Copy'; }, 2000);\n    }).catch(function () {\n      document.getElementById('bm-result').select();\n      document.execCommand('copy');\n    });\n  }\n\n  function testBookmarklet() {\n    var code = document.getElementById('bm-code').value.trim();\n    if (!code) { showAlert('Enter some JavaScript to test.', 'error'); return; }\n    try {\n      // Run in a sandboxed way: wrap in try/catch, use Function constructor\n      var fn = new Function(code);\n      fn();\n    } catch (e) {\n      showAlert('Runtime error: ' + e.message, 'error');\n    }\n  }\n\n  function loadPreset(key) {\n    var p = PRESETS[key];\n    if (!p) return;\n    document.getElementById('bm-code').value = p.code;\n    document.getElementById('bm-status').textContent = '—';\n    document.getElementById('bm-result').value = '';\n    document.getElementById('bm-drag-box').style.display = 'none';\n    updateStats('');\n  }\n\n  // Wire up events\n  document.getElementById('bm-convert-btn').addEventListener('click', convert);\n  document.getElementById('bm-copy-btn').addEventListener('click', copyResult);\n  document.getElementById('bm-test-btn').addEventListener('click', testBookmarklet);\n  document.getElementById('bm-clear-btn').addEventListener('click', function () {\n    document.getElementById('bm-code').value = '';\n    document.getElementById('bm-result').value = '';\n    document.getElementById('bm-drag-box').style.display = 'none';\n    document.getElementById('bm-status').textContent = '—';\n    updateStats('');\n  });\n\n  document.getElementById('bm-presets').addEventListener('click', function (e) {\n    if (e.target.classList.contains('bm-preset-btn')) {\n      loadPreset(e.target.dataset.preset);\n    }\n  });\n\n  // Live char count on code input\n  document.getElementById('bm-code').addEventListener('input', function () {\n    var url = document.getElementById('bm-result').value;\n    if (url) updateStats(url);\n  });\n\n  // Load default preset\n  loadPreset('scroll-top');\n}());\n\u003c/script\u003e\n\u003c/div\u003e\n\u003ch2 id=\"how-to-use-a-bookmarklet\"\u003eHow to Use a Bookmarklet\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003ePaste\u003c/strong\u003e your JavaScript into the code area above (or pick an example).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eClick Convert\u003c/strong\u003e — the tool wraps your code in a \u003ccode\u003ejavascript:\u003c/code\u003e URI.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eDrag\u003c/strong\u003e the generated button to your browser\u0026rsquo;s bookmark bar.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eVisit any page\u003c/strong\u003e and click the bookmark — your script runs instantly.\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"tips\"\u003eTips\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eMinify\u003c/strong\u003e strips comments and whitespace, keeping the URL short.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eIIFE wrap\u003c/strong\u003e prevents your variables from polluting the page\u0026rsquo;s global scope.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eURI encode\u003c/strong\u003e is required for special characters like spaces, quotes, and ampersands.\u003c/li\u003e\n\u003cli\u003eMost browsers cap bookmarklet URLs at ~2 KB — keep scripts lean.\u003c/li\u003e\n\u003cli\u003eUse \u003ccode\u003ealert()\u003c/code\u003e or \u003ccode\u003econsole.log()\u003c/code\u003e to debug output during testing.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://productivity-works.com/tools/code-minifier/\"\u003eCode Minifier\u003c/a\u003e\n — Minify JS, CSS, and HTML files.\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://productivity-works.com/tools/url-encoder/\"\u003eURL Encoder\u003c/a\u003e\n — Encode/decode URL components manually.\u003c/li\u003e\n\u003c/ul\u003e","title":"Bookmarklet Maker"},{"content":"Design and download professional business cards instantly — no signup required.\nCard Details \u0026lt;label\u0026gt;Full Name\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bc-name\u0026quot; placeholder=\u0026quot;Jane Smith\u0026quot; value=\u0026quot;Jane Smith\u0026quot; /\u0026gt; \u0026lt;label\u0026gt;Job Title\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bc-title\u0026quot; placeholder=\u0026quot;Product Designer\u0026quot; value=\u0026quot;Product Designer\u0026quot; /\u0026gt; \u0026lt;label\u0026gt;Company\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bc-company\u0026quot; placeholder=\u0026quot;Acme Corp\u0026quot; value=\u0026quot;Acme Corp\u0026quot; /\u0026gt; \u0026lt;label\u0026gt;Email\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;email\u0026quot; id=\u0026quot;bc-email\u0026quot; placeholder=\u0026quot;jane@acmecorp.com\u0026quot; value=\u0026quot;jane@acmecorp.com\u0026quot; /\u0026gt; \u0026lt;label\u0026gt;Phone\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;tel\u0026quot; id=\u0026quot;bc-phone\u0026quot; placeholder=\u0026quot;+1 (555) 000-0000\u0026quot; value=\u0026quot;+1 (555) 000-0000\u0026quot; /\u0026gt; \u0026lt;label\u0026gt;Website\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;url\u0026quot; id=\u0026quot;bc-website\u0026quot; placeholder=\u0026quot;acmecorp.com\u0026quot; value=\u0026quot;acmecorp.com\u0026quot; /\u0026gt; \u0026lt;label\u0026gt;Address\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bc-address\u0026quot; placeholder=\u0026quot;123 Main St, New York, NY\u0026quot; value=\u0026quot;123 Main St, New York, NY\u0026quot; /\u0026gt; \u0026lt;label style=\u0026quot;margin-top:16px;\u0026quot;\u0026gt;Layout Template\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;bc-templates\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;bc-tpl-btn active\u0026quot; data-tpl=\u0026quot;classic\u0026quot; onclick=\u0026quot;(function(){window._bcSetTpl('classic');})()\u0026quot;\u0026gt;Classic\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;bc-tpl-btn\u0026quot; data-tpl=\u0026quot;modern\u0026quot; onclick=\u0026quot;(function(){window._bcSetTpl('modern');})()\u0026quot;\u0026gt;Modern\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;bc-tpl-btn\u0026quot; data-tpl=\u0026quot;minimal\u0026quot; onclick=\u0026quot;(function(){window._bcSetTpl('minimal');})()\u0026quot;\u0026gt;Minimal\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;bc-tpl-btn\u0026quot; data-tpl=\u0026quot;bold\u0026quot; onclick=\u0026quot;(function(){window._bcSetTpl('bold');})()\u0026quot;\u0026gt;Bold\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;label style=\u0026quot;margin-top:16px;\u0026quot;\u0026gt;Colors\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;bc-colors\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;bc-color-group\u0026quot;\u0026gt; \u0026lt;label style=\u0026quot;margin-top:0;\u0026quot;\u0026gt;Background\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;bc-color-bg\u0026quot; value=\u0026quot;#1e293b\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bc-color-group\u0026quot;\u0026gt; \u0026lt;label style=\u0026quot;margin-top:0;\u0026quot;\u0026gt;Accent\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;bc-color-accent\u0026quot; value=\u0026quot;#6366f1\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bc-color-group\u0026quot;\u0026gt; \u0026lt;label style=\u0026quot;margin-top:0;\u0026quot;\u0026gt;Text\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;bc-color-text\u0026quot; value=\u0026quot;#ffffff\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bc-toggle-row\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;bc-toggle on\u0026quot; id=\u0026quot;bc-qr-toggle\u0026quot; onclick=\u0026quot;(function(){window._bcToggleQr();})()\u0026quot;\u0026gt;\u0026lt;/button\u0026gt; \u0026lt;span\u0026gt;Show QR code on back\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;p class=\u0026quot;bc-note\u0026quot;\u0026gt;Card size: 3.5 × 2 in (91 × 55 mm) — standard business card dimensions.\u0026lt;/p\u0026gt; Preview Front Back Download PNG Print Need more design tools?\nCreate invoices → Invoice Generator Generate email signatures → Email Signature Generator Generate QR codes → QR Code Generator ","permalink":"https://productivity-works.com/tools/business-card-generator/","summary":"\u003cp\u003eDesign and download professional business cards instantly — no signup required.\u003c/p\u003e\n\u003cdiv id=\"bc-app\"\u003e\n\u003cstyle\u003e\n#bc-app * { box-sizing: border-box; margin: 0; padding: 0; }\n#bc-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  color: #1e293b;\n}\n#bc-app h2 {\n  font-size: 1.1rem;\n  font-weight: 700;\n  color: #1e293b;\n  margin-bottom: 12px;\n  padding-bottom: 6px;\n  border-bottom: 2px solid #e2e8f0;\n}\n#bc-app .bc-layout {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 24px;\n  margin-top: 16px;\n}\n@media (max-width: 680px) {\n  #bc-app .bc-layout { grid-template-columns: 1fr; }\n}\n#bc-app .bc-panel {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 20px;\n}\n#bc-app label {\n  display: block;\n  font-size: 12px;\n  font-weight: 600;\n  color: #64748b;\n  margin-bottom: 4px;\n  margin-top: 12px;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#bc-app label:first-child { margin-top: 0; }\n#bc-app input[type=\"text\"],\n#bc-app input[type=\"email\"],\n#bc-app input[type=\"tel\"],\n#bc-app input[type=\"url\"] {\n  width: 100%;\n  padding: 8px 10px;\n  border: 1px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 14px;\n  color: #1e293b;\n  background: #fff;\n  transition: border-color 0.15s;\n}\n#bc-app input[type=\"text\"]:focus,\n#bc-app input[type=\"email\"]:focus,\n#bc-app input[type=\"tel\"]:focus,\n#bc-app input[type=\"url\"]:focus {\n  outline: none;\n  border-color: #6366f1;\n  box-shadow: 0 0 0 3px rgba(99,102,241,0.1);\n}\n#bc-app .bc-templates {\n  display: grid;\n  grid-template-columns: repeat(4, 1fr);\n  gap: 8px;\n  margin-bottom: 4px;\n}\n#bc-app .bc-tpl-btn {\n  padding: 7px 4px;\n  border: 2px solid #e2e8f0;\n  border-radius: 6px;\n  font-size: 11px;\n  font-weight: 600;\n  cursor: pointer;\n  background: #fff;\n  color: #475569;\n  transition: all 0.15s;\n  text-align: center;\n}\n#bc-app .bc-tpl-btn:hover { border-color: #6366f1; color: #6366f1; }\n#bc-app .bc-tpl-btn.active { border-color: #6366f1; background: #6366f1; color: #fff; }\n#bc-app .bc-colors {\n  display: flex;\n  gap: 12px;\n  align-items: flex-end;\n  flex-wrap: wrap;\n}\n#bc-app .bc-color-group { flex: 1; min-width: 80px; }\n#bc-app input[type=\"color\"] {\n  width: 100%;\n  height: 36px;\n  border: 1px solid #cbd5e1;\n  border-radius: 6px;\n  padding: 2px;\n  cursor: pointer;\n  background: #fff;\n}\n#bc-app .bc-preview-area {\n  margin-top: 0;\n}\n#bc-app .bc-tabs {\n  display: flex;\n  gap: 4px;\n  margin-bottom: 14px;\n}\n#bc-app .bc-tab {\n  padding: 6px 14px;\n  border: 1px solid #e2e8f0;\n  border-radius: 6px;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  background: #fff;\n  color: #64748b;\n  transition: all 0.15s;\n}\n#bc-app .bc-tab.active { background: #6366f1; color: #fff; border-color: #6366f1; }\n#bc-app .bc-canvas-wrap {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  padding: 24px;\n  background: #e2e8f0;\n  border-radius: 10px;\n  min-height: 180px;\n}\n#bc-app canvas {\n  border-radius: 8px;\n  box-shadow: 0 4px 24px rgba(0,0,0,0.18);\n  max-width: 100%;\n  height: auto;\n}\n#bc-app .bc-actions {\n  display: flex;\n  gap: 10px;\n  margin-top: 14px;\n  flex-wrap: wrap;\n}\n#bc-app .bc-btn {\n  flex: 1;\n  min-width: 120px;\n  padding: 10px 16px;\n  border: none;\n  border-radius: 7px;\n  font-size: 13px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: all 0.15s;\n  text-align: center;\n}\n#bc-app .bc-btn-primary {\n  background: #6366f1;\n  color: #fff;\n}\n#bc-app .bc-btn-primary:hover { background: #4f46e5; }\n#bc-app .bc-btn-secondary {\n  background: #fff;\n  color: #1e293b;\n  border: 1.5px solid #cbd5e1;\n}\n#bc-app .bc-btn-secondary:hover { border-color: #6366f1; color: #6366f1; }\n#bc-app .bc-toggle-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin-top: 12px;\n  font-size: 13px;\n  color: #475569;\n}\n#bc-app .bc-toggle {\n  width: 36px;\n  height: 20px;\n  border-radius: 10px;\n  background: #cbd5e1;\n  position: relative;\n  cursor: pointer;\n  border: none;\n  transition: background 0.2s;\n  flex-shrink: 0;\n}\n#bc-app .bc-toggle.on { background: #6366f1; }\n#bc-app .bc-toggle::after {\n  content: '';\n  position: absolute;\n  width: 14px;\n  height: 14px;\n  background: #fff;\n  border-radius: 50%;\n  top: 3px;\n  left: 3px;\n  transition: left 0.2s;\n}\n#bc-app .bc-toggle.on::after { left: 19px; }\n#bc-app .bc-note {\n  font-size: 12px;\n  color: #94a3b8;\n  margin-top: 8px;\n  line-height: 1.5;\n}\n\n@media print {\n  body * { visibility: hidden; }\n  #bc-print-target, #bc-print-target * { visibility: visible; }\n  #bc-print-target {\n    position: fixed;\n    top: 0; left: 0;\n    display: flex;\n    flex-direction: column;\n    gap: 16px;\n    align-items: center;\n    justify-content: center;\n    width: 100%;\n    height: 100%;\n    background: #fff;\n  }\n}\n\u003c/style\u003e\n\u003cdiv id=\"bc-print-target\" style=\"display:none;\"\u003e\u003c/div\u003e\n\u003cdiv class=\"bc-layout\"\u003e\n  \u003cdiv class=\"bc-panel\"\u003e\n    \u003ch2\u003eCard Details\u003c/h2\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;label\u0026gt;Full Name\u0026lt;/label\u0026gt;\n\u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bc-name\u0026quot; placeholder=\u0026quot;Jane Smith\u0026quot; value=\u0026quot;Jane Smith\u0026quot; /\u0026gt;\n\n\u0026lt;label\u0026gt;Job Title\u0026lt;/label\u0026gt;\n\u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bc-title\u0026quot; placeholder=\u0026quot;Product Designer\u0026quot; value=\u0026quot;Product Designer\u0026quot; /\u0026gt;\n\n\u0026lt;label\u0026gt;Company\u0026lt;/label\u0026gt;\n\u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bc-company\u0026quot; placeholder=\u0026quot;Acme Corp\u0026quot; value=\u0026quot;Acme Corp\u0026quot; /\u0026gt;\n\n\u0026lt;label\u0026gt;Email\u0026lt;/label\u0026gt;\n\u0026lt;input type=\u0026quot;email\u0026quot; id=\u0026quot;bc-email\u0026quot; placeholder=\u0026quot;jane@acmecorp.com\u0026quot; value=\u0026quot;jane@acmecorp.com\u0026quot; /\u0026gt;\n\n\u0026lt;label\u0026gt;Phone\u0026lt;/label\u0026gt;\n\u0026lt;input type=\u0026quot;tel\u0026quot; id=\u0026quot;bc-phone\u0026quot; placeholder=\u0026quot;+1 (555) 000-0000\u0026quot; value=\u0026quot;+1 (555) 000-0000\u0026quot; /\u0026gt;\n\n\u0026lt;label\u0026gt;Website\u0026lt;/label\u0026gt;\n\u0026lt;input type=\u0026quot;url\u0026quot; id=\u0026quot;bc-website\u0026quot; placeholder=\u0026quot;acmecorp.com\u0026quot; value=\u0026quot;acmecorp.com\u0026quot; /\u0026gt;\n\n\u0026lt;label\u0026gt;Address\u0026lt;/label\u0026gt;\n\u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bc-address\u0026quot; placeholder=\u0026quot;123 Main St, New York, NY\u0026quot; value=\u0026quot;123 Main St, New York, NY\u0026quot; /\u0026gt;\n\n\u0026lt;label style=\u0026quot;margin-top:16px;\u0026quot;\u0026gt;Layout Template\u0026lt;/label\u0026gt;\n\u0026lt;div class=\u0026quot;bc-templates\u0026quot;\u0026gt;\n  \u0026lt;button class=\u0026quot;bc-tpl-btn active\u0026quot; data-tpl=\u0026quot;classic\u0026quot; onclick=\u0026quot;(function(){window._bcSetTpl('classic');})()\u0026quot;\u0026gt;Classic\u0026lt;/button\u0026gt;\n  \u0026lt;button class=\u0026quot;bc-tpl-btn\u0026quot; data-tpl=\u0026quot;modern\u0026quot; onclick=\u0026quot;(function(){window._bcSetTpl('modern');})()\u0026quot;\u0026gt;Modern\u0026lt;/button\u0026gt;\n  \u0026lt;button class=\u0026quot;bc-tpl-btn\u0026quot; data-tpl=\u0026quot;minimal\u0026quot; onclick=\u0026quot;(function(){window._bcSetTpl('minimal');})()\u0026quot;\u0026gt;Minimal\u0026lt;/button\u0026gt;\n  \u0026lt;button class=\u0026quot;bc-tpl-btn\u0026quot; data-tpl=\u0026quot;bold\u0026quot; onclick=\u0026quot;(function(){window._bcSetTpl('bold');})()\u0026quot;\u0026gt;Bold\u0026lt;/button\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;label style=\u0026quot;margin-top:16px;\u0026quot;\u0026gt;Colors\u0026lt;/label\u0026gt;\n\u0026lt;div class=\u0026quot;bc-colors\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;bc-color-group\u0026quot;\u0026gt;\n    \u0026lt;label style=\u0026quot;margin-top:0;\u0026quot;\u0026gt;Background\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;bc-color-bg\u0026quot; value=\u0026quot;#1e293b\u0026quot; /\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;bc-color-group\u0026quot;\u0026gt;\n    \u0026lt;label style=\u0026quot;margin-top:0;\u0026quot;\u0026gt;Accent\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;bc-color-accent\u0026quot; value=\u0026quot;#6366f1\u0026quot; /\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;bc-color-group\u0026quot;\u0026gt;\n    \u0026lt;label style=\u0026quot;margin-top:0;\u0026quot;\u0026gt;Text\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;bc-color-text\u0026quot; value=\u0026quot;#ffffff\u0026quot; /\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;bc-toggle-row\u0026quot;\u0026gt;\n  \u0026lt;button class=\u0026quot;bc-toggle on\u0026quot; id=\u0026quot;bc-qr-toggle\u0026quot; onclick=\u0026quot;(function(){window._bcToggleQr();})()\u0026quot;\u0026gt;\u0026lt;/button\u0026gt;\n  \u0026lt;span\u0026gt;Show QR code on back\u0026lt;/span\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;p class=\u0026quot;bc-note\u0026quot;\u0026gt;Card size: 3.5 × 2 in (91 × 55 mm) — standard business card dimensions.\u0026lt;/p\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"bc-panel bc-preview-area\"\u003e\n    \u003ch2\u003ePreview\u003c/h2\u003e\n    \u003cdiv class=\"bc-tabs\"\u003e\n      \u003cbutton class=\"bc-tab active\" id=\"bc-tab-front\" onclick=\"(function(){window._bcShowSide('front');})()\"\u003eFront\u003c/button\u003e\n      \u003cbutton class=\"bc-tab\" id=\"bc-tab-back\" onclick=\"(function(){window._bcShowSide('back');})()\"\u003eBack\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"bc-canvas-wrap\"\u003e\n      \u003ccanvas id=\"bc-canvas\" width=\"700\" height=\"400\"\u003e\u003c/canvas\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"bc-actions\"\u003e\n      \u003cbutton class=\"bc-btn bc-btn-primary\" onclick=\"(function(){window._bcDownload();})()\"\u003eDownload PNG\u003c/button\u003e\n      \u003cbutton class=\"bc-btn bc-btn-secondary\" onclick=\"(function(){window._bcPrint();})()\"\u003ePrint\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var state = {\n    name: 'Jane Smith',\n    title: 'Product Designer',\n    company: 'Acme Corp',\n    email: 'jane@acmecorp.com',\n    phone: '+1 (555) 000-0000',\n    website: 'acmecorp.com',\n    address: '123 Main St, New York, NY',\n    tpl: 'classic',\n    bgColor: '#1e293b',\n    accentColor: '#6366f1',\n    textColor: '#ffffff',\n    showQr: true,\n    side: 'front'\n  };\n\n  var DPR = 2;\n  var CARD_W = 700;\n  var CARD_H = 400;\n\n  function bind(id, key) {\n    var el = document.getElementById(id);\n    if (!el) return;\n    el.addEventListener('input', function() {\n      state[key] = el.value;\n      render();\n    });\n  }\n\n  bind('bc-name', 'name');\n  bind('bc-title', 'title');\n  bind('bc-company', 'company');\n  bind('bc-email', 'email');\n  bind('bc-phone', 'phone');\n  bind('bc-website', 'website');\n  bind('bc-address', 'address');\n  bind('bc-color-bg', 'bgColor');\n  bind('bc-color-accent', 'accentColor');\n  bind('bc-color-text', 'textColor');\n\n  document.getElementById('bc-color-bg').addEventListener('input', function() {\n    state.bgColor = this.value; render();\n  });\n  document.getElementById('bc-color-accent').addEventListener('input', function() {\n    state.accentColor = this.value; render();\n  });\n  document.getElementById('bc-color-text').addEventListener('input', function() {\n    state.textColor = this.value; render();\n  });\n\n  window._bcSetTpl = function(tpl) {\n    state.tpl = tpl;\n    document.querySelectorAll('#bc-app .bc-tpl-btn').forEach(function(b) {\n      b.classList.toggle('active', b.dataset.tpl === tpl);\n    });\n    // Apply preset colors per template\n    var presets = {\n      classic: { bg: '#1e293b', accent: '#6366f1', text: '#ffffff' },\n      modern:  { bg: '#0f172a', accent: '#06b6d4', text: '#ffffff' },\n      minimal: { bg: '#ffffff', accent: '#1e293b', text: '#1e293b' },\n      bold:    { bg: '#7c3aed', accent: '#fbbf24', text: '#ffffff' }\n    };\n    if (presets[tpl]) {\n      state.bgColor = presets[tpl].bg;\n      state.accentColor = presets[tpl].accent;\n      state.textColor = presets[tpl].text;\n      document.getElementById('bc-color-bg').value = state.bgColor;\n      document.getElementById('bc-color-accent').value = state.accentColor;\n      document.getElementById('bc-color-text').value = state.textColor;\n    }\n    render();\n  };\n\n  window._bcShowSide = function(side) {\n    state.side = side;\n    document.getElementById('bc-tab-front').classList.toggle('active', side === 'front');\n    document.getElementById('bc-tab-back').classList.toggle('active', side === 'back');\n    render();\n  };\n\n  window._bcToggleQr = function() {\n    state.showQr = !state.showQr;\n    var btn = document.getElementById('bc-qr-toggle');\n    btn.classList.toggle('on', state.showQr);\n    render();\n  };\n\n  // --- Canvas rendering ---\n  function getCanvas(id) {\n    var el = document.getElementById(id);\n    return el;\n  }\n\n  function hexToRgb(hex) {\n    var r = parseInt(hex.slice(1,3),16);\n    var g = parseInt(hex.slice(3,5),16);\n    var b = parseInt(hex.slice(5,7),16);\n    return [r,g,b];\n  }\n\n  function lighten(hex, amt) {\n    var rgb = hexToRgb(hex);\n    return 'rgba(' + Math.min(255,rgb[0]+amt) + ',' + Math.min(255,rgb[1]+amt) + ',' + Math.min(255,rgb[2]+amt) + ',1)';\n  }\n\n  function drawFront(ctx, W, H, s) {\n    ctx.clearRect(0, 0, W, H);\n\n    // Background\n    ctx.fillStyle = s.bgColor;\n    ctx.beginPath();\n    ctx.roundRect(0, 0, W, H, 18);\n    ctx.fill();\n\n    if (s.tpl === 'classic') {\n      // Left accent bar\n      ctx.fillStyle = s.accentColor;\n      ctx.beginPath();\n      ctx.roundRect(0, 0, 10, H, [18, 0, 0, 18]);\n      ctx.fill();\n\n      // Name\n      ctx.fillStyle = s.textColor;\n      ctx.font = 'bold ' + Math.round(H * 0.115) + 'px -apple-system,BlinkMacSystemFont,sans-serif';\n      ctx.fillText(s.name, 36, H * 0.27);\n\n      // Title\n      ctx.fillStyle = s.accentColor;\n      ctx.font = Math.round(H * 0.072) + 'px -apple-system,BlinkMacSystemFont,sans-serif';\n      ctx.fillText(s.title, 36, H * 0.39);\n\n      // Divider\n      ctx.fillStyle = s.accentColor;\n      ctx.globalAlpha = 0.3;\n      ctx.fillRect(36, H * 0.43, W - 72, 1.5);\n      ctx.globalAlpha = 1;\n\n      // Company\n      ctx.fillStyle = s.textColor;\n      ctx.globalAlpha = 0.85;\n      ctx.font = 'bold ' + Math.round(H * 0.072) + 'px -apple-system,BlinkMacSystemFont,sans-serif';\n      ctx.fillText(s.company, 36, H * 0.55);\n      ctx.globalAlpha = 1;\n\n      // Contact info\n      var infoY = H * 0.66;\n      var lineH = H * 0.1;\n      ctx.font = Math.round(H * 0.062) + 'px -apple-system,BlinkMacSystemFont,sans-serif';\n      ctx.fillStyle = s.textColor;\n      ctx.globalAlpha = 0.75;\n      if (s.email) { ctx.fillText(s.email, 36, infoY); infoY += lineH; }\n      if (s.phone) { ctx.fillText(s.phone, 36, infoY); infoY += lineH; }\n      ctx.globalAlpha = 1;\n      if (s.website) {\n        ctx.fillStyle = s.accentColor;\n        ctx.fillText(s.website, 36, infoY);\n      }\n\n    } else if (s.tpl === 'modern') {\n      // Top gradient band\n      var grad = ctx.createLinearGradient(0, 0, W, 0);\n      grad.addColorStop(0, s.accentColor);\n      grad.addColorStop(1, s.bgColor);\n      ctx.fillStyle = grad;\n      ctx.fillRect(0, 0, W, H * 0.38);\n\n      // Re-draw card background below band\n      ctx.fillStyle = s.bgColor;\n      ctx.fillRect(0, H * 0.38, W, H * 0.62);\n\n      // Name over band\n      ctx.fillStyle = '#ffffff';\n      ctx.font = 'bold ' + Math.round(H * 0.11) + 'px -apple-system,BlinkMacSystemFont,sans-serif';\n      ctx.fillText(s.name, 30, H * 0.28);\n\n      // Title under band\n      ctx.fillStyle = s.accentColor;\n      ctx.font = Math.round(H * 0.07) + 'px -apple-system,BlinkMacSystemFont,sans-serif';\n      ctx.fillText(s.title + (s.company ? '  ·  ' + s.company : ''), 30, H * 0.5);\n\n      // Contact\n      ctx.fillStyle = s.textColor;\n      ctx.globalAlpha = 0.8;\n      ctx.font = Math.round(H * 0.062) + 'px -apple-system,BlinkMacSystemFont,sans-serif';\n      var cy = H * 0.64;\n      var cl = H * 0.1;\n      if (s.email) { ctx.fillText(s.email, 30, cy); cy += cl; }\n      if (s.phone) { ctx.fillText(s.phone, 30, cy); cy += cl; }\n      if (s.website) { ctx.fillStyle = s.accentColor; ctx.globalAlpha = 1; ctx.fillText(s.website, 30, cy); }\n      ctx.globalAlpha = 1;\n\n    } else if (s.tpl === 'minimal') {\n      // White card, thin accent line at top\n      ctx.fillStyle = s.accentColor;\n      ctx.fillRect(0, 0, W, 6);\n\n      // Company top right\n      ctx.fillStyle = s.accentColor;\n      ctx.font = 'bold ' + Math.round(H * 0.07) + 'px -apple-system,BlinkMacSystemFont,sans-serif';\n      ctx.textAlign = 'right';\n      ctx.fillText(s.company, W - 30, H * 0.18);\n      ctx.textAlign = 'left';\n\n      // Name large\n      ctx.fillStyle = s.textColor;\n      ctx.font = 'bold ' + Math.round(H * 0.12) + 'px -apple-system,BlinkMacSystemFont,sans-serif';\n      ctx.fillText(s.name, 30, H * 0.42);\n\n      // Title\n      ctx.fillStyle = s.accentColor;\n      ctx.globalAlpha = 0.75;\n      ctx.font = Math.round(H * 0.07) + 'px -apple-system,BlinkMacSystemFont,sans-serif';\n      ctx.fillText(s.title, 30, H * 0.56);\n      ctx.globalAlpha = 1;\n\n      // Bottom contact row\n      ctx.fillStyle = s.textColor;\n      ctx.globalAlpha = 0.6;\n      ctx.font = Math.round(H * 0.058) + 'px -apple-system,BlinkMacSystemFont,sans-serif';\n      var parts = [s.email, s.phone, s.website].filter(Boolean).join('   |   ');\n      ctx.fillText(parts, 30, H * 0.82);\n      ctx.globalAlpha = 1;\n\n      // Bottom accent line\n      ctx.fillStyle = s.accentColor;\n      ctx.globalAlpha = 0.15;\n      ctx.fillRect(0, H - 6, W, 6);\n      ctx.globalAlpha = 1;\n\n    } else if (s.tpl === 'bold') {\n      // Diagonal split\n      ctx.fillStyle = s.accentColor;\n      ctx.beginPath();\n      ctx.moveTo(0, 0);\n      ctx.lineTo(W * 0.55, 0);\n      ctx.lineTo(W * 0.38, H);\n      ctx.lineTo(0, H);\n      ctx.closePath();\n      ctx.fill();\n\n      // Name on left\n      ctx.fillStyle = '#ffffff';\n      ctx.font = 'bold ' + Math.round(H * 0.105) + 'px -apple-system,BlinkMacSystemFont,sans-serif';\n      ctx.fillText(s.name, 28, H * 0.28);\n\n      ctx.font = Math.round(H * 0.065) + 'px -apple-system,BlinkMacSystemFont,sans-serif';\n      ctx.globalAlpha = 0.85;\n      ctx.fillText(s.title, 28, H * 0.42);\n      ctx.globalAlpha = 1;\n\n      if (s.company) {\n        ctx.font = 'bold ' + Math.round(H * 0.065) + 'px -apple-system,BlinkMacSystemFont,sans-serif';\n        ctx.fillText(s.company, 28, H * 0.56);\n      }\n\n      // Contact on right\n      ctx.fillStyle = s.textColor;\n      ctx.font = Math.round(H * 0.062) + 'px -apple-system,BlinkMacSystemFont,sans-serif';\n      var ry = H * 0.3;\n      var rl = H * 0.1;\n      var rx = W * 0.46;\n      ctx.globalAlpha = 0.8;\n      if (s.email) { ctx.fillText(s.email, rx, ry); ry += rl; }\n      if (s.phone) { ctx.fillText(s.phone, rx, ry); ry += rl; }\n      if (s.website) { ctx.fillStyle = s.accentColor; ctx.globalAlpha = 1; ctx.fillText(s.website, rx, ry); ry += rl; }\n      ctx.globalAlpha = 0.7;\n      ctx.fillStyle = s.textColor;\n      if (s.address) { ctx.fillText(s.address, rx, ry); }\n      ctx.globalAlpha = 1;\n    }\n  }\n\n  function drawQr(ctx, x, y, size, data) {\n    // Simple visual QR-like grid representation\n    var cells = 17;\n    var cell = size / cells;\n    ctx.fillStyle = '#ffffff';\n    ctx.fillRect(x - 2, y - 2, size + 4, size + 4);\n    ctx.fillStyle = '#000000';\n\n    // Deterministic pattern from data string\n    var seed = 0;\n    for (var i = 0; i \u003c data.length; i++) seed = (seed * 31 + data.charCodeAt(i)) \u0026 0xffff;\n    function rand() { seed = (seed * 1664525 + 1013904223) \u0026 0xffffffff; return (seed \u003e\u003e\u003e 0) / 4294967296; }\n\n    for (var r = 0; r \u003c cells; r++) {\n      for (var c = 0; c \u003c cells; c++) {\n        var isFinder = (r \u003c 3 \u0026\u0026 c \u003c 3) || (r \u003c 3 \u0026\u0026 c \u003e= cells-3) || (r \u003e= cells-3 \u0026\u0026 c \u003c 3);\n        var filled = isFinder ? true : rand() \u003e 0.5;\n        if (filled) ctx.fillRect(x + c * cell, y + r * cell, cell - 0.5, cell - 0.5);\n      }\n    }\n\n    // Finder pattern squares (overwrite)\n    ctx.fillStyle = '#000';\n    [[0,0],[0,cells-3],[cells-3,0]].forEach(function(pos) {\n      var pr = pos[0], pc = pos[1];\n      ctx.fillStyle = '#000';\n      ctx.fillRect(x + pc*cell, y + pr*cell, 3*cell, 3*cell);\n      ctx.fillStyle = '#fff';\n      ctx.fillRect(x + pc*cell+cell*0.2, y + pr*cell+cell*0.2, cell*2.6, cell*2.6);\n      ctx.fillStyle = '#000';\n      ctx.fillRect(x + pc*cell+cell*0.6, y + pr*cell+cell*0.6, cell*1.8, cell*1.8);\n    });\n  }\n\n  function drawBack(ctx, W, H, s) {\n    ctx.clearRect(0, 0, W, H);\n\n    ctx.fillStyle = s.bgColor;\n    ctx.beginPath();\n    ctx.roundRect(0, 0, W, H, 18);\n    ctx.fill();\n\n    // Accent stripe\n    ctx.fillStyle = s.accentColor;\n    ctx.globalAlpha = 0.12;\n    ctx.fillRect(0, H * 0.35, W, H * 0.3);\n    ctx.globalAlpha = 1;\n\n    // Company name centered\n    ctx.fillStyle = s.textColor;\n    ctx.textAlign = 'center';\n    ctx.font = 'bold ' + Math.round(H * 0.13) + 'px -apple-system,BlinkMacSystemFont,sans-serif';\n    ctx.fillText(s.company || s.name, W / 2, H * 0.38);\n\n    ctx.fillStyle = s.accentColor;\n    ctx.font = Math.round(H * 0.07) + 'px -apple-system,BlinkMacSystemFont,sans-serif';\n    if (s.website) ctx.fillText(s.website, W / 2, H * 0.54);\n    ctx.textAlign = 'left';\n\n    // QR code\n    if (s.showQr) {\n      var qrSize = Math.round(H * 0.42);\n      var qrX = W - qrSize - 28;\n      var qrY = Math.round((H - qrSize) / 2);\n      drawQr(ctx, qrX, qrY, qrSize, s.website || s.email || s.name);\n\n      ctx.fillStyle = s.textColor;\n      ctx.globalAlpha = 0.5;\n      ctx.font = Math.round(H * 0.052) + 'px -apple-system,BlinkMacSystemFont,sans-serif';\n      ctx.textAlign = 'right';\n      ctx.fillText('Scan to connect', W - 28, H - 20);\n      ctx.textAlign = 'left';\n      ctx.globalAlpha = 1;\n    }\n\n    // Decorative dots\n    ctx.fillStyle = s.accentColor;\n    ctx.globalAlpha = 0.15;\n    for (var i = 0; i \u003c 5; i++) {\n      ctx.beginPath();\n      ctx.arc(28 + i * 18, H - 24, 5, 0, Math.PI * 2);\n      ctx.fill();\n    }\n    ctx.globalAlpha = 1;\n  }\n\n  function render() {\n    var canvas = document.getElementById('bc-canvas');\n    if (!canvas) return;\n    var ctx = canvas.getContext('2d');\n\n    canvas.width = CARD_W * DPR;\n    canvas.height = CARD_H * DPR;\n    canvas.style.width = CARD_W + 'px';\n    canvas.style.height = CARD_H + 'px';\n    ctx.scale(DPR, DPR);\n\n    if (state.side === 'front') {\n      drawFront(ctx, CARD_W, CARD_H, state);\n    } else {\n      drawBack(ctx, CARD_W, CARD_H, state);\n    }\n  }\n\n  window._bcDownload = function() {\n    var canvas = document.getElementById('bc-canvas');\n    var link = document.createElement('a');\n    link.download = 'business-card-' + state.side + '.png';\n    link.href = canvas.toDataURL('image/png');\n    link.click();\n  };\n\n  window._bcPrint = function() {\n    var canvas = document.getElementById('bc-canvas');\n    var pt = document.getElementById('bc-print-target');\n    // Draw both sides\n    var tempF = document.createElement('canvas');\n    var tempB = document.createElement('canvas');\n    tempF.width = CARD_W * DPR; tempF.height = CARD_H * DPR;\n    tempB.width = CARD_W * DPR; tempB.height = CARD_H * DPR;\n    tempF.style.width = CARD_W + 'px'; tempF.style.height = CARD_H + 'px';\n    tempB.style.width = CARD_W + 'px'; tempB.style.height = CARD_H + 'px';\n\n    var ctxF = tempF.getContext('2d'); ctxF.scale(DPR, DPR);\n    var ctxB = tempB.getContext('2d'); ctxB.scale(DPR, DPR);\n    drawFront(ctxF, CARD_W, CARD_H, state);\n    drawBack(ctxB, CARD_W, CARD_H, state);\n\n    pt.style.display = 'flex';\n    pt.innerHTML = '';\n    pt.appendChild(tempF);\n    pt.appendChild(tempB);\n    window.print();\n    pt.style.display = 'none';\n  };\n\n  // Init\n  render();\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cstrong\u003eNeed more design tools?\u003c/strong\u003e\u003c/p\u003e","title":"Business Card Generator"},{"content":" Enter a value in any unit below and instantly see it converted into all other data size units — both binary (base-2) and decimal (base-10). The bandwidth calculator at the bottom estimates download time from file size and connection speed. Data Size Converter Value Bit (b) Byte (B) Kilobyte — KB (10³) Megabyte — MB (10⁶) Gigabyte — GB (10⁹) Terabyte — TB (10¹²) Petabyte — PB (10¹⁵) Kibibyte — KiB (2¹⁰) Mebibyte — MiB (2²⁰) Gibibyte — GiB (2³⁰) Tebibyte — TiB (2⁴⁰) Pebibyte — PiB (2⁵⁰) Convert Clear Please enter a valid non-negative number.\nUnit Value Exact Value (bytes) Bandwidth / Download Time Calculator File Size File Size Unit Byte (B) Kilobyte (KB) Megabyte (MB) Gigabyte (GB) Terabyte (TB) Kibibyte (KiB) Mebibyte (MiB) Gibibyte (GiB) Connection Speed Speed Unit bps (bits/s) Kbps (10³ bits/s) Mbps (10⁶ bits/s) Gbps (10⁹ bits/s) KB/s (10³ bytes/s) MB/s (10⁶ bytes/s) GB/s (10⁹ bytes/s) Calculate Download Time Clear Please enter valid positive numbers for both fields.\nNote: Actual download speed depends on network conditions, server capacity, and protocol overhead.\nUnit Reference Unit Symbol System Exact Size Bitb—1/8 byte ByteB—8 bits Kilobyte decimalKBSI (base-10)1,000 bytes Kibibyte binaryKiBIEC (base-2)1,024 bytes Megabyte decimalMBSI (base-10)1,000,000 bytes Mebibyte binaryMiBIEC (base-2)1,048,576 bytes Gigabyte decimalGBSI (base-10)1,000,000,000 bytes Gibibyte binaryGiBIEC (base-2)1,073,741,824 bytes Terabyte decimalTBSI (base-10)10¹² bytes Tebibyte binaryTiBIEC (base-2)2⁴⁰ bytes Petabyte decimalPBSI (base-10)10¹⁵ bytes Pebibyte binaryPiBIEC (base-2)2⁵⁰ bytes Why Binary vs. Decimal? Hard drive manufacturers use decimal (1 GB = 1,000,000,000 bytes) because the numbers look larger. Operating systems traditionally use binary (1 GiB = 1,073,741,824 bytes), which is why a \"500 GB\" drive shows as ~465 GiB in Windows or macOS. The IEC standardized the KiB/MiB/GiB suffixes in 1998 to eliminate ambiguity.\nRelated Tools: Unit Converter Binary Converter IP Address Info ","permalink":"https://productivity-works.com/tools/byte-converter/","summary":"\u003cdiv id=\"byte-app\"\u003e\n\u003cstyle\u003e\n  #byte-app {\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n    max-width: 860px;\n    margin: 0 auto;\n    color: #1a1a2e;\n  }\n  #byte-app h2.ba-section-title {\n    font-size: 1.1rem;\n    font-weight: 700;\n    color: #0f3460;\n    border-left: 4px solid #e94560;\n    padding-left: 0.6rem;\n    margin: 1.8rem 0 1rem;\n  }\n  #byte-app .ba-intro {\n    background: #f0f4ff;\n    border-radius: 8px;\n    padding: 1rem 1.2rem;\n    font-size: 0.92rem;\n    color: #444;\n    margin-bottom: 1.5rem;\n    line-height: 1.6;\n  }\n  #byte-app .ba-input-row {\n    display: flex;\n    gap: 0.5rem;\n    align-items: center;\n    margin-bottom: 1rem;\n    flex-wrap: wrap;\n  }\n  #byte-app .ba-input-row label {\n    font-size: 0.88rem;\n    font-weight: 600;\n    color: #333;\n    min-width: 40px;\n  }\n  #byte-app .ba-input-row input[type=\"number\"] {\n    flex: 1;\n    min-width: 160px;\n    padding: 0.55rem 0.75rem;\n    border: 1.5px solid #c8d0e0;\n    border-radius: 6px;\n    font-size: 1rem;\n    outline: none;\n    transition: border-color 0.2s;\n    background: #fff;\n    color: #1a1a2e;\n  }\n  #byte-app .ba-input-row input[type=\"number\"]:focus {\n    border-color: #4a90e2;\n  }\n  #byte-app .ba-input-row select {\n    padding: 0.55rem 0.65rem;\n    border: 1.5px solid #c8d0e0;\n    border-radius: 6px;\n    font-size: 0.92rem;\n    background: #fff;\n    color: #1a1a2e;\n    cursor: pointer;\n    outline: none;\n  }\n  #byte-app .ba-btn {\n    padding: 0.55rem 1.2rem;\n    background: #e94560;\n    color: #fff;\n    border: none;\n    border-radius: 6px;\n    font-size: 0.92rem;\n    font-weight: 600;\n    cursor: pointer;\n    transition: background 0.2s;\n    white-space: nowrap;\n  }\n  #byte-app .ba-btn:hover {\n    background: #c73652;\n  }\n  #byte-app .ba-btn-secondary {\n    background: #6c757d;\n  }\n  #byte-app .ba-btn-secondary:hover {\n    background: #545b62;\n  }\n  #byte-app .ba-table-wrap {\n    overflow-x: auto;\n    border-radius: 8px;\n    box-shadow: 0 1px 6px rgba(0,0,0,0.08);\n    margin-bottom: 1.5rem;\n  }\n  #byte-app table.ba-table {\n    width: 100%;\n    border-collapse: collapse;\n    font-size: 0.92rem;\n  }\n  #byte-app table.ba-table thead tr {\n    background: #0f3460;\n    color: #fff;\n  }\n  #byte-app table.ba-table thead th {\n    padding: 0.65rem 0.9rem;\n    text-align: left;\n    font-weight: 600;\n    white-space: nowrap;\n  }\n  #byte-app table.ba-table tbody tr:nth-child(even) {\n    background: #f5f7fb;\n  }\n  #byte-app table.ba-table tbody tr:hover {\n    background: #eef2ff;\n  }\n  #byte-app table.ba-table tbody td {\n    padding: 0.6rem 0.9rem;\n    border-bottom: 1px solid #e5e9f2;\n    white-space: nowrap;\n  }\n  #byte-app table.ba-table tbody td:nth-child(2),\n  #byte-app table.ba-table tbody td:nth-child(3) {\n    font-family: \"Courier New\", Courier, monospace;\n    color: #0f3460;\n    font-weight: 600;\n  }\n  #byte-app .ba-badge {\n    display: inline-block;\n    font-size: 0.75rem;\n    border-radius: 4px;\n    padding: 0.1rem 0.4rem;\n    font-weight: 600;\n    margin-left: 0.3rem;\n    vertical-align: middle;\n  }\n  #byte-app .ba-badge-bin {\n    background: #d4edda;\n    color: #155724;\n  }\n  #byte-app .ba-badge-dec {\n    background: #cce5ff;\n    color: #004085;\n  }\n  #byte-app .ba-bw-grid {\n    display: grid;\n    grid-template-columns: 1fr 1fr;\n    gap: 1rem;\n    margin-bottom: 1rem;\n  }\n  @media (max-width: 600px) {\n    #byte-app .ba-bw-grid {\n      grid-template-columns: 1fr;\n    }\n    #byte-app .ba-input-row {\n      flex-direction: column;\n      align-items: flex-start;\n    }\n    #byte-app .ba-input-row input[type=\"number\"] {\n      width: 100%;\n    }\n  }\n  #byte-app .ba-field-group {\n    display: flex;\n    flex-direction: column;\n    gap: 0.35rem;\n  }\n  #byte-app .ba-field-group label {\n    font-size: 0.85rem;\n    font-weight: 600;\n    color: #444;\n  }\n  #byte-app .ba-field-group input,\n  #byte-app .ba-field-group select {\n    padding: 0.55rem 0.75rem;\n    border: 1.5px solid #c8d0e0;\n    border-radius: 6px;\n    font-size: 0.95rem;\n    outline: none;\n    transition: border-color 0.2s;\n    background: #fff;\n    color: #1a1a2e;\n  }\n  #byte-app .ba-field-group input:focus,\n  #byte-app .ba-field-group select:focus {\n    border-color: #4a90e2;\n  }\n  #byte-app .ba-result-box {\n    background: #eef6ff;\n    border: 1.5px solid #b3d4f5;\n    border-radius: 8px;\n    padding: 1rem 1.2rem;\n    font-size: 1rem;\n    color: #0f3460;\n    font-weight: 600;\n    margin-top: 0.8rem;\n    min-height: 2.5rem;\n    line-height: 1.6;\n  }\n  #byte-app .ba-result-box span.ba-highlight {\n    font-size: 1.3rem;\n    color: #e94560;\n  }\n  #byte-app .ba-note {\n    font-size: 0.82rem;\n    color: #666;\n    margin-top: 0.4rem;\n  }\n  #byte-app .ba-crosslinks {\n    margin-top: 2rem;\n    padding-top: 1.2rem;\n    border-top: 1px solid #e5e9f2;\n    font-size: 0.9rem;\n    color: #555;\n  }\n  #byte-app .ba-crosslinks a {\n    color: #4a90e2;\n    text-decoration: none;\n    margin-right: 1rem;\n    font-weight: 500;\n  }\n  #byte-app .ba-crosslinks a:hover {\n    text-decoration: underline;\n  }\n  #byte-app .ba-error {\n    color: #c0392b;\n    font-size: 0.88rem;\n    margin-top: 0.3rem;\n    display: none;\n  }\n\u003c/style\u003e\n\u003cdiv class=\"ba-intro\"\u003e\n  Enter a value in any unit below and instantly see it converted into all other data size units — both binary (base-2) and decimal (base-10). The bandwidth calculator at the bottom estimates download time from file size and connection speed.\n\u003c/div\u003e\n\u003c!-- ===== CONVERTER ===== --\u003e\n\u003ch2 class=\"ba-section-title\"\u003eData Size Converter\u003c/h2\u003e\n\u003cdiv class=\"ba-input-row\"\u003e\n  \u003clabel for=\"ba-val\"\u003eValue\u003c/label\u003e\n  \u003cinput type=\"number\" id=\"ba-val\" placeholder=\"e.g. 1024\" min=\"0\" step=\"any\" /\u003e\n  \u003cselect id=\"ba-unit\"\u003e\n    \u003coption value=\"bit\"\u003eBit (b)\u003c/option\u003e\n    \u003coption value=\"byte\" selected\u003eByte (B)\u003c/option\u003e\n    \u003coption value=\"kb_dec\"\u003eKilobyte — KB (10³)\u003c/option\u003e\n    \u003coption value=\"mb_dec\"\u003eMegabyte — MB (10⁶)\u003c/option\u003e\n    \u003coption value=\"gb_dec\"\u003eGigabyte — GB (10⁹)\u003c/option\u003e\n    \u003coption value=\"tb_dec\"\u003eTerabyte — TB (10¹²)\u003c/option\u003e\n    \u003coption value=\"pb_dec\"\u003ePetabyte — PB (10¹⁵)\u003c/option\u003e\n    \u003coption value=\"kib\"\u003eKibibyte — KiB (2¹⁰)\u003c/option\u003e\n    \u003coption value=\"mib\"\u003eMebibyte — MiB (2²⁰)\u003c/option\u003e\n    \u003coption value=\"gib\"\u003eGibibyte — GiB (2³⁰)\u003c/option\u003e\n    \u003coption value=\"tib\"\u003eTebibyte — TiB (2⁴⁰)\u003c/option\u003e\n    \u003coption value=\"pib\"\u003ePebibyte — PiB (2⁵⁰)\u003c/option\u003e\n  \u003c/select\u003e\n  \u003cbutton class=\"ba-btn\" onclick=\"baConvert()\"\u003eConvert\u003c/button\u003e\n  \u003cbutton class=\"ba-btn ba-btn-secondary\" onclick=\"baClear()\"\u003eClear\u003c/button\u003e\n\u003c/div\u003e\n\u003cp class=\"ba-error\" id=\"ba-error\"\u003ePlease enter a valid non-negative number.\u003c/p\u003e","title":"Byte Converter — Data Size Calculator"},{"content":" View: Monthly Year at a Glance Month: JanuaryFebruary MarchApril MayJune JulyAugust SeptemberOctober NovemberDecember Year: Week starts: Sunday Monday \u0026#128438; Print \u0026#8592; Prev Today Next \u0026#8594; Add Event Close Add How to use:\nSelect a month and year, or switch to Year at a Glance for all 12 months. Choose whether your week starts on Sunday or Monday. Click any date to add events or notes — they save automatically in your browser. Hit Print to generate a clean, print-optimized calendar. Plan your day → Daily Planner Event countdown → Event Countdown ","permalink":"https://productivity-works.com/tools/calendar-generator/","summary":"\u003cdiv id=\"cg-app\"\u003e\n\u003cstyle\u003e\n#cg-app *,#cg-app *::before,#cg-app *::after{box-sizing:border-box;margin:0;padding:0}\n#cg-app{\n  font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;\n  font-size:15px;line-height:1.6;\n  color:#1e293b;\n  background:#f8fafc;\n  border-radius:12px;\n  padding:20px;\n  max-width:980px;\n  margin:0 auto;\n}\n\u003cp\u003e/* ── Toolbar ── */\n#cg-app .cg-toolbar{\ndisplay:flex;flex-wrap:wrap;gap:10px;align-items:center;\nbackground:#fff;border:1px solid #e2e8f0;border-radius:10px;\npadding:14px 16px;margin-bottom:18px;\n}\n#cg-app .cg-toolbar label{font-size:13px;font-weight:600;color:#475569;margin-right:4px;}\n#cg-app .cg-toolbar select,\n#cg-app .cg-toolbar input[type=number]{\nborder:1px solid #cbd5e1;border-radius:6px;padding:5px 8px;\nfont-size:14px;color:#1e293b;background:#f8fafc;\noutline:none;cursor:pointer;\n}\n#cg-app .cg-toolbar select:focus,\n#cg-app .cg-toolbar input[type=number]:focus{border-color:#3b82f6;}\n#cg-app .cg-toolbar input[type=number]{width:72px;}\n#cg-app .cg-btn{\npadding:7px 14px;border-radius:7px;border:none;cursor:pointer;\nfont-size:13px;font-weight:600;transition:background 0.15s,transform 0.1s;\n}\n#cg-app .cg-btn:active{transform:scale(0.97);}\n#cg-app .cg-btn-primary{background:#3b82f6;color:#fff;}\n#cg-app .cg-btn-primary:hover{background:#2563eb;}\n#cg-app .cg-btn-secondary{background:#e2e8f0;color:#334155;}\n#cg-app .cg-btn-secondary:hover{background:#cbd5e1;}\n#cg-app .cg-btn-print{background:#10b981;color:#fff;}\n#cg-app .cg-btn-print:hover{background:#059669;}\n#cg-app .cg-sep{width:1px;height:28px;background:#e2e8f0;margin:0 4px;}\n#cg-app .cg-toolbar-group{display:flex;align-items:center;gap:6px;}\u003c/p\u003e","title":"Calendar Generator"},{"content":"Find out how many calories your body needs each day — then plan smarter with macro breakdowns and weight goal targets tailored to your lifestyle.\nYour Details Gender Male Female Age Height cm ft/in cm ft in Weight kg lbs kg Activity Level Sedentary (little or no exercise) Lightly Active (1–3 days/week) Moderately Active (3–5 days/week) Very Active (6–7 days/week) Extra Active (hard exercise + physical job) Formula Mifflin-St Jeor (recommended) Harris-Benedict Mifflin-St Jeor is considered the most accurate for most adults.\nPlease fill in all fields with valid values. Calculate My Calories\nBMR — Basal Metabolic Rate — kcal/day at complete rest TDEE — Daily Caloric Need — kcal/day to maintain weight Calorie Targets by Goal Goal Calories/day Weekly Change Macro Breakdown Show macros for: Macro Split Check your BMI \u0026rarr; BMI Calculator Plan your budget \u0026rarr; 50/30/20 Budget Calculator Track your investments \u0026rarr; ROI Calculator Related Tools Bmi Calculator Body Fat Calculator Water Intake Calculator ","permalink":"https://productivity-works.com/tools/calorie-calculator/","summary":"\u003cp\u003eFind out how many calories your body needs each day — then plan smarter with macro breakdowns and weight goal targets tailored to your lifestyle.\u003c/p\u003e\n\u003cdiv id=\"cc-app\"\u003e\n\u003cstyle\u003e\n#cc-app {\n  font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n  max-width: 760px;\n  margin: 0 auto;\n  color: #1e293b;\n  font-size: 15px;\n  line-height: 1.6;\n}\n#cc-app * {\n  box-sizing: border-box;\n}\n#cc-app h2 {\n  font-size: 1.15rem;\n  font-weight: 700;\n  color: #0f172a;\n  margin: 0 0 1rem;\n  padding-bottom: 0.4rem;\n  border-bottom: 2px solid #e2e8f0;\n}\n#cc-app .cc-card {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 1.25rem 1.5rem;\n  margin-bottom: 1.25rem;\n}\n#cc-app .cc-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1rem;\n}\n@media (max-width: 540px) {\n  #cc-app .cc-grid {\n    grid-template-columns: 1fr;\n  }\n}\n#cc-app .cc-field {\n  display: flex;\n  flex-direction: column;\n  gap: 0.3rem;\n}\n#cc-app .cc-field label {\n  font-size: 0.8rem;\n  font-weight: 600;\n  color: #475569;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#cc-app input[type=\"number\"],\n#cc-app select {\n  width: 100%;\n  padding: 0.55rem 0.75rem;\n  border: 1px solid #cbd5e1;\n  border-radius: 8px;\n  background: #fff;\n  color: #1e293b;\n  font-size: 0.95rem;\n  font-family: inherit;\n  transition: border-color 0.15s;\n  -moz-appearance: textfield;\n}\n#cc-app input[type=\"number\"]::-webkit-inner-spin-button,\n#cc-app input[type=\"number\"]::-webkit-outer-spin-button {\n  opacity: 1;\n}\n#cc-app input[type=\"number\"]:focus,\n#cc-app select:focus {\n  outline: none;\n  border-color: #6366f1;\n  box-shadow: 0 0 0 3px rgba(99,102,241,0.12);\n}\n#cc-app .cc-radio-group {\n  display: flex;\n  gap: 0.75rem;\n}\n#cc-app .cc-radio-btn {\n  flex: 1;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  gap: 0.4rem;\n  padding: 0.5rem 0.75rem;\n  border: 1px solid #cbd5e1;\n  border-radius: 8px;\n  background: #fff;\n  cursor: pointer;\n  font-size: 0.9rem;\n  font-weight: 500;\n  color: #475569;\n  transition: all 0.15s;\n  user-select: none;\n}\n#cc-app .cc-radio-btn input[type=\"radio\"] {\n  display: none;\n}\n#cc-app .cc-radio-btn.active {\n  background: #6366f1;\n  border-color: #6366f1;\n  color: #fff;\n}\n#cc-app .cc-toggle-row {\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n  margin-bottom: 0.5rem;\n}\n#cc-app .cc-toggle-label {\n  font-size: 0.78rem;\n  font-weight: 600;\n  color: #475569;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#cc-app .cc-toggle {\n  display: flex;\n  border: 1px solid #cbd5e1;\n  border-radius: 6px;\n  overflow: hidden;\n  background: #fff;\n}\n#cc-app .cc-toggle button {\n  padding: 0.25rem 0.65rem;\n  border: none;\n  background: transparent;\n  font-size: 0.78rem;\n  font-weight: 600;\n  color: #64748b;\n  cursor: pointer;\n  transition: all 0.15s;\n  font-family: inherit;\n}\n#cc-app .cc-toggle button.active {\n  background: #6366f1;\n  color: #fff;\n}\n#cc-app .cc-height-inputs {\n  display: flex;\n  gap: 0.5rem;\n}\n#cc-app .cc-height-inputs input {\n  flex: 1;\n}\n#cc-app .cc-height-inputs .cc-unit-hint {\n  display: flex;\n  align-items: center;\n  font-size: 0.8rem;\n  color: #94a3b8;\n  white-space: nowrap;\n  padding: 0 0.2rem;\n}\n#cc-app .cc-formula-row {\n  display: flex;\n  gap: 0.75rem;\n  flex-wrap: wrap;\n}\n#cc-app .cc-formula-btn {\n  padding: 0.4rem 0.9rem;\n  border: 1px solid #cbd5e1;\n  border-radius: 20px;\n  background: #fff;\n  font-size: 0.8rem;\n  font-weight: 600;\n  color: #475569;\n  cursor: pointer;\n  transition: all 0.15s;\n  font-family: inherit;\n}\n#cc-app .cc-formula-btn.active {\n  background: #0f172a;\n  border-color: #0f172a;\n  color: #fff;\n}\n#cc-app .cc-btn-calc {\n  width: 100%;\n  padding: 0.75rem 1.5rem;\n  background: linear-gradient(135deg, #6366f1, #4f46e5);\n  color: #fff;\n  border: none;\n  border-radius: 10px;\n  font-size: 1rem;\n  font-weight: 700;\n  cursor: pointer;\n  font-family: inherit;\n  letter-spacing: 0.02em;\n  transition: opacity 0.15s, transform 0.1s;\n  box-shadow: 0 2px 8px rgba(99,102,241,0.3);\n}\n#cc-app .cc-btn-calc:hover {\n  opacity: 0.93;\n  transform: translateY(-1px);\n}\n#cc-app .cc-btn-calc:active {\n  transform: translateY(0);\n}\n#cc-app .cc-results {\n  display: none;\n}\n#cc-app .cc-results.visible {\n  display: block;\n}\n#cc-app .cc-summary-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1rem;\n  margin-bottom: 1.25rem;\n}\n@media (max-width: 480px) {\n  #cc-app .cc-summary-grid {\n    grid-template-columns: 1fr;\n  }\n}\n#cc-app .cc-stat-box {\n  border-radius: 10px;\n  padding: 1rem 1.2rem;\n  display: flex;\n  flex-direction: column;\n  gap: 0.2rem;\n}\n#cc-app .cc-stat-box .cc-stat-label {\n  font-size: 0.75rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  opacity: 0.75;\n}\n#cc-app .cc-stat-box .cc-stat-value {\n  font-size: 1.8rem;\n  font-weight: 800;\n  line-height: 1.1;\n}\n#cc-app .cc-stat-box .cc-stat-sub {\n  font-size: 0.78rem;\n  opacity: 0.7;\n}\n#cc-app .cc-stat-bmr {\n  background: #ede9fe;\n  color: #4c1d95;\n}\n#cc-app .cc-stat-tdee {\n  background: #dbeafe;\n  color: #1e3a8a;\n}\n#cc-app .cc-goals-section h2 {\n  margin-top: 0.25rem;\n}\n#cc-app .cc-goal-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.88rem;\n}\n#cc-app .cc-goal-table th {\n  text-align: left;\n  padding: 0.5rem 0.75rem;\n  font-size: 0.75rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  color: #64748b;\n  background: #f1f5f9;\n  border-bottom: 1px solid #e2e8f0;\n}\n#cc-app .cc-goal-table th:not(:first-child) {\n  text-align: right;\n}\n#cc-app .cc-goal-table td {\n  padding: 0.6rem 0.75rem;\n  border-bottom: 1px solid #f1f5f9;\n  color: #334155;\n  vertical-align: middle;\n}\n#cc-app .cc-goal-table td:not(:first-child) {\n  text-align: right;\n  font-variant-numeric: tabular-nums;\n}\n#cc-app .cc-goal-table tr:last-child td {\n  border-bottom: none;\n}\n#cc-app .cc-badge {\n  display: inline-block;\n  padding: 0.15rem 0.55rem;\n  border-radius: 20px;\n  font-size: 0.75rem;\n  font-weight: 700;\n}\n#cc-app .cc-badge-loss { background: #fef2f2; color: #991b1b; }\n#cc-app .cc-badge-maintain { background: #f0fdf4; color: #166534; }\n#cc-app .cc-badge-gain { background: #eff6ff; color: #1d4ed8; }\n#cc-app .cc-macro-section {\n  margin-top: 1.25rem;\n}\n#cc-app .cc-macro-layout {\n  display: flex;\n  gap: 1.5rem;\n  align-items: center;\n  flex-wrap: wrap;\n}\n#cc-app .cc-macro-chart-wrap {\n  flex: 0 0 140px;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 0.4rem;\n}\n#cc-app .cc-macro-chart-wrap canvas {\n  display: block;\n}\n#cc-app .cc-macro-chart-label {\n  font-size: 0.75rem;\n  color: #64748b;\n  text-align: center;\n}\n#cc-app .cc-macro-details {\n  flex: 1;\n  min-width: 200px;\n  display: flex;\n  flex-direction: column;\n  gap: 0.6rem;\n}\n#cc-app .cc-macro-row {\n  display: flex;\n  align-items: center;\n  gap: 0.65rem;\n}\n#cc-app .cc-macro-dot {\n  width: 12px;\n  height: 12px;\n  border-radius: 50%;\n  flex-shrink: 0;\n}\n#cc-app .cc-macro-name {\n  flex: 1;\n  font-size: 0.88rem;\n  font-weight: 600;\n  color: #334155;\n}\n#cc-app .cc-macro-val {\n  font-size: 0.88rem;\n  font-weight: 700;\n  color: #1e293b;\n  font-variant-numeric: tabular-nums;\n}\n#cc-app .cc-macro-pct {\n  font-size: 0.78rem;\n  color: #94a3b8;\n  width: 2.5rem;\n  text-align: right;\n}\n#cc-app .cc-macro-goal-select {\n  margin-bottom: 0.9rem;\n  display: flex;\n  align-items: center;\n  gap: 0.65rem;\n  flex-wrap: wrap;\n}\n#cc-app .cc-macro-goal-select label {\n  font-size: 0.8rem;\n  font-weight: 600;\n  color: #475569;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#cc-app .cc-macro-goal-select select {\n  padding: 0.35rem 0.65rem;\n  font-size: 0.88rem;\n  border-radius: 7px;\n  width: auto;\n}\n#cc-app .cc-error {\n  background: #fef2f2;\n  border: 1px solid #fca5a5;\n  border-radius: 8px;\n  padding: 0.75rem 1rem;\n  color: #991b1b;\n  font-size: 0.88rem;\n  margin-bottom: 1rem;\n  display: none;\n}\n#cc-app .cc-error.visible {\n  display: block;\n}\n#cc-app .cc-crosslinks {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 1rem 1.25rem;\n  margin-top: 1.5rem;\n  font-size: 0.88rem;\n  color: #475569;\n  display: flex;\n  flex-direction: column;\n  gap: 0.4rem;\n}\n#cc-app .cc-crosslinks a {\n  color: #6366f1;\n  text-decoration: none;\n  font-weight: 600;\n}\n#cc-app .cc-crosslinks a:hover {\n  text-decoration: underline;\n}\n#cc-app .cc-divider {\n  height: 1px;\n  background: #e2e8f0;\n  margin: 1.25rem 0;\n}\n#cc-app .cc-formula-note {\n  font-size: 0.78rem;\n  color: #94a3b8;\n  margin-top: 0.5rem;\n}\n\u003c/style\u003e\n\u003c!-- INPUTS --\u003e\n\u003cdiv class=\"cc-card\"\u003e\n  \u003ch2\u003eYour Details\u003c/h2\u003e\n  \u003cdiv class=\"cc-grid\" style=\"margin-bottom:1rem;\"\u003e\n    \u003cdiv class=\"cc-field\"\u003e\n      \u003clabel\u003eGender\u003c/label\u003e\n      \u003cdiv class=\"cc-radio-group\"\u003e\n        \u003clabel class=\"cc-radio-btn active\" id=\"cc-lbl-male\"\u003e\n          \u003cinput type=\"radio\" name=\"cc-gender\" value=\"male\" checked\u003e Male\n        \u003c/label\u003e\n        \u003clabel class=\"cc-radio-btn\" id=\"cc-lbl-female\"\u003e\n          \u003cinput type=\"radio\" name=\"cc-gender\" value=\"female\"\u003e Female\n        \u003c/label\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cc-field\"\u003e\n      \u003clabel\u003eAge\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"cc-age\" min=\"10\" max=\"120\" placeholder=\"e.g. 30\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cc-grid\" style=\"margin-bottom:1rem;\"\u003e\n    \u003cdiv class=\"cc-field\"\u003e\n      \u003cdiv class=\"cc-toggle-row\"\u003e\n        \u003cspan class=\"cc-toggle-label\"\u003eHeight\u003c/span\u003e\n        \u003cdiv class=\"cc-toggle\" id=\"cc-height-toggle\"\u003e\n          \u003cbutton class=\"active\" data-unit=\"cm\" onclick=\"ccSetHeightUnit('cm')\"\u003ecm\u003c/button\u003e\n          \u003cbutton data-unit=\"ftin\" onclick=\"ccSetHeightUnit('ftin')\"\u003eft/in\u003c/button\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv id=\"cc-height-cm-wrap\" class=\"cc-height-inputs\"\u003e\n        \u003cinput type=\"number\" id=\"cc-height-cm\" min=\"50\" max=\"280\" placeholder=\"e.g. 170\"\u003e\n        \u003cspan class=\"cc-unit-hint\"\u003ecm\u003c/span\u003e\n      \u003c/div\u003e\n      \u003cdiv id=\"cc-height-ftin-wrap\" class=\"cc-height-inputs\" style=\"display:none;\"\u003e\n        \u003cinput type=\"number\" id=\"cc-height-ft\" min=\"1\" max=\"9\" placeholder=\"5\"\u003e\n        \u003cspan class=\"cc-unit-hint\"\u003eft\u003c/span\u003e\n        \u003cinput type=\"number\" id=\"cc-height-in\" min=\"0\" max=\"11\" placeholder=\"7\"\u003e\n        \u003cspan class=\"cc-unit-hint\"\u003ein\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cc-field\"\u003e\n      \u003cdiv class=\"cc-toggle-row\"\u003e\n        \u003cspan class=\"cc-toggle-label\"\u003eWeight\u003c/span\u003e\n        \u003cdiv class=\"cc-toggle\" id=\"cc-weight-toggle\"\u003e\n          \u003cbutton class=\"active\" data-unit=\"kg\" onclick=\"ccSetWeightUnit('kg')\"\u003ekg\u003c/button\u003e\n          \u003cbutton data-unit=\"lbs\" onclick=\"ccSetWeightUnit('lbs')\"\u003elbs\u003c/button\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"cc-height-inputs\"\u003e\n        \u003cinput type=\"number\" id=\"cc-weight\" min=\"1\" max=\"1000\" placeholder=\"e.g. 70\"\u003e\n        \u003cspan class=\"cc-unit-hint\" id=\"cc-weight-hint\"\u003ekg\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cc-field\" style=\"margin-bottom:1rem;\"\u003e\n    \u003clabel\u003eActivity Level\u003c/label\u003e\n    \u003cselect id=\"cc-activity\"\u003e\n      \u003coption value=\"1.2\"\u003eSedentary (little or no exercise)\u003c/option\u003e\n      \u003coption value=\"1.375\"\u003eLightly Active (1–3 days/week)\u003c/option\u003e\n      \u003coption value=\"1.55\" selected\u003eModerately Active (3–5 days/week)\u003c/option\u003e\n      \u003coption value=\"1.725\"\u003eVery Active (6–7 days/week)\u003c/option\u003e\n      \u003coption value=\"1.9\"\u003eExtra Active (hard exercise + physical job)\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cc-field\" style=\"margin-bottom:0.25rem;\"\u003e\n    \u003clabel\u003eFormula\u003c/label\u003e\n    \u003cdiv class=\"cc-formula-row\"\u003e\n      \u003cbutton class=\"cc-formula-btn active\" id=\"cc-formula-mifflin\" onclick=\"ccSetFormula('mifflin')\"\u003eMifflin-St Jeor (recommended)\u003c/button\u003e\n      \u003cbutton class=\"cc-formula-btn\" id=\"cc-formula-harris\" onclick=\"ccSetFormula('harris')\"\u003eHarris-Benedict\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cp class=\"cc-formula-note\"\u003eMifflin-St Jeor is considered the most accurate for most adults.\u003c/p\u003e","title":"Calorie Calculator"},{"content":" Carbon Footprint Calculator Estimate your annual CO2 emissions across transport, home energy, and food choices.\n🚗 Transport Car driven (km/year) Average car: ~0.21 kg CO2/km Flights per year Average flight: ~0.9 t CO2 each 🏠 Home Energy Electricity (kWh/year) Average US home: ~10,500 kWh Natural gas (kWh/year) Average US home: ~22,000 kWh 🍽️ Food Meat meals per week Each meal: ~2.5 kg CO2 Dairy servings per week Each serving: ~0.6 kg CO2 Calculate My Carbon Footprint\nYour Estimated Annual Carbon Footprint 0.0 tonnes of CO2 per year You: 0t US avg: 16t World avg: 4t Target: 2t Your Footprint vs. Benchmarks Breakdown by Category Breakdown by Category Transport 0 t Home Energy 0 t Food 0 t Reduction Tips Related Free Tools Time Zone Converter — plan remote meetings across time zones Pomodoro Timer — boost focus with timed work sessions Meeting Cost Calculator — see the true cost of your meetings WFH Expense Tracker — track and categorize home office costs Related Tools Cooking Unit Converter Electricity Cost Calculator Fuel Cost Calculator ","permalink":"https://productivity-works.com/tools/carbon-footprint-calculator/","summary":"\u003cdiv id=\"cf-app\"\u003e\n\u003cstyle\u003e\n#cf-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  padding: 24px 16px;\n  color: #1a1a2e;\n  box-sizing: border-box;\n}\n#cf-app *, #cf-app *::before, #cf-app *::after {\n  box-sizing: inherit;\n}\n#cf-app h1 {\n  font-size: 1.8rem;\n  font-weight: 700;\n  text-align: center;\n  margin: 0 0 6px 0;\n  color: #1a1a2e;\n}\n#cf-app .cf-subtitle {\n  text-align: center;\n  color: #555;\n  margin: 0 0 32px 0;\n  font-size: 0.97rem;\n}\n#cf-app .cf-sections {\n  display: grid;\n  grid-template-columns: 1fr 1fr 1fr;\n  gap: 18px;\n  margin-bottom: 28px;\n}\n@media (max-width: 640px) {\n  #cf-app .cf-sections { grid-template-columns: 1fr; }\n  #cf-app h1 { font-size: 1.35rem; }\n}\n#cf-app .cf-card {\n  background: #fff;\n  border: 1.5px solid #e0e7ff;\n  border-radius: 14px;\n  padding: 20px 18px;\n  box-shadow: 0 2px 10px rgba(99,102,241,0.06);\n}\n#cf-app .cf-card-title {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  font-size: 1.0rem;\n  font-weight: 700;\n  margin-bottom: 16px;\n  color: #3730a3;\n}\n#cf-app .cf-card-title .cf-icon {\n  font-size: 1.4rem;\n}\n#cf-app .cf-field {\n  margin-bottom: 14px;\n}\n#cf-app .cf-field label {\n  display: block;\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #444;\n  margin-bottom: 5px;\n}\n#cf-app .cf-field input[type=\"number\"] {\n  width: 100%;\n  padding: 8px 11px;\n  border: 1.5px solid #c7d2fe;\n  border-radius: 8px;\n  font-size: 0.95rem;\n  color: #1a1a2e;\n  background: #f8f9ff;\n  outline: none;\n  transition: border-color 0.2s;\n}\n#cf-app .cf-field input[type=\"number\"]:focus {\n  border-color: #6366f1;\n  background: #fff;\n}\n#cf-app .cf-field .cf-hint {\n  font-size: 0.73rem;\n  color: #888;\n  margin-top: 3px;\n}\n#cf-app .cf-btn {\n  display: block;\n  width: 100%;\n  padding: 14px;\n  background: linear-gradient(135deg, #6366f1 0%, #22d3ee 100%);\n  color: #fff;\n  font-size: 1.05rem;\n  font-weight: 700;\n  border: none;\n  border-radius: 10px;\n  cursor: pointer;\n  margin: 8px 0 32px 0;\n  letter-spacing: 0.02em;\n  transition: opacity 0.2s, transform 0.1s;\n  box-shadow: 0 4px 14px rgba(99,102,241,0.25);\n}\n#cf-app .cf-btn:hover { opacity: 0.92; transform: translateY(-1px); }\n#cf-app .cf-btn:active { transform: translateY(0); }\n#cf-app .cf-results {\n  display: none;\n}\n#cf-app .cf-results.visible {\n  display: block;\n}\n#cf-app .cf-total-box {\n  background: linear-gradient(135deg, #1a1a2e 0%, #3730a3 100%);\n  border-radius: 16px;\n  padding: 28px 24px;\n  text-align: center;\n  color: #fff;\n  margin-bottom: 24px;\n  box-shadow: 0 6px 24px rgba(55,48,163,0.18);\n}\n#cf-app .cf-total-box .cf-total-label {\n  font-size: 0.9rem;\n  opacity: 0.8;\n  margin-bottom: 6px;\n  letter-spacing: 0.04em;\n  text-transform: uppercase;\n}\n#cf-app .cf-total-box .cf-total-value {\n  font-size: 3.2rem;\n  font-weight: 800;\n  line-height: 1;\n  margin-bottom: 4px;\n}\n#cf-app .cf-total-box .cf-total-unit {\n  font-size: 1.1rem;\n  opacity: 0.75;\n}\n#cf-app .cf-total-box .cf-tree-line {\n  margin-top: 14px;\n  font-size: 0.88rem;\n  background: rgba(255,255,255,0.12);\n  border-radius: 8px;\n  padding: 8px 12px;\n  display: inline-block;\n}\n#cf-app .cf-charts-row {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 18px;\n  margin-bottom: 24px;\n}\n@media (max-width: 600px) {\n  #cf-app .cf-charts-row { grid-template-columns: 1fr; }\n}\n#cf-app .cf-chart-box {\n  background: #fff;\n  border: 1.5px solid #e0e7ff;\n  border-radius: 14px;\n  padding: 18px 16px;\n  box-shadow: 0 2px 10px rgba(99,102,241,0.06);\n}\n#cf-app .cf-chart-box h3 {\n  font-size: 0.88rem;\n  font-weight: 700;\n  color: #3730a3;\n  margin: 0 0 12px 0;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#cf-app canvas {\n  width: 100% !important;\n  height: auto;\n}\n#cf-app .cf-breakdown {\n  background: #fff;\n  border: 1.5px solid #e0e7ff;\n  border-radius: 14px;\n  padding: 20px 18px;\n  margin-bottom: 24px;\n  box-shadow: 0 2px 10px rgba(99,102,241,0.06);\n}\n#cf-app .cf-breakdown h3 {\n  font-size: 0.92rem;\n  font-weight: 700;\n  color: #3730a3;\n  margin: 0 0 14px 0;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#cf-app .cf-bar-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-bottom: 10px;\n}\n#cf-app .cf-bar-label {\n  width: 90px;\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #444;\n  flex-shrink: 0;\n}\n#cf-app .cf-bar-track {\n  flex: 1;\n  background: #f0f4ff;\n  border-radius: 999px;\n  height: 14px;\n  overflow: hidden;\n}\n#cf-app .cf-bar-fill {\n  height: 100%;\n  border-radius: 999px;\n  transition: width 0.6s ease;\n}\n#cf-app .cf-bar-val {\n  width: 60px;\n  font-size: 0.82rem;\n  font-weight: 700;\n  color: #1a1a2e;\n  text-align: right;\n  flex-shrink: 0;\n}\n#cf-app .cf-tips {\n  background: #f0fdf4;\n  border: 1.5px solid #86efac;\n  border-radius: 14px;\n  padding: 20px 18px;\n  margin-bottom: 24px;\n}\n#cf-app .cf-tips h3 {\n  font-size: 0.92rem;\n  font-weight: 700;\n  color: #15803d;\n  margin: 0 0 12px 0;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#cf-app .cf-tips ul {\n  margin: 0;\n  padding-left: 18px;\n}\n#cf-app .cf-tips ul li {\n  margin-bottom: 6px;\n  font-size: 0.92rem;\n  color: #166534;\n  line-height: 1.5;\n}\n#cf-app .cf-related {\n  background: #fff;\n  border: 1.5px solid #e0e7ff;\n  border-radius: 14px;\n  padding: 20px 18px;\n  box-shadow: 0 2px 10px rgba(99,102,241,0.06);\n}\n#cf-app .cf-related h3 {\n  font-size: 0.92rem;\n  font-weight: 700;\n  color: #3730a3;\n  margin: 0 0 12px 0;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#cf-app .cf-related ul {\n  margin: 0;\n  padding-left: 18px;\n}\n#cf-app .cf-related ul li {\n  margin-bottom: 7px;\n  font-size: 0.92rem;\n}\n#cf-app .cf-related ul li a {\n  color: #6366f1;\n  text-decoration: none;\n  font-weight: 600;\n}\n#cf-app .cf-related ul li a:hover { text-decoration: underline; }\n#cf-app .cf-badge-row {\n  display: flex;\n  gap: 12px;\n  justify-content: center;\n  flex-wrap: wrap;\n  margin-top: 16px;\n}\n#cf-app .cf-badge {\n  padding: 6px 14px;\n  border-radius: 999px;\n  font-size: 0.82rem;\n  font-weight: 700;\n}\n#cf-app .cf-badge-you { background: #818cf8; color: #fff; }\n#cf-app .cf-badge-us { background: #f87171; color: #fff; }\n#cf-app .cf-badge-world { background: #fb923c; color: #fff; }\n#cf-app .cf-badge-target { background: #4ade80; color: #166534; }\n\u003c/style\u003e\n\u003ch1\u003eCarbon Footprint Calculator\u003c/h1\u003e\n\u003cp class=\"cf-subtitle\"\u003eEstimate your annual CO2 emissions across transport, home energy, and food choices.\u003c/p\u003e","title":"Carbon Footprint Calculator - Estimate Your CO2 Emissions"},{"content":" Input Text Characters: 0 Words: 0 Lines: 0 Related Tools Format your JSON with ease → JSON Formatter Remove extra spaces, blank lines, and tabs from text → Whitespace Remover Count words, characters, and lines → Word Counter Encode special characters to HTML entities → HTML Entity Encoder ","permalink":"https://productivity-works.com/tools/case-converter/","summary":"\u003cdiv id=\"case-app\"\u003e\n\u003cstyle\u003e\n#case-app {\n  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;\n  background: #0f0f13;\n  color: #e2e8f0;\n  border-radius: 12px;\n  padding: 24px;\n  margin: 0 auto;\n  max-width: 900px;\n  box-sizing: border-box;\n}\n\n#case-app * {\n  box-sizing: border-box;\n}\n\n#case-app h2 {\n  font-size: 1.1rem;\n  font-weight: 600;\n  color: #f1f5f9;\n  margin: 0 0 12px 0;\n  letter-spacing: 0.02em;\n}\n\n.case-input-section {\n  margin-bottom: 20px;\n}\n\n#case-input {\n  width: 100%;\n  min-height: 110px;\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  color: #e2e8f0;\n  font-size: 0.97rem;\n  padding: 12px 14px;\n  resize: vertical;\n  font-family: inherit;\n  transition: border-color 0.2s;\n  outline: none;\n}\n\n#case-input:focus {\n  border-color: #e11d48;\n}\n\n.case-stats {\n  display: flex;\n  gap: 20px;\n  margin-top: 8px;\n  font-size: 0.82rem;\n  color: #94a3b8;\n}\n\n.case-stats span {\n  display: flex;\n  align-items: center;\n  gap: 4px;\n}\n\n.case-stats .stat-val {\n  color: #e11d48;\n  font-weight: 600;\n}\n\n.case-results {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 10px;\n}\n\n@media (max-width: 600px) {\n  .case-results {\n    grid-template-columns: 1fr;\n  }\n}\n\n.case-card {\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  padding: 12px 14px;\n  transition: border-color 0.2s;\n}\n\n.case-card:hover {\n  border-color: #e11d48;\n}\n\n.case-card-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin-bottom: 8px;\n}\n\n.case-label {\n  font-size: 0.75rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #e11d48;\n}\n\n.case-copy-btn {\n  background: none;\n  border: 1px solid #2d2d3d;\n  border-radius: 5px;\n  color: #94a3b8;\n  font-size: 0.75rem;\n  padding: 2px 10px;\n  cursor: pointer;\n  transition: all 0.18s;\n  font-family: inherit;\n}\n\n.case-copy-btn:hover {\n  background: #e11d48;\n  border-color: #e11d48;\n  color: #fff;\n}\n\n.case-copy-btn.copied {\n  background: #16a34a;\n  border-color: #16a34a;\n  color: #fff;\n}\n\n.case-output {\n  font-size: 0.9rem;\n  color: #cbd5e1;\n  word-break: break-all;\n  min-height: 1.4em;\n  font-family: 'Fira Mono', 'Consolas', monospace;\n  line-height: 1.5;\n}\n\n.case-placeholder {\n  color: #4a5568;\n  font-style: italic;\n}\n\u003c/style\u003e\n\u003cdiv class=\"case-input-section\"\u003e\n  \u003ch2\u003eInput Text\u003c/h2\u003e\n  \u003ctextarea id=\"case-input\" placeholder=\"Type or paste your text here...\" oninput=\"caseConvertAll()\"\u003e\u003c/textarea\u003e\n  \u003cdiv class=\"case-stats\"\u003e\n    \u003cspan\u003eCharacters: \u003cspan class=\"stat-val\" id=\"char-count\"\u003e0\u003c/span\u003e\u003c/span\u003e\n    \u003cspan\u003eWords: \u003cspan class=\"stat-val\" id=\"word-count\"\u003e0\u003c/span\u003e\u003c/span\u003e\n    \u003cspan\u003eLines: \u003cspan class=\"stat-val\" id=\"line-count\"\u003e0\u003c/span\u003e\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"case-results\" id=\"case-results\"\u003e\n  \u003c!-- Cards rendered by JS --\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  const CASES = [\n    { id: 'upper',    label: 'UPPERCASE',     fn: s =\u003e s.toUpperCase() },\n    { id: 'lower',    label: 'lowercase',     fn: s =\u003e s.toLowerCase() },\n    { id: 'title',    label: 'Title Case',    fn: titleCase },\n    { id: 'sentence', label: 'Sentence case', fn: sentenceCase },\n    { id: 'camel',    label: 'camelCase',     fn: camelCase },\n    { id: 'pascal',   label: 'PascalCase',    fn: pascalCase },\n    { id: 'snake',    label: 'snake_case',    fn: snakeCase },\n    { id: 'kebab',    label: 'kebab-case',    fn: kebabCase },\n    { id: 'constant', label: 'CONSTANT_CASE', fn: constantCase },\n    { id: 'dot',      label: 'dot.case',      fn: dotCase },\n    { id: 'alt',      label: 'aLtErNaTiNg',   fn: alternatingCase },\n    { id: 'inverse',  label: 'iNVERSE cASE',  fn: inverseCase },\n  ];\n\n  function words(s) {\n    return s.trim().split(/[\\s\\-_\\.]+/).filter(Boolean);\n  }\n\n  function titleCase(s) {\n    return s.replace(/\\w\\S*/g, w =\u003e w.charAt(0).toUpperCase() + w.slice(1).toLowerCase());\n  }\n\n  function sentenceCase(s) {\n    return s.toLowerCase().replace(/(^\\s*\\w|[.!?]\\s+\\w)/g, c =\u003e c.toUpperCase());\n  }\n\n  function camelCase(s) {\n    const w = words(s);\n    if (!w.length) return '';\n    return w[0].toLowerCase() + w.slice(1).map(w =\u003e w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join('');\n  }\n\n  function pascalCase(s) {\n    return words(s).map(w =\u003e w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join('');\n  }\n\n  function snakeCase(s) {\n    return words(s).join('_').toLowerCase();\n  }\n\n  function kebabCase(s) {\n    return words(s).join('-').toLowerCase();\n  }\n\n  function constantCase(s) {\n    return words(s).join('_').toUpperCase();\n  }\n\n  function dotCase(s) {\n    return words(s).join('.').toLowerCase();\n  }\n\n  function alternatingCase(s) {\n    return s.split('').map((c, i) =\u003e i % 2 === 0 ? c.toLowerCase() : c.toUpperCase()).join('');\n  }\n\n  function inverseCase(s) {\n    return s.split('').map(c =\u003e c === c.toUpperCase() ? c.toLowerCase() : c.toUpperCase()).join('');\n  }\n\n  function buildCards() {\n    const container = document.getElementById('case-results');\n    container.innerHTML = CASES.map(c =\u003e `\n      \u003cdiv class=\"case-card\"\u003e\n        \u003cdiv class=\"case-card-header\"\u003e\n          \u003cspan class=\"case-label\"\u003e${c.label}\u003c/span\u003e\n          \u003cbutton class=\"case-copy-btn\" onclick=\"caseCopy('${c.id}', this)\"\u003eCopy\u003c/button\u003e\n        \u003c/div\u003e\n        \u003cdiv class=\"case-output case-placeholder\" id=\"out-${c.id}\"\u003eResult will appear here...\u003c/div\u003e\n      \u003c/div\u003e\n    `).join('');\n  }\n\n  window.caseConvertAll = function() {\n    const input = document.getElementById('case-input').value;\n\n    // Stats\n    document.getElementById('char-count').textContent = input.length;\n    document.getElementById('word-count').textContent = input.trim() ? input.trim().split(/\\s+/).length : 0;\n    document.getElementById('line-count').textContent = input ? input.split('\\n').length : 0;\n\n    // Conversions\n    CASES.forEach(c =\u003e {\n      const el = document.getElementById('out-' + c.id);\n      if (!input.trim()) {\n        el.textContent = 'Result will appear here...';\n        el.classList.add('case-placeholder');\n      } else {\n        el.textContent = c.fn(input);\n        el.classList.remove('case-placeholder');\n      }\n    });\n  };\n\n  window.caseCopy = function(id, btn) {\n    const text = document.getElementById('out-' + id).textContent;\n    if (!text || text === 'Result will appear here...') return;\n    navigator.clipboard.writeText(text).then(() =\u003e {\n      btn.textContent = 'Copied!';\n      btn.classList.add('copied');\n      setTimeout(() =\u003e {\n        btn.textContent = 'Copy';\n        btn.classList.remove('copied');\n      }, 1800);\n    }).catch(() =\u003e {\n      const ta = document.createElement('textarea');\n      ta.value = text;\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      btn.textContent = 'Copied!';\n      btn.classList.add('copied');\n      setTimeout(() =\u003e {\n        btn.textContent = 'Copy';\n        btn.classList.remove('copied');\n      }, 1800);\n    });\n  };\n\n  buildCards();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003eFormat your JSON with ease → \u003ca href=\"https://productivity-works.com/tools/json-formatter/\"\u003eJSON Formatter\u003c/a\u003e\n\u003c/p\u003e","title":"Case Converter - Free Online Text Case Tool"},{"content":"Generate professional changelogs in Keep a Changelog format. Add version info, categorize your changes, preview the output, and copy or download — no account needed.\nQuick Presets Major Release Minor Update Patch / Bugfix Security Update Clear All Release Info Version Number Release Date Project Name (optional) Change Entries Added 0 \u0026#9660; + Add entry Changed 0 \u0026#9660; + Add entry Deprecated 0 \u0026#9660; + Add entry Removed 0 \u0026#9660; + Add entry Fixed 0 \u0026#9660; + Add entry Security 0 \u0026#9660; + Add entry Append to Existing Changelog Paste your existing changelog below. The new release will be prepended above it.\nPreview \u0026amp; Output Markdown HTML Plain Text Copy to Clipboard Download File Reset All Related Tools Preview markdown → Markdown Preview Generate markdown tables → Markdown Table Generator Format JSON → JSON Formatter ","permalink":"https://productivity-works.com/tools/changelog-generator/","summary":"\u003cp\u003eGenerate professional changelogs in \u003ca href=\"https://keepachangelog.com/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eKeep a Changelog\u003c/a\u003e\n format. Add version info, categorize your changes, preview the output, and copy or download — no account needed.\u003c/p\u003e\n\u003cdiv id=\"cl-app\"\u003e\n\u003cstyle\u003e\n  #cl-app *,\n  #cl-app *::before,\n  #cl-app *::after {\n    box-sizing: border-box;\n  }\n  #cl-app {\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n    font-size: 15px;\n    color: #1a1a2e;\n    margin: 0 auto;\n    max-width: 900px;\n  }\n  #cl-app h2 {\n    font-size: 1.1rem;\n    font-weight: 700;\n    margin: 0 0 12px 0;\n    color: #1a1a2e;\n  }\n  #cl-app .cl-section {\n    background: #f8f9fc;\n    border: 1px solid #e2e8f0;\n    border-radius: 10px;\n    padding: 20px;\n    margin-bottom: 18px;\n  }\n  #cl-app .cl-row {\n    display: flex;\n    gap: 14px;\n    flex-wrap: wrap;\n  }\n  #cl-app .cl-field {\n    display: flex;\n    flex-direction: column;\n    gap: 5px;\n    flex: 1;\n    min-width: 180px;\n  }\n  #cl-app label {\n    font-size: 0.82rem;\n    font-weight: 600;\n    color: #4a5568;\n    text-transform: uppercase;\n    letter-spacing: 0.04em;\n  }\n  #cl-app input[type=\"text\"],\n  #cl-app input[type=\"date\"],\n  #cl-app select,\n  #cl-app textarea {\n    width: 100%;\n    padding: 9px 12px;\n    border: 1px solid #cbd5e0;\n    border-radius: 7px;\n    font-size: 0.95rem;\n    font-family: inherit;\n    background: #fff;\n    color: #1a1a2e;\n    transition: border-color 0.18s;\n    outline: none;\n  }\n  #cl-app input[type=\"text\"]:focus,\n  #cl-app input[type=\"date\"]:focus,\n  #cl-app select:focus,\n  #cl-app textarea:focus {\n    border-color: #667eea;\n    box-shadow: 0 0 0 3px rgba(102,126,234,0.12);\n  }\n  #cl-app .cl-presets {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 8px;\n    margin-bottom: 0;\n  }\n  #cl-app .cl-preset-btn {\n    padding: 6px 14px;\n    border-radius: 20px;\n    border: 1.5px solid #667eea;\n    background: #fff;\n    color: #667eea;\n    font-size: 0.83rem;\n    font-weight: 600;\n    cursor: pointer;\n    transition: background 0.15s, color 0.15s;\n  }\n  #cl-app .cl-preset-btn:hover {\n    background: #667eea;\n    color: #fff;\n  }\n  #cl-app .cl-category {\n    border: 1px solid #e2e8f0;\n    border-radius: 9px;\n    margin-bottom: 12px;\n    overflow: hidden;\n    background: #fff;\n  }\n  #cl-app .cl-cat-header {\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    padding: 10px 14px;\n    cursor: pointer;\n    user-select: none;\n    background: #fff;\n    border-bottom: 1px solid #e2e8f0;\n  }\n  #cl-app .cl-cat-header:hover {\n    background: #f0f4ff;\n  }\n  #cl-app .cl-cat-badge {\n    display: inline-block;\n    padding: 2px 10px;\n    border-radius: 12px;\n    font-size: 0.78rem;\n    font-weight: 700;\n    letter-spacing: 0.03em;\n    text-transform: uppercase;\n  }\n  #cl-app .cl-cat-added    .cl-cat-badge { background: #c6f6d5; color: #22543d; }\n  #cl-app .cl-cat-changed  .cl-cat-badge { background: #bee3f8; color: #2a4365; }\n  #cl-app .cl-cat-deprecated .cl-cat-badge { background: #fefcbf; color: #744210; }\n  #cl-app .cl-cat-removed  .cl-cat-badge { background: #fed7d7; color: #742a2a; }\n  #cl-app .cl-cat-fixed    .cl-cat-badge { background: #e9d8fd; color: #44337a; }\n  #cl-app .cl-cat-security .cl-cat-badge { background: #feebc8; color: #7b341e; }\n  #cl-app .cl-cat-toggle {\n    margin-left: auto;\n    font-size: 0.8rem;\n    color: #718096;\n  }\n  #cl-app .cl-cat-count {\n    font-size: 0.78rem;\n    color: #718096;\n    background: #edf2f7;\n    border-radius: 10px;\n    padding: 1px 8px;\n  }\n  #cl-app .cl-cat-body {\n    padding: 12px 14px;\n  }\n  #cl-app .cl-entry-row {\n    display: flex;\n    gap: 8px;\n    margin-bottom: 8px;\n    align-items: center;\n  }\n  #cl-app .cl-entry-input {\n    flex: 1;\n  }\n  #cl-app .cl-remove-btn {\n    background: #fff5f5;\n    border: 1px solid #feb2b2;\n    color: #c53030;\n    border-radius: 6px;\n    padding: 6px 10px;\n    cursor: pointer;\n    font-size: 0.85rem;\n    font-weight: 700;\n    transition: background 0.15s;\n    flex-shrink: 0;\n  }\n  #cl-app .cl-remove-btn:hover {\n    background: #fed7d7;\n  }\n  #cl-app .cl-add-btn {\n    background: #ebf8ff;\n    border: 1px dashed #90cdf4;\n    color: #2b6cb0;\n    border-radius: 7px;\n    padding: 7px 14px;\n    cursor: pointer;\n    font-size: 0.85rem;\n    font-weight: 600;\n    width: 100%;\n    margin-top: 4px;\n    transition: background 0.15s;\n  }\n  #cl-app .cl-add-btn:hover {\n    background: #bee3f8;\n  }\n  #cl-app .cl-format-tabs {\n    display: flex;\n    gap: 0;\n    border-bottom: 2px solid #e2e8f0;\n    margin-bottom: 14px;\n  }\n  #cl-app .cl-tab {\n    padding: 8px 20px;\n    border: none;\n    background: transparent;\n    font-size: 0.9rem;\n    font-weight: 600;\n    color: #718096;\n    cursor: pointer;\n    border-bottom: 2px solid transparent;\n    margin-bottom: -2px;\n    transition: color 0.15s, border-color 0.15s;\n  }\n  #cl-app .cl-tab.active {\n    color: #667eea;\n    border-bottom-color: #667eea;\n  }\n  #cl-app .cl-tab:hover:not(.active) {\n    color: #4a5568;\n  }\n  #cl-app .cl-preview-box {\n    background: #1a202c;\n    color: #e2e8f0;\n    border-radius: 8px;\n    padding: 18px 20px;\n    font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n    font-size: 0.85rem;\n    line-height: 1.7;\n    white-space: pre-wrap;\n    word-break: break-word;\n    min-height: 180px;\n    max-height: 420px;\n    overflow-y: auto;\n    tab-size: 2;\n  }\n  #cl-app .cl-preview-box.html-mode {\n    background: #fff;\n    color: #1a1a2e;\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n    font-size: 0.95rem;\n    border: 1px solid #e2e8f0;\n    line-height: 1.7;\n    white-space: normal;\n  }\n  #cl-app .cl-action-row {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin-top: 14px;\n  }\n  #cl-app .cl-btn {\n    padding: 9px 20px;\n    border-radius: 7px;\n    border: none;\n    font-size: 0.9rem;\n    font-weight: 600;\n    cursor: pointer;\n    transition: opacity 0.15s, transform 0.1s;\n  }\n  #cl-app .cl-btn:active {\n    transform: scale(0.97);\n  }\n  #cl-app .cl-btn-primary {\n    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n    color: #fff;\n  }\n  #cl-app .cl-btn-secondary {\n    background: #edf2f7;\n    color: #2d3748;\n  }\n  #cl-app .cl-btn-danger {\n    background: #fff5f5;\n    border: 1px solid #feb2b2;\n    color: #c53030;\n  }\n  #cl-app .cl-btn:hover {\n    opacity: 0.88;\n  }\n  #cl-app .cl-toast {\n    display: none;\n    position: fixed;\n    bottom: 24px;\n    right: 24px;\n    background: #2d3748;\n    color: #fff;\n    padding: 10px 20px;\n    border-radius: 8px;\n    font-size: 0.9rem;\n    font-weight: 600;\n    z-index: 9999;\n    box-shadow: 0 4px 16px rgba(0,0,0,0.18);\n    animation: cl-fadein 0.2s ease;\n  }\n  @keyframes cl-fadein {\n    from { opacity: 0; transform: translateY(8px); }\n    to   { opacity: 1; transform: translateY(0); }\n  }\n  #cl-app .cl-append-area {\n    width: 100%;\n    min-height: 90px;\n    font-family: \"SFMono-Regular\", Consolas, monospace;\n    font-size: 0.83rem;\n    resize: vertical;\n  }\n  #cl-app .cl-info {\n    font-size: 0.82rem;\n    color: #718096;\n    margin-top: 6px;\n  }\n  #cl-app .cl-section-title {\n    font-size: 0.78rem;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 0.05em;\n    color: #667eea;\n    margin-bottom: 10px;\n  }\n  @media (max-width: 600px) {\n    #cl-app .cl-row { flex-direction: column; }\n    #cl-app .cl-tab { padding: 8px 12px; font-size: 0.82rem; }\n    #cl-app .cl-action-row { flex-direction: column; }\n    #cl-app .cl-btn { width: 100%; text-align: center; }\n  }\n\u003c/style\u003e\n\u003c!-- PRESETS --\u003e\n\u003cdiv class=\"cl-section\"\u003e\n  \u003cdiv class=\"cl-section-title\"\u003eQuick Presets\u003c/div\u003e\n  \u003cdiv class=\"cl-presets\"\u003e\n    \u003cbutton class=\"cl-preset-btn\" onclick=\"clApplyPreset('major')\"\u003eMajor Release\u003c/button\u003e\n    \u003cbutton class=\"cl-preset-btn\" onclick=\"clApplyPreset('minor')\"\u003eMinor Update\u003c/button\u003e\n    \u003cbutton class=\"cl-preset-btn\" onclick=\"clApplyPreset('patch')\"\u003ePatch / Bugfix\u003c/button\u003e\n    \u003cbutton class=\"cl-preset-btn\" onclick=\"clApplyPreset('security')\"\u003eSecurity Update\u003c/button\u003e\n    \u003cbutton class=\"cl-preset-btn\" onclick=\"clApplyPreset('clear')\"\u003eClear All\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- RELEASE INFO --\u003e\n\u003cdiv class=\"cl-section\"\u003e\n  \u003ch2\u003eRelease Info\u003c/h2\u003e\n  \u003cdiv class=\"cl-row\"\u003e\n    \u003cdiv class=\"cl-field\"\u003e\n      \u003clabel for=\"cl-version\"\u003eVersion Number\u003c/label\u003e\n      \u003cinput type=\"text\" id=\"cl-version\" placeholder=\"e.g. 2.1.0\" /\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cl-field\"\u003e\n      \u003clabel for=\"cl-date\"\u003eRelease Date\u003c/label\u003e\n      \u003cinput type=\"date\" id=\"cl-date\" /\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cl-field\"\u003e\n      \u003clabel for=\"cl-project\"\u003eProject Name (optional)\u003c/label\u003e\n      \u003cinput type=\"text\" id=\"cl-project\" placeholder=\"e.g. My App\" /\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- CATEGORIES --\u003e\n\u003cdiv class=\"cl-section\"\u003e\n  \u003ch2\u003eChange Entries\u003c/h2\u003e\n  \u003cdiv class=\"cl-category cl-cat-added\" id=\"cl-cat-added\"\u003e\n    \u003cdiv class=\"cl-cat-header\" onclick=\"clToggleCat('added')\"\u003e\n      \u003cspan class=\"cl-cat-badge\"\u003eAdded\u003c/span\u003e\n      \u003cspan class=\"cl-cat-count\" id=\"cl-count-added\"\u003e0\u003c/span\u003e\n      \u003cspan class=\"cl-cat-toggle\" id=\"cl-toggle-added\"\u003e\u0026#9660;\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cl-cat-body\" id=\"cl-body-added\"\u003e\n      \u003cdiv id=\"cl-entries-added\"\u003e\u003c/div\u003e\n      \u003cbutton class=\"cl-add-btn\" onclick=\"clAddEntry('added')\"\u003e+ Add entry\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cl-category cl-cat-changed\" id=\"cl-cat-changed\"\u003e\n    \u003cdiv class=\"cl-cat-header\" onclick=\"clToggleCat('changed')\"\u003e\n      \u003cspan class=\"cl-cat-badge\"\u003eChanged\u003c/span\u003e\n      \u003cspan class=\"cl-cat-count\" id=\"cl-count-changed\"\u003e0\u003c/span\u003e\n      \u003cspan class=\"cl-cat-toggle\" id=\"cl-toggle-changed\"\u003e\u0026#9660;\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cl-cat-body\" id=\"cl-body-changed\"\u003e\n      \u003cdiv id=\"cl-entries-changed\"\u003e\u003c/div\u003e\n      \u003cbutton class=\"cl-add-btn\" onclick=\"clAddEntry('changed')\"\u003e+ Add entry\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cl-category cl-cat-deprecated\" id=\"cl-cat-deprecated\"\u003e\n    \u003cdiv class=\"cl-cat-header\" onclick=\"clToggleCat('deprecated')\"\u003e\n      \u003cspan class=\"cl-cat-badge\"\u003eDeprecated\u003c/span\u003e\n      \u003cspan class=\"cl-cat-count\" id=\"cl-count-deprecated\"\u003e0\u003c/span\u003e\n      \u003cspan class=\"cl-cat-toggle\" id=\"cl-toggle-deprecated\"\u003e\u0026#9660;\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cl-cat-body\" id=\"cl-body-deprecated\"\u003e\n      \u003cdiv id=\"cl-entries-deprecated\"\u003e\u003c/div\u003e\n      \u003cbutton class=\"cl-add-btn\" onclick=\"clAddEntry('deprecated')\"\u003e+ Add entry\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cl-category cl-cat-removed\" id=\"cl-cat-removed\"\u003e\n    \u003cdiv class=\"cl-cat-header\" onclick=\"clToggleCat('removed')\"\u003e\n      \u003cspan class=\"cl-cat-badge\"\u003eRemoved\u003c/span\u003e\n      \u003cspan class=\"cl-cat-count\" id=\"cl-count-removed\"\u003e0\u003c/span\u003e\n      \u003cspan class=\"cl-cat-toggle\" id=\"cl-toggle-removed\"\u003e\u0026#9660;\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cl-cat-body\" id=\"cl-body-removed\"\u003e\n      \u003cdiv id=\"cl-entries-removed\"\u003e\u003c/div\u003e\n      \u003cbutton class=\"cl-add-btn\" onclick=\"clAddEntry('removed')\"\u003e+ Add entry\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cl-category cl-cat-fixed\" id=\"cl-cat-fixed\"\u003e\n    \u003cdiv class=\"cl-cat-header\" onclick=\"clToggleCat('fixed')\"\u003e\n      \u003cspan class=\"cl-cat-badge\"\u003eFixed\u003c/span\u003e\n      \u003cspan class=\"cl-cat-count\" id=\"cl-count-fixed\"\u003e0\u003c/span\u003e\n      \u003cspan class=\"cl-cat-toggle\" id=\"cl-toggle-fixed\"\u003e\u0026#9660;\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cl-cat-body\" id=\"cl-body-fixed\"\u003e\n      \u003cdiv id=\"cl-entries-fixed\"\u003e\u003c/div\u003e\n      \u003cbutton class=\"cl-add-btn\" onclick=\"clAddEntry('fixed')\"\u003e+ Add entry\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cl-category cl-cat-security\" id=\"cl-cat-security\"\u003e\n    \u003cdiv class=\"cl-cat-header\" onclick=\"clToggleCat('security')\"\u003e\n      \u003cspan class=\"cl-cat-badge\"\u003eSecurity\u003c/span\u003e\n      \u003cspan class=\"cl-cat-count\" id=\"cl-count-security\"\u003e0\u003c/span\u003e\n      \u003cspan class=\"cl-cat-toggle\" id=\"cl-toggle-security\"\u003e\u0026#9660;\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cl-cat-body\" id=\"cl-body-security\"\u003e\n      \u003cdiv id=\"cl-entries-security\"\u003e\u003c/div\u003e\n      \u003cbutton class=\"cl-add-btn\" onclick=\"clAddEntry('security')\"\u003e+ Add entry\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- APPEND EXISTING --\u003e\n\u003cdiv class=\"cl-section\"\u003e\n  \u003ch2\u003eAppend to Existing Changelog\u003c/h2\u003e\n  \u003cp class=\"cl-info\"\u003ePaste your existing changelog below. The new release will be prepended above it.\u003c/p\u003e","title":"Changelog Generator — Release Notes Tool"},{"content":" Copy Text Clear Custom limit: characters 0 / 0 Over limit! 0 Characters (with spaces) 0 Characters (no spaces) 0 Words 0 Sentences 0 Paragraphs 0 Lines 0 Unique Characters 0 UTF-8 Bytes Platform Character Limits X / Twitter 0 / 280 SMS (standard) 0 / 160 Meta Description 0 / 160 Title Tag (SEO) 0 / 60 Instagram Caption 0 / 2200 Top 10 Character Frequency Type text above to see character frequency chart Copied to clipboard! How to Use the Character Counter Paste or type any text into the box above — all statistics update instantly. There is nothing to submit or install; everything runs in your browser.\nCharacters with spaces counts every character including whitespace. This is what most platform limits (Twitter, SMS, meta descriptions) measure. Characters without spaces strips all whitespace first, useful for academic or legal requirements that specify \u0026ldquo;characters excluding spaces.\u0026rdquo;\nThe platform limit bars turn amber at 80% and red when you exceed the limit, so you can see at a glance whether your text fits. Enter any number in the Custom Limit field to track your own target — a house style guide, a form field maximum, or a client brief.\nThe character frequency chart shows the top 10 non-whitespace characters by count, with their Unicode code points displayed below each bar. This is handy for spotting repeated filler words rendered as single characters, checking whether an unusual glyph crept into your text, or simply satisfying curiosity about your writing patterns.\nWhy Character Counts Matter Twitter / X enforces a 280-character limit per post. Unlike older counting methods, Twitter counts most Unicode characters — including CJK ideographs — as a single character. Staying under the limit before you paste into the app saves the frustration of a last-second trim.\nSMS uses 160 characters for a single segment in GSM-7 encoding. If your message includes any character outside GSM-7 (such as curly quotes, em dashes, or emoji), the encoding automatically switches to UCS-2 and the segment limit drops to 70 characters. The UTF-8 byte counter on this page can help you catch unexpected multi-byte characters before sending.\nSEO title tags are typically truncated in search results at around 60 characters. Meta descriptions display up to about 160 characters. Keeping within these ranges prevents Google from rewriting your snippets and helps users understand what your page is about before they click.\nUTF-8 byte size matters whenever you are writing to a database column defined in bytes rather than characters, sending text over a size-limited API, or generating files where byte count affects storage or processing costs. A plain ASCII character is 1 byte; a CJK character or emoji is typically 3–4 bytes.\nRelated Tools Need to count words and estimate reading time? Word Counter Analyze which words appear most often in your text: Word Frequency Counter Calculate how long it takes to read your article: Reading Time Calculator ","permalink":"https://productivity-works.com/tools/character-counter/","summary":"\u003cdiv id=\"cc-app\"\u003e\n\u003cstyle\u003e\n#cc-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 880px;\n  margin: 0 auto;\n  color: #1e1b4b;\n}\n\n#cc-app * {\n  box-sizing: border-box;\n}\n\n#cc-textarea {\n  width: 100%;\n  min-height: 200px;\n  padding: 16px;\n  font-size: 1rem;\n  line-height: 1.6;\n  border: 2px solid #c4b5fd;\n  border-radius: 10px;\n  resize: vertical;\n  outline: none;\n  transition: border-color 0.2s;\n  background: #faf5ff;\n  color: #1e1b4b;\n  font-family: inherit;\n}\n#cc-textarea:focus {\n  border-color: #7c3aed;\n  background: #fff;\n  box-shadow: 0 0 0 3px rgba(124,58,237,0.1);\n}\n#cc-textarea::placeholder { color: #a78bfa; }\n\n#cc-actions {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-top: 10px;\n}\n#cc-app button {\n  padding: 7px 14px;\n  border: none;\n  border-radius: 6px;\n  font-size: 0.85rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s;\n  font-family: inherit;\n}\n#cc-app button:active { transform: scale(0.97); }\n\n.cc-btn-primary { background: #7c3aed; color: #fff; }\n.cc-btn-primary:hover { background: #6d28d9; }\n.cc-btn-secondary { background: #ede9fe; color: #5b21b6; }\n.cc-btn-secondary:hover { background: #ddd6fe; }\n.cc-btn-danger { background: #fee2e2; color: #b91c1c; }\n.cc-btn-danger:hover { background: #fecaca; }\n\n/* Custom limit row */\n#cc-limit-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-top: 12px;\n  flex-wrap: wrap;\n}\n#cc-limit-row label {\n  font-size: 0.85rem;\n  color: #5b21b6;\n  font-weight: 600;\n  white-space: nowrap;\n}\n#cc-limit-input {\n  width: 100px;\n  padding: 6px 10px;\n  border: 2px solid #c4b5fd;\n  border-radius: 6px;\n  font-size: 0.85rem;\n  color: #1e1b4b;\n  outline: none;\n  font-family: inherit;\n}\n#cc-limit-input:focus { border-color: #7c3aed; }\n\n/* Custom limit progress */\n#cc-custom-progress-wrap {\n  margin-top: 8px;\n  display: none;\n}\n#cc-custom-progress-label {\n  font-size: 0.8rem;\n  color: #5b21b6;\n  margin-bottom: 4px;\n  display: flex;\n  justify-content: space-between;\n}\n#cc-custom-progress-bg {\n  width: 100%;\n  height: 10px;\n  background: #ede9fe;\n  border-radius: 999px;\n  overflow: hidden;\n}\n#cc-custom-progress-bar {\n  height: 100%;\n  background: #7c3aed;\n  border-radius: 999px;\n  transition: width 0.2s, background 0.2s;\n  width: 0%;\n}\n#cc-custom-progress-bar.warn { background: #f59e0b; }\n#cc-custom-progress-bar.over { background: #ef4444; }\n\n/* Stats grid */\n#cc-stats-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(138px, 1fr));\n  gap: 12px;\n  margin-top: 20px;\n}\n.cc-stat-card {\n  background: #faf5ff;\n  border: 1.5px solid #ddd6fe;\n  border-radius: 10px;\n  padding: 14px 10px;\n  text-align: center;\n}\n.cc-stat-value {\n  font-size: 1.7rem;\n  font-weight: 700;\n  color: #7c3aed;\n  line-height: 1;\n  word-break: break-all;\n}\n.cc-stat-label {\n  font-size: 0.72rem;\n  color: #6b7280;\n  margin-top: 6px;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n\n/* Platform limit bars */\n#cc-platform-section {\n  margin-top: 28px;\n}\n#cc-platform-section h3 {\n  font-size: 0.9rem;\n  font-weight: 700;\n  color: #5b21b6;\n  margin: 0 0 14px 0;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n.cc-platform-row {\n  margin-bottom: 12px;\n}\n.cc-platform-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin-bottom: 4px;\n}\n.cc-platform-name {\n  font-size: 0.85rem;\n  font-weight: 600;\n  color: #374151;\n}\n.cc-platform-count {\n  font-size: 0.82rem;\n  color: #6b7280;\n  font-variant-numeric: tabular-nums;\n}\n.cc-platform-count.over { color: #ef4444; font-weight: 700; }\n.cc-platform-track {\n  width: 100%;\n  height: 8px;\n  background: #ede9fe;\n  border-radius: 999px;\n  overflow: hidden;\n}\n.cc-platform-bar {\n  height: 100%;\n  border-radius: 999px;\n  transition: width 0.25s, background 0.25s;\n  width: 0%;\n  background: linear-gradient(90deg, #7c3aed, #a78bfa);\n}\n.cc-platform-bar.warn { background: linear-gradient(90deg, #f59e0b, #fbbf24); }\n.cc-platform-bar.over { background: linear-gradient(90deg, #ef4444, #f87171); }\n\n/* Character density chart */\n#cc-density-section {\n  margin-top: 28px;\n}\n#cc-density-section h3 {\n  font-size: 0.9rem;\n  font-weight: 700;\n  color: #5b21b6;\n  margin: 0 0 14px 0;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n#cc-density-canvas {\n  width: 100%;\n  max-width: 100%;\n  border-radius: 8px;\n  background: #faf5ff;\n  border: 1.5px solid #ddd6fe;\n  display: block;\n}\n#cc-density-empty {\n  text-align: center;\n  color: #a78bfa;\n  font-size: 0.9rem;\n  padding: 24px;\n  background: #faf5ff;\n  border: 1.5px solid #ddd6fe;\n  border-radius: 8px;\n}\n\n/* Toast */\n#cc-toast {\n  position: fixed;\n  bottom: 24px;\n  left: 50%;\n  transform: translateX(-50%) translateY(20px);\n  background: #7c3aed;\n  color: #fff;\n  padding: 10px 22px;\n  border-radius: 8px;\n  font-size: 0.9rem;\n  font-weight: 600;\n  opacity: 0;\n  transition: opacity 0.3s, transform 0.3s;\n  pointer-events: none;\n  z-index: 9999;\n}\n#cc-toast.show {\n  opacity: 1;\n  transform: translateX(-50%) translateY(0);\n}\n\n@media (max-width: 500px) {\n  #cc-stats-grid { grid-template-columns: repeat(2, 1fr); }\n}\n\u003c/style\u003e\n\u003ctextarea id=\"cc-textarea\" placeholder=\"Paste or type your text here — stats update in real time...\"\u003e\u003c/textarea\u003e\n\u003cdiv id=\"cc-actions\"\u003e\n  \u003cbutton class=\"cc-btn-primary\" id=\"cc-copy-btn\"\u003eCopy Text\u003c/button\u003e\n  \u003cbutton class=\"cc-btn-danger\" id=\"cc-clear-btn\"\u003eClear\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv id=\"cc-limit-row\"\u003e\n  \u003clabel for=\"cc-limit-input\"\u003eCustom limit:\u003c/label\u003e\n  \u003cinput type=\"number\" id=\"cc-limit-input\" min=\"1\" placeholder=\"e.g. 500\"\u003e\n  \u003cspan style=\"font-size:0.8rem;color:#8b5cf6;\"\u003echaracters\u003c/span\u003e\n\u003c/div\u003e\n\u003cdiv id=\"cc-custom-progress-wrap\"\u003e\n  \u003cdiv id=\"cc-custom-progress-label\"\u003e\n    \u003cspan id=\"cc-custom-progress-text\"\u003e0 / 0\u003c/span\u003e\n    \u003cspan id=\"cc-custom-progress-over\" style=\"color:#ef4444;display:none;\"\u003eOver limit!\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv id=\"cc-custom-progress-bg\"\u003e\n    \u003cdiv id=\"cc-custom-progress-bar\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Stats grid --\u003e\n\u003cdiv id=\"cc-stats-grid\"\u003e\n  \u003cdiv class=\"cc-stat-card\"\u003e\n    \u003cdiv class=\"cc-stat-value\" id=\"cc-chars-with\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"cc-stat-label\"\u003eCharacters (with spaces)\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cc-stat-card\"\u003e\n    \u003cdiv class=\"cc-stat-value\" id=\"cc-chars-without\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"cc-stat-label\"\u003eCharacters (no spaces)\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cc-stat-card\"\u003e\n    \u003cdiv class=\"cc-stat-value\" id=\"cc-words\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"cc-stat-label\"\u003eWords\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cc-stat-card\"\u003e\n    \u003cdiv class=\"cc-stat-value\" id=\"cc-sentences\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"cc-stat-label\"\u003eSentences\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cc-stat-card\"\u003e\n    \u003cdiv class=\"cc-stat-value\" id=\"cc-paragraphs\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"cc-stat-label\"\u003eParagraphs\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cc-stat-card\"\u003e\n    \u003cdiv class=\"cc-stat-value\" id=\"cc-lines\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"cc-stat-label\"\u003eLines\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cc-stat-card\"\u003e\n    \u003cdiv class=\"cc-stat-value\" id=\"cc-unique\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"cc-stat-label\"\u003eUnique Characters\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cc-stat-card\"\u003e\n    \u003cdiv class=\"cc-stat-value\" id=\"cc-bytes\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"cc-stat-label\"\u003eUTF-8 Bytes\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Platform limit bars --\u003e\n\u003cdiv id=\"cc-platform-section\"\u003e\n  \u003ch3\u003ePlatform Character Limits\u003c/h3\u003e\n  \u003cdiv class=\"cc-platform-row\"\u003e\n    \u003cdiv class=\"cc-platform-header\"\u003e\n      \u003cspan class=\"cc-platform-name\"\u003eX / Twitter\u003c/span\u003e\n      \u003cspan class=\"cc-platform-count\" id=\"cc-plat-twitter-label\"\u003e0 / 280\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cc-platform-track\"\u003e\u003cdiv class=\"cc-platform-bar\" id=\"cc-plat-twitter-bar\"\u003e\u003c/div\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cc-platform-row\"\u003e\n    \u003cdiv class=\"cc-platform-header\"\u003e\n      \u003cspan class=\"cc-platform-name\"\u003eSMS (standard)\u003c/span\u003e\n      \u003cspan class=\"cc-platform-count\" id=\"cc-plat-sms-label\"\u003e0 / 160\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cc-platform-track\"\u003e\u003cdiv class=\"cc-platform-bar\" id=\"cc-plat-sms-bar\"\u003e\u003c/div\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cc-platform-row\"\u003e\n    \u003cdiv class=\"cc-platform-header\"\u003e\n      \u003cspan class=\"cc-platform-name\"\u003eMeta Description\u003c/span\u003e\n      \u003cspan class=\"cc-platform-count\" id=\"cc-plat-meta-label\"\u003e0 / 160\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cc-platform-track\"\u003e\u003cdiv class=\"cc-platform-bar\" id=\"cc-plat-meta-bar\"\u003e\u003c/div\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cc-platform-row\"\u003e\n    \u003cdiv class=\"cc-platform-header\"\u003e\n      \u003cspan class=\"cc-platform-name\"\u003eTitle Tag (SEO)\u003c/span\u003e\n      \u003cspan class=\"cc-platform-count\" id=\"cc-plat-title-label\"\u003e0 / 60\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cc-platform-track\"\u003e\u003cdiv class=\"cc-platform-bar\" id=\"cc-plat-title-bar\"\u003e\u003c/div\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cc-platform-row\"\u003e\n    \u003cdiv class=\"cc-platform-header\"\u003e\n      \u003cspan class=\"cc-platform-name\"\u003eInstagram Caption\u003c/span\u003e\n      \u003cspan class=\"cc-platform-count\" id=\"cc-plat-ig-label\"\u003e0 / 2200\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cc-platform-track\"\u003e\u003cdiv class=\"cc-platform-bar\" id=\"cc-plat-ig-bar\"\u003e\u003c/div\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Character density chart --\u003e\n\u003cdiv id=\"cc-density-section\"\u003e\n  \u003ch3\u003eTop 10 Character Frequency\u003c/h3\u003e\n  \u003cdiv id=\"cc-density-empty\"\u003eType text above to see character frequency chart\u003c/div\u003e\n  \u003ccanvas id=\"cc-density-canvas\" style=\"display:none;\" height=\"220\"\u003e\u003c/canvas\u003e\n\u003c/div\u003e\n\u003cdiv id=\"cc-toast\"\u003eCopied to clipboard!\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var ta = document.getElementById('cc-textarea');\n  var limitInput = document.getElementById('cc-limit-input');\n  var customProgressWrap = document.getElementById('cc-custom-progress-wrap');\n  var customProgressText = document.getElementById('cc-custom-progress-text');\n  var customProgressOver = document.getElementById('cc-custom-progress-over');\n  var customBar = document.getElementById('cc-custom-progress-bar');\n  var toast = document.getElementById('cc-toast');\n  var canvas = document.getElementById('cc-density-canvas');\n  var densityEmpty = document.getElementById('cc-density-empty');\n  var ctx = canvas.getContext('2d');\n  var customLimit = 0;\n\n  var platforms = [\n    { id: 'twitter', limit: 280 },\n    { id: 'sms',     limit: 160 },\n    { id: 'meta',    limit: 160 },\n    { id: 'title',   limit: 60  },\n    { id: 'ig',      limit: 2200 }\n  ];\n\n  function getUtf8Bytes(str) {\n    var bytes = 0;\n    for (var i = 0; i \u003c str.length; i++) {\n      var code = str.charCodeAt(i);\n      if (code \u003c 0x80) bytes += 1;\n      else if (code \u003c 0x800) bytes += 2;\n      else if (code \u003e= 0xD800 \u0026\u0026 code \u003c= 0xDBFF) { bytes += 4; i++; }\n      else bytes += 3;\n    }\n    return bytes;\n  }\n\n  function countWords(text) {\n    var t = text.trim();\n    if (!t) return 0;\n    return t.split(/\\s+/).filter(function(w) { return w.length \u003e 0; }).length;\n  }\n\n  function countSentences(text) {\n    var t = text.trim();\n    if (!t) return 0;\n    var matches = t.match(/[^.!?]*[.!?]+/g);\n    return matches ? matches.length : (t.length \u003e 0 ? 1 : 0);\n  }\n\n  function countParagraphs(text) {\n    var t = text.trim();\n    if (!t) return 0;\n    return t.split(/\\n\\s*\\n/).filter(function(p) { return p.trim().length \u003e 0; }).length;\n  }\n\n  function countLines(text) {\n    if (!text) return 0;\n    return text.split('\\n').length;\n  }\n\n  function countUnique(text) {\n    if (!text) return 0;\n    var seen = {};\n    for (var i = 0; i \u003c text.length; i++) {\n      seen[text[i]] = true;\n    }\n    return Object.keys(seen).length;\n  }\n\n  function getCharFrequency(text) {\n    var freq = {};\n    for (var i = 0; i \u003c text.length; i++) {\n      var c = text[i];\n      if (c === ' ' || c === '\\n' || c === '\\r' || c === '\\t') continue;\n      freq[c] = (freq[c] || 0) + 1;\n    }\n    var arr = [];\n    for (var ch in freq) {\n      arr.push({ char: ch, count: freq[ch] });\n    }\n    arr.sort(function(a, b) { return b.count - a.count; });\n    return arr.slice(0, 10);\n  }\n\n  function updatePlatformBar(id, count, limit) {\n    var labelEl = document.getElementById('cc-plat-' + id + '-label');\n    var barEl = document.getElementById('cc-plat-' + id + '-bar');\n    var pct = Math.min((count / limit) * 100, 100);\n    barEl.style.width = pct + '%';\n    labelEl.textContent = count.toLocaleString() + ' / ' + limit.toLocaleString();\n    if (count \u003e limit) {\n      barEl.className = 'cc-platform-bar over';\n      labelEl.className = 'cc-platform-count over';\n    } else if (pct \u003e= 80) {\n      barEl.className = 'cc-platform-bar warn';\n      labelEl.className = 'cc-platform-count';\n    } else {\n      barEl.className = 'cc-platform-bar';\n      labelEl.className = 'cc-platform-count';\n    }\n  }\n\n  function drawDensityChart(freq) {\n    if (freq.length === 0) {\n      canvas.style.display = 'none';\n      densityEmpty.style.display = 'block';\n      return;\n    }\n    densityEmpty.style.display = 'none';\n    canvas.style.display = 'block';\n\n    var dpr = window.devicePixelRatio || 1;\n    var w = canvas.parentElement.clientWidth || 600;\n    canvas.width = w * dpr;\n    canvas.height = 220 * dpr;\n    canvas.style.width = w + 'px';\n    canvas.style.height = '220px';\n    ctx.scale(dpr, dpr);\n\n    var padL = 44, padR = 16, padT = 20, padB = 48;\n    var chartW = w - padL - padR;\n    var chartH = 220 - padT - padB;\n    var barCount = freq.length;\n    var barW = Math.floor((chartW / barCount) * 0.65);\n    var gap = Math.floor(chartW / barCount);\n    var maxCount = freq[0].count;\n\n    ctx.clearRect(0, 0, w, 220);\n\n    // Grid lines\n    ctx.strokeStyle = '#ddd6fe';\n    ctx.lineWidth = 1;\n    for (var g = 0; g \u003c= 4; g++) {\n      var gy = padT + (chartH / 4) * g;\n      ctx.beginPath();\n      ctx.moveTo(padL, gy);\n      ctx.lineTo(padL + chartW, gy);\n      ctx.stroke();\n      var gridVal = Math.round(maxCount * (1 - g / 4));\n      ctx.fillStyle = '#8b5cf6';\n      ctx.font = '10px -apple-system,sans-serif';\n      ctx.textAlign = 'right';\n      ctx.fillText(gridVal, padL - 4, gy + 4);\n    }\n\n    // Bars\n    var colors = ['#7c3aed','#8b5cf6','#a78bfa','#6d28d9','#9333ea','#7c3aed','#8b5cf6','#a78bfa','#6d28d9','#9333ea'];\n    for (var i = 0; i \u003c freq.length; i++) {\n      var bh = maxCount \u003e 0 ? (freq[i].count / maxCount) * chartH : 0;\n      var bx = padL + i * gap + (gap - barW) / 2;\n      var by = padT + chartH - bh;\n\n      ctx.fillStyle = colors[i % colors.length];\n      ctx.beginPath();\n      ctx.roundRect ? ctx.roundRect(bx, by, barW, bh, [4, 4, 0, 0]) : ctx.rect(bx, by, barW, bh);\n      ctx.fill();\n\n      // Count label on bar\n      ctx.fillStyle = '#5b21b6';\n      ctx.font = 'bold 10px -apple-system,sans-serif';\n      ctx.textAlign = 'center';\n      ctx.fillText(freq[i].count, bx + barW / 2, by - 4);\n\n      // Char label below\n      ctx.fillStyle = '#374151';\n      ctx.font = 'bold 13px -apple-system,sans-serif';\n      ctx.fillText(freq[i].char, bx + barW / 2, padT + chartH + 18);\n\n      // Hex label\n      var code = freq[i].char.codePointAt(0).toString(16).toUpperCase();\n      ctx.fillStyle = '#9ca3af';\n      ctx.font = '9px -apple-system,sans-serif';\n      ctx.fillText('U+' + code.padStart(4, '0'), bx + barW / 2, padT + chartH + 32);\n    }\n  }\n\n  function updateStats() {\n    var text = ta.value;\n    var withSpaces = text.length;\n    var withoutSpaces = text.replace(/\\s/g, '').length;\n    var words = countWords(text);\n    var sentences = countSentences(text);\n    var paragraphs = countParagraphs(text);\n    var lines = countLines(text);\n    var unique = countUnique(text);\n    var bytes = getUtf8Bytes(text);\n\n    document.getElementById('cc-chars-with').textContent = withSpaces.toLocaleString();\n    document.getElementById('cc-chars-without').textContent = withoutSpaces.toLocaleString();\n    document.getElementById('cc-words').textContent = words.toLocaleString();\n    document.getElementById('cc-sentences').textContent = sentences.toLocaleString();\n    document.getElementById('cc-paragraphs').textContent = paragraphs.toLocaleString();\n    document.getElementById('cc-lines').textContent = lines.toLocaleString();\n    document.getElementById('cc-unique').textContent = unique.toLocaleString();\n    document.getElementById('cc-bytes').textContent = bytes.toLocaleString();\n\n    // Platform bars\n    platforms.forEach(function(p) {\n      updatePlatformBar(p.id, withSpaces, p.limit);\n    });\n\n    // Custom limit\n    if (customLimit \u003e 0) {\n      var pct = Math.min((withSpaces / customLimit) * 100, 100);\n      customBar.style.width = pct + '%';\n      customProgressText.textContent = withSpaces.toLocaleString() + ' / ' + customLimit.toLocaleString();\n      if (withSpaces \u003e customLimit) {\n        customBar.className = 'over';\n        customProgressOver.style.display = 'inline';\n      } else if (pct \u003e= 80) {\n        customBar.className = 'warn';\n        customProgressOver.style.display = 'none';\n      } else {\n        customBar.className = '';\n        customProgressOver.style.display = 'none';\n      }\n    }\n\n    // Character density chart\n    var freq = getCharFrequency(text);\n    drawDensityChart(freq);\n  }\n\n  function showToast() {\n    toast.classList.add('show');\n    setTimeout(function() { toast.classList.remove('show'); }, 2000);\n  }\n\n  ta.addEventListener('input', updateStats);\n\n  limitInput.addEventListener('input', function() {\n    var v = parseInt(limitInput.value, 10);\n    customLimit = (isNaN(v) || v \u003c= 0) ? 0 : v;\n    if (customLimit \u003e 0) {\n      customProgressWrap.style.display = 'block';\n    } else {\n      customProgressWrap.style.display = 'none';\n    }\n    updateStats();\n  });\n\n  document.getElementById('cc-copy-btn').addEventListener('click', function() {\n    var text = ta.value;\n    if (!text) return;\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(text).then(showToast);\n    } else {\n      ta.select();\n      document.execCommand('copy');\n      showToast();\n    }\n  });\n\n  document.getElementById('cc-clear-btn').addEventListener('click', function() {\n    ta.value = '';\n    updateStats();\n    ta.focus();\n  });\n\n  window.addEventListener('resize', function() {\n    var freq = getCharFrequency(ta.value);\n    drawDensityChart(freq);\n  });\n\n  updateStats();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-to-use-the-character-counter\"\u003eHow to Use the Character Counter\u003c/h2\u003e\n\u003cp\u003ePaste or type any text into the box above — all statistics update instantly. There is nothing to submit or install; everything runs in your browser.\u003c/p\u003e","title":"Character Counter - Text Length Analyzer"},{"content":" Checklist Maker Export as Text Clear Completed + Add List + Add No items yet. Add one above! Delete This List Kanban board -\u0026gt; Kanban Board Daily planner -\u0026gt; Daily Planner Related Articles How to Build a Notion Book Tracker: Complete Template Guide (2026) ","permalink":"https://productivity-works.com/tools/checklist-maker/","summary":"\u003cdiv id=\"cl-app\"\u003e\n\u003cstyle\u003e\n#cl-app *,#cl-app *::before,#cl-app *::after{box-sizing:border-box;margin:0;padding:0}\n#cl-app{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;max-width:720px;margin:0 auto;padding:16px;color:#1e293b}\n#cl-app h2{font-size:1.25rem;font-weight:700;margin-bottom:12px;color:#0f172a}\n#cl-app .cl-header{display:flex;align-items:center;gap:10px;margin-bottom:18px;flex-wrap:wrap}\n#cl-app .cl-header h1{font-size:1.5rem;font-weight:800;color:#0f172a;flex:1}\n#cl-app .cl-controls{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:14px}\n#cl-app input[type=\"text\"]{border:1.5px solid #cbd5e1;border-radius:7px;padding:8px 12px;font-size:14px;outline:none;transition:border-color .2s;background:#fff;color:#1e293b}\n#cl-app input[type=\"text\"]:focus{border-color:#3b82f6}\n#cl-app button{border:none;border-radius:7px;padding:8px 14px;font-size:13px;font-weight:600;cursor:pointer;transition:background .15s,transform .1s}\n#cl-app button:active{transform:scale(.97)}\n#cl-app .btn-primary{background:#3b82f6;color:#fff}\n#cl-app .btn-primary:hover{background:#2563eb}\n#cl-app .btn-secondary{background:#e2e8f0;color:#475569}\n#cl-app .btn-secondary:hover{background:#cbd5e1}\n#cl-app .btn-danger{background:#fee2e2;color:#dc2626}\n#cl-app .btn-danger:hover{background:#fecaca}\n#cl-app .btn-sm{padding:4px 10px;font-size:12px}\n#cl-app .cl-top-bar{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:14px;align-items:center}\n#cl-app .cl-search{flex:1;min-width:160px}\n#cl-app .cl-list-tabs{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:12px}\n#cl-app .cl-tab{padding:6px 14px;border-radius:20px;font-size:13px;font-weight:600;cursor:pointer;border:1.5px solid #cbd5e1;background:#fff;color:#475569;transition:all .15s}\n#cl-app .cl-tab.active{background:#3b82f6;color:#fff;border-color:#3b82f6}\n#cl-app .cl-tab-del{margin-left:4px;opacity:.6;font-size:11px}\n#cl-app .cl-tab-del:hover{opacity:1}\n#cl-app .cl-new-list{display:flex;gap:6px;margin-bottom:14px;flex-wrap:wrap}\n#cl-app .cl-progress-bar-wrap{background:#e2e8f0;border-radius:99px;height:10px;margin-bottom:14px;overflow:hidden}\n#cl-app .cl-progress-bar{background:linear-gradient(90deg,#3b82f6,#06b6d4);height:100%;border-radius:99px;transition:width .3s}\n#cl-app .cl-progress-label{font-size:12px;color:#64748b;margin-bottom:8px}\n#cl-app .cl-add-row{display:flex;gap:8px;margin-bottom:14px;flex-wrap:wrap}\n#cl-app .cl-add-row input{flex:1;min-width:160px}\n#cl-app .cl-cat-select{border:1.5px solid #cbd5e1;border-radius:7px;padding:8px 10px;font-size:13px;background:#fff;color:#1e293b;outline:none;cursor:pointer}\n#cl-app .cl-list{list-style:none;display:flex;flex-direction:column;gap:6px}\n#cl-app .cl-item{display:flex;align-items:center;gap:8px;background:#fff;border:1.5px solid #e2e8f0;border-radius:9px;padding:10px 12px;transition:box-shadow .15s,opacity .2s;cursor:grab;user-select:none}\n#cl-app .cl-item:active{cursor:grabbing}\n#cl-app .cl-item.dragging{opacity:.4;box-shadow:0 4px 16px rgba(59,130,246,.15)}\n#cl-app .cl-item.drag-over{border-color:#3b82f6;background:#eff6ff}\n#cl-app .cl-item.done .cl-item-text{text-decoration:line-through;color:#94a3b8}\n#cl-app .cl-item-drag{color:#94a3b8;font-size:16px;cursor:grab;flex-shrink:0}\n#cl-app .cl-item input[type=\"checkbox\"]{width:17px;height:17px;accent-color:#3b82f6;cursor:pointer;flex-shrink:0}\n#cl-app .cl-item-text{flex:1;font-size:14px;word-break:break-word}\n#cl-app .cl-item-edit{flex:1;font-size:14px;border:none;outline:none;background:transparent;color:#1e293b}\n#cl-app .cl-item-cat{font-size:11px;padding:2px 8px;border-radius:20px;background:#dbeafe;color:#1d4ed8;font-weight:600;flex-shrink:0}\n#cl-app .cl-item-actions{display:flex;gap:4px;flex-shrink:0}\n#cl-app .cl-empty{text-align:center;color:#94a3b8;font-size:14px;padding:32px 0}\n#cl-app .cl-footer-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:14px}\n#cl-app .cl-section-label{font-size:12px;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.05em;margin:14px 0 6px}\n#cl-app .cl-cat-group-label{font-size:12px;font-weight:700;color:#3b82f6;margin-top:10px;margin-bottom:4px;padding-left:4px}\n@media(max-width:480px){\n  #cl-app .cl-header h1{font-size:1.2rem}\n  #cl-app .cl-add-row{flex-direction:column}\n  #cl-app .cl-footer-actions{flex-direction:column}\n}\n\u003c/style\u003e\n\u003cdiv class=\"cl-header\"\u003e\n  \u003ch1\u003eChecklist Maker\u003c/h1\u003e\n\u003c/div\u003e\n\u003cdiv class=\"cl-top-bar\"\u003e\n  \u003cinput type=\"text\" id=\"cl-search\" class=\"cl-search\" placeholder=\"Search items...\" oninput=\"clRender()\"\u003e\n  \u003cbutton class=\"btn-secondary btn-sm\" onclick=\"clExport()\"\u003eExport as Text\u003c/button\u003e\n  \u003cbutton class=\"btn-danger btn-sm\" onclick=\"clClearCompleted()\"\u003eClear Completed\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"cl-new-list\" id=\"cl-new-list-row\"\u003e\n  \u003cinput type=\"text\" id=\"cl-new-list-name\" placeholder=\"New list name...\" style=\"width:200px\"\u003e\n  \u003cbutton class=\"btn-primary btn-sm\" onclick=\"clAddList()\"\u003e+ Add List\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv id=\"cl-tabs\" class=\"cl-list-tabs\"\u003e\u003c/div\u003e\n\u003cdiv class=\"cl-progress-label\" id=\"cl-progress-label\"\u003e\u003c/div\u003e\n\u003cdiv class=\"cl-progress-bar-wrap\"\u003e\u003cdiv class=\"cl-progress-bar\" id=\"cl-progress-bar\" style=\"width:0%\"\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cdiv class=\"cl-add-row\"\u003e\n  \u003cinput type=\"text\" id=\"cl-new-item\" placeholder=\"Add item...\" onkeydown=\"if(event.key==='Enter')clAddItem()\"\u003e\n  \u003cinput type=\"text\" id=\"cl-new-cat\" placeholder=\"Category (optional)\" style=\"width:160px\"\u003e\n  \u003cbutton class=\"btn-primary\" onclick=\"clAddItem()\"\u003e+ Add\u003c/button\u003e\n\u003c/div\u003e\n\u003cul class=\"cl-list\" id=\"cl-list\"\u003e\u003c/ul\u003e\n\u003cdiv id=\"cl-empty\" class=\"cl-empty\" style=\"display:none\"\u003eNo items yet. Add one above!\u003c/div\u003e\n\u003cdiv class=\"cl-footer-actions\"\u003e\n  \u003cbutton class=\"btn-danger btn-sm\" onclick=\"clDeleteList()\"\u003eDelete This List\u003c/button\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n  var STORE_KEY='cl_app_v1';\n  var state={lists:[],activeList:0};\n\n  function load(){\n    try{var s=localStorage.getItem(STORE_KEY);if(s)state=JSON.parse(s);}catch(e){}\n    if(!state.lists||!state.lists.length){\n      state={lists:[{name:'My Checklist',items:[]}],activeList:0};\n    }\n    if(state.activeList\u003e=state.lists.length)state.activeList=0;\n  }\n\n  function save(){\n    try{localStorage.setItem(STORE_KEY,JSON.stringify(state));}catch(e){}\n  }\n\n  function currentList(){return state.lists[state.activeList];}\n\n  function clAddList(){\n    var n=document.getElementById('cl-new-list-name');\n    var name=(n.value||'').trim();\n    if(!name)return;\n    state.lists.push({name:name,items:[]});\n    state.activeList=state.lists.length-1;\n    n.value='';\n    save();clRender();\n  }\n  window.clAddList=clAddList;\n\n  function clDeleteList(){\n    if(state.lists.length\u003c=1){alert('Cannot delete the last list.');return;}\n    if(!confirm('Delete this list and all its items?'))return;\n    state.lists.splice(state.activeList,1);\n    if(state.activeList\u003e=state.lists.length)state.activeList=state.lists.length-1;\n    save();clRender();\n  }\n  window.clDeleteList=clDeleteList;\n\n  function clSwitchList(i){\n    state.activeList=i;\n    save();clRender();\n  }\n  window.clSwitchList=clSwitchList;\n\n  function clAddItem(){\n    var n=document.getElementById('cl-new-item');\n    var c=document.getElementById('cl-new-cat');\n    var text=(n.value||'').trim();\n    if(!text)return;\n    currentList().items.push({id:Date.now()+'_'+Math.random(),text:text,done:false,cat:(c.value||'').trim()});\n    n.value='';c.value='';\n    save();clRender();\n  }\n  window.clAddItem=clAddItem;\n\n  function clToggle(id){\n    var item=currentList().items.find(function(x){return x.id===id;});\n    if(item)item.done=!item.done;\n    save();clRender();\n  }\n  window.clToggle=clToggle;\n\n  function clDelete(id){\n    var list=currentList();\n    list.items=list.items.filter(function(x){return x.id!==id;});\n    save();clRender();\n  }\n  window.clDelete=clDelete;\n\n  function clStartEdit(id){\n    var el=document.getElementById('cl-text-'+id);\n    var input=document.getElementById('cl-edit-'+id);\n    if(!el||!input)return;\n    el.style.display='none';\n    input.style.display='block';\n    input.focus();\n    input.select();\n  }\n  window.clStartEdit=clStartEdit;\n\n  function clFinishEdit(id){\n    var input=document.getElementById('cl-edit-'+id);\n    var el=document.getElementById('cl-text-'+id);\n    if(!input||!el)return;\n    var item=currentList().items.find(function(x){return x.id===id;});\n    if(item){item.text=input.value.trim()||item.text;}\n    el.style.display='';\n    input.style.display='none';\n    save();clRender();\n  }\n  window.clFinishEdit=clFinishEdit;\n\n  function clClearCompleted(){\n    var list=currentList();\n    list.items=list.items.filter(function(x){return !x.done;});\n    save();clRender();\n  }\n  window.clClearCompleted=clClearCompleted;\n\n  function clExport(){\n    var list=currentList();\n    var lines=[list.name,''];\n    var cats={};\n    list.items.forEach(function(item){\n      var c=item.cat||'';\n      if(!cats[c])cats[c]=[];\n      cats[c].push(item);\n    });\n    Object.keys(cats).forEach(function(cat){\n      if(cat)lines.push('['+cat+']');\n      cats[cat].forEach(function(item){\n        lines.push((item.done?'[x] ':'[ ] ')+item.text);\n      });\n      lines.push('');\n    });\n    var blob=new Blob([lines.join('\\n')],{type:'text/plain'});\n    var a=document.createElement('a');\n    a.href=URL.createObjectURL(blob);\n    a.download=(list.name||'checklist')+'.txt';\n    a.click();\n    URL.revokeObjectURL(a.href);\n  }\n  window.clExport=clExport;\n\n  // Drag and drop\n  var dragSrcId=null;\n\n  function clDragStart(id){dragSrcId=id;}\n  window.clDragStart=clDragStart;\n\n  function clDragOver(e,id){\n    e.preventDefault();\n    document.querySelectorAll('#cl-app .cl-item').forEach(function(el){el.classList.remove('drag-over');});\n    var el=document.getElementById('cl-li-'+id);\n    if(el)el.classList.add('drag-over');\n  }\n  window.clDragOver=clDragOver;\n\n  function clDrop(id){\n    if(!dragSrcId||dragSrcId===id)return;\n    var items=currentList().items;\n    var srcIdx=items.findIndex(function(x){return x.id===dragSrcId;});\n    var dstIdx=items.findIndex(function(x){return x.id===id;});\n    if(srcIdx\u003c0||dstIdx\u003c0)return;\n    var removed=items.splice(srcIdx,1)[0];\n    items.splice(dstIdx,0,removed);\n    dragSrcId=null;\n    save();clRender();\n  }\n  window.clDrop=clDrop;\n\n  function clDragEnd(){\n    dragSrcId=null;\n    document.querySelectorAll('#cl-app .cl-item').forEach(function(el){el.classList.remove('drag-over','dragging');});\n  }\n  window.clDragEnd=clDragEnd;\n\n  function clRender(){\n    var list=currentList();\n    var search=(document.getElementById('cl-search')||{}).value||'';\n    var sq=search.trim().toLowerCase();\n\n    // Tabs\n    var tabsEl=document.getElementById('cl-tabs');\n    tabsEl.innerHTML='';\n    state.lists.forEach(function(l,i){\n      var btn=document.createElement('button');\n      btn.className='cl-tab'+(i===state.activeList?' active':'');\n      btn.textContent=l.name;\n      btn.onclick=function(){clSwitchList(i);};\n      tabsEl.appendChild(btn);\n    });\n\n    // Progress\n    var total=list.items.length;\n    var done=list.items.filter(function(x){return x.done;}).length;\n    var pct=total?Math.round(done/total*100):0;\n    document.getElementById('cl-progress-label').textContent=total?done+' / '+total+' complete ('+pct+'%)':'No items';\n    document.getElementById('cl-progress-bar').style.width=pct+'%';\n\n    // Filter\n    var filtered=list.items.filter(function(item){\n      return !sq||(item.text.toLowerCase().includes(sq)||(item.cat||'').toLowerCase().includes(sq));\n    });\n\n    // Group by category\n    var cats={};\n    var catOrder=[];\n    filtered.forEach(function(item){\n      var c=item.cat||'';\n      if(!cats[c]){cats[c]=[];catOrder.push(c);}\n      cats[c].push(item);\n    });\n\n    var ul=document.getElementById('cl-list');\n    var emptyEl=document.getElementById('cl-empty');\n    ul.innerHTML='';\n\n    if(!filtered.length){\n      emptyEl.style.display='';\n    } else {\n      emptyEl.style.display='none';\n      catOrder.forEach(function(cat){\n        if(cat){\n          var lbl=document.createElement('li');\n          lbl.className='cl-cat-group-label';\n          lbl.textContent=cat;\n          ul.appendChild(lbl);\n        }\n        cats[cat].forEach(function(item){\n          var li=document.createElement('li');\n          li.className='cl-item'+(item.done?' done':'');\n          li.id='cl-li-'+item.id;\n          li.draggable=true;\n          li.ondragstart=function(){li.classList.add('dragging');clDragStart(item.id);};\n          li.ondragover=function(e){clDragOver(e,item.id);};\n          li.ondrop=function(){clDrop(item.id);};\n          li.ondragend=clDragEnd;\n\n          li.innerHTML=\n            '\u003cspan class=\"cl-item-drag\" title=\"Drag to reorder\"\u003e\u0026#8942;\u0026#8942;\u003c/span\u003e'+\n            '\u003cinput type=\"checkbox\"'+(item.done?' checked':'')+' onchange=\"clToggle(\\''+item.id+'\\')\"\u003e'+\n            '\u003cspan class=\"cl-item-text\" id=\"cl-text-'+item.id+'\" ondblclick=\"clStartEdit(\\''+item.id+'\\')\"\u003e'+escHtml(item.text)+'\u003c/span\u003e'+\n            '\u003cinput type=\"text\" class=\"cl-item-edit\" id=\"cl-edit-'+item.id+'\" value=\"'+escHtml(item.text)+'\" style=\"display:none\" onblur=\"clFinishEdit(\\''+item.id+'\\')\" onkeydown=\"if(event.key===\\'Enter\\')clFinishEdit(\\''+item.id+'\\')\"\u003e'+\n            (item.cat?'\u003cspan class=\"cl-item-cat\"\u003e'+escHtml(item.cat)+'\u003c/span\u003e':'')+\n            '\u003cspan class=\"cl-item-actions\"\u003e'+\n              '\u003cbutton class=\"btn-secondary btn-sm\" onclick=\"clStartEdit(\\''+item.id+'\\')\"\u003eEdit\u003c/button\u003e'+\n              '\u003cbutton class=\"btn-danger btn-sm\" onclick=\"clDelete(\\''+item.id+'\\')\"\u003eDel\u003c/button\u003e'+\n            '\u003c/span\u003e';\n          ul.appendChild(li);\n        });\n      });\n    }\n  }\n\n  function escHtml(s){\n    return String(s).replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;').replace(/\"/g,'\u0026quot;').replace(/'/g,'\u0026#39;');\n  }\n\n  load();\n  clRender();\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003eKanban board -\u0026gt; \u003ca href=\"https://productivity-works.com/tools/kanban-board/\"\u003eKanban Board\u003c/a\u003e\n\u003c/p\u003e","title":"Checklist Maker"},{"content":" Chmod Permission Calculator Quick Presets 644 — Files 755 — Directories 600 — Private 777 — All Access 444 — Read-Only 700 — Owner Only 664 — Group Write 640 — Group Read Octal Value Type any octal value (000–777)\nor use the grid below Permission Grid Entity Read (r) Write (w) Execute (x) Octal Owner (u) ✓ ✓ ✓ 7 Group (g) ✓ ✓ ✓ 5 Others (o) ✓ ✓ ✓ 5 Special Permissions ✓ setuid — Run as file owner ✓ setgid — Run as group owner ✓ Sticky bit — Restrict deletion Result Octal (Numeric) 755 Symbolic Notation rwxr-xr-x rwxr-xr-x chmod 755 filename Copy What do permissions mean? Read (r) = 4 File: view contents.\nDirectory: list contents (ls). Write (w) = 2 File: modify or delete.\nDirectory: create/delete files inside. Execute (x) = 1 File: run as a program.\nDirectory: enter (cd) the directory. Owner (u) The user who owns the file. Usually the creator. Group (g) Users in the file's associated group. Others (o) Everyone else on the system. setuid (4000) Executable runs with the owner's privileges. Appears as 's' in owner execute bit. setgid (2000) Executable runs with group's privileges. New files in dir inherit group. Sticky bit (1000) Only the file owner can delete files in a directory. Used on /tmp. Related Tools Build cron expressions → Cron Expression Generator Generate .htaccess → .htaccess Generator Format SQL → SQL Formatter ","permalink":"https://productivity-works.com/tools/chmod-calculator/","summary":"\u003cstyle\u003e\n#chmod-app *,\n#chmod-app *::before,\n#chmod-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\n#chmod-app {\n  font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n  background: #0f172a;\n  color: #e2e8f0;\n  border-radius: 12px;\n  padding: 28px;\n  max-width: 860px;\n  margin: 0 auto;\n}\n\n#chmod-app h2 {\n  font-family: ui-sans-serif, system-ui, sans-serif;\n  font-size: 1.4rem;\n  font-weight: 700;\n  color: #84cc16;\n  margin-bottom: 20px;\n  letter-spacing: -0.02em;\n}\n\n#chmod-app .ch-section-label {\n  font-family: ui-sans-serif, system-ui, sans-serif;\n  font-size: 0.7rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.1em;\n  color: #64748b;\n  margin-bottom: 10px;\n}\n\n/* Presets */\n#chmod-app .ch-presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 24px;\n}\n\n#chmod-app .ch-preset-btn {\n  background: #1e293b;\n  border: 1px solid #334155;\n  color: #94a3b8;\n  border-radius: 6px;\n  padding: 6px 14px;\n  font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace;\n  font-size: 0.85rem;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n\n#chmod-app .ch-preset-btn:hover {\n  background: #334155;\n  color: #e2e8f0;\n  border-color: #84cc16;\n}\n\n#chmod-app .ch-preset-btn span {\n  font-weight: 700;\n  color: #84cc16;\n}\n\n/* Numeric input */\n#chmod-app .ch-numeric-row {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  margin-bottom: 24px;\n}\n\n#chmod-app .ch-numeric-input {\n  background: #1e293b;\n  border: 1px solid #334155;\n  color: #84cc16;\n  border-radius: 8px;\n  padding: 10px 16px;\n  font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace;\n  font-size: 1.8rem;\n  font-weight: 700;\n  width: 120px;\n  text-align: center;\n  outline: none;\n  transition: border-color 0.15s;\n  letter-spacing: 0.1em;\n}\n\n#chmod-app .ch-numeric-input:focus {\n  border-color: #84cc16;\n}\n\n#chmod-app .ch-numeric-hint {\n  font-family: ui-sans-serif, system-ui, sans-serif;\n  font-size: 0.78rem;\n  color: #64748b;\n}\n\n/* Permission grid */\n#chmod-app .ch-grid-wrapper {\n  overflow-x: auto;\n  margin-bottom: 24px;\n}\n\n#chmod-app .ch-grid {\n  width: 100%;\n  border-collapse: collapse;\n  min-width: 380px;\n}\n\n#chmod-app .ch-grid th {\n  font-family: ui-sans-serif, system-ui, sans-serif;\n  font-size: 0.72rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #64748b;\n  padding: 8px 12px;\n  text-align: center;\n  border-bottom: 1px solid #1e293b;\n}\n\n#chmod-app .ch-grid th.ch-th-owner {\n  text-align: left;\n  color: #94a3b8;\n}\n\n#chmod-app .ch-grid td {\n  padding: 10px 12px;\n  text-align: center;\n  border-bottom: 1px solid #1e293b;\n}\n\n#chmod-app .ch-grid td.ch-td-label {\n  text-align: left;\n  font-family: ui-sans-serif, system-ui, sans-serif;\n  font-size: 0.88rem;\n  font-weight: 600;\n  color: #e2e8f0;\n  padding-left: 4px;\n}\n\n#chmod-app .ch-grid tr:last-child td {\n  border-bottom: none;\n}\n\n#chmod-app .ch-grid tr:hover td {\n  background: #1e293b22;\n}\n\n/* Checkbox custom */\n#chmod-app .ch-cb-wrap {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  cursor: pointer;\n}\n\n#chmod-app .ch-cb-wrap input[type=\"checkbox\"] {\n  display: none;\n}\n\n#chmod-app .ch-cb-box {\n  width: 28px;\n  height: 28px;\n  border-radius: 6px;\n  border: 2px solid #334155;\n  background: #1e293b;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  transition: all 0.15s;\n  font-size: 0.9rem;\n  color: transparent;\n  user-select: none;\n}\n\n#chmod-app .ch-cb-wrap input:checked + .ch-cb-box {\n  background: #84cc16;\n  border-color: #84cc16;\n  color: #0f172a;\n}\n\n#chmod-app .ch-cb-wrap:hover .ch-cb-box {\n  border-color: #84cc16;\n}\n\n/* Row color accents */\n#chmod-app .ch-row-owner td.ch-td-label { color: #38bdf8; }\n#chmod-app .ch-row-group  td.ch-td-label { color: #a78bfa; }\n#chmod-app .ch-row-others td.ch-td-label { color: #f97316; }\n\n/* Special permissions */\n#chmod-app .ch-special-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 12px;\n  margin-bottom: 24px;\n}\n\n#chmod-app .ch-special-item {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  cursor: pointer;\n}\n\n#chmod-app .ch-special-item input[type=\"checkbox\"] {\n  display: none;\n}\n\n#chmod-app .ch-special-box {\n  width: 26px;\n  height: 26px;\n  border-radius: 5px;\n  border: 2px solid #334155;\n  background: #1e293b;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  transition: all 0.15s;\n  font-size: 0.8rem;\n  color: transparent;\n}\n\n#chmod-app .ch-special-item input:checked + .ch-special-box {\n  background: #f59e0b;\n  border-color: #f59e0b;\n  color: #0f172a;\n}\n\n#chmod-app .ch-special-item:hover .ch-special-box {\n  border-color: #f59e0b;\n}\n\n#chmod-app .ch-special-name {\n  font-family: ui-sans-serif, system-ui, sans-serif;\n  font-size: 0.83rem;\n  color: #94a3b8;\n}\n\n/* Output cards */\n#chmod-app .ch-outputs {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));\n  gap: 12px;\n  margin-bottom: 24px;\n}\n\n#chmod-app .ch-output-card {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 10px;\n  padding: 14px 16px;\n}\n\n#chmod-app .ch-output-card .ch-output-label {\n  font-family: ui-sans-serif, system-ui, sans-serif;\n  font-size: 0.68rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.1em;\n  color: #64748b;\n  margin-bottom: 6px;\n}\n\n#chmod-app .ch-output-card .ch-output-value {\n  font-size: 1.1rem;\n  font-weight: 700;\n  color: #84cc16;\n  word-break: break-all;\n}\n\n/* Copy button */\n#chmod-app .ch-copy-row {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 10px;\n  padding: 12px 16px;\n  margin-bottom: 28px;\n}\n\n#chmod-app .ch-copy-cmd {\n  flex: 1;\n  font-size: 1rem;\n  color: #e2e8f0;\n  font-weight: 600;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n#chmod-app .ch-copy-btn {\n  background: #84cc16;\n  border: none;\n  border-radius: 6px;\n  color: #0f172a;\n  font-family: ui-sans-serif, system-ui, sans-serif;\n  font-size: 0.8rem;\n  font-weight: 700;\n  padding: 7px 16px;\n  cursor: pointer;\n  transition: background 0.15s;\n  white-space: nowrap;\n  flex-shrink: 0;\n}\n\n#chmod-app .ch-copy-btn:hover {\n  background: #a3e635;\n}\n\n#chmod-app .ch-copy-btn.copied {\n  background: #22c55e;\n}\n\n/* Explanation */\n#chmod-app .ch-explain {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 10px;\n  padding: 20px;\n  margin-bottom: 20px;\n}\n\n#chmod-app .ch-explain h3 {\n  font-family: ui-sans-serif, system-ui, sans-serif;\n  font-size: 0.95rem;\n  font-weight: 700;\n  color: #e2e8f0;\n  margin-bottom: 14px;\n}\n\n#chmod-app .ch-explain-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n  gap: 10px;\n}\n\n#chmod-app .ch-explain-item {\n  display: flex;\n  flex-direction: column;\n  gap: 2px;\n}\n\n#chmod-app .ch-explain-item .ch-ei-title {\n  font-family: ui-sans-serif, system-ui, sans-serif;\n  font-size: 0.78rem;\n  font-weight: 700;\n  color: #84cc16;\n}\n\n#chmod-app .ch-explain-item .ch-ei-desc {\n  font-family: ui-sans-serif, system-ui, sans-serif;\n  font-size: 0.78rem;\n  color: #94a3b8;\n  line-height: 1.45;\n}\n\n/* Symbolic breakdown */\n#chmod-app .ch-sym-breakdown {\n  display: flex;\n  gap: 0;\n  font-size: 1.3rem;\n  font-weight: 700;\n  letter-spacing: 0.05em;\n  margin-bottom: 6px;\n  flex-wrap: wrap;\n}\n\n#chmod-app .ch-sym-part {\n  padding: 4px 8px;\n  border-radius: 4px;\n}\n\n#chmod-app .ch-sym-owner { color: #38bdf8; background: #0c2340; }\n#chmod-app .ch-sym-group { color: #a78bfa; background: #1a0c40; }\n#chmod-app .ch-sym-others { color: #f97316; background: #3d1500; }\n\n/* Responsive */\n@media (max-width: 600px) {\n  #chmod-app {\n    padding: 18px 14px;\n  }\n  #chmod-app .ch-numeric-input {\n    font-size: 1.4rem;\n    width: 100px;\n  }\n  #chmod-app .ch-sym-breakdown {\n    font-size: 1rem;\n  }\n}\n\u003c/style\u003e\n\u003cdiv id=\"chmod-app\"\u003e\n\u003ch2\u003eChmod Permission Calculator\u003c/h2\u003e\n\u003cdiv class=\"ch-section-label\"\u003eQuick Presets\u003c/div\u003e\n\u003cdiv class=\"ch-presets\"\u003e\n  \u003cbutton class=\"ch-preset-btn\" onclick=\"chApplyPreset(644)\"\u003e\u003cspan\u003e644\u003c/span\u003e — Files\u003c/button\u003e\n  \u003cbutton class=\"ch-preset-btn\" onclick=\"chApplyPreset(755)\"\u003e\u003cspan\u003e755\u003c/span\u003e — Directories\u003c/button\u003e\n  \u003cbutton class=\"ch-preset-btn\" onclick=\"chApplyPreset(600)\"\u003e\u003cspan\u003e600\u003c/span\u003e — Private\u003c/button\u003e\n  \u003cbutton class=\"ch-preset-btn\" onclick=\"chApplyPreset(777)\"\u003e\u003cspan\u003e777\u003c/span\u003e — All Access\u003c/button\u003e\n  \u003cbutton class=\"ch-preset-btn\" onclick=\"chApplyPreset(444)\"\u003e\u003cspan\u003e444\u003c/span\u003e — Read-Only\u003c/button\u003e\n  \u003cbutton class=\"ch-preset-btn\" onclick=\"chApplyPreset(700)\"\u003e\u003cspan\u003e700\u003c/span\u003e — Owner Only\u003c/button\u003e\n  \u003cbutton class=\"ch-preset-btn\" onclick=\"chApplyPreset(664)\"\u003e\u003cspan\u003e664\u003c/span\u003e — Group Write\u003c/button\u003e\n  \u003cbutton class=\"ch-preset-btn\" onclick=\"chApplyPreset(640)\"\u003e\u003cspan\u003e640\u003c/span\u003e — Group Read\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ch-section-label\"\u003eOctal Value\u003c/div\u003e\n\u003cdiv class=\"ch-numeric-row\"\u003e\n  \u003cinput class=\"ch-numeric-input\" id=\"ch-octal-input\" type=\"text\" maxlength=\"4\" value=\"755\"\n    oninput=\"chFromOctal(this.value)\" placeholder=\"755\" /\u003e\n  \u003cspan class=\"ch-numeric-hint\"\u003eType any octal value (000–777)\u003cbr\u003eor use the grid below\u003c/span\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ch-section-label\"\u003ePermission Grid\u003c/div\u003e\n\u003cdiv class=\"ch-grid-wrapper\"\u003e\n  \u003ctable class=\"ch-grid\"\u003e\n    \u003cthead\u003e\n      \u003ctr\u003e\n        \u003cth class=\"ch-th-owner\"\u003eEntity\u003c/th\u003e\n        \u003cth\u003eRead (r)\u003c/th\u003e\n        \u003cth\u003eWrite (w)\u003c/th\u003e\n        \u003cth\u003eExecute (x)\u003c/th\u003e\n        \u003cth\u003eOctal\u003c/th\u003e\n      \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody\u003e\n      \u003ctr class=\"ch-row-owner\"\u003e\n        \u003ctd class=\"ch-td-label\"\u003eOwner (u)\u003c/td\u003e\n        \u003ctd\u003e\u003clabel class=\"ch-cb-wrap\"\u003e\u003cinput type=\"checkbox\" id=\"ch-ur\" onchange=\"chUpdate()\"\u003e\u003cspan class=\"ch-cb-box\"\u003e✓\u003c/span\u003e\u003c/label\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003clabel class=\"ch-cb-wrap\"\u003e\u003cinput type=\"checkbox\" id=\"ch-uw\" onchange=\"chUpdate()\"\u003e\u003cspan class=\"ch-cb-box\"\u003e✓\u003c/span\u003e\u003c/label\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003clabel class=\"ch-cb-wrap\"\u003e\u003cinput type=\"checkbox\" id=\"ch-ux\" onchange=\"chUpdate()\"\u003e\u003cspan class=\"ch-cb-box\"\u003e✓\u003c/span\u003e\u003c/label\u003e\u003c/td\u003e\n        \u003ctd id=\"ch-uval\" style=\"color:#38bdf8;font-weight:700;\"\u003e7\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr class=\"ch-row-group\"\u003e\n        \u003ctd class=\"ch-td-label\"\u003eGroup (g)\u003c/td\u003e\n        \u003ctd\u003e\u003clabel class=\"ch-cb-wrap\"\u003e\u003cinput type=\"checkbox\" id=\"ch-gr\" onchange=\"chUpdate()\"\u003e\u003cspan class=\"ch-cb-box\"\u003e✓\u003c/span\u003e\u003c/label\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003clabel class=\"ch-cb-wrap\"\u003e\u003cinput type=\"checkbox\" id=\"ch-gw\" onchange=\"chUpdate()\"\u003e\u003cspan class=\"ch-cb-box\"\u003e✓\u003c/span\u003e\u003c/label\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003clabel class=\"ch-cb-wrap\"\u003e\u003cinput type=\"checkbox\" id=\"ch-gx\" onchange=\"chUpdate()\"\u003e\u003cspan class=\"ch-cb-box\"\u003e✓\u003c/span\u003e\u003c/label\u003e\u003c/td\u003e\n        \u003ctd id=\"ch-gval\" style=\"color:#a78bfa;font-weight:700;\"\u003e5\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr class=\"ch-row-others\"\u003e\n        \u003ctd class=\"ch-td-label\"\u003eOthers (o)\u003c/td\u003e\n        \u003ctd\u003e\u003clabel class=\"ch-cb-wrap\"\u003e\u003cinput type=\"checkbox\" id=\"ch-or\" onchange=\"chUpdate()\"\u003e\u003cspan class=\"ch-cb-box\"\u003e✓\u003c/span\u003e\u003c/label\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003clabel class=\"ch-cb-wrap\"\u003e\u003cinput type=\"checkbox\" id=\"ch-ow\" onchange=\"chUpdate()\"\u003e\u003cspan class=\"ch-cb-box\"\u003e✓\u003c/span\u003e\u003c/label\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003clabel class=\"ch-cb-wrap\"\u003e\u003cinput type=\"checkbox\" id=\"ch-ox\" onchange=\"chUpdate()\"\u003e\u003cspan class=\"ch-cb-box\"\u003e✓\u003c/span\u003e\u003c/label\u003e\u003c/td\u003e\n        \u003ctd id=\"ch-oval\" style=\"color:#f97316;font-weight:700;\"\u003e5\u003c/td\u003e\n      \u003c/tr\u003e\n    \u003c/tbody\u003e\n  \u003c/table\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ch-section-label\"\u003eSpecial Permissions\u003c/div\u003e\n\u003cdiv class=\"ch-special-row\"\u003e\n  \u003clabel class=\"ch-special-item\"\u003e\n    \u003cinput type=\"checkbox\" id=\"ch-setuid\" onchange=\"chUpdate()\"\u003e\n    \u003cspan class=\"ch-special-box\"\u003e✓\u003c/span\u003e\n    \u003cspan class=\"ch-special-name\"\u003esetuid — Run as file owner\u003c/span\u003e\n  \u003c/label\u003e\n  \u003clabel class=\"ch-special-item\"\u003e\n    \u003cinput type=\"checkbox\" id=\"ch-setgid\" onchange=\"chUpdate()\"\u003e\n    \u003cspan class=\"ch-special-box\"\u003e✓\u003c/span\u003e\n    \u003cspan class=\"ch-special-name\"\u003esetgid — Run as group owner\u003c/span\u003e\n  \u003c/label\u003e\n  \u003clabel class=\"ch-special-item\"\u003e\n    \u003cinput type=\"checkbox\" id=\"ch-sticky\" onchange=\"chUpdate()\"\u003e\n    \u003cspan class=\"ch-special-box\"\u003e✓\u003c/span\u003e\n    \u003cspan class=\"ch-special-name\"\u003eSticky bit — Restrict deletion\u003c/span\u003e\n  \u003c/label\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ch-section-label\"\u003eResult\u003c/div\u003e\n\u003cdiv class=\"ch-outputs\"\u003e\n  \u003cdiv class=\"ch-output-card\"\u003e\n    \u003cdiv class=\"ch-output-label\"\u003eOctal (Numeric)\u003c/div\u003e\n    \u003cdiv class=\"ch-output-value\" id=\"ch-out-octal\"\u003e755\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ch-output-card\"\u003e\n    \u003cdiv class=\"ch-output-label\"\u003eSymbolic Notation\u003c/div\u003e\n    \u003cdiv class=\"ch-sym-breakdown\" id=\"ch-out-sym-parts\"\u003e\n      \u003cspan class=\"ch-sym-part ch-sym-owner\"\u003erwx\u003c/span\u003e\u003cspan class=\"ch-sym-part ch-sym-group\"\u003er-x\u003c/span\u003e\u003cspan class=\"ch-sym-part ch-sym-others\"\u003er-x\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ch-output-value\" style=\"font-size:0.85rem;color:#64748b;\" id=\"ch-out-sym-full\"\u003erwxr-xr-x\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ch-copy-row\"\u003e\n  \u003cspan class=\"ch-copy-cmd\" id=\"ch-cmd-text\"\u003echmod 755 filename\u003c/span\u003e\n  \u003cbutton class=\"ch-copy-btn\" id=\"ch-copy-btn\" onclick=\"chCopyCmd()\"\u003eCopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ch-explain\"\u003e\n  \u003ch3\u003eWhat do permissions mean?\u003c/h3\u003e\n  \u003cdiv class=\"ch-explain-grid\"\u003e\n    \u003cdiv class=\"ch-explain-item\"\u003e\n      \u003cspan class=\"ch-ei-title\"\u003eRead (r) = 4\u003c/span\u003e\n      \u003cspan class=\"ch-ei-desc\"\u003eFile: view contents.\u003cbr\u003eDirectory: list contents (ls).\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ch-explain-item\"\u003e\n      \u003cspan class=\"ch-ei-title\"\u003eWrite (w) = 2\u003c/span\u003e\n      \u003cspan class=\"ch-ei-desc\"\u003eFile: modify or delete.\u003cbr\u003eDirectory: create/delete files inside.\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ch-explain-item\"\u003e\n      \u003cspan class=\"ch-ei-title\"\u003eExecute (x) = 1\u003c/span\u003e\n      \u003cspan class=\"ch-ei-desc\"\u003eFile: run as a program.\u003cbr\u003eDirectory: enter (cd) the directory.\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ch-explain-item\"\u003e\n      \u003cspan class=\"ch-ei-title\"\u003eOwner (u)\u003c/span\u003e\n      \u003cspan class=\"ch-ei-desc\"\u003eThe user who owns the file. Usually the creator.\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ch-explain-item\"\u003e\n      \u003cspan class=\"ch-ei-title\"\u003eGroup (g)\u003c/span\u003e\n      \u003cspan class=\"ch-ei-desc\"\u003eUsers in the file's associated group.\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ch-explain-item\"\u003e\n      \u003cspan class=\"ch-ei-title\"\u003eOthers (o)\u003c/span\u003e\n      \u003cspan class=\"ch-ei-desc\"\u003eEveryone else on the system.\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ch-explain-item\"\u003e\n      \u003cspan class=\"ch-ei-title\"\u003esetuid (4000)\u003c/span\u003e\n      \u003cspan class=\"ch-ei-desc\"\u003eExecutable runs with the owner's privileges. Appears as 's' in owner execute bit.\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ch-explain-item\"\u003e\n      \u003cspan class=\"ch-ei-title\"\u003esetgid (2000)\u003c/span\u003e\n      \u003cspan class=\"ch-ei-desc\"\u003eExecutable runs with group's privileges. New files in dir inherit group.\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ch-explain-item\"\u003e\n      \u003cspan class=\"ch-ei-title\"\u003eSticky bit (1000)\u003c/span\u003e\n      \u003cspan class=\"ch-ei-desc\"\u003eOnly the file owner can delete files in a directory. Used on /tmp.\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  // Initialize to 755\n  chApplyPreset(755);\n\n  function chGetBits() {\n    return {\n      ur: document.getElementById('ch-ur').checked,\n      uw: document.getElementById('ch-uw').checked,\n      ux: document.getElementById('ch-ux').checked,\n      gr: document.getElementById('ch-gr').checked,\n      gw: document.getElementById('ch-gw').checked,\n      gx: document.getElementById('ch-gx').checked,\n      or: document.getElementById('ch-or').checked,\n      ow: document.getElementById('ch-ow').checked,\n      ox: document.getElementById('ch-ox').checked,\n      setuid: document.getElementById('ch-setuid').checked,\n      setgid: document.getElementById('ch-setgid').checked,\n      sticky: document.getElementById('ch-sticky').checked\n    };\n  }\n\n  function chBitsToOctal(b) {\n    var u = (b.ur ? 4 : 0) + (b.uw ? 2 : 0) + (b.ux ? 1 : 0);\n    var g = (b.gr ? 4 : 0) + (b.gw ? 2 : 0) + (b.gx ? 1 : 0);\n    var o = (b.or ? 4 : 0) + (b.ow ? 2 : 0) + (b.ox ? 1 : 0);\n    var s = (b.setuid ? 4 : 0) + (b.setgid ? 2 : 0) + (b.sticky ? 1 : 0);\n    return { u: u, g: g, o: o, s: s };\n  }\n\n  function chBitsToSymbol(b) {\n    function bit(r, w, x, special, sChar) {\n      return (r ? 'r' : '-') + (w ? 'w' : '-') + (special ? (x ? sChar : sChar.toUpperCase()) : (x ? 'x' : '-'));\n    }\n    var uSym = bit(b.ur, b.uw, b.ux, b.setuid, 's');\n    var gSym = bit(b.gr, b.gw, b.gx, b.setgid, 's');\n    var oSym = bit(b.or, b.ow, b.ox, b.sticky, 't');\n    return { u: uSym, g: gSym, o: oSym };\n  }\n\n  window.chUpdate = function() {\n    var b = chGetBits();\n    var oct = chBitsToOctal(b);\n    var sym = chBitsToSymbol(b);\n\n    document.getElementById('ch-uval').textContent = oct.u;\n    document.getElementById('ch-gval').textContent = oct.g;\n    document.getElementById('ch-oval').textContent = oct.o;\n\n    var octalStr = (oct.s \u003e 0 ? oct.s : '') + oct.u + '' + oct.g + '' + oct.o;\n    // Always show 3 or 4 digits\n    if (oct.s \u003e 0) {\n      octalStr = oct.s + '' + oct.u + '' + oct.g + '' + oct.o;\n    } else {\n      octalStr = oct.u + '' + oct.g + '' + oct.o;\n    }\n\n    document.getElementById('ch-out-octal').textContent = octalStr;\n    document.getElementById('ch-out-sym-parts').innerHTML =\n      '\u003cspan class=\"ch-sym-part ch-sym-owner\"\u003e' + sym.u + '\u003c/span\u003e' +\n      '\u003cspan class=\"ch-sym-part ch-sym-group\"\u003e' + sym.g + '\u003c/span\u003e' +\n      '\u003cspan class=\"ch-sym-part ch-sym-others\"\u003e' + sym.o + '\u003c/span\u003e';\n    document.getElementById('ch-out-sym-full').textContent = sym.u + sym.g + sym.o;\n    document.getElementById('ch-cmd-text').textContent = 'chmod ' + octalStr + ' filename';\n    document.getElementById('ch-octal-input').value = octalStr;\n\n    // Reset copy button\n    var btn = document.getElementById('ch-copy-btn');\n    btn.textContent = 'Copy';\n    btn.classList.remove('copied');\n  };\n\n  window.chFromOctal = function(val) {\n    val = val.replace(/[^0-7]/g, '');\n    if (val.length \u003c 3 || val.length \u003e 4) return;\n\n    var digits;\n    var special = 0;\n    if (val.length === 4) {\n      special = parseInt(val[0], 8);\n      digits = [parseInt(val[1], 8), parseInt(val[2], 8), parseInt(val[3], 8)];\n    } else {\n      digits = [parseInt(val[0], 8), parseInt(val[1], 8), parseInt(val[2], 8)];\n    }\n\n    function setRow(r, w, x, prefix) {\n      document.getElementById('ch-' + prefix + 'r').checked = !!(r \u0026 4);\n      document.getElementById('ch-' + prefix + 'w').checked = !!(r \u0026 2);\n      document.getElementById('ch-' + prefix + 'x').checked = !!(r \u0026 1);\n    }\n\n    setRow(digits[0], digits[0], digits[0], 'u');\n    setRow(digits[1], digits[1], digits[1], 'g');\n    setRow(digits[2], digits[2], digits[2], 'o');\n    document.getElementById('ch-setuid').checked = !!(special \u0026 4);\n    document.getElementById('ch-setgid').checked = !!(special \u0026 2);\n    document.getElementById('ch-sticky').checked = !!(special \u0026 1);\n\n    chUpdate();\n  };\n\n  window.chApplyPreset = function(val) {\n    chFromOctal(String(val));\n    document.getElementById('ch-octal-input').value = String(val);\n  };\n\n  window.chCopyCmd = function() {\n    var cmd = document.getElementById('ch-cmd-text').textContent;\n    navigator.clipboard.writeText(cmd).then(function() {\n      var btn = document.getElementById('ch-copy-btn');\n      btn.textContent = 'Copied!';\n      btn.classList.add('copied');\n      setTimeout(function() {\n        btn.textContent = 'Copy';\n        btn.classList.remove('copied');\n      }, 2000);\n    }).catch(function() {\n      var ta = document.createElement('textarea');\n      ta.value = cmd;\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      var btn = document.getElementById('ch-copy-btn');\n      btn.textContent = 'Copied!';\n      btn.classList.add('copied');\n      setTimeout(function() {\n        btn.textContent = 'Copy';\n        btn.classList.remove('copied');\n      }, 2000);\n    });\n  };\n\n  // Initialize\n  chApplyPreset(755);\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch3 id=\"related-tools\"\u003eRelated Tools\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003eBuild cron expressions → \u003ca href=\"https://productivity-works.com/tools/cron-generator/\"\u003eCron Expression Generator\u003c/a\u003e\n\u003c/p\u003e","title":"Chmod Calculator — Unix Permission Tool"},{"content":"Generate properly formatted citations in APA, MLA, Chicago, and Harvard styles — for books, journal articles, websites, and conference papers. Paste them directly into your bibliography or export as text.\nGenerator Bibliography Source Type Book Journal Article Website Conference Paper Citation Style APA 7th MLA 9th Chicago 17th Harvard \u0026lt;!-- Authors --\u0026gt; \u0026lt;div class=\u0026quot;cg-section\u0026quot;\u0026gt; \u0026lt;label style=\u0026quot;font-size:12px;font-weight:600;color:#475569;text-transform:uppercase;letter-spacing:.03em;\u0026quot;\u0026gt;Author(s)\u0026lt;/label\u0026gt; \u0026lt;div id=\u0026quot;cg-authors\u0026quot; style=\u0026quot;margin-top:6px;\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;cg-btn cg-btn-secondary cg-btn-sm\u0026quot; id=\u0026quot;cg-add-author\u0026quot; style=\u0026quot;margin-top:6px;\u0026quot;\u0026gt;+ Add Author\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;hr class=\u0026quot;cg-divider\u0026quot;\u0026gt; \u0026lt;!-- Core fields --\u0026gt; \u0026lt;div class=\u0026quot;cg-row\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cg-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Title\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-title\u0026quot; placeholder=\u0026quot;Work title\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cg-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Year\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-year\u0026quot; placeholder=\u0026quot;2024\u0026quot; maxlength=\u0026quot;4\u0026quot; style=\u0026quot;max-width:120px;\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cg-row\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cg-field\u0026quot; id=\u0026quot;cg-field-publisher\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Publisher\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-publisher\u0026quot; placeholder=\u0026quot;Publisher name\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cg-field\u0026quot; id=\u0026quot;cg-field-place\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Place of Publication\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-place\u0026quot; placeholder=\u0026quot;New York\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cg-row\u0026quot; id=\u0026quot;cg-row-journal\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cg-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Journal Name\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-journal\u0026quot; placeholder=\u0026quot;Nature, Science, ...\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cg-field\u0026quot; style=\u0026quot;max-width:100px;\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Volume\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-volume\u0026quot; placeholder=\u0026quot;12\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cg-field\u0026quot; style=\u0026quot;max-width:100px;\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Issue\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-issue\u0026quot; placeholder=\u0026quot;3\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cg-field\u0026quot; style=\u0026quot;max-width:130px;\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Pages\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-pages\u0026quot; placeholder=\u0026quot;45–67\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cg-row\u0026quot; id=\u0026quot;cg-row-conference\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cg-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Conference Name\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-conference\u0026quot; placeholder=\u0026quot;Proceedings of ...\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cg-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Conference Location\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-conf-location\u0026quot; placeholder=\u0026quot;San Francisco, CA\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cg-row\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cg-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;DOI\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-doi\u0026quot; placeholder=\u0026quot;10.1000/xyz123\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cg-field\u0026quot; id=\u0026quot;cg-field-url\u0026quot;\u0026gt; \u0026lt;label\u0026gt;URL\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-url\u0026quot; placeholder=\u0026quot;https://example.com\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cg-row\u0026quot; id=\u0026quot;cg-row-access\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cg-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Access Date\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-access-date\u0026quot; placeholder=\u0026quot;May 16, 2025\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cg-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Website Name\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-site-name\u0026quot; placeholder=\u0026quot;BBC News\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;hr class=\u0026quot;cg-divider\u0026quot;\u0026gt; \u0026lt;!-- Preview --\u0026gt; \u0026lt;h2\u0026gt;Citation Preview\u0026lt;/h2\u0026gt; \u0026lt;div class=\u0026quot;cg-preview-box\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cg-preview-label\u0026quot;\u0026gt;Reference List\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cg-preview-text\u0026quot; id=\u0026quot;cg-preview-ref\u0026quot;\u0026gt;Fill in the fields above to generate a citation.\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cg-intext\u0026quot; id=\u0026quot;cg-preview-intext\u0026quot; style=\u0026quot;display:none;\u0026quot;\u0026gt; \u0026lt;strong\u0026gt;In-text:\u0026lt;/strong\u0026gt; \u0026lt;span id=\u0026quot;cg-intext-text\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cg-copy-row\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;cg-btn cg-btn-primary\u0026quot; id=\u0026quot;cg-copy-ref\u0026quot;\u0026gt;Copy Reference\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cg-btn cg-btn-secondary\u0026quot; id=\u0026quot;cg-copy-intext\u0026quot;\u0026gt;Copy In-text\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cg-btn cg-btn-secondary\u0026quot; id=\u0026quot;cg-add-to-bib\u0026quot;\u0026gt;Add to Bibliography\u0026lt;/button\u0026gt; \u0026lt;span class=\u0026quot;cg-toast\u0026quot; id=\u0026quot;cg-toast\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; Bibliography Sort A–Z Export as Text Clear All No citations yet. Generate one and click \"Add to Bibliography\". Related tools:\nCount words → Word Counter Estimate reading time → Reading Time Calculator ","permalink":"https://productivity-works.com/tools/citation-generator/","summary":"\u003cp\u003eGenerate properly formatted citations in APA, MLA, Chicago, and Harvard styles — for books, journal articles, websites, and conference papers. Paste them directly into your bibliography or export as text.\u003c/p\u003e\n\u003cdiv id=\"cg-app\"\u003e\n\u003cstyle\u003e\n#cg-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 820px;\n  margin: 0 auto;\n  color: #1e293b;\n}\n#cg-app * { box-sizing: border-box; }\n#cg-app h2 {\n  font-size: 1.15rem;\n  font-weight: 700;\n  margin: 0 0 12px;\n  color: #0f172a;\n}\n#cg-app .cg-row {\n  display: flex;\n  gap: 12px;\n  flex-wrap: wrap;\n  margin-bottom: 14px;\n}\n#cg-app .cg-field {\n  display: flex;\n  flex-direction: column;\n  gap: 4px;\n  flex: 1;\n  min-width: 180px;\n}\n#cg-app .cg-field label {\n  font-size: 12px;\n  font-weight: 600;\n  color: #475569;\n  text-transform: uppercase;\n  letter-spacing: .03em;\n}\n#cg-app .cg-field input,\n#cg-app .cg-field select {\n  padding: 8px 10px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  font-size: 14px;\n  color: #1e293b;\n  background: #fff;\n  outline: none;\n  transition: border-color .15s;\n  width: 100%;\n}\n#cg-app .cg-field input:focus,\n#cg-app .cg-field select:focus {\n  border-color: #6366f1;\n}\n#cg-app .cg-card {\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 20px 22px;\n  margin-bottom: 18px;\n}\n#cg-app .cg-author-row {\n  display: flex;\n  gap: 8px;\n  align-items: center;\n  margin-bottom: 8px;\n}\n#cg-app .cg-author-row input {\n  flex: 1;\n  padding: 8px 10px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  font-size: 14px;\n  color: #1e293b;\n  outline: none;\n  transition: border-color .15s;\n}\n#cg-app .cg-author-row input:focus { border-color: #6366f1; }\n#cg-app .cg-btn {\n  padding: 8px 16px;\n  border: none;\n  border-radius: 7px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background .15s, transform .1s;\n}\n#cg-app .cg-btn:active { transform: scale(.97); }\n#cg-app .cg-btn-primary {\n  background: #6366f1;\n  color: #fff;\n}\n#cg-app .cg-btn-primary:hover { background: #4f46e5; }\n#cg-app .cg-btn-secondary {\n  background: #e2e8f0;\n  color: #334155;\n}\n#cg-app .cg-btn-secondary:hover { background: #cbd5e1; }\n#cg-app .cg-btn-danger {\n  background: #fee2e2;\n  color: #dc2626;\n  padding: 8px 10px;\n}\n#cg-app .cg-btn-danger:hover { background: #fecaca; }\n#cg-app .cg-btn-sm {\n  padding: 5px 11px;\n  font-size: 12px;\n}\n#cg-app .cg-preview-box {\n  background: #fff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 9px;\n  padding: 16px 18px;\n  margin-bottom: 10px;\n}\n#cg-app .cg-preview-label {\n  font-size: 11px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: .06em;\n  color: #94a3b8;\n  margin-bottom: 6px;\n}\n#cg-app .cg-preview-text {\n  font-size: 14px;\n  line-height: 1.7;\n  color: #1e293b;\n  white-space: pre-wrap;\n  word-break: break-word;\n}\n#cg-app .cg-intext {\n  font-size: 13px;\n  color: #64748b;\n  margin-top: 8px;\n  padding: 9px 12px;\n  background: #f1f5f9;\n  border-radius: 7px;\n}\n#cg-app .cg-intext strong { color: #0f172a; }\n#cg-app .cg-copy-row {\n  display: flex;\n  gap: 8px;\n  flex-wrap: wrap;\n  margin-top: 10px;\n}\n#cg-app .cg-toast {\n  display: inline-block;\n  font-size: 12px;\n  color: #16a34a;\n  font-weight: 600;\n  opacity: 0;\n  transition: opacity .3s;\n  margin-left: 6px;\n  align-self: center;\n}\n#cg-app .cg-toast.show { opacity: 1; }\n#cg-app .cg-bib-list {\n  list-style: none;\n  padding: 0;\n  margin: 0 0 12px;\n}\n#cg-app .cg-bib-item {\n  display: flex;\n  align-items: flex-start;\n  gap: 8px;\n  padding: 10px 0;\n  border-bottom: 1px solid #e2e8f0;\n  font-size: 13px;\n  line-height: 1.6;\n  color: #1e293b;\n  word-break: break-word;\n}\n#cg-app .cg-bib-item:last-child { border-bottom: none; }\n#cg-app .cg-bib-num {\n  font-size: 11px;\n  font-weight: 700;\n  color: #94a3b8;\n  min-width: 22px;\n  padding-top: 2px;\n}\n#cg-app .cg-bib-text { flex: 1; }\n#cg-app .cg-tabs {\n  display: flex;\n  gap: 4px;\n  margin-bottom: 16px;\n  border-bottom: 2px solid #e2e8f0;\n}\n#cg-app .cg-tab {\n  padding: 8px 16px;\n  font-size: 13px;\n  font-weight: 600;\n  color: #64748b;\n  border: none;\n  background: none;\n  cursor: pointer;\n  border-bottom: 2px solid transparent;\n  margin-bottom: -2px;\n  border-radius: 6px 6px 0 0;\n  transition: color .15s;\n}\n#cg-app .cg-tab.active {\n  color: #6366f1;\n  border-bottom-color: #6366f1;\n}\n#cg-app .cg-tab:hover:not(.active) { color: #334155; }\n#cg-app .cg-section { margin-bottom: 6px; }\n#cg-app .cg-divider {\n  border: none;\n  border-top: 1.5px solid #e2e8f0;\n  margin: 18px 0;\n}\n#cg-app .cg-empty {\n  color: #94a3b8;\n  font-size: 13px;\n  text-align: center;\n  padding: 18px 0;\n}\n@media (max-width: 600px) {\n  #cg-app .cg-row { flex-direction: column; }\n  #cg-app .cg-field { min-width: 100%; }\n}\n\u003c/style\u003e\n\u003cdiv class=\"cg-card\"\u003e\n  \u003cdiv class=\"cg-tabs\"\u003e\n    \u003cbutton class=\"cg-tab active\" data-tab=\"generator\"\u003eGenerator\u003c/button\u003e\n    \u003cbutton class=\"cg-tab\" data-tab=\"bibliography\"\u003eBibliography\u003c/button\u003e\n  \u003c/div\u003e\n  \u003c!-- GENERATOR TAB --\u003e\n  \u003cdiv id=\"cg-tab-generator\"\u003e\n    \u003cdiv class=\"cg-row\"\u003e\n      \u003cdiv class=\"cg-field\"\u003e\n        \u003clabel\u003eSource Type\u003c/label\u003e\n        \u003cselect id=\"cg-source-type\"\u003e\n          \u003coption value=\"book\"\u003eBook\u003c/option\u003e\n          \u003coption value=\"journal\"\u003eJournal Article\u003c/option\u003e\n          \u003coption value=\"website\"\u003eWebsite\u003c/option\u003e\n          \u003coption value=\"conference\"\u003eConference Paper\u003c/option\u003e\n        \u003c/select\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"cg-field\"\u003e\n        \u003clabel\u003eCitation Style\u003c/label\u003e\n        \u003cselect id=\"cg-style\"\u003e\n          \u003coption value=\"apa7\"\u003eAPA 7th\u003c/option\u003e\n          \u003coption value=\"mla9\"\u003eMLA 9th\u003c/option\u003e\n          \u003coption value=\"chicago17\"\u003eChicago 17th\u003c/option\u003e\n          \u003coption value=\"harvard\"\u003eHarvard\u003c/option\u003e\n        \u003c/select\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;!-- Authors --\u0026gt;\n\u0026lt;div class=\u0026quot;cg-section\u0026quot;\u0026gt;\n  \u0026lt;label style=\u0026quot;font-size:12px;font-weight:600;color:#475569;text-transform:uppercase;letter-spacing:.03em;\u0026quot;\u0026gt;Author(s)\u0026lt;/label\u0026gt;\n  \u0026lt;div id=\u0026quot;cg-authors\u0026quot; style=\u0026quot;margin-top:6px;\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n  \u0026lt;button class=\u0026quot;cg-btn cg-btn-secondary cg-btn-sm\u0026quot; id=\u0026quot;cg-add-author\u0026quot; style=\u0026quot;margin-top:6px;\u0026quot;\u0026gt;+ Add Author\u0026lt;/button\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;hr class=\u0026quot;cg-divider\u0026quot;\u0026gt;\n\n\u0026lt;!-- Core fields --\u0026gt;\n\u0026lt;div class=\u0026quot;cg-row\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;cg-field\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Title\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-title\u0026quot; placeholder=\u0026quot;Work title\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cg-field\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Year\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-year\u0026quot; placeholder=\u0026quot;2024\u0026quot; maxlength=\u0026quot;4\u0026quot; style=\u0026quot;max-width:120px;\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;cg-row\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;cg-field\u0026quot; id=\u0026quot;cg-field-publisher\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Publisher\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-publisher\u0026quot; placeholder=\u0026quot;Publisher name\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cg-field\u0026quot; id=\u0026quot;cg-field-place\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Place of Publication\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-place\u0026quot; placeholder=\u0026quot;New York\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;cg-row\u0026quot; id=\u0026quot;cg-row-journal\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;cg-field\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Journal Name\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-journal\u0026quot; placeholder=\u0026quot;Nature, Science, ...\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cg-field\u0026quot; style=\u0026quot;max-width:100px;\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Volume\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-volume\u0026quot; placeholder=\u0026quot;12\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cg-field\u0026quot; style=\u0026quot;max-width:100px;\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Issue\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-issue\u0026quot; placeholder=\u0026quot;3\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cg-field\u0026quot; style=\u0026quot;max-width:130px;\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Pages\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-pages\u0026quot; placeholder=\u0026quot;45–67\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;cg-row\u0026quot; id=\u0026quot;cg-row-conference\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;cg-field\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Conference Name\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-conference\u0026quot; placeholder=\u0026quot;Proceedings of ...\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cg-field\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Conference Location\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-conf-location\u0026quot; placeholder=\u0026quot;San Francisco, CA\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;cg-row\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;cg-field\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;DOI\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-doi\u0026quot; placeholder=\u0026quot;10.1000/xyz123\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cg-field\u0026quot; id=\u0026quot;cg-field-url\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;URL\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-url\u0026quot; placeholder=\u0026quot;https://example.com\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;cg-row\u0026quot; id=\u0026quot;cg-row-access\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;cg-field\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Access Date\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-access-date\u0026quot; placeholder=\u0026quot;May 16, 2025\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cg-field\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Website Name\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;cg-site-name\u0026quot; placeholder=\u0026quot;BBC News\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;hr class=\u0026quot;cg-divider\u0026quot;\u0026gt;\n\n\u0026lt;!-- Preview --\u0026gt;\n\u0026lt;h2\u0026gt;Citation Preview\u0026lt;/h2\u0026gt;\n\u0026lt;div class=\u0026quot;cg-preview-box\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;cg-preview-label\u0026quot;\u0026gt;Reference List\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cg-preview-text\u0026quot; id=\u0026quot;cg-preview-ref\u0026quot;\u0026gt;Fill in the fields above to generate a citation.\u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\u0026lt;div class=\u0026quot;cg-intext\u0026quot; id=\u0026quot;cg-preview-intext\u0026quot; style=\u0026quot;display:none;\u0026quot;\u0026gt;\n  \u0026lt;strong\u0026gt;In-text:\u0026lt;/strong\u0026gt; \u0026lt;span id=\u0026quot;cg-intext-text\u0026quot;\u0026gt;\u0026lt;/span\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;cg-copy-row\u0026quot;\u0026gt;\n  \u0026lt;button class=\u0026quot;cg-btn cg-btn-primary\u0026quot; id=\u0026quot;cg-copy-ref\u0026quot;\u0026gt;Copy Reference\u0026lt;/button\u0026gt;\n  \u0026lt;button class=\u0026quot;cg-btn cg-btn-secondary\u0026quot; id=\u0026quot;cg-copy-intext\u0026quot;\u0026gt;Copy In-text\u0026lt;/button\u0026gt;\n  \u0026lt;button class=\u0026quot;cg-btn cg-btn-secondary\u0026quot; id=\u0026quot;cg-add-to-bib\u0026quot;\u0026gt;Add to Bibliography\u0026lt;/button\u0026gt;\n  \u0026lt;span class=\u0026quot;cg-toast\u0026quot; id=\u0026quot;cg-toast\u0026quot;\u0026gt;\u0026lt;/span\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n  \u003c!-- BIBLIOGRAPHY TAB --\u003e\n  \u003cdiv id=\"cg-tab-bibliography\" style=\"display:none;\"\u003e\n    \u003cdiv style=\"display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;flex-wrap:wrap;gap:8px;\"\u003e\n      \u003ch2 style=\"margin:0;\"\u003eBibliography\u003c/h2\u003e\n      \u003cdiv style=\"display:flex;gap:8px;flex-wrap:wrap;\"\u003e\n        \u003cbutton class=\"cg-btn cg-btn-secondary cg-btn-sm\" id=\"cg-sort-alpha\"\u003eSort A–Z\u003c/button\u003e\n        \u003cbutton class=\"cg-btn cg-btn-secondary cg-btn-sm\" id=\"cg-export-txt\"\u003eExport as Text\u003c/button\u003e\n        \u003cbutton class=\"cg-btn cg-btn-danger cg-btn-sm\" id=\"cg-clear-bib\"\u003eClear All\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cul class=\"cg-bib-list\" id=\"cg-bib-list\"\u003e\n      \u003cli class=\"cg-empty\" id=\"cg-bib-empty\"\u003eNo citations yet. Generate one and click \"Add to Bibliography\".\u003c/li\u003e\n    \u003c/ul\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  'use strict';\n\n  // ── State ──\n  var authors = [{ first: '', last: '' }];\n  var bibItems = [];\n\n  // ── DOM refs ──\n  var sourceTypeEl = document.getElementById('cg-source-type');\n  var styleEl = document.getElementById('cg-style');\n  var titleEl = document.getElementById('cg-title');\n  var yearEl = document.getElementById('cg-year');\n  var publisherEl = document.getElementById('cg-publisher');\n  var placeEl = document.getElementById('cg-place');\n  var journalEl = document.getElementById('cg-journal');\n  var volumeEl = document.getElementById('cg-volume');\n  var issueEl = document.getElementById('cg-issue');\n  var pagesEl = document.getElementById('cg-pages');\n  var doiEl = document.getElementById('cg-doi');\n  var urlEl = document.getElementById('cg-url');\n  var accessEl = document.getElementById('cg-access-date');\n  var siteNameEl = document.getElementById('cg-site-name');\n  var conferenceEl = document.getElementById('cg-conference');\n  var confLocEl = document.getElementById('cg-conf-location');\n  var authorsContainer = document.getElementById('cg-authors');\n  var previewRef = document.getElementById('cg-preview-ref');\n  var previewIntextEl = document.getElementById('cg-preview-intext');\n  var intextText = document.getElementById('cg-intext-text');\n  var toast = document.getElementById('cg-toast');\n  var bibList = document.getElementById('cg-bib-list');\n  var bibEmpty = document.getElementById('cg-bib-empty');\n\n  // ── Helpers ──\n  function v(el) { return el ? el.value.trim() : ''; }\n\n  function italic(text) { return '_' + text + '_'; }\n\n  function formatAuthorsAPA(auths) {\n    if (!auths.length) return '';\n    return auths.map(function(a) {\n      var last = a.last || a.first || 'Unknown';\n      var first = a.last ? a.first : '';\n      if (!first) return last;\n      var initials = first.split(/\\s+/).map(function(n) { return n[0].toUpperCase() + '.'; }).join(' ');\n      return last + ', ' + initials;\n    }).join(', ');\n  }\n\n  function formatAuthorsMLAFull(auths) {\n    if (!auths.length) return '';\n    if (auths.length === 1) {\n      var a = auths[0];\n      return (a.last \u0026\u0026 a.first) ? a.last + ', ' + a.first : (a.last || a.first || 'Unknown');\n    }\n    var first = auths[0];\n    var firstStr = (first.last \u0026\u0026 first.first) ? first.last + ', ' + first.first : (first.last || first.first || 'Unknown');\n    var rest = auths.slice(1).map(function(a) { return (a.first ? a.first + ' ' : '') + (a.last || ''); });\n    if (auths.length === 2) return firstStr + ', and ' + rest[0];\n    return firstStr + ', et al.';\n  }\n\n  function formatAuthorsChicago(auths) {\n    if (!auths.length) return '';\n    if (auths.length === 1) {\n      var a = auths[0];\n      return (a.last \u0026\u0026 a.first) ? a.last + ', ' + a.first : (a.last || a.first || 'Unknown');\n    }\n    var first = auths[0];\n    var firstStr = (first.last \u0026\u0026 first.first) ? first.last + ', ' + first.first : (first.last || first.first || 'Unknown');\n    var rest = auths.slice(1).map(function(a) { return (a.first ? a.first + ' ' : '') + (a.last || ''); });\n    return firstStr + ', and ' + rest.join(', and ');\n  }\n\n  function formatAuthorsHarvard(auths) {\n    if (!auths.length) return '';\n    return auths.map(function(a, i) {\n      var last = a.last || a.first || 'Unknown';\n      var first = a.last ? a.first : '';\n      var initials = first ? first.split(/\\s+/).map(function(n) { return n[0].toUpperCase() + '.'; }).join('') : '';\n      return last + (initials ? ', ' + initials : '');\n    }).join(', ');\n  }\n\n  function intextAPA(auths, year) {\n    var name = auths.length ? (auths[0].last || auths[0].first || 'Author') : 'Author';\n    var suffix = auths.length \u003e 2 ? ' et al.' : (auths.length === 2 ? ' \u0026 ' + (auths[1].last || auths[1].first || '') : '');\n    return '(' + name + (auths.length === 2 ? suffix : (auths.length \u003e 2 ? suffix : '')) + ', ' + (year || 'n.d.') + ')';\n  }\n\n  function intextMLA(auths) {\n    var name = auths.length ? (auths[0].last || auths[0].first || 'Author') : 'Author';\n    return '(' + name + ')';\n  }\n\n  function intextChicago(auths, year) {\n    var names = auths.slice(0, 3).map(function(a) { return a.last || a.first || 'Author'; });\n    if (auths.length \u003e 3) names.push('et al.');\n    return '(' + names.join(', ') + ' ' + (year || 'n.d.') + ')';\n  }\n\n  function intextHarvard(auths, year) {\n    var name = auths.length ? (auths[0].last || auths[0].first || 'Author') : 'Author';\n    var suffix = auths.length \u003e 2 ? ' et al.' : (auths.length === 2 ? ' and ' + (auths[1].last || auths[1].first || '') : '');\n    return '(' + name + suffix + ', ' + (year || 'n.d.') + ')';\n  }\n\n  // ── Citation Formatters ──\n  function buildAPA7(data) {\n    var a = formatAuthorsAPA(data.authors);\n    var yr = data.year ? '(' + data.year + ').' : '(n.d.).';\n    var doi = data.doi ? ' https://doi.org/' + data.doi.replace(/^https?:\\/\\/doi\\.org\\//i,'') : '';\n    var url = !data.doi \u0026\u0026 data.url ? ' ' + data.url : '';\n\n    if (data.type === 'book') {\n      return (a ? a + ' ' : '') + yr + ' ' + italic(data.title || 'Untitled') + '.' + (data.publisher ? ' ' + data.publisher + '.' : '') + doi + url;\n    }\n    if (data.type === 'journal') {\n      var vol = data.volume ? ', ' + italic(data.volume) : '';\n      var iss = data.issue ? '(' + data.issue + ')' : '';\n      var pg = data.pages ? ', ' + data.pages : '';\n      return (a ? a + ' ' : '') + yr + ' ' + (data.title || 'Untitled') + '. ' + italic(data.journal || 'Journal') + vol + iss + pg + '.' + doi + url;\n    }\n    if (data.type === 'website') {\n      var site = data.siteName ? data.siteName + '. ' : '';\n      var acc = data.accessDate ? ' Retrieved ' + data.accessDate + ', from' : '';\n      return (a ? a + ' ' : '') + yr + ' ' + italic(data.title || 'Untitled') + '. ' + site + acc + (data.url ? ' ' + data.url : '');\n    }\n    if (data.type === 'conference') {\n      var pg2 = data.pages ? ', ' + data.pages : '';\n      return (a ? a + ' ' : '') + yr + ' ' + (data.title || 'Untitled') + '. In ' + italic(data.conference || 'Conference') + pg2 + '.' + doi + url;\n    }\n    return '';\n  }\n\n  function buildMLA9(data) {\n    var a = formatAuthorsMLAFull(data.authors);\n    if (data.type === 'book') {\n      return (a ? a + '. ' : '') + italic(data.title || 'Untitled') + '. ' + (data.publisher || '') + ', ' + (data.year || 'n.d.') + '.';\n    }\n    if (data.type === 'journal') {\n      var vol = data.volume ? 'vol. ' + data.volume + ', ' : '';\n      var iss = data.issue ? 'no. ' + data.issue + ', ' : '';\n      var pg = data.pages ? 'pp. ' + data.pages + ', ' : '';\n      var doi = data.doi ? 'https://doi.org/' + data.doi.replace(/^https?:\\/\\/doi\\.org\\//i,'') + '.' : (data.url ? data.url + '.' : '');\n      return (a ? a + '. ' : '') + '\"' + (data.title || 'Untitled') + '.\" ' + italic(data.journal || 'Journal') + ', ' + vol + iss + (data.year || 'n.d.') + ', ' + pg + doi;\n    }\n    if (data.type === 'website') {\n      var acc = data.accessDate ? ' Accessed ' + data.accessDate + '.' : '';\n      return (a ? a + '. ' : '') + '\"' + (data.title || 'Untitled') + '.\" ' + (data.siteName ? italic(data.siteName) + ', ' : '') + (data.year || 'n.d.') + ', ' + (data.url || '') + '.' + acc;\n    }\n    if (data.type === 'conference') {\n      return (a ? a + '. ' : '') + '\"' + (data.title || 'Untitled') + '.\" ' + italic(data.conference || 'Conference') + ', ' + (data.year || 'n.d.') + (data.pages ? ', pp. ' + data.pages : '') + '.';\n    }\n    return '';\n  }\n\n  function buildChicago17(data) {\n    var a = formatAuthorsChicago(data.authors);\n    if (data.type === 'book') {\n      return (a ? a + '. ' : '') + italic(data.title || 'Untitled') + '. ' + (data.place ? data.place + ': ' : '') + (data.publisher || '') + ', ' + (data.year || 'n.d.') + '.';\n    }\n    if (data.type === 'journal') {\n      var vol = data.volume ? data.volume : '';\n      var iss = data.issue ? ', no. ' + data.issue : '';\n      var yr = data.year ? ' (' + data.year + ')' : '';\n      var pg = data.pages ? ': ' + data.pages : '';\n      var doi = data.doi ? ' https://doi.org/' + data.doi.replace(/^https?:\\/\\/doi\\.org\\//i,'') : (data.url ? ' ' + data.url : '');\n      return (a ? a + '. ' : '') + '\"' + (data.title || 'Untitled') + '.\" ' + italic(data.journal || 'Journal') + ' ' + vol + iss + yr + pg + '.' + doi;\n    }\n    if (data.type === 'website') {\n      var acc = data.accessDate ? ' Accessed ' + data.accessDate + '.' : '';\n      return (a ? a + '. ' : '') + '\"' + (data.title || 'Untitled') + '.\" ' + (data.siteName ? data.siteName + '. ' : '') + (data.year || 'n.d.') + '.' + (data.url ? ' ' + data.url + '.' : '') + acc;\n    }\n    if (data.type === 'conference') {\n      return (a ? a + '. ' : '') + '\"' + (data.title || 'Untitled') + '.\" Paper presented at ' + (data.conference || 'Conference') + ', ' + (data.confLocation || '') + ', ' + (data.year || 'n.d.') + '.';\n    }\n    return '';\n  }\n\n  function buildHarvard(data) {\n    var a = formatAuthorsHarvard(data.authors);\n    var yr = data.year ? data.year : 'n.d.';\n    if (data.type === 'book') {\n      return (a ? a + ' ' : '') + '(' + yr + ') ' + italic(data.title || 'Untitled') + '. ' + (data.place ? data.place + ': ' : '') + (data.publisher || '') + '.';\n    }\n    if (data.type === 'journal') {\n      var vol = data.volume ? data.volume : '';\n      var iss = data.issue ? '(' + data.issue + ')' : '';\n      var pg = data.pages ? ', pp.' + data.pages : '';\n      var doi = data.doi ? ' doi:' + data.doi.replace(/^https?:\\/\\/doi\\.org\\//i,'') : (data.url ? ' Available at: ' + data.url : '');\n      return (a ? a + ' ' : '') + '(' + yr + ') \"' + (data.title || 'Untitled') + '\", ' + italic(data.journal || 'Journal') + ', ' + vol + iss + pg + '.' + doi;\n    }\n    if (data.type === 'website') {\n      var acc = data.accessDate ? ' [Accessed: ' + data.accessDate + ']' : '';\n      return (a ? a + ' ' : '') + '(' + yr + ') ' + italic(data.title || 'Untitled') + '. ' + (data.siteName ? data.siteName + '.' : '') + (data.url ? ' Available at: ' + data.url + '.' : '') + acc;\n    }\n    if (data.type === 'conference') {\n      var pg2 = data.pages ? ', pp.' + data.pages : '';\n      return (a ? a + ' ' : '') + '(' + yr + ') \"' + (data.title || 'Untitled') + '\", ' + italic(data.conference || 'Conference') + pg2 + '.';\n    }\n    return '';\n  }\n\n  function getCitationData() {\n    return {\n      type: v(sourceTypeEl),\n      authors: authors.filter(function(a) { return a.first || a.last; }),\n      title: v(titleEl),\n      year: v(yearEl),\n      publisher: v(publisherEl),\n      place: v(placeEl),\n      journal: v(journalEl),\n      volume: v(volumeEl),\n      issue: v(issueEl),\n      pages: v(pagesEl),\n      doi: v(doiEl),\n      url: v(urlEl),\n      accessDate: v(accessEl),\n      siteName: v(siteNameEl),\n      conference: v(conferenceEl),\n      confLocation: v(confLocEl)\n    };\n  }\n\n  function buildCitation(data) {\n    var style = v(styleEl);\n    if (style === 'apa7') return buildAPA7(data);\n    if (style === 'mla9') return buildMLA9(data);\n    if (style === 'chicago17') return buildChicago17(data);\n    if (style === 'harvard') return buildHarvard(data);\n    return '';\n  }\n\n  function buildIntext(data) {\n    var style = v(styleEl);\n    var auths = data.authors;\n    if (style === 'apa7') return intextAPA(auths, data.year);\n    if (style === 'mla9') return intextMLA(auths);\n    if (style === 'chicago17') return intextChicago(auths, data.year);\n    if (style === 'harvard') return intextHarvard(auths, data.year);\n    return '';\n  }\n\n  // Render italic markers as actual italics in the display\n  function renderForDisplay(text) {\n    return text.replace(/_([^_]+)_/g, '\u003cem\u003e$1\u003c/em\u003e');\n  }\n\n  function renderForCopy(text) {\n    return text.replace(/_([^_]+)_/g, '$1');\n  }\n\n  // ── Update visibility ──\n  function updateFieldVisibility() {\n    var type = v(sourceTypeEl);\n    var isJournal = type === 'journal';\n    var isWebsite = type === 'website';\n    var isConference = type === 'conference';\n    var isBook = type === 'book';\n\n    document.getElementById('cg-row-journal').style.display = (isJournal || isConference) ? 'flex' : 'none';\n    if (isConference) {\n      journalEl.closest('.cg-field').style.display = 'none';\n    } else {\n      journalEl.closest('.cg-field').style.display = '';\n    }\n    document.getElementById('cg-row-conference').style.display = isConference ? 'flex' : 'none';\n    document.getElementById('cg-row-access').style.display = isWebsite ? 'flex' : 'none';\n    document.getElementById('cg-field-url').style.display = isWebsite ? '' : 'flex';\n    document.getElementById('cg-field-publisher').style.display = (isBook || isConference || isJournal) ? '' : 'none';\n    document.getElementById('cg-field-place').style.display = (isBook) ? '' : 'none';\n  }\n\n  // ── Render authors ──\n  function renderAuthors() {\n    authorsContainer.innerHTML = '';\n    authors.forEach(function(a, i) {\n      var row = document.createElement('div');\n      row.className = 'cg-author-row';\n      row.innerHTML =\n        '\u003cinput type=\"text\" placeholder=\"Last name\" value=\"' + escAttr(a.last) + '\" data-i=\"' + i + '\" data-field=\"last\"\u003e' +\n        '\u003cinput type=\"text\" placeholder=\"First name / Initials\" value=\"' + escAttr(a.first) + '\" data-i=\"' + i + '\" data-field=\"first\"\u003e' +\n        (authors.length \u003e 1 ? '\u003cbutton class=\"cg-btn cg-btn-danger cg-btn-sm cg-remove-author\" data-i=\"' + i + '\"\u003e✕\u003c/button\u003e' : '');\n      authorsContainer.appendChild(row);\n    });\n    authorsContainer.querySelectorAll('input').forEach(function(inp) {\n      inp.addEventListener('input', function() {\n        authors[+this.dataset.i][this.dataset.field] = this.value;\n        updatePreview();\n      });\n    });\n    authorsContainer.querySelectorAll('.cg-remove-author').forEach(function(btn) {\n      btn.addEventListener('click', function() {\n        authors.splice(+this.dataset.i, 1);\n        renderAuthors();\n        updatePreview();\n      });\n    });\n  }\n\n  function escAttr(s) { return (s || '').replace(/\"/g, '\u0026quot;'); }\n\n  // ── Preview ──\n  function updatePreview() {\n    var data = getCitationData();\n    var ref = buildCitation(data);\n    var intext = buildIntext(data);\n    if (ref) {\n      previewRef.innerHTML = renderForDisplay(ref);\n      previewIntextEl.style.display = '';\n      intextText.textContent = renderForCopy(intext);\n    } else {\n      previewRef.textContent = 'Fill in the fields above to generate a citation.';\n      previewIntextEl.style.display = 'none';\n    }\n  }\n\n  // ── Toast ──\n  var toastTimer;\n  function showToast(msg) {\n    toast.textContent = msg;\n    toast.classList.add('show');\n    clearTimeout(toastTimer);\n    toastTimer = setTimeout(function() { toast.classList.remove('show'); }, 2000);\n  }\n\n  // ── Copy ──\n  function copyText(text) {\n    if (navigator.clipboard) {\n      navigator.clipboard.writeText(text).catch(function() { fallbackCopy(text); });\n    } else { fallbackCopy(text); }\n  }\n  function fallbackCopy(text) {\n    var ta = document.createElement('textarea');\n    ta.value = text;\n    ta.style.position = 'fixed';\n    ta.style.opacity = '0';\n    document.body.appendChild(ta);\n    ta.select();\n    document.execCommand('copy');\n    document.body.removeChild(ta);\n  }\n\n  // ── Bibliography ──\n  function renderBib() {\n    var items = bibItems.slice();\n    var html = '';\n    if (!items.length) {\n      bibList.innerHTML = '\u003cli class=\"cg-empty\" id=\"cg-bib-empty\"\u003eNo citations yet. Generate one and click \"Add to Bibliography\".\u003c/li\u003e';\n      return;\n    }\n    items.forEach(function(item, i) {\n      html += '\u003cli class=\"cg-bib-item\"\u003e\u003cspan class=\"cg-bib-num\"\u003e' + (i+1) + '.\u003c/span\u003e\u003cspan class=\"cg-bib-text\"\u003e' + renderForDisplay(item) + '\u003c/span\u003e' +\n        '\u003cbutton class=\"cg-btn cg-btn-danger cg-btn-sm cg-remove-bib\" data-i=\"' + i + '\" style=\"flex-shrink:0;\"\u003e✕\u003c/button\u003e\u003c/li\u003e';\n    });\n    bibList.innerHTML = html;\n    bibList.querySelectorAll('.cg-remove-bib').forEach(function(btn) {\n      btn.addEventListener('click', function() {\n        bibItems.splice(+this.dataset.i, 1);\n        renderBib();\n      });\n    });\n  }\n\n  // ── Tabs ──\n  document.querySelectorAll('#cg-app .cg-tab').forEach(function(tab) {\n    tab.addEventListener('click', function() {\n      document.querySelectorAll('#cg-app .cg-tab').forEach(function(t) { t.classList.remove('active'); });\n      this.classList.add('active');\n      var name = this.dataset.tab;\n      document.getElementById('cg-tab-generator').style.display = name === 'generator' ? '' : 'none';\n      document.getElementById('cg-tab-bibliography').style.display = name === 'bibliography' ? '' : 'none';\n    });\n  });\n\n  // ── Event listeners ──\n  sourceTypeEl.addEventListener('change', function() { updateFieldVisibility(); updatePreview(); });\n  styleEl.addEventListener('change', updatePreview);\n  [titleEl, yearEl, publisherEl, placeEl, journalEl, volumeEl, issueEl, pagesEl, doiEl, urlEl, accessEl, siteNameEl, conferenceEl, confLocEl].forEach(function(el) {\n    if (el) el.addEventListener('input', updatePreview);\n  });\n\n  document.getElementById('cg-add-author').addEventListener('click', function() {\n    authors.push({ first: '', last: '' });\n    renderAuthors();\n  });\n\n  document.getElementById('cg-copy-ref').addEventListener('click', function() {\n    var data = getCitationData();\n    var ref = renderForCopy(buildCitation(data));\n    if (!ref) { showToast('Nothing to copy'); return; }\n    copyText(ref);\n    showToast('Copied!');\n  });\n\n  document.getElementById('cg-copy-intext').addEventListener('click', function() {\n    var data = getCitationData();\n    var it = buildIntext(data);\n    if (!it) { showToast('Nothing to copy'); return; }\n    copyText(it);\n    showToast('In-text copied!');\n  });\n\n  document.getElementById('cg-add-to-bib').addEventListener('click', function() {\n    var data = getCitationData();\n    var ref = buildCitation(data);\n    if (!ref) { showToast('Generate a citation first'); return; }\n    bibItems.push(ref);\n    renderBib();\n    showToast('Added to bibliography!');\n  });\n\n  document.getElementById('cg-sort-alpha').addEventListener('click', function() {\n    bibItems.sort(function(a, b) { return renderForCopy(a).localeCompare(renderForCopy(b)); });\n    renderBib();\n    showToast('Sorted!');\n  });\n\n  document.getElementById('cg-export-txt').addEventListener('click', function() {\n    if (!bibItems.length) { showToast('Bibliography is empty'); return; }\n    var text = bibItems.map(function(item, i) { return (i+1) + '. ' + renderForCopy(item); }).join('\\n\\n');\n    var blob = new Blob([text], { type: 'text/plain' });\n    var a = document.createElement('a');\n    a.href = URL.createObjectURL(blob);\n    a.download = 'bibliography.txt';\n    a.click();\n    URL.revokeObjectURL(a.href);\n  });\n\n  document.getElementById('cg-clear-bib').addEventListener('click', function() {\n    if (bibItems.length \u0026\u0026 confirm('Clear all citations from bibliography?')) {\n      bibItems = [];\n      renderBib();\n    }\n  });\n\n  // ── Init ──\n  renderAuthors();\n  updateFieldVisibility();\n  updatePreview();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cstrong\u003eRelated tools:\u003c/strong\u003e\u003c/p\u003e","title":"Citation Generator - APA, MLA, Chicago"},{"content":"Paste your HTML, CSS, or JavaScript below and click Minify to strip whitespace, comments, and unnecessary characters. Works entirely in your browser — nothing is sent to a server.\nHTML CSS JavaScript Remove comments Collapse whitespace Remove optional closing tags Remove comments Remove whitespace Remove last semicolons Merge duplicate selectors Remove // comments Remove /* */ comments Collapse whitespace \u0026#128194; Upload file No file chosen \u0026#9889; Minify \u0026#10024; Beautify \u0026#128203; Copy Result Clear Output Related tools: SQL Formatter \u0026nbsp;|\u0026nbsp; JSON Formatter How It Works The tool runs entirely in your browser using pure JavaScript — your code is never uploaded anywhere.\nLanguage What gets removed HTML Comments, redundant whitespace, optional closing tags CSS Comments, whitespace, trailing semicolons, duplicate selectors JavaScript // and /* */ comments, excess whitespace (string-safe) The Beautify button does the reverse: it re-indents and formats your code for readability.\nTips Upload a file directly using the Upload file button — the language tab switches automatically based on the file extension. The size reduction percentage is calculated on UTF-8 byte length, matching what browsers and servers measure. For JavaScript, whitespace inside string literals and template literals is preserved to avoid breaking your code. Run minified code through Beautify first if you need to debug it. Related tools: SQL Formatter | JSON Formatter ","permalink":"https://productivity-works.com/tools/code-minifier/","summary":"\u003cp\u003ePaste your HTML, CSS, or JavaScript below and click \u003cstrong\u003eMinify\u003c/strong\u003e to strip whitespace, comments, and unnecessary characters. Works entirely in your browser — nothing is sent to a server.\u003c/p\u003e\n\u003cdiv id=\"mn-app\"\u003e\n\u003cstyle\u003e\n#mn-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #1a1a1a;\n}\n#mn-app .mn-tabs {\n  display: flex;\n  gap: 0;\n  border-bottom: 2px solid #e0e0e0;\n  margin-bottom: 0;\n}\n#mn-app .mn-tab {\n  padding: 10px 22px;\n  cursor: pointer;\n  background: #f5f5f5;\n  border: 1px solid #e0e0e0;\n  border-bottom: none;\n  border-radius: 6px 6px 0 0;\n  font-size: 0.95rem;\n  font-weight: 500;\n  color: #555;\n  transition: background 0.15s;\n  margin-right: 4px;\n  user-select: none;\n}\n#mn-app .mn-tab.active {\n  background: #fff;\n  color: #2563eb;\n  border-color: #e0e0e0;\n  border-bottom: 2px solid #fff;\n  margin-bottom: -2px;\n  z-index: 1;\n}\n#mn-app .mn-tab:hover:not(.active) {\n  background: #eee;\n}\n#mn-app .mn-panel {\n  border: 1px solid #e0e0e0;\n  border-top: none;\n  border-radius: 0 0 8px 8px;\n  background: #fff;\n  padding: 18px;\n}\n#mn-app .mn-options {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 14px;\n  margin-bottom: 14px;\n  padding: 12px 14px;\n  background: #f8f9fa;\n  border-radius: 6px;\n  border: 1px solid #e8e8e8;\n}\n#mn-app .mn-options label {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 0.88rem;\n  color: #444;\n  cursor: pointer;\n  user-select: none;\n}\n#mn-app .mn-options input[type=\"checkbox\"] {\n  width: 15px;\n  height: 15px;\n  cursor: pointer;\n}\n#mn-app .mn-upload-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-bottom: 10px;\n}\n#mn-app .mn-upload-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  padding: 6px 14px;\n  background: #f0f0f0;\n  border: 1px solid #ccc;\n  border-radius: 5px;\n  font-size: 0.85rem;\n  cursor: pointer;\n  color: #333;\n  transition: background 0.15s;\n  white-space: nowrap;\n}\n#mn-app .mn-upload-btn:hover { background: #e4e4e4; }\n#mn-app .mn-file-name {\n  font-size: 0.82rem;\n  color: #888;\n}\n#mn-app textarea {\n  width: 100%;\n  box-sizing: border-box;\n  height: 200px;\n  padding: 12px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 0.82rem;\n  border: 1px solid #d0d0d0;\n  border-radius: 6px;\n  resize: vertical;\n  background: #fafafa;\n  color: #1a1a1a;\n  line-height: 1.5;\n  transition: border-color 0.15s;\n}\n#mn-app textarea:focus {\n  outline: none;\n  border-color: #2563eb;\n  background: #fff;\n}\n#mn-app .mn-actions {\n  display: flex;\n  gap: 10px;\n  margin-top: 12px;\n  flex-wrap: wrap;\n  align-items: center;\n}\n#mn-app .mn-btn {\n  padding: 9px 20px;\n  border: none;\n  border-radius: 6px;\n  font-size: 0.9rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s;\n}\n#mn-app .mn-btn:active { transform: scale(0.97); }\n#mn-app .mn-btn-primary {\n  background: #2563eb;\n  color: #fff;\n}\n#mn-app .mn-btn-primary:hover { background: #1d4ed8; }\n#mn-app .mn-btn-secondary {\n  background: #f3f4f6;\n  color: #374151;\n  border: 1px solid #d1d5db;\n}\n#mn-app .mn-btn-secondary:hover { background: #e5e7eb; }\n#mn-app .mn-btn-copy {\n  background: #10b981;\n  color: #fff;\n}\n#mn-app .mn-btn-copy:hover { background: #059669; }\n#mn-app .mn-btn-clear {\n  background: #f9fafb;\n  color: #6b7280;\n  border: 1px solid #e5e7eb;\n  margin-left: auto;\n}\n#mn-app .mn-btn-clear:hover { background: #f3f4f6; }\n#mn-app .mn-stats {\n  margin-top: 14px;\n  padding: 10px 14px;\n  background: #f0fdf4;\n  border: 1px solid #bbf7d0;\n  border-radius: 6px;\n  font-size: 0.88rem;\n  color: #166534;\n  display: none;\n}\n#mn-app .mn-stats.visible { display: block; }\n#mn-app .mn-stats strong { color: #15803d; }\n#mn-app .mn-output-label {\n  font-size: 0.88rem;\n  font-weight: 600;\n  color: #555;\n  margin-top: 16px;\n  margin-bottom: 6px;\n}\n#mn-app .mn-error {\n  margin-top: 10px;\n  padding: 10px 14px;\n  background: #fef2f2;\n  border: 1px solid #fecaca;\n  border-radius: 6px;\n  font-size: 0.88rem;\n  color: #b91c1c;\n  display: none;\n}\n#mn-app .mn-error.visible { display: block; }\n#mn-app .mn-divider {\n  border: none;\n  border-top: 1px solid #e5e7eb;\n  margin: 24px 0;\n}\n#mn-app .mn-links {\n  font-size: 0.88rem;\n  color: #555;\n}\n#mn-app .mn-links a {\n  color: #2563eb;\n  text-decoration: none;\n}\n#mn-app .mn-links a:hover { text-decoration: underline; }\n\u003c/style\u003e\n\u003cdiv class=\"mn-tabs\"\u003e\n  \u003cdiv class=\"mn-tab active\" data-lang=\"html\" onclick=\"mnSwitchTab('html')\"\u003eHTML\u003c/div\u003e\n  \u003cdiv class=\"mn-tab\" data-lang=\"css\" onclick=\"mnSwitchTab('css')\"\u003eCSS\u003c/div\u003e\n  \u003cdiv class=\"mn-tab\" data-lang=\"js\" onclick=\"mnSwitchTab('js')\"\u003eJavaScript\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"mn-panel\"\u003e\n  \u003c!-- HTML options --\u003e\n  \u003cdiv id=\"mn-opts-html\" class=\"mn-options\"\u003e\n    \u003clabel\u003e\u003cinput type=\"checkbox\" id=\"mn-html-comments\" checked\u003e Remove comments\u003c/label\u003e\n    \u003clabel\u003e\u003cinput type=\"checkbox\" id=\"mn-html-whitespace\" checked\u003e Collapse whitespace\u003c/label\u003e\n    \u003clabel\u003e\u003cinput type=\"checkbox\" id=\"mn-html-optional-tags\"\u003e Remove optional closing tags\u003c/label\u003e\n  \u003c/div\u003e\n  \u003c!-- CSS options --\u003e\n  \u003cdiv id=\"mn-opts-css\" class=\"mn-options\" style=\"display:none\"\u003e\n    \u003clabel\u003e\u003cinput type=\"checkbox\" id=\"mn-css-comments\" checked\u003e Remove comments\u003c/label\u003e\n    \u003clabel\u003e\u003cinput type=\"checkbox\" id=\"mn-css-whitespace\" checked\u003e Remove whitespace\u003c/label\u003e\n    \u003clabel\u003e\u003cinput type=\"checkbox\" id=\"mn-css-last-semi\" checked\u003e Remove last semicolons\u003c/label\u003e\n    \u003clabel\u003e\u003cinput type=\"checkbox\" id=\"mn-css-merge\"\u003e Merge duplicate selectors\u003c/label\u003e\n  \u003c/div\u003e\n  \u003c!-- JS options --\u003e\n  \u003cdiv id=\"mn-opts-js\" class=\"mn-options\" style=\"display:none\"\u003e\n    \u003clabel\u003e\u003cinput type=\"checkbox\" id=\"mn-js-line-comments\" checked\u003e Remove // comments\u003c/label\u003e\n    \u003clabel\u003e\u003cinput type=\"checkbox\" id=\"mn-js-block-comments\" checked\u003e Remove /* */ comments\u003c/label\u003e\n    \u003clabel\u003e\u003cinput type=\"checkbox\" id=\"mn-js-whitespace\" checked\u003e Collapse whitespace\u003c/label\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"mn-upload-row\"\u003e\n    \u003clabel class=\"mn-upload-btn\" for=\"mn-file-input\"\u003e\u0026#128194; Upload file\u003c/label\u003e\n    \u003cinput type=\"file\" id=\"mn-file-input\" style=\"display:none\" accept=\".html,.htm,.css,.js,.txt\" onchange=\"mnLoadFile(this)\"\u003e\n    \u003cspan class=\"mn-file-name\" id=\"mn-file-name\"\u003eNo file chosen\u003c/span\u003e\n  \u003c/div\u003e\n  \u003ctextarea id=\"mn-input\" placeholder=\"Paste your HTML, CSS, or JavaScript here…\" spellcheck=\"false\"\u003e\u003c/textarea\u003e\n  \u003cdiv class=\"mn-actions\"\u003e\n    \u003cbutton class=\"mn-btn mn-btn-primary\" onclick=\"mnMinify()\"\u003e\u0026#9889; Minify\u003c/button\u003e\n    \u003cbutton class=\"mn-btn mn-btn-secondary\" onclick=\"mnBeautify()\"\u003e\u0026#10024; Beautify\u003c/button\u003e\n    \u003cbutton class=\"mn-btn mn-btn-copy\" onclick=\"mnCopy()\"\u003e\u0026#128203; Copy Result\u003c/button\u003e\n    \u003cbutton class=\"mn-btn mn-btn-clear\" onclick=\"mnClear()\"\u003eClear\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"mn-error\" id=\"mn-error\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"mn-stats\" id=\"mn-stats\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"mn-output-label\"\u003eOutput\u003c/div\u003e\n  \u003ctextarea id=\"mn-output\" placeholder=\"Minified output will appear here…\" spellcheck=\"false\" readonly\u003e\u003c/textarea\u003e\n\u003c/div\u003e\n\u003chr class=\"mn-divider\"\u003e\n\u003cdiv class=\"mn-links\"\u003e\n  Related tools: \u003ca href=\"/tools/sql-formatter/\"\u003eSQL Formatter\u003c/a\u003e \u0026nbsp;|\u0026nbsp; \u003ca href=\"/tools/json-formatter/\"\u003eJSON Formatter\u003c/a\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  'use strict';\n\n  var currentLang = 'html';\n\n  function mnSwitchTab(lang) {\n    currentLang = lang;\n    document.querySelectorAll('#mn-app .mn-tab').forEach(function (t) {\n      t.classList.toggle('active', t.dataset.lang === lang);\n    });\n    document.getElementById('mn-opts-html').style.display = lang === 'html' ? '' : 'none';\n    document.getElementById('mn-opts-css').style.display  = lang === 'css'  ? '' : 'none';\n    document.getElementById('mn-opts-js').style.display   = lang === 'js'   ? '' : 'none';\n    mnHideMessages();\n  }\n  window.mnSwitchTab = mnSwitchTab;\n\n  function mnHideMessages() {\n    document.getElementById('mn-error').classList.remove('visible');\n    document.getElementById('mn-stats').classList.remove('visible');\n  }\n\n  function mnShowError(msg) {\n    var el = document.getElementById('mn-error');\n    el.textContent = msg;\n    el.classList.add('visible');\n    document.getElementById('mn-stats').classList.remove('visible');\n  }\n\n  function mnShowStats(before, after) {\n    var saved = before - after;\n    var pct = before \u003e 0 ? ((saved / before) * 100).toFixed(1) : '0.0';\n    var el = document.getElementById('mn-stats');\n    el.innerHTML =\n      'Before: \u003cstrong\u003e' + before.toLocaleString() + ' bytes\u003c/strong\u003e \u0026nbsp;|\u0026nbsp; ' +\n      'After: \u003cstrong\u003e' + after.toLocaleString() + ' bytes\u003c/strong\u003e \u0026nbsp;|\u0026nbsp; ' +\n      'Saved: \u003cstrong\u003e' + saved.toLocaleString() + ' bytes (' + pct + '%)\u003c/strong\u003e';\n    el.classList.add('visible');\n    document.getElementById('mn-error').classList.remove('visible');\n  }\n\n  /* ---------- HTML minifier ---------- */\n  function minifyHTML(src) {\n    var removeComments  = document.getElementById('mn-html-comments').checked;\n    var collapseWS      = document.getElementById('mn-html-whitespace').checked;\n    var removeOptional  = document.getElementById('mn-html-optional-tags').checked;\n\n    var out = src;\n\n    if (removeComments) {\n      // Remove HTML comments (but not IE conditionals)\n      out = out.replace(/\u003c!--(?!\\[if)[\\s\\S]*?--\u003e/g, '');\n    }\n\n    if (collapseWS) {\n      // Collapse runs of whitespace (space/tab/newline) to single space\n      // Preserve content inside \u003cpre\u003e, \u003cscript\u003e, \u003cstyle\u003e, \u003ctextarea\u003e\n      out = preserveBlocks(out, function (s) {\n        return s.replace(/\\s+/g, ' ');\n      });\n      out = out.trim();\n    }\n\n    if (removeOptional) {\n      // Remove optional closing tags: \u003c/li\u003e, \u003c/dt\u003e, \u003c/dd\u003e, \u003c/p\u003e","title":"Code Minifier"},{"content":"Instantly preview how any color or palette appears to people with different types of color vision deficiency. Useful for designers, developers, and accessibility auditors.\nColor Input + Add Color (max 5) Simulate Reset Simulation Results Colors are approximated using the Vienot/Brettel algorithm. Results are visual estimates, not medical definitions. Accessibility Tips Designing for Color Vision Deficiency Never rely on color alone to convey meaning — use labels, patterns, or icons alongside color. Ensure sufficient contrast between adjacent colors (aim for WCAG AA: 4.5:1 for text). Avoid red-green combinations as the primary distinguishing factor in charts or UI. Use colorblind-friendly palettes (e.g., Okabe-Ito, IBM Carbon, or Colorbrewer) for data visualization. Test your designs with a simulator — tools like this one cover the most common deficiency types. Add texture or pattern fills to charts in addition to color coding. Provide alternative text descriptions for color-coded information. Color Vision Deficiency Statistics Type Description Affected (approx.) DeuteranopiaGreen cone absent (red-green)1% of males ProtanopiaRed cone absent (red-green)1% of males DeuteranomalyGreen cone shifted (most common)5% of males ProtanomalyRed cone shifted1% of males TritanopiaBlue cone absent (blue-yellow)~0.003% (all) TritanomalyBlue cone shifted~0.01% (all) AchromatopsiaNo color vision (total)~0.003% (all) AchromatomalyReduced color visionRare Source: Colour Blind Awareness. Male prevalence is higher for X-linked types (red-green). ~8% of males and ~0.5% of females have some form of color vision deficiency.\nCheck contrast ratios: Color Contrast Checker Convert colors: Color Converter Generate palettes: Color Palette Generator ","permalink":"https://productivity-works.com/tools/color-blindness-simulator/","summary":"\u003cp\u003eInstantly preview how any color or palette appears to people with different types of color vision deficiency. Useful for designers, developers, and accessibility auditors.\u003c/p\u003e\n\u003cdiv id=\"cb-app\"\u003e\n\u003cstyle\u003e\n#cb-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #1e293b;\n}\n#cb-app * { box-sizing: border-box; }\n\n#cb-app h2 {\n  font-size: 1.15rem;\n  font-weight: 700;\n  margin: 28px 0 12px;\n  color: #0f172a;\n  border-left: 4px solid #6366f1;\n  padding-left: 10px;\n}\n\n#cb-app .cb-input-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  align-items: center;\n  margin-bottom: 18px;\n}\n\n#cb-app .cb-color-entry {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 8px;\n  padding: 8px 12px;\n}\n\n#cb-app .cb-color-entry input[type=\"color\"] {\n  width: 40px;\n  height: 36px;\n  border: none;\n  border-radius: 6px;\n  cursor: pointer;\n  padding: 0;\n  background: none;\n}\n\n#cb-app .cb-color-entry input[type=\"text\"] {\n  width: 100px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 6px;\n  padding: 6px 10px;\n  font-size: 0.9rem;\n  font-family: monospace;\n  color: #334155;\n}\n\n#cb-app .cb-color-entry input[type=\"text\"]:focus {\n  outline: none;\n  border-color: #6366f1;\n}\n\n#cb-app .cb-remove-btn {\n  background: none;\n  border: none;\n  color: #94a3b8;\n  cursor: pointer;\n  font-size: 1.1rem;\n  padding: 2px 4px;\n  border-radius: 4px;\n  line-height: 1;\n}\n#cb-app .cb-remove-btn:hover { color: #ef4444; background: #fee2e2; }\n\n#cb-app .cb-btn {\n  padding: 9px 18px;\n  border-radius: 8px;\n  border: none;\n  font-size: 0.9rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s;\n}\n#cb-app .cb-btn:active { transform: scale(0.97); }\n\n#cb-app .cb-btn-primary {\n  background: #6366f1;\n  color: #fff;\n}\n#cb-app .cb-btn-primary:hover { background: #4f46e5; }\n\n#cb-app .cb-btn-secondary {\n  background: #e0e7ff;\n  color: #4338ca;\n}\n#cb-app .cb-btn-secondary:hover { background: #c7d2fe; }\n\n#cb-app .cb-btn-row {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n  margin-bottom: 24px;\n}\n\n#cb-app .cb-results-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n  gap: 20px;\n  margin-bottom: 32px;\n}\n\n#cb-app .cb-palette-block {\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 14px;\n}\n\n#cb-app .cb-palette-label {\n  font-size: 0.8rem;\n  font-weight: 600;\n  color: #64748b;\n  margin-bottom: 10px;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n\n#cb-app .cb-swatches {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n}\n\n#cb-app .cb-swatch-item {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 4px;\n}\n\n#cb-app .cb-swatch {\n  width: 56px;\n  height: 56px;\n  border-radius: 8px;\n  border: 1px solid rgba(0,0,0,0.1);\n  box-shadow: 0 1px 3px rgba(0,0,0,0.08);\n}\n\n#cb-app .cb-swatch-hex {\n  font-size: 0.7rem;\n  font-family: monospace;\n  color: #475569;\n}\n\n#cb-app .cb-type-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin-bottom: 6px;\n}\n\n#cb-app .cb-type-name {\n  font-size: 0.95rem;\n  font-weight: 700;\n  color: #1e293b;\n}\n\n#cb-app .cb-type-stat {\n  font-size: 0.78rem;\n  color: #64748b;\n  background: #e2e8f0;\n  border-radius: 99px;\n  padding: 2px 8px;\n}\n\n#cb-app .cb-type-desc {\n  font-size: 0.82rem;\n  color: #64748b;\n  margin-bottom: 10px;\n}\n\n#cb-app .cb-tips {\n  background: #f0fdf4;\n  border: 1.5px solid #bbf7d0;\n  border-radius: 10px;\n  padding: 16px 20px;\n  margin-top: 8px;\n}\n\n#cb-app .cb-tips h3 {\n  font-size: 1rem;\n  font-weight: 700;\n  color: #15803d;\n  margin: 0 0 10px;\n}\n\n#cb-app .cb-tips ul {\n  margin: 0;\n  padding-left: 20px;\n  color: #166534;\n  font-size: 0.88rem;\n  line-height: 1.7;\n}\n\n#cb-app .cb-stats-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.88rem;\n  margin-bottom: 12px;\n}\n\n#cb-app .cb-stats-table th {\n  background: #f1f5f9;\n  padding: 8px 12px;\n  text-align: left;\n  font-weight: 600;\n  color: #475569;\n  border-bottom: 2px solid #e2e8f0;\n}\n\n#cb-app .cb-stats-table td {\n  padding: 8px 12px;\n  border-bottom: 1px solid #f1f5f9;\n  color: #334155;\n}\n\n#cb-app .cb-stats-table tr:hover td { background: #f8fafc; }\n\n#cb-app .cb-error {\n  color: #dc2626;\n  font-size: 0.85rem;\n  margin-top: 4px;\n}\n\n#cb-app .cb-notice {\n  background: #fefce8;\n  border: 1.5px solid #fde68a;\n  border-radius: 8px;\n  padding: 10px 14px;\n  font-size: 0.85rem;\n  color: #92400e;\n  margin-bottom: 16px;\n}\n\n@media (max-width: 600px) {\n  #cb-app .cb-results-grid { grid-template-columns: 1fr; }\n  #cb-app .cb-swatch { width: 44px; height: 44px; }\n}\n\u003c/style\u003e\n\u003ch2\u003eColor Input\u003c/h2\u003e\n\u003cdiv id=\"cb-color-list\"\u003e\u003c/div\u003e\n\u003cdiv class=\"cb-btn-row\"\u003e\n  \u003cbutton class=\"cb-btn cb-btn-secondary\" onclick=\"cbAddColor()\"\u003e+ Add Color (max 5)\u003c/button\u003e\n  \u003cbutton class=\"cb-btn cb-btn-primary\" onclick=\"cbSimulate()\"\u003eSimulate\u003c/button\u003e\n  \u003cbutton class=\"cb-btn cb-btn-secondary\" onclick=\"cbReset()\"\u003eReset\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv id=\"cb-error\" class=\"cb-error\" style=\"display:none;\"\u003e\u003c/div\u003e\n\u003ch2\u003eSimulation Results\u003c/h2\u003e\n\u003cdiv class=\"cb-notice\"\u003eColors are approximated using the Vienot/Brettel algorithm. Results are visual estimates, not medical definitions.\u003c/div\u003e\n\u003cdiv id=\"cb-results\" class=\"cb-results-grid\"\u003e\u003c/div\u003e\n\u003ch2\u003eAccessibility Tips\u003c/h2\u003e\n\u003cdiv class=\"cb-tips\"\u003e\n  \u003ch3\u003eDesigning for Color Vision Deficiency\u003c/h3\u003e\n  \u003cul\u003e\n    \u003cli\u003eNever rely on color alone to convey meaning — use labels, patterns, or icons alongside color.\u003c/li\u003e\n    \u003cli\u003eEnsure sufficient contrast between adjacent colors (aim for WCAG AA: 4.5:1 for text).\u003c/li\u003e\n    \u003cli\u003eAvoid red-green combinations as the primary distinguishing factor in charts or UI.\u003c/li\u003e\n    \u003cli\u003eUse colorblind-friendly palettes (e.g., Okabe-Ito, IBM Carbon, or Colorbrewer) for data visualization.\u003c/li\u003e\n    \u003cli\u003eTest your designs with a simulator — tools like this one cover the most common deficiency types.\u003c/li\u003e\n    \u003cli\u003eAdd texture or pattern fills to charts in addition to color coding.\u003c/li\u003e\n    \u003cli\u003eProvide alternative text descriptions for color-coded information.\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003ch2\u003eColor Vision Deficiency Statistics\u003c/h2\u003e\n\u003ctable class=\"cb-stats-table\"\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n      \u003cth\u003eType\u003c/th\u003e\n      \u003cth\u003eDescription\u003c/th\u003e\n      \u003cth\u003eAffected (approx.)\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\u003ctd\u003eDeuteranopia\u003c/td\u003e\u003ctd\u003eGreen cone absent (red-green)\u003c/td\u003e\u003ctd\u003e1% of males\u003c/td\u003e\u003c/tr\u003e\n    \u003ctr\u003e\u003ctd\u003eProtanopia\u003c/td\u003e\u003ctd\u003eRed cone absent (red-green)\u003c/td\u003e\u003ctd\u003e1% of males\u003c/td\u003e\u003c/tr\u003e\n    \u003ctr\u003e\u003ctd\u003eDeuteranomaly\u003c/td\u003e\u003ctd\u003eGreen cone shifted (most common)\u003c/td\u003e\u003ctd\u003e5% of males\u003c/td\u003e\u003c/tr\u003e\n    \u003ctr\u003e\u003ctd\u003eProtanomaly\u003c/td\u003e\u003ctd\u003eRed cone shifted\u003c/td\u003e\u003ctd\u003e1% of males\u003c/td\u003e\u003c/tr\u003e\n    \u003ctr\u003e\u003ctd\u003eTritanopia\u003c/td\u003e\u003ctd\u003eBlue cone absent (blue-yellow)\u003c/td\u003e\u003ctd\u003e~0.003% (all)\u003c/td\u003e\u003c/tr\u003e\n    \u003ctr\u003e\u003ctd\u003eTritanomaly\u003c/td\u003e\u003ctd\u003eBlue cone shifted\u003c/td\u003e\u003ctd\u003e~0.01% (all)\u003c/td\u003e\u003c/tr\u003e\n    \u003ctr\u003e\u003ctd\u003eAchromatopsia\u003c/td\u003e\u003ctd\u003eNo color vision (total)\u003c/td\u003e\u003ctd\u003e~0.003% (all)\u003c/td\u003e\u003c/tr\u003e\n    \u003ctr\u003e\u003ctd\u003eAchromatomaly\u003c/td\u003e\u003ctd\u003eReduced color vision\u003c/td\u003e\u003ctd\u003eRare\u003c/td\u003e\u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp style=\"font-size:0.82rem;color:#64748b;\"\u003eSource: Colour Blind Awareness. Male prevalence is higher for X-linked types (red-green). ~8% of males and ~0.5% of females have some form of color vision deficiency.\u003c/p\u003e","title":"Color Blindness Simulator"},{"content":" Enter any foreground and background color to instantly calculate the WCAG 2.1 contrast ratio. See whether your color pair passes AA or AAA compliance for normal text, large text, and UI components.\nForeground (Text) \u0026#8644; Background Contrast Ratio 21:1 Maximum possible contrast Live Preview Normal text sample Large Text Sample Popular Color Pairs Need a design tool for your team? Pair this checker with a solid project management setup. We recommend checking out tools like Notion for organizing your design system — keeping your approved accessible color palettes documented and accessible to the whole team. Related Free Tools Color Name Finder Color Blindness Simulator ","permalink":"https://productivity-works.com/tools/contrast-checker/","summary":"\u003cdiv id=\"cc-app\"\u003e\n\u003cstyle\u003e\n#cc-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #1a1a1a;\n}\n#cc-app *, #cc-app *::before, #cc-app *::after {\n  box-sizing: border-box;\n}\n#cc-app h2 {\n  font-size: 1.4rem;\n  font-weight: 700;\n  margin: 0 0 1rem;\n  color: #111;\n}\n#cc-app h3 {\n  font-size: 1.05rem;\n  font-weight: 600;\n  margin: 0 0 0.75rem;\n  color: #333;\n}\n#cc-app .cc-intro {\n  font-size: 0.97rem;\n  color: #555;\n  margin-bottom: 1.75rem;\n  line-height: 1.6;\n}\n#cc-app .cc-pickers-row {\n  display: flex;\n  gap: 1.25rem;\n  align-items: flex-end;\n  flex-wrap: wrap;\n  margin-bottom: 1.25rem;\n}\n#cc-app .cc-picker-group {\n  flex: 1;\n  min-width: 200px;\n}\n#cc-app .cc-picker-label {\n  display: block;\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #555;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 0.5rem;\n}\n#cc-app .cc-picker-inner {\n  display: flex;\n  align-items: center;\n  gap: 0.6rem;\n  border: 1.5px solid #d0d5dd;\n  border-radius: 8px;\n  padding: 0.45rem 0.75rem;\n  background: #fff;\n}\n#cc-app .cc-picker-inner input[type=\"color\"] {\n  width: 38px;\n  height: 38px;\n  border: none;\n  border-radius: 6px;\n  padding: 0;\n  cursor: pointer;\n  background: transparent;\n  flex-shrink: 0;\n}\n#cc-app .cc-hex-input {\n  flex: 1;\n  border: none;\n  outline: none;\n  font-size: 1rem;\n  font-family: \"Courier New\", monospace;\n  font-weight: 600;\n  color: #1a1a1a;\n  background: transparent;\n  width: 90px;\n}\n#cc-app .cc-swap-btn {\n  background: #f4f5f7;\n  border: 1.5px solid #d0d5dd;\n  border-radius: 8px;\n  padding: 0.6rem 1.1rem;\n  cursor: pointer;\n  font-size: 1.25rem;\n  transition: background 0.15s;\n  flex-shrink: 0;\n  align-self: flex-end;\n  margin-bottom: 0;\n  line-height: 1;\n}\n#cc-app .cc-swap-btn:hover {\n  background: #e8eaed;\n}\n#cc-app .cc-ratio-card {\n  background: linear-gradient(135deg, #1e3a5f 0%, #2d5a9e 100%);\n  color: #fff;\n  border-radius: 12px;\n  padding: 1.5rem 2rem;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 1.5rem;\n  flex-wrap: wrap;\n  gap: 1rem;\n}\n#cc-app .cc-ratio-label {\n  font-size: 0.9rem;\n  opacity: 0.85;\n  font-weight: 500;\n  margin-bottom: 0.25rem;\n}\n#cc-app .cc-ratio-value {\n  font-size: 2.8rem;\n  font-weight: 800;\n  line-height: 1;\n  letter-spacing: -0.02em;\n}\n#cc-app .cc-ratio-tag {\n  font-size: 0.8rem;\n  opacity: 0.75;\n  margin-top: 0.3rem;\n}\n#cc-app .cc-preview-box {\n  border-radius: 10px;\n  padding: 1.25rem 1.75rem;\n  min-width: 220px;\n  border: 1px solid rgba(255,255,255,0.2);\n}\n#cc-app .cc-preview-label {\n  font-size: 0.75rem;\n  opacity: 0.8;\n  font-weight: 500;\n  margin-bottom: 0.4rem;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n#cc-app .cc-preview-normal {\n  font-size: 1rem;\n  margin-bottom: 0.35rem;\n}\n#cc-app .cc-preview-large {\n  font-size: 1.4rem;\n  font-weight: 700;\n}\n#cc-app .cc-results-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));\n  gap: 1rem;\n  margin-bottom: 1.75rem;\n}\n#cc-app .cc-result-card {\n  background: #fff;\n  border: 1.5px solid #e2e6ea;\n  border-radius: 10px;\n  padding: 1.1rem 1.25rem;\n  transition: border-color 0.15s;\n}\n#cc-app .cc-result-card.pass {\n  border-left: 4px solid #22c55e;\n}\n#cc-app .cc-result-card.fail {\n  border-left: 4px solid #ef4444;\n}\n#cc-app .cc-result-type {\n  font-size: 0.78rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  color: #888;\n  margin-bottom: 0.3rem;\n}\n#cc-app .cc-result-name {\n  font-size: 0.97rem;\n  font-weight: 600;\n  color: #1a1a1a;\n  margin-bottom: 0.5rem;\n}\n#cc-app .cc-result-req {\n  font-size: 0.82rem;\n  color: #777;\n  margin-bottom: 0.5rem;\n}\n#cc-app .cc-badge {\n  display: inline-block;\n  padding: 0.2rem 0.7rem;\n  border-radius: 999px;\n  font-size: 0.82rem;\n  font-weight: 700;\n  letter-spacing: 0.03em;\n}\n#cc-app .cc-badge.pass {\n  background: #dcfce7;\n  color: #166534;\n}\n#cc-app .cc-badge.fail {\n  background: #fee2e2;\n  color: #991b1b;\n}\n#cc-app .cc-section-title {\n  font-size: 0.82rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  color: #888;\n  margin: 1.75rem 0 0.85rem;\n  padding-bottom: 0.4rem;\n  border-bottom: 1px solid #e8eaed;\n}\n#cc-app .cc-suggestions {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.6rem;\n  margin-bottom: 1.75rem;\n}\n#cc-app .cc-sug-btn {\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n  padding: 0.4rem 0.85rem;\n  border-radius: 7px;\n  border: 1.5px solid #d0d5dd;\n  cursor: pointer;\n  font-size: 0.83rem;\n  font-weight: 500;\n  background: #fff;\n  transition: all 0.15s;\n  color: #333;\n}\n#cc-app .cc-sug-btn:hover {\n  border-color: #2d5a9e;\n  background: #f0f4ff;\n}\n#cc-app .cc-sug-swatch {\n  display: inline-flex;\n  gap: 2px;\n}\n#cc-app .cc-sug-swatch span {\n  display: inline-block;\n  width: 14px;\n  height: 14px;\n  border-radius: 3px;\n  border: 1px solid rgba(0,0,0,0.12);\n}\n#cc-app .cc-cta-box {\n  background: #f8f9fb;\n  border: 1.5px solid #e2e6ea;\n  border-radius: 10px;\n  padding: 1.25rem 1.5rem;\n  margin-top: 2rem;\n  font-size: 0.9rem;\n  color: #555;\n  line-height: 1.6;\n}\n#cc-app .cc-cta-box a {\n  color: #2d5a9e;\n  font-weight: 600;\n  text-decoration: underline;\n}\n#cc-app .cc-related {\n  margin-top: 2rem;\n  padding-top: 1.25rem;\n  border-top: 1px solid #e8eaed;\n}\n#cc-app .cc-related-links {\n  display: flex;\n  gap: 1rem;\n  flex-wrap: wrap;\n  margin-top: 0.75rem;\n}\n#cc-app .cc-related-links a {\n  display: inline-block;\n  padding: 0.45rem 1rem;\n  background: #f4f5f7;\n  border: 1.5px solid #d0d5dd;\n  border-radius: 7px;\n  color: #2d5a9e;\n  font-size: 0.88rem;\n  font-weight: 600;\n  text-decoration: none;\n  transition: all 0.15s;\n}\n#cc-app .cc-related-links a:hover {\n  background: #e8edf7;\n  border-color: #2d5a9e;\n}\n@media (max-width: 540px) {\n  #cc-app .cc-ratio-card {\n    flex-direction: column;\n    align-items: flex-start;\n  }\n  #cc-app .cc-pickers-row {\n    flex-direction: column;\n  }\n  #cc-app .cc-swap-btn {\n    align-self: center;\n  }\n}\n\u003c/style\u003e\n\u003cp class=\"cc-intro\"\u003eEnter any foreground and background color to instantly calculate the WCAG 2.1 contrast ratio. See whether your color pair passes AA or AAA compliance for normal text, large text, and UI components.\u003c/p\u003e","title":"Color Contrast Checker"},{"content":" Color Contrast Checker WCAG 2.1 — AA \u0026amp; AAA compliance checker with live preview and color blindness simulation\nForeground (Text)\nHEX RGB HSL Background\nHEX RGB HSL \u0026#8645; Swap Colors \u0026#10022; Random Accessible Pair 14.69 contrast ratio AA Normal PASS AA Large PASS AAA Normal PASS AAA Large PASS Live Preview\nHeading Text (24px bold)\nLarge text sample — 18px regular weight, qualifying as \"large text\" under WCAG 2.1.\nNormal body text at 15px. This size requires a contrast ratio of at least 4.5:1 for WCAG AA compliance and 7:1 for AAA. Make sure your color combinations are readable for all users, including those with low vision or color blindness.\nSuggested Accessible Alternatives\nColor Blindness Simulation\nNormal Vision Sample Text Protanopia (red-blind) Sample Text Deuteranopia (green-blind) Sample Text Tritanopia (blue-blind) Sample Text How to Use This Contrast Checker Pick your colors by clicking the color swatch to open the native color picker, or type directly into the HEX field. Switch between HEX, RGB, and HSL formats using the tabs above the input — all formats stay in sync automatically.\nRead the ratio in the large number displayed below the pickers. A higher ratio means more contrast. The four badges (AA Normal, AA Large, AAA Normal, AAA Large) each show PASS or FAIL based on WCAG 2.1 thresholds.\nUse the live preview to see exactly how your text will look at heading, large, and normal body sizes — all rendered in real time with your chosen colors.\nCheck suggested fixes when your ratio falls below AA. The tool calculates the nearest accessible alternative by incrementally adjusting lightness, and each suggestion is clickable to instantly apply it.\nSimulate color blindness in the bottom grid. The four panels show how your color pair appears under normal vision, protanopia (red-blind), deuteranopia (green-blind), and tritanopia (blue-blind).\nSwap colors at any time to flip foreground and background — useful for checking both light-on-dark and dark-on-light variants of the same palette.\nGenerate a random accessible pair to explore color combinations that already meet WCAG AA requirements.\nUnderstanding WCAG Contrast Requirements WCAG 2.1 (Web Content Accessibility Guidelines) defines contrast ratio thresholds that ensure text remains readable for users with low vision or color blindness. Contrast ratio is calculated using the relative luminance of two colors — a value derived from the linear RGB channels — and expressed as a ratio such as 4.5:1 or 7:1.\nLevel AA is the standard legal and industry baseline. It requires a contrast ratio of at least 4.5:1 for normal text (below 18pt, or below 14pt bold) and 3:1 for large text (18pt or larger, or 14pt bold or larger). Most web accessibility laws worldwide — including WCAG referenced by the EU Web Accessibility Directive, Section 508 in the US, and JIS X 8341-3 in Japan — require Level AA compliance.\nLevel AAA is the enhanced standard. It requires 7:1 for normal text and 4.5:1 for large text. AAA is recommended for critical interfaces such as healthcare portals, government services, and financial applications where text legibility is essential.\nColor blindness affects approximately 8% of males and 0.5% of females worldwide. The most common forms are protanopia (reduced red sensitivity), deuteranopia (reduced green sensitivity), and tritanopia (reduced blue sensitivity). Because color blindness changes how colors are perceived — not just their brightness — it is possible to have a passing contrast ratio under normal vision but poor readability under color-blind simulation. Always check both.\nRelated Tools Pick colors → Color Picker Generate palettes → Color Palette Generator Create gradients → CSS Gradient Generator ","permalink":"https://productivity-works.com/tools/color-contrast-checker/","summary":"\u003cdiv id=\"cc-app\"\u003e\n\u003cstyle\u003e\n#cc-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 0 0 40px 0;\n  color: #1a1a2e;\n}\n\n#cc-app * {\n  box-sizing: border-box;\n}\n\n/* ── Hero ── */\n.cc-hero {\n  background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);\n  border-radius: 16px;\n  padding: 28px 24px;\n  margin-bottom: 24px;\n  color: #fff;\n}\n\n.cc-hero-title {\n  font-size: 1.5rem;\n  font-weight: 700;\n  margin: 0 0 4px;\n}\n\n.cc-hero-sub {\n  font-size: 0.9rem;\n  opacity: 0.75;\n  margin: 0;\n}\n\n/* ── Layout ── */\n.cc-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 16px;\n  margin-bottom: 16px;\n}\n\n@media (max-width: 600px) {\n  .cc-grid { grid-template-columns: 1fr; }\n}\n\n.cc-card {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 20px;\n}\n\n.cc-card-title {\n  font-size: 0.75rem;\n  font-weight: 700;\n  letter-spacing: 0.08em;\n  text-transform: uppercase;\n  color: #64748b;\n  margin: 0 0 14px;\n}\n\n/* ── Color pickers ── */\n.cc-color-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-bottom: 12px;\n}\n\n.cc-swatch-btn {\n  width: 48px;\n  height: 48px;\n  border-radius: 10px;\n  border: 3px solid #e2e8f0;\n  cursor: pointer;\n  position: relative;\n  flex-shrink: 0;\n  overflow: hidden;\n  padding: 0;\n  background: transparent;\n}\n\n.cc-swatch-btn input[type=\"color\"] {\n  position: absolute;\n  inset: 0;\n  width: 100%;\n  height: 100%;\n  opacity: 0;\n  cursor: pointer;\n  border: none;\n  padding: 0;\n}\n\n.cc-swatch-inner {\n  display: block;\n  width: 100%;\n  height: 100%;\n  border-radius: 7px;\n  pointer-events: none;\n}\n\n.cc-inputs {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n  flex: 1;\n}\n\n.cc-mode-tabs {\n  display: flex;\n  gap: 4px;\n  margin-bottom: 4px;\n}\n\n.cc-mode-tab {\n  padding: 2px 8px;\n  border-radius: 6px;\n  border: 1px solid #e2e8f0;\n  background: #f8fafc;\n  font-size: 0.7rem;\n  font-weight: 600;\n  cursor: pointer;\n  color: #64748b;\n  transition: all 0.15s;\n}\n\n.cc-mode-tab.active {\n  background: #0f3460;\n  border-color: #0f3460;\n  color: #fff;\n}\n\n.cc-text-input {\n  width: 100%;\n  padding: 7px 10px;\n  border: 1px solid #e2e8f0;\n  border-radius: 8px;\n  font-size: 0.85rem;\n  font-family: 'Courier New', monospace;\n  color: #1a1a2e;\n  background: #f8fafc;\n  outline: none;\n  transition: border-color 0.15s;\n}\n\n.cc-text-input:focus {\n  border-color: #0f3460;\n  background: #fff;\n}\n\n.cc-text-input.invalid {\n  border-color: #ef4444;\n  background: #fef2f2;\n}\n\n/* ── Swap button ── */\n.cc-swap-row {\n  display: flex;\n  justify-content: center;\n  margin-bottom: 16px;\n}\n\n.cc-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  padding: 9px 18px;\n  border-radius: 9px;\n  border: none;\n  font-size: 0.85rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n\n.cc-btn-primary {\n  background: #0f3460;\n  color: #fff;\n}\n\n.cc-btn-primary:hover {\n  background: #1a4a80;\n  transform: translateY(-1px);\n}\n\n.cc-btn-secondary {\n  background: #f1f5f9;\n  color: #334155;\n  border: 1px solid #e2e8f0;\n}\n\n.cc-btn-secondary:hover {\n  background: #e2e8f0;\n}\n\n.cc-btn-swap {\n  background: #f8fafc;\n  color: #334155;\n  border: 1px solid #e2e8f0;\n  border-radius: 50px;\n  padding: 8px 20px;\n  font-size: 0.85rem;\n  font-weight: 600;\n}\n\n.cc-btn-swap:hover {\n  background: #e2e8f0;\n}\n\n.cc-action-row {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n  justify-content: center;\n  margin-bottom: 24px;\n}\n\n/* ── Ratio display ── */\n.cc-ratio-card {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 20px 24px;\n  margin-bottom: 16px;\n  display: flex;\n  align-items: center;\n  gap: 24px;\n  flex-wrap: wrap;\n}\n\n.cc-ratio-number {\n  font-size: 3rem;\n  font-weight: 800;\n  line-height: 1;\n  color: #1a1a2e;\n  letter-spacing: -0.03em;\n}\n\n.cc-ratio-label {\n  font-size: 0.8rem;\n  color: #64748b;\n  margin-top: 2px;\n}\n\n.cc-badges {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  flex: 1;\n}\n\n.cc-badge {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  padding: 10px 14px;\n  border-radius: 10px;\n  min-width: 80px;\n  text-align: center;\n}\n\n.cc-badge-label {\n  font-size: 0.7rem;\n  font-weight: 700;\n  letter-spacing: 0.06em;\n  text-transform: uppercase;\n  margin-bottom: 4px;\n}\n\n.cc-badge-result {\n  font-size: 0.95rem;\n  font-weight: 800;\n}\n\n.cc-badge.pass {\n  background: #dcfce7;\n  color: #15803d;\n}\n\n.cc-badge.fail {\n  background: #fee2e2;\n  color: #dc2626;\n}\n\n/* ── Live Preview ── */\n.cc-preview-card {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 20px;\n  margin-bottom: 16px;\n}\n\n.cc-preview-stage {\n  border-radius: 10px;\n  padding: 24px;\n  transition: background-color 0.2s;\n}\n\n.cc-preview-heading {\n  font-size: 1.5rem;\n  font-weight: 700;\n  margin: 0 0 8px;\n  line-height: 1.3;\n}\n\n.cc-preview-large {\n  font-size: 1.125rem;\n  font-weight: 400;\n  margin: 0 0 10px;\n  line-height: 1.5;\n}\n\n.cc-preview-normal {\n  font-size: 0.9375rem;\n  font-weight: 400;\n  margin: 0;\n  line-height: 1.6;\n}\n\n/* ── Suggestions ── */\n.cc-suggestions {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 20px;\n  margin-bottom: 16px;\n}\n\n.cc-suggestions-title {\n  font-size: 0.75rem;\n  font-weight: 700;\n  letter-spacing: 0.08em;\n  text-transform: uppercase;\n  color: #64748b;\n  margin: 0 0 14px;\n}\n\n.cc-suggest-grid {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n}\n\n.cc-suggest-item {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 8px 12px;\n  border-radius: 8px;\n  border: 1px solid #e2e8f0;\n  cursor: pointer;\n  background: #f8fafc;\n  transition: all 0.15s;\n  font-size: 0.8rem;\n}\n\n.cc-suggest-item:hover {\n  border-color: #0f3460;\n  background: #eff6ff;\n}\n\n.cc-suggest-swatch {\n  width: 24px;\n  height: 24px;\n  border-radius: 6px;\n  border: 1px solid rgba(0,0,0,0.1);\n  flex-shrink: 0;\n}\n\n.cc-suggest-info {\n  display: flex;\n  flex-direction: column;\n  gap: 2px;\n}\n\n.cc-suggest-hex {\n  font-family: 'Courier New', monospace;\n  font-size: 0.78rem;\n  font-weight: 600;\n  color: #1a1a2e;\n}\n\n.cc-suggest-ratio {\n  font-size: 0.72rem;\n  color: #64748b;\n}\n\n/* ── Color Blindness Simulation ── */\n.cc-cbsim {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 20px;\n  margin-bottom: 16px;\n}\n\n.cc-cbsim-title {\n  font-size: 0.75rem;\n  font-weight: 700;\n  letter-spacing: 0.08em;\n  text-transform: uppercase;\n  color: #64748b;\n  margin: 0 0 14px;\n}\n\n.cc-cbsim-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));\n  gap: 10px;\n}\n\n.cc-cbsim-item {\n  border-radius: 8px;\n  overflow: hidden;\n  border: 1px solid #e2e8f0;\n}\n\n.cc-cbsim-label {\n  font-size: 0.7rem;\n  font-weight: 600;\n  padding: 5px 10px;\n  background: #f8fafc;\n  color: #64748b;\n  border-bottom: 1px solid #e2e8f0;\n  text-align: center;\n}\n\n.cc-cbsim-stage {\n  padding: 14px;\n  font-size: 0.875rem;\n  font-weight: 500;\n  text-align: center;\n  line-height: 1.4;\n  min-height: 60px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n/* ── Hidden ── */\n.cc-hidden { display: none; }\n\u003c/style\u003e\n\u003c!-- Hero --\u003e\n\u003cdiv class=\"cc-hero\"\u003e\n  \u003cdiv class=\"cc-hero-title\"\u003eColor Contrast Checker\u003c/div\u003e\n  \u003cp class=\"cc-hero-sub\"\u003eWCAG 2.1 — AA \u0026amp; AAA compliance checker with live preview and color blindness simulation\u003c/p\u003e","title":"Color Contrast Checker — WCAG Accessibility Tool"},{"content":"Convert any color between HEX, RGB, HSL, HSV/HSB, CMYK, and CSS named colors instantly. Pick a color, paste a value in any format, and every other format updates live. Generate lighter and darker shades, find the complementary color, and copy any format with one click.\nColor Converter Picker Enter any format (HEX, RGB, HSL, HSV, CMYK, CSS name) Selected Color Complementary HEXCopy RGBCopy HSLCopy HSVCopy CMYKCopy CSS—Copy Lighter \u0026amp; Darker Shades (click to apply) How to Use Pick a color using the color picker, or type any value into the text box. Supported input formats: #rrggbb, #rgb, rgb(r, g, b), hsl(h, s%, l%), hsv(h, s%, v%), cmyk(c%, m%, y%, k%), or any CSS named color (e.g. coral, steelblue). All output formats update instantly. Click any shade block in the Lighter/Darker section to jump to that shade. Hit Copy next to any format to copy it to your clipboard. Format Reference Format Example Use case HEX #5B6AF0 Web development, design tools RGB rgb(91, 106, 240) CSS, canvas, image processing HSL hsl(235, 82%, 65%) CSS theming, intuitive adjustments HSV/HSB hsv(235, 62%, 94%) Photoshop, Illustrator color pickers CMYK cmyk(62%, 56%, 0%, 6%) Print design, press-ready files CSS Name cornflowerblue Quick CSS shorthand Related Tools Color Picker — Advanced eyedropper and palette builder Color Palette Generator — Generate harmonious color palettes Color Contrast Checker — WCAG accessibility contrast ratio checker ","permalink":"https://productivity-works.com/tools/color-converter/","summary":"\u003cp\u003eConvert any color between \u003cstrong\u003eHEX, RGB, HSL, HSV/HSB, CMYK\u003c/strong\u003e, and CSS named colors instantly. Pick a color, paste a value in any format, and every other format updates live. Generate lighter and darker shades, find the complementary color, and copy any format with one click.\u003c/p\u003e\n\u003cdiv id=\"colconv-app\"\u003e\n\u003cstyle\u003e\n#colconv-app *,\n#colconv-app *::before,\n#colconv-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#colconv-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 15px;\n  color: #1a1a2e;\n  background: #f7f8fc;\n  border-radius: 12px;\n  padding: 24px;\n  max-width: 720px;\n  margin: 0 auto;\n}\n#colconv-app h2 {\n  font-size: 1.3rem;\n  font-weight: 700;\n  margin-bottom: 16px;\n  color: #12122a;\n}\n#colconv-app h3 {\n  font-size: 1rem;\n  font-weight: 600;\n  margin-bottom: 10px;\n  color: #12122a;\n}\n\u003cp\u003e/* Input section */\n#colconv-input-row {\ndisplay: flex;\nflex-wrap: wrap;\ngap: 12px;\nalign-items: center;\nmargin-bottom: 20px;\nbackground: #fff;\nborder-radius: 10px;\npadding: 16px;\nbox-shadow: 0 1px 4px rgba(0,0,0,0.07);\n}\n#colconv-picker-wrap {\ndisplay: flex;\nflex-direction: column;\nalign-items: center;\ngap: 6px;\n}\n#colconv-picker-wrap label {\nfont-size: 0.75rem;\ncolor: #666;\ntext-transform: uppercase;\nletter-spacing: 0.05em;\n}\n#colconv-picker {\nwidth: 56px;\nheight: 56px;\nborder: none;\nborder-radius: 8px;\ncursor: pointer;\npadding: 2px;\nbackground: none;\n}\n#colconv-picker::-webkit-color-swatch-wrapper { padding: 0; border-radius: 6px; }\n#colconv-picker::-webkit-color-swatch { border: none; border-radius: 6px; }\n#colconv-text-input-wrap {\nflex: 1;\nmin-width: 200px;\ndisplay: flex;\nflex-direction: column;\ngap: 4px;\n}\n#colconv-text-input-wrap label {\nfont-size: 0.75rem;\ncolor: #666;\ntext-transform: uppercase;\nletter-spacing: 0.05em;\n}\n#colconv-raw-input {\nwidth: 100%;\npadding: 10px 14px;\nborder: 1.5px solid #d0d4e8;\nborder-radius: 8px;\nfont-size: 1rem;\noutline: none;\ntransition: border-color 0.2s;\nbackground: #fafbff;\n}\n#colconv-raw-input:focus { border-color: #5b6af0; }\n#colconv-error {\nfont-size: 0.8rem;\ncolor: #e53e3e;\nmin-height: 18px;\n}\u003c/p\u003e","title":"Color Converter — HEX RGB HSL CMYK"},{"content":"Type your text, pick up to 5 color stops, and generate a beautiful color gradient across every character. Preview the result live, then copy the HTML (inline color spans) or the CSS background-clip gradient version.\nYour Text Hello World! Preview size: 36px \u0026lt;div class=\u0026quot;gt-panel\u0026quot; style=\u0026quot;margin-top:16px;\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Color Stops\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;gt-stops\u0026quot; id=\u0026quot;gt-stops-list\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;gt-add-stop\u0026quot; id=\u0026quot;gt-add-stop-btn\u0026quot;\u0026gt;+ Add Color Stop\u0026lt;/button\u0026gt; \u0026lt;div class=\u0026quot;gt-options\u0026quot;\u0026gt; \u0026lt;label class=\u0026quot;gt-option-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;gt-skip-spaces\u0026quot;\u0026gt; Skip spaces (no color on spaces) \u0026lt;/label\u0026gt; \u0026lt;label class=\u0026quot;gt-option-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;gt-bold\u0026quot; checked\u0026gt; Bold preview \u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Live Preview \u0026lt;div class=\u0026quot;gt-panel\u0026quot; style=\u0026quot;margin-top:16px;\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Output\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;gt-tabs\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;gt-tab active\u0026quot; id=\u0026quot;gt-tab-html\u0026quot; onclick=\u0026quot;gtSwitchTab('html')\u0026quot;\u0026gt;HTML (inline spans)\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;gt-tab\u0026quot; id=\u0026quot;gt-tab-css\u0026quot; onclick=\u0026quot;gtSwitchTab('css')\u0026quot;\u0026gt;CSS gradient\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;gt-output\u0026quot;\u0026gt; \u0026lt;pre id=\u0026quot;gt-output-code\u0026quot;\u0026gt;\u0026lt;/pre\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;gt-btn-row\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;gt-btn gt-btn-primary\u0026quot; onclick=\u0026quot;gtCopy()\u0026quot;\u0026gt;Copy Output\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;gt-btn gt-btn-ghost\u0026quot; onclick=\u0026quot;gtReset()\u0026quot;\u0026gt;Reset\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Copied! Generate color schemes → Color Scheme Generator Convert colors → RGB HEX Converter ","permalink":"https://productivity-works.com/tools/color-gradient-text/","summary":"\u003cp\u003eType your text, pick up to 5 color stops, and generate a beautiful color gradient across every character. Preview the result live, then copy the HTML (inline \u003ccode\u003ecolor\u003c/code\u003e spans) or the CSS \u003ccode\u003ebackground-clip\u003c/code\u003e gradient version.\u003c/p\u003e\n\u003cdiv id=\"gt-app\"\u003e\n\u003cstyle\u003e\n/* ── Reset ──────────────────────────────────────────────────── */\n#gt-app *, #gt-app *::before, #gt-app *::after {\n  box-sizing: border-box; margin: 0; padding: 0;\n}\n#gt-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  font-size: 14px;\n  color: #1a1a2e;\n  line-height: 1.5;\n}\n\n/* ── Layout ─────────────────────────────────────────────────── */\n#gt-app .gt-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 20px;\n  margin-top: 20px;\n}\n@media (max-width: 740px) {\n  #gt-app .gt-grid { grid-template-columns: 1fr; }\n}\n\n/* ── Panel ──────────────────────────────────────────────────── */\n#gt-app .gt-panel {\n  background: #f8f9fc;\n  border: 1px solid #e2e6f0;\n  border-radius: 12px;\n  padding: 18px;\n}\n#gt-app .gt-panel h3 {\n  font-size: 12px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: .07em;\n  color: #6b7280;\n  margin-bottom: 14px;\n}\n\n/* ── Text input ─────────────────────────────────────────────── */\n#gt-app textarea {\n  width: 100%;\n  border: 1px solid #d1d5db;\n  border-radius: 8px;\n  padding: 10px 12px;\n  font-size: 15px;\n  font-family: inherit;\n  resize: vertical;\n  min-height: 80px;\n  outline: none;\n  transition: border-color .2s;\n}\n#gt-app textarea:focus { border-color: #6366f1; }\n\n/* ── Color stops ────────────────────────────────────────────── */\n#gt-app .gt-stops {\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n}\n#gt-app .gt-stop-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n#gt-app .gt-stop-row input[type=\"color\"] {\n  width: 38px;\n  height: 38px;\n  border: 2px solid #e2e6f0;\n  border-radius: 8px;\n  cursor: pointer;\n  padding: 2px;\n  background: #fff;\n}\n#gt-app .gt-stop-row input[type=\"text\"] {\n  width: 90px;\n  border: 1px solid #d1d5db;\n  border-radius: 6px;\n  padding: 6px 10px;\n  font-size: 13px;\n  font-family: monospace;\n  outline: none;\n  text-transform: uppercase;\n}\n#gt-app .gt-stop-row input[type=\"text\"]:focus { border-color: #6366f1; }\n#gt-app .gt-stop-row .gt-stop-label {\n  font-size: 12px;\n  color: #6b7280;\n  flex: 1;\n}\n#gt-app .gt-stop-row button.gt-rm {\n  background: none;\n  border: 1px solid #e5e7eb;\n  border-radius: 6px;\n  width: 28px; height: 28px;\n  cursor: pointer;\n  font-size: 15px;\n  color: #ef4444;\n  line-height: 1;\n  display: flex; align-items: center; justify-content: center;\n}\n#gt-app .gt-stop-row button.gt-rm:hover { background: #fef2f2; }\n#gt-app button.gt-add-stop {\n  margin-top: 6px;\n  background: none;\n  border: 1.5px dashed #6366f1;\n  border-radius: 8px;\n  padding: 7px 14px;\n  font-size: 13px;\n  color: #6366f1;\n  cursor: pointer;\n  width: 100%;\n  font-weight: 600;\n}\n#gt-app button.gt-add-stop:hover { background: #eef2ff; }\n#gt-app button.gt-add-stop:disabled { opacity: .4; cursor: not-allowed; }\n\n/* ── Options ────────────────────────────────────────────────── */\n#gt-app .gt-options {\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n  margin-top: 14px;\n}\n#gt-app .gt-option-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  font-size: 13px;\n  color: #374151;\n  cursor: pointer;\n}\n#gt-app .gt-option-row input[type=\"checkbox\"] { accent-color: #6366f1; width: 15px; height: 15px; }\n\n/* ── Preview ────────────────────────────────────────────────── */\n#gt-app .gt-preview-box {\n  min-height: 90px;\n  background: #fff;\n  border: 1px solid #e2e6f0;\n  border-radius: 10px;\n  padding: 20px 16px;\n  font-size: 32px;\n  font-weight: 800;\n  letter-spacing: .01em;\n  word-break: break-all;\n  line-height: 1.3;\n  margin-bottom: 14px;\n  display: flex;\n  align-items: center;\n  flex-wrap: wrap;\n}\n#gt-app .gt-preview-box.gt-css-mode {\n  background: linear-gradient(90deg, #6366f1, #ec4899);\n  -webkit-background-clip: text;\n  -webkit-text-fill-color: transparent;\n  background-clip: text;\n}\n\n/* ── Gradient bar ───────────────────────────────────────────── */\n#gt-app .gt-bar {\n  height: 10px;\n  border-radius: 5px;\n  margin-bottom: 16px;\n  border: 1px solid #e2e6f0;\n}\n\n/* ── Output ─────────────────────────────────────────────────── */\n#gt-app .gt-output {\n  margin-top: 4px;\n}\n#gt-app .gt-output pre {\n  background: #0f172a;\n  color: #e2e8f0;\n  border-radius: 8px;\n  padding: 14px;\n  font-size: 12px;\n  font-family: monospace;\n  overflow-x: auto;\n  white-space: pre-wrap;\n  word-break: break-all;\n  max-height: 180px;\n  line-height: 1.6;\n}\n#gt-app .gt-tabs {\n  display: flex;\n  gap: 6px;\n  margin-bottom: 10px;\n}\n#gt-app .gt-tab {\n  padding: 6px 14px;\n  border-radius: 6px;\n  border: 1.5px solid #d1d5db;\n  background: #fff;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  color: #374151;\n}\n#gt-app .gt-tab.active {\n  background: #6366f1;\n  color: #fff;\n  border-color: #6366f1;\n}\n\n/* ── Buttons ────────────────────────────────────────────────── */\n#gt-app .gt-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  padding: 8px 16px;\n  border-radius: 8px;\n  border: none;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background .15s, transform .1s;\n}\n#gt-app .gt-btn:active { transform: scale(.97); }\n#gt-app .gt-btn-primary {\n  background: #6366f1;\n  color: #fff;\n}\n#gt-app .gt-btn-primary:hover { background: #4f46e5; }\n#gt-app .gt-btn-ghost {\n  background: #f3f4f6;\n  color: #374151;\n}\n#gt-app .gt-btn-ghost:hover { background: #e5e7eb; }\n#gt-app .gt-btn-row {\n  display: flex;\n  gap: 8px;\n  margin-top: 10px;\n  flex-wrap: wrap;\n}\n\n/* ── Toast ──────────────────────────────────────────────────── */\n#gt-app .gt-toast {\n  display: none;\n  position: fixed;\n  bottom: 24px;\n  left: 50%;\n  transform: translateX(-50%);\n  background: #1e293b;\n  color: #f8fafc;\n  padding: 10px 22px;\n  border-radius: 8px;\n  font-size: 13px;\n  z-index: 9999;\n  pointer-events: none;\n}\n#gt-app .gt-toast.show { display: block; }\n\n/* ── Font size ──────────────────────────────────────────────── */\n#gt-app .gt-font-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-top: 10px;\n  font-size: 13px;\n  color: #374151;\n}\n#gt-app .gt-font-row input[type=\"range\"] {\n  flex: 1;\n  accent-color: #6366f1;\n}\n\u003c/style\u003e\n\u003cdiv class=\"gt-grid\"\u003e\n  \u003c!-- LEFT: Controls --\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"gt-panel\"\u003e\n      \u003ch3\u003eYour Text\u003c/h3\u003e\n      \u003ctextarea id=\"gt-text\" rows=\"3\" placeholder=\"Type your text here...\"\u003eHello World!\u003c/textarea\u003e\n      \u003cdiv class=\"gt-font-row\"\u003e\n        \u003cspan\u003ePreview size:\u003c/span\u003e\n        \u003cinput type=\"range\" id=\"gt-font-size\" min=\"16\" max=\"72\" value=\"36\"\u003e\n        \u003cspan id=\"gt-font-label\"\u003e36px\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;div class=\u0026quot;gt-panel\u0026quot; style=\u0026quot;margin-top:16px;\u0026quot;\u0026gt;\n  \u0026lt;h3\u0026gt;Color Stops\u0026lt;/h3\u0026gt;\n  \u0026lt;div class=\u0026quot;gt-stops\u0026quot; id=\u0026quot;gt-stops-list\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n  \u0026lt;button class=\u0026quot;gt-add-stop\u0026quot; id=\u0026quot;gt-add-stop-btn\u0026quot;\u0026gt;+ Add Color Stop\u0026lt;/button\u0026gt;\n  \u0026lt;div class=\u0026quot;gt-options\u0026quot;\u0026gt;\n    \u0026lt;label class=\u0026quot;gt-option-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;gt-skip-spaces\u0026quot;\u0026gt; Skip spaces (no color on spaces)\n    \u0026lt;/label\u0026gt;\n    \u0026lt;label class=\u0026quot;gt-option-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;gt-bold\u0026quot; checked\u0026gt; Bold preview\n    \u0026lt;/label\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n  \u003c!-- RIGHT: Preview + Output --\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"gt-panel\"\u003e\n      \u003ch3\u003eLive Preview\u003c/h3\u003e\n      \u003cdiv class=\"gt-bar\" id=\"gt-gradient-bar\"\u003e\u003c/div\u003e\n      \u003cdiv class=\"gt-preview-box\" id=\"gt-preview\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;div class=\u0026quot;gt-panel\u0026quot; style=\u0026quot;margin-top:16px;\u0026quot;\u0026gt;\n  \u0026lt;h3\u0026gt;Output\u0026lt;/h3\u0026gt;\n  \u0026lt;div class=\u0026quot;gt-tabs\u0026quot;\u0026gt;\n    \u0026lt;button class=\u0026quot;gt-tab active\u0026quot; id=\u0026quot;gt-tab-html\u0026quot; onclick=\u0026quot;gtSwitchTab('html')\u0026quot;\u0026gt;HTML (inline spans)\u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;gt-tab\u0026quot; id=\u0026quot;gt-tab-css\u0026quot; onclick=\u0026quot;gtSwitchTab('css')\u0026quot;\u0026gt;CSS gradient\u0026lt;/button\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;gt-output\u0026quot;\u0026gt;\n    \u0026lt;pre id=\u0026quot;gt-output-code\u0026quot;\u0026gt;\u0026lt;/pre\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;gt-btn-row\u0026quot;\u0026gt;\n    \u0026lt;button class=\u0026quot;gt-btn gt-btn-primary\u0026quot; onclick=\u0026quot;gtCopy()\u0026quot;\u0026gt;Copy Output\u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;gt-btn gt-btn-ghost\u0026quot; onclick=\u0026quot;gtReset()\u0026quot;\u0026gt;Reset\u0026lt;/button\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"gt-toast\" id=\"gt-toast\"\u003eCopied!\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  'use strict';\n\n  /* ── State ───────────────────────────────────────────────── */\n  var gtStops = [\n    { color: '#6366f1' },\n    { color: '#ec4899' },\n  ];\n  var gtTab = 'html';\n\n  /* ── DOM refs ────────────────────────────────────────────── */\n  var elText     = document.getElementById('gt-text');\n  var elPreview  = document.getElementById('gt-preview');\n  var elBar      = document.getElementById('gt-gradient-bar');\n  var elCode     = document.getElementById('gt-output-code');\n  var elAddBtn   = document.getElementById('gt-add-stop-btn');\n  var elStopList = document.getElementById('gt-stops-list');\n  var elSkip     = document.getElementById('gt-skip-spaces');\n  var elBold     = document.getElementById('gt-bold');\n  var elFontSize = document.getElementById('gt-font-size');\n  var elFontLbl  = document.getElementById('gt-font-label');\n  var elToast    = document.getElementById('gt-toast');\n\n  /* ── Color helpers ───────────────────────────────────────── */\n  function hexToRgb(hex) {\n    hex = hex.replace(/^#/, '');\n    if (hex.length === 3) hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];\n    var n = parseInt(hex, 16);\n    return [(n \u003e\u003e 16) \u0026 255, (n \u003e\u003e 8) \u0026 255, n \u0026 255];\n  }\n\n  function rgbToHex(r, g, b) {\n    return '#' + [r, g, b].map(function (v) {\n      return Math.round(v).toString(16).padStart(2, '0');\n    }).join('');\n  }\n\n  function lerpColor(stops, t) {\n    if (stops.length === 1) return stops[0].color;\n    var seg = 1 / (stops.length - 1);\n    var i = Math.min(Math.floor(t / seg), stops.length - 2);\n    var lt = (t - i * seg) / seg;\n    var a = hexToRgb(stops[i].color);\n    var b = hexToRgb(stops[i + 1].color);\n    return rgbToHex(\n      a[0] + (b[0] - a[0]) * lt,\n      a[1] + (b[1] - a[1]) * lt,\n      a[2] + (b[2] - a[2]) * lt\n    );\n  }\n\n  /* ── Render stop list ────────────────────────────────────── */\n  function renderStops() {\n    elStopList.innerHTML = '';\n    gtStops.forEach(function (stop, i) {\n      var row = document.createElement('div');\n      row.className = 'gt-stop-row';\n\n      var picker = document.createElement('input');\n      picker.type = 'color';\n      picker.value = stop.color;\n      picker.addEventListener('input', function () {\n        gtStops[i].color = picker.value;\n        hexInput.value = picker.value.toUpperCase();\n        update();\n      });\n\n      var hexInput = document.createElement('input');\n      hexInput.type = 'text';\n      hexInput.maxLength = 7;\n      hexInput.value = stop.color.toUpperCase();\n      hexInput.addEventListener('input', function () {\n        var v = hexInput.value.trim();\n        if (/^#?[0-9a-fA-F]{6}$/.test(v)) {\n          var hex = v.startsWith('#') ? v : '#' + v;\n          gtStops[i].color = hex;\n          picker.value = hex;\n          update();\n        }\n      });\n\n      var label = document.createElement('span');\n      label.className = 'gt-stop-label';\n      label.textContent = i === 0 ? 'Start' : i === gtStops.length - 1 ? 'End' : 'Stop ' + (i + 1);\n\n      row.appendChild(picker);\n      row.appendChild(hexInput);\n      row.appendChild(label);\n\n      if (gtStops.length \u003e 2) {\n        var rmBtn = document.createElement('button');\n        rmBtn.className = 'gt-rm';\n        rmBtn.textContent = '×';\n        rmBtn.title = 'Remove stop';\n        rmBtn.addEventListener('click', function () {\n          gtStops.splice(i, 1);\n          renderStops();\n          update();\n        });\n        row.appendChild(rmBtn);\n      }\n\n      elStopList.appendChild(row);\n    });\n    elAddBtn.disabled = gtStops.length \u003e= 5;\n  }\n\n  elAddBtn.addEventListener('click', function () {\n    if (gtStops.length \u003c 5) {\n      gtStops.push({ color: '#facc15' });\n      renderStops();\n      update();\n    }\n  });\n\n  /* ── Update preview \u0026 output ─────────────────────────────── */\n  function update() {\n    var text = elText.value || '';\n    var skip = elSkip.checked;\n    var bold = elBold.checked;\n    var fs   = elFontSize.value + 'px';\n\n    /* gradient bar */\n    var colors = gtStops.map(function (s) { return s.color; });\n    elBar.style.background = 'linear-gradient(90deg, ' + colors.join(', ') + ')';\n\n    /* colorable chars */\n    var chars = text.split('');\n    var colorable = skip\n      ? chars.filter(function (c) { return c !== ' '; })\n      : chars;\n    var total = colorable.length;\n    var colorIdx = 0;\n\n    /* build preview spans */\n    elPreview.innerHTML = '';\n    elPreview.style.fontSize = fs;\n    elPreview.style.fontWeight = bold ? '800' : '400';\n\n    chars.forEach(function (ch) {\n      var span = document.createElement('span');\n      if (skip \u0026\u0026 ch === ' ') {\n        span.textContent = '\\u00a0';\n      } else {\n        var t = total \u003c= 1 ? 0 : colorIdx / (total - 1);\n        var col = lerpColor(gtStops, t);\n        span.style.color = col;\n        span.textContent = ch;\n        colorIdx++;\n      }\n      elPreview.appendChild(span);\n    });\n\n    /* build output */\n    if (gtTab === 'html') {\n      renderHtmlOutput(chars, skip, bold);\n    } else {\n      renderCssOutput(colors);\n    }\n  }\n\n  function renderHtmlOutput(chars, skip, bold) {\n    var total = skip\n      ? chars.filter(function (c) { return c !== ' '; }).length\n      : chars.length;\n    var colorIdx = 0;\n    var parts = chars.map(function (ch) {\n      if (skip \u0026\u0026 ch === ' ') return ' ';\n      var t = total \u003c= 1 ? 0 : colorIdx / (total - 1);\n      var col = lerpColor(gtStops, t);\n      colorIdx++;\n      return '\u003cspan style=\"color:' + col + '\"\u003e' + escHtml(ch) + '\u003c/span\u003e';\n    });\n    var tag = bold ? 'b' : 'span';\n    elCode.textContent = '\u003c' + tag + '\u003e' + parts.join('') + '\u003c/' + tag + '\u003e';\n  }\n\n  function renderCssOutput(colors) {\n    var gradient = 'linear-gradient(90deg, ' + colors.join(', ') + ')';\n    elCode.textContent =\n      '.gradient-text {\\n' +\n      '  background: ' + gradient + ';\\n' +\n      '  -webkit-background-clip: text;\\n' +\n      '  -webkit-text-fill-color: transparent;\\n' +\n      '  background-clip: text;\\n' +\n      '  display: inline-block;\\n' +\n      '}';\n  }\n\n  function escHtml(s) {\n    return s.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n  }\n\n  /* ── Tab switch ──────────────────────────────────────────── */\n  window.gtSwitchTab = function (tab) {\n    gtTab = tab;\n    document.getElementById('gt-tab-html').classList.toggle('active', tab === 'html');\n    document.getElementById('gt-tab-css').classList.toggle('active', tab === 'css');\n    update();\n  };\n\n  /* ── Copy ────────────────────────────────────────────────── */\n  window.gtCopy = function () {\n    var txt = elCode.textContent;\n    if (navigator.clipboard) {\n      navigator.clipboard.writeText(txt).then(showToast);\n    } else {\n      var ta = document.createElement('textarea');\n      ta.value = txt;\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      showToast();\n    }\n  };\n\n  function showToast() {\n    elToast.classList.add('show');\n    setTimeout(function () { elToast.classList.remove('show'); }, 1800);\n  }\n\n  /* ── Reset ───────────────────────────────────────────────── */\n  window.gtReset = function () {\n    elText.value = 'Hello World!';\n    gtStops = [{ color: '#6366f1' }, { color: '#ec4899' }];\n    elSkip.checked = false;\n    elBold.checked = true;\n    elFontSize.value = 36;\n    elFontLbl.textContent = '36px';\n    renderStops();\n    update();\n  };\n\n  /* ── Events ──────────────────────────────────────────────── */\n  elText.addEventListener('input', update);\n  elSkip.addEventListener('change', update);\n  elBold.addEventListener('change', update);\n  elFontSize.addEventListener('input', function () {\n    elFontLbl.textContent = elFontSize.value + 'px';\n    update();\n  });\n\n  /* ── Init ────────────────────────────────────────────────── */\n  renderStops();\n  update();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003eGenerate color schemes → \u003ca href=\"https://productivity-works.com/tools/color-scheme-generator/\"\u003eColor Scheme Generator\u003c/a\u003e\n\u003c/p\u003e","title":"Color Gradient Text Generator — Free Online Tool"},{"content":" Mixed Color #7B6FBD #7B6FBD\u0026#x2398; rgb(123, 111, 189)\u0026#x2398; hsl(248, 35%, 59%)\u0026#x2398; Colors to Mix \u0026#43; Add Another Color (up to 5) Total: 100% Tints \u0026amp; Shades Tints (+ White) Shades (+ Black) Complementary Color #8D7F3B Opposite on the color wheel — great for contrast \u0026amp; accents Use as Color 1 Color History Mix colors to build history... Clear History How to Use the Color Mixer Select colors: Click the color swatch (left of each row) to open the browser\u0026rsquo;s native color picker, or type a HEX code directly into the input field. Up to 5 colors can be mixed simultaneously.\nAdjust ratios: Drag the slider or type a number for each color\u0026rsquo;s ratio. The ratio bar at the bottom gives a visual breakdown. Ratios don\u0026rsquo;t have to add to 100 — the tool normalizes them automatically to calculate the weighted mix.\nAdd more colors: Click + Add Another Color to add a third, fourth, or fifth color to the mix.\nCopy values: Click any of the HEX, RGB, or HSL badges in the result panel to copy that color value to your clipboard.\nTints \u0026amp; Shades: The Tints panel shows the mixed color blended with white (lighter versions); the Shades panel shows it blended with black (darker versions). Click any swatch to use it.\nComplementary color: The tool automatically calculates the color directly opposite on the color wheel. Click Use as Color 1 to swap it into the mix.\nColor history: Every unique mixed result is saved automatically. Click any history swatch to reload it as your first color input.\nColor Mixing Tips Why ratios matter: A 90/10 mix produces a very different result from 60/40. Use the ratio bar to see proportions at a glance — small ratio changes near one extreme shift the result color dramatically.\nStart with two, then add: Build complex mixes gradually. Start with your two dominant colors, confirm the mid-tone, then add a third color at a low ratio (10–20%) to add warmth or coolness without overpowering the blend.\nComplementary for contrast: If your mixed color will appear alongside text or UI elements, the complementary suggestion gives you a mathematically opposite hue — ideal for accent buttons, borders, and highlights.\nTints for UI states: Design systems often use the same hue at multiple lightness levels for hover, focus, and disabled states. Use the tint generator to instantly derive those variants from your mixed base color.\nRelated Tools Pick and convert colors visually → Color Picker Generate full color palettes → Color Palette Generator Check color contrast ratios → Color Contrast Checker ","permalink":"https://productivity-works.com/tools/color-mixer/","summary":"\u003cdiv id=\"cm-app\"\u003e\n\u003cstyle\u003e\n#cm-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 0 0 40px 0;\n  color: #1a1a2e;\n}\n#cm-app * { box-sizing: border-box; }\n\n/* Card */\n.cm-card {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 14px;\n  padding: 22px 24px;\n  margin-bottom: 20px;\n}\n.cm-card h3 {\n  margin: 0 0 16px;\n  font-size: 15px;\n  font-weight: 700;\n  color: #2d3748;\n  border-bottom: 2px solid #f0f4f8;\n  padding-bottom: 8px;\n}\n\n/* Result hero */\n.cm-result-hero {\n  border-radius: 14px;\n  padding: 24px;\n  margin-bottom: 20px;\n  display: flex;\n  align-items: center;\n  gap: 24px;\n  flex-wrap: wrap;\n  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n}\n.cm-result-swatch {\n  width: 120px;\n  height: 120px;\n  border-radius: 14px;\n  border: 4px solid rgba(255,255,255,0.5);\n  box-shadow: 0 8px 32px rgba(0,0,0,0.25);\n  flex-shrink: 0;\n  transition: background 0.2s;\n}\n.cm-result-info { flex: 1; min-width: 220px; }\n.cm-result-label {\n  color: rgba(255,255,255,0.85);\n  font-size: 13px;\n  font-weight: 600;\n  letter-spacing: .04em;\n  text-transform: uppercase;\n  margin-bottom: 6px;\n}\n.cm-result-hex {\n  color: #fff;\n  font-size: 32px;\n  font-weight: 800;\n  letter-spacing: .02em;\n  margin-bottom: 12px;\n  font-family: 'Courier New', monospace;\n}\n.cm-codes { display: flex; flex-wrap: wrap; gap: 8px; }\n.cm-code-badge {\n  background: rgba(255,255,255,0.18);\n  border: 1px solid rgba(255,255,255,0.35);\n  border-radius: 8px;\n  padding: 6px 12px;\n  color: #fff;\n  font-size: 12px;\n  font-family: 'Courier New', monospace;\n  cursor: pointer;\n  transition: background 0.15s;\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n.cm-code-badge:hover { background: rgba(255,255,255,0.3); }\n.cm-copy-icon { font-size: 11px; opacity: 0.8; }\n.cm-copied { background: rgba(72,199,142,0.5) !important; }\n\n/* Color inputs */\n.cm-colors-grid {\n  display: flex;\n  flex-direction: column;\n  gap: 14px;\n}\n.cm-color-row {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  flex-wrap: wrap;\n  padding: 14px;\n  background: #f8fafc;\n  border-radius: 10px;\n  border: 1px solid #e8edf2;\n  position: relative;\n}\n.cm-color-row.cm-removing {\n  opacity: 0;\n  transition: opacity 0.2s;\n}\n.cm-color-swatch-wrap {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n.cm-native-picker {\n  width: 48px;\n  height: 48px;\n  border: none;\n  border-radius: 10px;\n  padding: 2px;\n  cursor: pointer;\n  background: none;\n  flex-shrink: 0;\n}\n.cm-hex-input {\n  width: 100px;\n  padding: 8px 10px;\n  border: 1px solid #cbd5e0;\n  border-radius: 8px;\n  font-size: 13px;\n  font-family: 'Courier New', monospace;\n  color: #2d3748;\n  background: #fff;\n  transition: border-color 0.15s;\n}\n.cm-hex-input:focus { outline: none; border-color: #667eea; }\n.cm-hex-input.cm-invalid { border-color: #fc8181; background: #fff5f5; }\n\n/* Ratio */\n.cm-ratio-wrap {\n  flex: 1;\n  min-width: 160px;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n.cm-ratio-label {\n  font-size: 12px;\n  color: #718096;\n  white-space: nowrap;\n}\n.cm-ratio-slider {\n  flex: 1;\n  accent-color: #667eea;\n  height: 4px;\n  cursor: pointer;\n}\n.cm-ratio-num {\n  width: 44px;\n  padding: 4px 6px;\n  border: 1px solid #cbd5e0;\n  border-radius: 6px;\n  font-size: 12px;\n  text-align: center;\n  color: #2d3748;\n  background: #fff;\n}\n.cm-ratio-num:focus { outline: none; border-color: #667eea; }\n\n/* Remove button */\n.cm-remove-btn {\n  width: 28px;\n  height: 28px;\n  border-radius: 50%;\n  border: 1px solid #e2e8f0;\n  background: #fff;\n  color: #a0aec0;\n  cursor: pointer;\n  font-size: 16px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  flex-shrink: 0;\n  transition: background 0.15s, color 0.15s;\n  line-height: 1;\n  padding: 0;\n}\n.cm-remove-btn:hover { background: #fed7d7; color: #e53e3e; border-color: #fc8181; }\n\n/* Add button */\n.cm-add-btn {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 10px 18px;\n  background: #fff;\n  border: 2px dashed #cbd5e0;\n  border-radius: 10px;\n  color: #667eea;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: border-color 0.15s, background 0.15s;\n  width: 100%;\n  justify-content: center;\n}\n.cm-add-btn:hover { border-color: #667eea; background: #f0f0ff; }\n.cm-add-btn:disabled { opacity: 0.4; cursor: not-allowed; }\n\n/* Ratio bar */\n.cm-ratio-bar {\n  display: flex;\n  height: 12px;\n  border-radius: 8px;\n  overflow: hidden;\n  margin-bottom: 6px;\n  border: 1px solid #e2e8f0;\n}\n.cm-ratio-bar-seg {\n  transition: width 0.2s, background 0.2s;\n  height: 100%;\n}\n.cm-ratio-total {\n  font-size: 12px;\n  color: #a0aec0;\n  text-align: right;\n}\n.cm-ratio-total.cm-over { color: #e53e3e; font-weight: 700; }\n\n/* Tint/Shade */\n.cm-ts-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 16px;\n}\n@media (max-width: 560px) { .cm-ts-grid { grid-template-columns: 1fr; } }\n.cm-ts-label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #4a5568;\n  margin-bottom: 8px;\n}\n.cm-ts-swatches {\n  display: flex;\n  gap: 4px;\n  flex-wrap: wrap;\n}\n.cm-ts-swatch {\n  width: 36px;\n  height: 36px;\n  border-radius: 6px;\n  cursor: pointer;\n  border: 2px solid transparent;\n  transition: transform 0.1s, border-color 0.15s;\n  flex-shrink: 0;\n  position: relative;\n}\n.cm-ts-swatch:hover { transform: scale(1.12); border-color: #667eea; }\n.cm-ts-swatch[title]:hover::after {\n  content: attr(title);\n  position: absolute;\n  bottom: calc(100% + 4px);\n  left: 50%;\n  transform: translateX(-50%);\n  background: #2d3748;\n  color: #fff;\n  font-size: 10px;\n  padding: 2px 6px;\n  border-radius: 4px;\n  white-space: nowrap;\n  pointer-events: none;\n  z-index: 10;\n}\n\n/* Complementary */\n.cm-comp-row {\n  display: flex;\n  align-items: center;\n  gap: 14px;\n  flex-wrap: wrap;\n}\n.cm-comp-swatch {\n  width: 56px;\n  height: 56px;\n  border-radius: 10px;\n  cursor: pointer;\n  border: 2px solid #e2e8f0;\n  transition: transform 0.1s, border-color 0.15s;\n  flex-shrink: 0;\n}\n.cm-comp-swatch:hover { transform: scale(1.08); border-color: #667eea; }\n.cm-comp-info { flex: 1; }\n.cm-comp-hex {\n  font-family: 'Courier New', monospace;\n  font-size: 14px;\n  font-weight: 700;\n  color: #2d3748;\n}\n.cm-comp-desc { font-size: 12px; color: #718096; margin-top: 2px; }\n\n/* History */\n.cm-history-row {\n  display: flex;\n  gap: 8px;\n  flex-wrap: wrap;\n  align-items: center;\n}\n.cm-history-swatch {\n  width: 36px;\n  height: 36px;\n  border-radius: 8px;\n  cursor: pointer;\n  border: 2px solid transparent;\n  transition: transform 0.1s, border-color 0.15s;\n  flex-shrink: 0;\n  position: relative;\n}\n.cm-history-swatch:hover { transform: scale(1.15); border-color: #667eea; }\n.cm-history-empty { font-size: 13px; color: #a0aec0; }\n\n/* Buttons row */\n.cm-btn-row {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n  margin-top: 10px;\n}\n.cm-btn {\n  padding: 9px 18px;\n  border-radius: 8px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  border: 1px solid transparent;\n  transition: background 0.15s, color 0.15s;\n}\n.cm-btn-primary {\n  background: #667eea;\n  color: #fff;\n  border-color: #667eea;\n}\n.cm-btn-primary:hover { background: #5a67d8; }\n.cm-btn-secondary {\n  background: #fff;\n  color: #4a5568;\n  border-color: #cbd5e0;\n}\n.cm-btn-secondary:hover { background: #f7fafc; }\n\n@media (max-width: 480px) {\n  .cm-result-hex { font-size: 24px; }\n  .cm-result-swatch { width: 80px; height: 80px; }\n}\n\u003c/style\u003e\n\u003c!-- Result Hero --\u003e\n\u003cdiv class=\"cm-result-hero\" id=\"cm-hero\"\u003e\n  \u003cdiv class=\"cm-result-swatch\" id=\"cm-result-swatch\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"cm-result-info\"\u003e\n    \u003cdiv class=\"cm-result-label\"\u003eMixed Color\u003c/div\u003e\n    \u003cdiv class=\"cm-result-hex\" id=\"cm-result-hex\"\u003e#7B6FBD\u003c/div\u003e\n    \u003cdiv class=\"cm-codes\"\u003e\n      \u003cspan class=\"cm-code-badge\" id=\"cm-badge-hex\" title=\"Click to copy HEX\"\u003e\u003cspan id=\"cm-val-hex\"\u003e#7B6FBD\u003c/span\u003e\u003cspan class=\"cm-copy-icon\"\u003e\u0026#x2398;\u003c/span\u003e\u003c/span\u003e\n      \u003cspan class=\"cm-code-badge\" id=\"cm-badge-rgb\" title=\"Click to copy RGB\"\u003e\u003cspan id=\"cm-val-rgb\"\u003ergb(123, 111, 189)\u003c/span\u003e\u003cspan class=\"cm-copy-icon\"\u003e\u0026#x2398;\u003c/span\u003e\u003c/span\u003e\n      \u003cspan class=\"cm-code-badge\" id=\"cm-badge-hsl\" title=\"Click to copy HSL\"\u003e\u003cspan id=\"cm-val-hsl\"\u003ehsl(248, 35%, 59%)\u003c/span\u003e\u003cspan class=\"cm-copy-icon\"\u003e\u0026#x2398;\u003c/span\u003e\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Color Inputs --\u003e\n\u003cdiv class=\"cm-card\"\u003e\n  \u003ch3\u003eColors to Mix\u003c/h3\u003e\n  \u003cdiv class=\"cm-colors-grid\" id=\"cm-colors-grid\"\u003e\u003c/div\u003e\n  \u003cdiv style=\"margin-top:12px;\"\u003e\n    \u003cbutton class=\"cm-add-btn\" id=\"cm-add-btn\"\u003e\u0026#43; Add Another Color (up to 5)\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv style=\"margin-top:16px;\"\u003e\n    \u003cdiv class=\"cm-ratio-bar\" id=\"cm-ratio-bar\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"cm-ratio-total\" id=\"cm-ratio-total\"\u003eTotal: 100%\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Tint \u0026 Shade --\u003e\n\u003cdiv class=\"cm-card\"\u003e\n  \u003ch3\u003eTints \u0026amp; Shades\u003c/h3\u003e\n  \u003cdiv class=\"cm-ts-grid\"\u003e\n    \u003cdiv\u003e\n      \u003cdiv class=\"cm-ts-label\"\u003eTints (+ White)\u003c/div\u003e\n      \u003cdiv class=\"cm-ts-swatches\" id=\"cm-tints\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003cdiv class=\"cm-ts-label\"\u003eShades (+ Black)\u003c/div\u003e\n      \u003cdiv class=\"cm-ts-swatches\" id=\"cm-shades\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Complementary --\u003e\n\u003cdiv class=\"cm-card\"\u003e\n  \u003ch3\u003eComplementary Color\u003c/h3\u003e\n  \u003cdiv class=\"cm-comp-row\"\u003e\n    \u003cdiv class=\"cm-comp-swatch\" id=\"cm-comp-swatch\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"cm-comp-info\"\u003e\n      \u003cdiv class=\"cm-comp-hex\" id=\"cm-comp-hex\"\u003e#8D7F3B\u003c/div\u003e\n      \u003cdiv class=\"cm-comp-desc\"\u003eOpposite on the color wheel — great for contrast \u0026amp; accents\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003cbutton class=\"cm-btn cm-btn-secondary\" id=\"cm-use-comp\"\u003eUse as Color 1\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- History --\u003e\n\u003cdiv class=\"cm-card\"\u003e\n  \u003ch3\u003eColor History\u003c/h3\u003e\n  \u003cdiv class=\"cm-history-row\" id=\"cm-history-row\"\u003e\n    \u003cspan class=\"cm-history-empty\"\u003eMix colors to build history...\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cm-btn-row\"\u003e\n    \u003cbutton class=\"cm-btn cm-btn-secondary\" id=\"cm-clear-history\"\u003eClear History\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  'use strict';\n\n  // ===== State =====\n  var colors = [\n    { hex: '#667eea', ratio: 50 },\n    { hex: '#f093fb', ratio: 50 }\n  ];\n  var history = [];\n  var MAX_COLORS = 5;\n  var MAX_HISTORY = 12;\n\n  // ===== Utilities =====\n  function hexToRgb(hex) {\n    var h = hex.replace('#', '');\n    if (h.length === 3) h = h[0]+h[0]+h[1]+h[1]+h[2]+h[2];\n    if (h.length !== 6) return null;\n    var n = parseInt(h, 16);\n    if (isNaN(n)) return null;\n    return { r: (n \u003e\u003e 16) \u0026 255, g: (n \u003e\u003e 8) \u0026 255, b: n \u0026 255 };\n  }\n\n  function rgbToHex(r, g, b) {\n    return '#' + [r,g,b].map(function(v) {\n      return Math.round(v).toString(16).padStart(2,'0');\n    }).join('').toUpperCase();\n  }\n\n  function rgbToHsl(r, g, b) {\n    r /= 255; g /= 255; b /= 255;\n    var max = Math.max(r,g,b), min = Math.min(r,g,b);\n    var h, s, l = (max+min)/2;\n    if (max === min) { h = s = 0; }\n    else {\n      var d = max - min;\n      s = l \u003e 0.5 ? d/(2-max-min) : d/(max+min);\n      switch(max) {\n        case r: h = ((g-b)/d + (g\u003cb?6:0))/6; break;\n        case g: h = ((b-r)/d + 2)/6; break;\n        default: h = ((r-g)/d + 4)/6;\n      }\n    }\n    return { h: Math.round(h*360), s: Math.round(s*100), l: Math.round(l*100) };\n  }\n\n  function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); }\n\n  function isValidHex(s) { return /^#?[0-9A-Fa-f]{6}$/.test(s.trim()); }\n\n  function normalizeHex(s) {\n    s = s.trim().replace(/^#/,'');\n    if (s.length === 3) s = s[0]+s[0]+s[1]+s[1]+s[2]+s[2];\n    return '#' + s.toUpperCase();\n  }\n\n  function mixColors(colorsArr) {\n    var total = colorsArr.reduce(function(a,c){ return a+c.ratio; }, 0);\n    if (total === 0) return { r:128, g:128, b:128 };\n    var r=0, g=0, b=0;\n    colorsArr.forEach(function(c) {\n      var rgb = hexToRgb(c.hex);\n      if (!rgb) return;\n      var w = c.ratio / total;\n      r += rgb.r * w;\n      g += rgb.g * w;\n      b += rgb.b * w;\n    });\n    return { r: Math.round(r), g: Math.round(g), b: Math.round(b) };\n  }\n\n  function complementaryHex(hex) {\n    var rgb = hexToRgb(hex);\n    if (!rgb) return '#888888';\n    var hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);\n    var ch = (hsl.h + 180) % 360;\n    return hslToHex(ch, hsl.s, hsl.l);\n  }\n\n  function hslToHex(h, s, l) {\n    s /= 100; l /= 100;\n    var c = (1 - Math.abs(2*l-1)) * s;\n    var x = c * (1 - Math.abs((h/60) % 2 - 1));\n    var m = l - c/2;\n    var r,g,b;\n    if (h\u003c60){r=c;g=x;b=0;}\n    else if(h\u003c120){r=x;g=c;b=0;}\n    else if(h\u003c180){r=0;g=c;b=x;}\n    else if(h\u003c240){r=0;g=x;b=c;}\n    else if(h\u003c300){r=x;g=0;b=c;}\n    else{r=c;g=0;b=x;}\n    return rgbToHex(Math.round((r+m)*255), Math.round((g+m)*255), Math.round((b+m)*255));\n  }\n\n  function blendWithWhite(hex, t) {\n    var rgb = hexToRgb(hex);\n    if (!rgb) return '#ffffff';\n    return rgbToHex(\n      Math.round(rgb.r + (255-rgb.r)*t),\n      Math.round(rgb.g + (255-rgb.g)*t),\n      Math.round(rgb.b + (255-rgb.b)*t)\n    );\n  }\n\n  function blendWithBlack(hex, t) {\n    var rgb = hexToRgb(hex);\n    if (!rgb) return '#000000';\n    return rgbToHex(\n      Math.round(rgb.r*(1-t)),\n      Math.round(rgb.g*(1-t)),\n      Math.round(rgb.b*(1-t))\n    );\n  }\n\n  function copyText(text, badge) {\n    navigator.clipboard.writeText(text).then(function() {\n      badge.classList.add('cm-copied');\n      setTimeout(function(){ badge.classList.remove('cm-copied'); }, 1200);\n    }).catch(function() {\n      var ta = document.createElement('textarea');\n      ta.value = text;\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      badge.classList.add('cm-copied');\n      setTimeout(function(){ badge.classList.remove('cm-copied'); }, 1200);\n    });\n  }\n\n  // ===== Render color rows =====\n  function renderColorRows() {\n    var grid = document.getElementById('cm-colors-grid');\n    grid.innerHTML = '';\n    colors.forEach(function(c, i) {\n      var row = document.createElement('div');\n      row.className = 'cm-color-row';\n      row.setAttribute('data-index', i);\n\n      // Native picker\n      var picker = document.createElement('input');\n      picker.type = 'color';\n      picker.className = 'cm-native-picker';\n      picker.value = c.hex.toUpperCase();\n      picker.title = 'Pick color ' + (i+1);\n\n      // Hex input\n      var hexInput = document.createElement('input');\n      hexInput.type = 'text';\n      hexInput.className = 'cm-hex-input';\n      hexInput.value = c.hex.toUpperCase();\n      hexInput.maxLength = 7;\n      hexInput.placeholder = '#RRGGBB';\n\n      // Sync picker -\u003e hex\n      picker.addEventListener('input', function() {\n        colors[i].hex = picker.value.toUpperCase();\n        hexInput.value = picker.value.toUpperCase();\n        hexInput.classList.remove('cm-invalid');\n        update();\n      });\n\n      // Sync hex -\u003e picker\n      hexInput.addEventListener('input', function() {\n        var v = hexInput.value.trim();\n        if (!v.startsWith('#')) v = '#' + v;\n        if (isValidHex(v)) {\n          colors[i].hex = normalizeHex(v);\n          picker.value = normalizeHex(v).toLowerCase();\n          hexInput.classList.remove('cm-invalid');\n          update();\n        } else {\n          hexInput.classList.add('cm-invalid');\n        }\n      });\n\n      var swatchWrap = document.createElement('div');\n      swatchWrap.className = 'cm-color-swatch-wrap';\n      swatchWrap.appendChild(picker);\n      swatchWrap.appendChild(hexInput);\n\n      // Ratio controls\n      var ratioWrap = document.createElement('div');\n      ratioWrap.className = 'cm-ratio-wrap';\n\n      var ratioLabel = document.createElement('span');\n      ratioLabel.className = 'cm-ratio-label';\n      ratioLabel.textContent = 'Ratio:';\n\n      var ratioSlider = document.createElement('input');\n      ratioSlider.type = 'range';\n      ratioSlider.className = 'cm-ratio-slider';\n      ratioSlider.min = 0;\n      ratioSlider.max = 100;\n      ratioSlider.value = c.ratio;\n\n      var ratioNum = document.createElement('input');\n      ratioNum.type = 'number';\n      ratioNum.className = 'cm-ratio-num';\n      ratioNum.min = 0;\n      ratioNum.max = 100;\n      ratioNum.value = c.ratio;\n\n      ratioSlider.addEventListener('input', function() {\n        colors[i].ratio = parseInt(ratioSlider.value) || 0;\n        ratioNum.value = colors[i].ratio;\n        update();\n      });\n      ratioNum.addEventListener('input', function() {\n        colors[i].ratio = clamp(parseInt(ratioNum.value) || 0, 0, 100);\n        ratioSlider.value = colors[i].ratio;\n        update();\n      });\n\n      ratioWrap.appendChild(ratioLabel);\n      ratioWrap.appendChild(ratioSlider);\n      ratioWrap.appendChild(ratioNum);\n\n      row.appendChild(swatchWrap);\n      row.appendChild(ratioWrap);\n\n      // Remove button (only if \u003e 2 colors)\n      if (colors.length \u003e 2) {\n        var removeBtn = document.createElement('button');\n        removeBtn.className = 'cm-remove-btn';\n        removeBtn.title = 'Remove color';\n        removeBtn.innerHTML = '\u0026times;';\n        removeBtn.addEventListener('click', function() {\n          colors.splice(i, 1);\n          renderColorRows();\n          update();\n        });\n        row.appendChild(removeBtn);\n      }\n\n      grid.appendChild(row);\n    });\n\n    // Add button state\n    var addBtn = document.getElementById('cm-add-btn');\n    addBtn.disabled = colors.length \u003e= MAX_COLORS;\n  }\n\n  // ===== Render ratio bar =====\n  function renderRatioBar() {\n    var bar = document.getElementById('cm-ratio-bar');\n    var totalEl = document.getElementById('cm-ratio-total');\n    var total = colors.reduce(function(a,c){ return a+c.ratio; }, 0);\n    bar.innerHTML = '';\n    colors.forEach(function(c) {\n      var seg = document.createElement('div');\n      seg.className = 'cm-ratio-bar-seg';\n      seg.style.width = (total \u003e 0 ? (c.ratio/total*100) : (100/colors.length)) + '%';\n      seg.style.background = c.hex;\n      bar.appendChild(seg);\n    });\n    totalEl.textContent = 'Total: ' + total + '%';\n    totalEl.className = 'cm-ratio-total' + (total !== 100 ? ' cm-over' : '');\n  }\n\n  // ===== Render tints \u0026 shades =====\n  function renderTintsShades(resultHex) {\n    var steps = [0.1, 0.2, 0.35, 0.5, 0.65, 0.8];\n    var tintsEl = document.getElementById('cm-tints');\n    var shadesEl = document.getElementById('cm-shades');\n    tintsEl.innerHTML = '';\n    shadesEl.innerHTML = '';\n\n    steps.forEach(function(t) {\n      var tintHex = blendWithWhite(resultHex, t);\n      var ts = document.createElement('div');\n      ts.className = 'cm-ts-swatch';\n      ts.style.background = tintHex;\n      ts.title = tintHex;\n      ts.addEventListener('click', function() {\n        addToHistory(resultHex);\n        colors[0].hex = tintHex;\n        colors[0].ratio = 100;\n        if (colors.length \u003e 1) {\n          colors = colors.slice(0,1);\n          colors.push({ hex: '#ffffff', ratio: 0 });\n        }\n        renderColorRows();\n        update();\n      });\n      tintsEl.appendChild(ts);\n    });\n\n    steps.forEach(function(t) {\n      var shadeHex = blendWithBlack(resultHex, t);\n      var ss = document.createElement('div');\n      ss.className = 'cm-ts-swatch';\n      ss.style.background = shadeHex;\n      ss.title = shadeHex;\n      ss.addEventListener('click', function() {\n        addToHistory(resultHex);\n        colors[0].hex = shadeHex;\n        renderColorRows();\n        update();\n      });\n      shadesEl.appendChild(ss);\n    });\n  }\n\n  // ===== Render complementary =====\n  function renderComplementary(resultHex) {\n    var compHex = complementaryHex(resultHex);\n    document.getElementById('cm-comp-swatch').style.background = compHex;\n    document.getElementById('cm-comp-hex').textContent = compHex;\n    document.getElementById('cm-use-comp').onclick = function() {\n      colors[0].hex = compHex;\n      document.querySelector('[data-index=\"0\"] input[type=\"color\"]') \u0026\u0026 (document.querySelector('[data-index=\"0\"] input[type=\"color\"]').value = compHex.toLowerCase());\n      renderColorRows();\n      update();\n    };\n  }\n\n  // ===== History =====\n  function addToHistory(hex) {\n    if (history.length \u0026\u0026 history[0] === hex) return;\n    history = history.filter(function(h){ return h !== hex; });\n    history.unshift(hex);\n    if (history.length \u003e MAX_HISTORY) history = history.slice(0, MAX_HISTORY);\n    renderHistory();\n  }\n\n  function renderHistory() {\n    var row = document.getElementById('cm-history-row');\n    row.innerHTML = '';\n    if (!history.length) {\n      var empty = document.createElement('span');\n      empty.className = 'cm-history-empty';\n      empty.textContent = 'Mix colors to build history...';\n      row.appendChild(empty);\n      return;\n    }\n    history.forEach(function(hex) {\n      var sw = document.createElement('div');\n      sw.className = 'cm-history-swatch';\n      sw.style.background = hex;\n      sw.title = hex;\n      sw.addEventListener('click', function() {\n        colors[0].hex = hex;\n        renderColorRows();\n        update();\n      });\n      row.appendChild(sw);\n    });\n  }\n\n  // ===== Main update =====\n  var lastResultHex = '';\n  function update() {\n    var validColors = colors.filter(function(c){ return hexToRgb(c.hex); });\n    var rgb = mixColors(validColors.length ? validColors : colors);\n    var hex = rgbToHex(rgb.r, rgb.g, rgb.b);\n    var hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);\n\n    // Result hero\n    document.getElementById('cm-result-swatch').style.background = hex;\n    document.getElementById('cm-result-hex').textContent = hex;\n    document.getElementById('cm-val-hex').textContent = hex;\n    document.getElementById('cm-val-rgb').textContent = 'rgb(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ')';\n    document.getElementById('cm-val-hsl').textContent = 'hsl(' + hsl.h + ', ' + hsl.s + '%, ' + hsl.l + '%)';\n\n    // Hero gradient follows mix\n    var heroColors = colors.map(function(c){ return c.hex; });\n    document.getElementById('cm-hero').style.background =\n      'linear-gradient(135deg, ' + heroColors.join(', ') + ')';\n\n    renderRatioBar();\n    renderTintsShades(hex);\n    renderComplementary(hex);\n\n    // History: add when result changes\n    if (hex !== lastResultHex) {\n      addToHistory(hex);\n      lastResultHex = hex;\n    }\n  }\n\n  // ===== Copy badges =====\n  document.getElementById('cm-badge-hex').addEventListener('click', function() {\n    copyText(document.getElementById('cm-val-hex').textContent, this);\n  });\n  document.getElementById('cm-badge-rgb').addEventListener('click', function() {\n    copyText(document.getElementById('cm-val-rgb').textContent, this);\n  });\n  document.getElementById('cm-badge-hsl').addEventListener('click', function() {\n    copyText(document.getElementById('cm-val-hsl').textContent, this);\n  });\n\n  // ===== Add color =====\n  document.getElementById('cm-add-btn').addEventListener('click', function() {\n    if (colors.length \u003e= MAX_COLORS) return;\n    colors.push({ hex: '#' + Math.floor(Math.random()*0xffffff).toString(16).padStart(6,'0').toUpperCase(), ratio: 20 });\n    renderColorRows();\n    update();\n  });\n\n  // ===== Clear history =====\n  document.getElementById('cm-clear-history').addEventListener('click', function() {\n    history = [];\n    renderHistory();\n  });\n\n  // ===== Init =====\n  renderColorRows();\n  update();\n\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003ch2 id=\"how-to-use-the-color-mixer\"\u003eHow to Use the Color Mixer\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eSelect colors\u003c/strong\u003e: Click the color swatch (left of each row) to open the browser\u0026rsquo;s native color picker, or type a HEX code directly into the input field. Up to 5 colors can be mixed simultaneously.\u003c/p\u003e","title":"Color Mixer - Blend Colors Online"},{"content":"Find the closest CSS named color to any hex, RGB, or HSL value — instantly. Browse all 148 CSS named colors, search by name, and copy values with one click.\nFind Closest CSS Color Name HEX RGB Picker HEX Color Find Name Enter a 6-digit hex code (e.g. #ff6347)\nR G B Find Name Color Picker Pick any color to find its CSS name Result Your Color Closest CSS Name Browse All 148 CSS Named Colors Search \u0026times; Use This Color Related tools:\nPick colors → Color Picker Check contrast → Color Contrast Checker Generate palettes → Color Palette Generator ","permalink":"https://productivity-works.com/tools/color-name-finder/","summary":"\u003cp\u003eFind the closest CSS named color to any hex, RGB, or HSL value — instantly. Browse all 148 CSS named colors, search by name, and copy values with one click.\u003c/p\u003e\n\u003cdiv id=\"cn-app\"\u003e\n\u003cstyle\u003e\n#cn-app { font-family: system-ui, -apple-system, sans-serif; color: #1e293b; }\n#cn-app *, #cn-app *::before, #cn-app *::after { box-sizing: border-box; }\n#cn-app h2 { font-size: 1.1rem; font-weight: 700; margin: 0 0 12px; color: #0f172a; }\n#cn-app .cn-card { background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; margin-bottom: 18px; }\n#cn-app .cn-row { display: flex; gap: 14px; flex-wrap: wrap; align-items: flex-end; }\n#cn-app .cn-field { display: flex; flex-direction: column; gap: 5px; }\n#cn-app label { font-size: 12px; font-weight: 600; color: #64748b; text-transform: uppercase; letter-spacing: .04em; }\n#cn-app input[type=text], #cn-app input[type=number] { padding: 8px 10px; border: 1.5px solid #cbd5e1; border-radius: 7px; font-size: 14px; width: 120px; outline: none; transition: border-color .2s; }\n#cn-app input[type=text]:focus, #cn-app input[type=number]:focus { border-color: #6366f1; }\n#cn-app input[type=color] { width: 44px; height: 38px; padding: 2px; border: 1.5px solid #cbd5e1; border-radius: 7px; cursor: pointer; background: #fff; }\n#cn-app input[type=range] { width: 100px; accent-color: #6366f1; }\n#cn-app .cn-btn { padding: 9px 18px; background: #6366f1; color: #fff; border: none; border-radius: 7px; font-size: 13px; font-weight: 700; cursor: pointer; transition: background .2s; white-space: nowrap; }\n#cn-app .cn-btn:hover { background: #4f46e5; }\n#cn-app .cn-btn-sm { padding: 5px 11px; font-size: 12px; background: #f1f5f9; color: #334155; border: 1px solid #cbd5e1; border-radius: 6px; cursor: pointer; font-weight: 600; transition: background .2s; }\n#cn-app .cn-btn-sm:hover { background: #e2e8f0; }\n#cn-app .cn-swatch-row { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 14px; }\n#cn-app .cn-swatch-box { flex: 1; min-width: 160px; border-radius: 10px; overflow: hidden; border: 1px solid #e2e8f0; }\n#cn-app .cn-swatch-color { height: 80px; }\n#cn-app .cn-swatch-info { padding: 10px 12px; background: #f8fafc; }\n#cn-app .cn-swatch-label { font-size: 11px; color: #64748b; font-weight: 600; text-transform: uppercase; margin-bottom: 4px; }\n#cn-app .cn-swatch-name { font-size: 15px; font-weight: 700; color: #0f172a; margin-bottom: 6px; }\n#cn-app .cn-values { display: flex; flex-direction: column; gap: 3px; }\n#cn-app .cn-val-row { display: flex; align-items: center; gap: 6px; font-size: 12px; }\n#cn-app .cn-val-label { color: #94a3b8; width: 28px; flex-shrink: 0; }\n#cn-app .cn-val-text { font-family: monospace; color: #334155; flex: 1; }\n#cn-app .cn-copy-icon { cursor: pointer; color: #94a3b8; font-size: 11px; padding: 2px 5px; border-radius: 4px; border: 1px solid #e2e8f0; background: #fff; }\n#cn-app .cn-copy-icon:hover { background: #6366f1; color: #fff; border-color: #6366f1; }\n#cn-app .cn-distance { margin-top: 10px; font-size: 12px; color: #64748b; background: #f8fafc; padding: 7px 10px; border-radius: 7px; }\n#cn-app .cn-distance span { font-weight: 700; color: #6366f1; }\n#cn-app .cn-search-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: 14px; }\n#cn-app .cn-search-row input[type=text] { width: 200px; }\n#cn-app .cn-filter-btns { display: flex; gap: 6px; flex-wrap: wrap; }\n#cn-app .cn-filter-btn { padding: 5px 11px; font-size: 12px; border-radius: 20px; border: 1.5px solid #cbd5e1; background: #fff; cursor: pointer; font-weight: 600; color: #475569; transition: all .15s; }\n#cn-app .cn-filter-btn.active, #cn-app .cn-filter-btn:hover { background: #6366f1; color: #fff; border-color: #6366f1; }\n#cn-app .cn-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); gap: 8px; }\n#cn-app .cn-color-card { border-radius: 8px; overflow: hidden; border: 2px solid transparent; cursor: pointer; transition: all .15s; }\n#cn-app .cn-color-card:hover, #cn-app .cn-color-card.selected { border-color: #6366f1; box-shadow: 0 0 0 2px #a5b4fc; }\n#cn-app .cn-color-card-swatch { height: 54px; }\n#cn-app .cn-color-card-name { font-size: 10px; padding: 5px 6px; background: #f8fafc; font-weight: 600; color: #334155; line-height: 1.3; word-break: break-word; }\n#cn-app .cn-modal-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: 9999; align-items: center; justify-content: center; }\n#cn-app .cn-modal-overlay.open { display: flex; }\n#cn-app .cn-modal { background: #fff; border-radius: 14px; padding: 24px; max-width: 340px; width: 90%; box-shadow: 0 20px 60px rgba(0,0,0,.2); position: relative; }\n#cn-app .cn-modal-close { position: absolute; top: 12px; right: 14px; background: none; border: none; font-size: 20px; cursor: pointer; color: #94a3b8; }\n#cn-app .cn-modal-swatch { height: 90px; border-radius: 8px; margin-bottom: 14px; }\n#cn-app .cn-modal-name { font-size: 20px; font-weight: 800; color: #0f172a; margin-bottom: 12px; }\n#cn-app .cn-modal-vals { display: flex; flex-direction: column; gap: 7px; }\n#cn-app .cn-modal-val-row { display: flex; align-items: center; gap: 8px; background: #f8fafc; padding: 7px 10px; border-radius: 7px; }\n#cn-app .cn-modal-val-label { font-size: 11px; font-weight: 700; color: #94a3b8; text-transform: uppercase; width: 30px; }\n#cn-app .cn-modal-val-text { font-family: monospace; font-size: 13px; color: #334155; flex: 1; }\n#cn-app .cn-modal-copy { padding: 4px 10px; font-size: 11px; font-weight: 700; background: #6366f1; color: #fff; border: none; border-radius: 5px; cursor: pointer; }\n#cn-app .cn-tabs { display: flex; gap: 0; border-bottom: 2px solid #e2e8f0; margin-bottom: 18px; }\n#cn-app .cn-tab { padding: 9px 18px; font-size: 13px; font-weight: 700; border: none; background: none; cursor: pointer; color: #64748b; border-bottom: 2px solid transparent; margin-bottom: -2px; transition: all .15s; }\n#cn-app .cn-tab.active { color: #6366f1; border-bottom-color: #6366f1; }\n#cn-app .cn-section { display: none; }\n#cn-app .cn-section.active { display: block; }\n#cn-app .cn-rgb-sliders { display: flex; flex-direction: column; gap: 8px; }\n#cn-app .cn-slider-row { display: flex; align-items: center; gap: 8px; }\n#cn-app .cn-slider-row label { width: 14px; font-size: 12px; font-weight: 700; }\n#cn-app .cn-slider-row span { font-size: 12px; font-family: monospace; width: 28px; text-align: right; }\n#cn-app .cn-notice { font-size: 12px; color: #64748b; margin-top: 10px; }\n#cn-app .cn-count { font-size: 12px; color: #94a3b8; margin-bottom: 8px; }\n@media (max-width: 480px) {\n  #cn-app .cn-row { flex-direction: column; }\n  #cn-app input[type=text], #cn-app input[type=number] { width: 100%; }\n  #cn-app .cn-search-row input[type=text] { width: 100%; }\n}\n\u003c/style\u003e\n\u003c!-- Input Section --\u003e\n\u003cdiv class=\"cn-card\"\u003e\n  \u003ch2\u003eFind Closest CSS Color Name\u003c/h2\u003e\n  \u003cdiv class=\"cn-tabs\"\u003e\n    \u003cbutton class=\"cn-tab active\" onclick=\"(function(){document.querySelectorAll('#cn-app .cn-tab').forEach(t=\u003et.classList.remove('active'));this.classList.add('active');document.querySelectorAll('#cn-app .cn-section').forEach(s=\u003es.classList.remove('active'));document.getElementById('cn-sec-hex').classList.add('active');}).call(this)\"\u003eHEX\u003c/button\u003e\n    \u003cbutton class=\"cn-tab\" onclick=\"(function(){document.querySelectorAll('#cn-app .cn-tab').forEach(t=\u003et.classList.remove('active'));this.classList.add('active');document.querySelectorAll('#cn-app .cn-section').forEach(s=\u003es.classList.remove('active'));document.getElementById('cn-sec-rgb').classList.add('active');}).call(this)\"\u003eRGB\u003c/button\u003e\n    \u003cbutton class=\"cn-tab\" onclick=\"(function(){document.querySelectorAll('#cn-app .cn-tab').forEach(t=\u003et.classList.remove('active'));this.classList.add('active');document.querySelectorAll('#cn-app .cn-section').forEach(s=\u003es.classList.remove('active'));document.getElementById('cn-sec-pick').classList.add('active');}).call(this)\"\u003ePicker\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv id=\"cn-sec-hex\" class=\"cn-section active\"\u003e\n    \u003cdiv class=\"cn-row\"\u003e\n      \u003cdiv class=\"cn-field\"\u003e\n        \u003clabel\u003eHEX Color\u003c/label\u003e\n        \u003cinput type=\"text\" id=\"cn-hex-input\" placeholder=\"#6366f1\" maxlength=\"7\" style=\"width:140px;\"\u003e\n      \u003c/div\u003e\n      \u003cbutton class=\"cn-btn\" onclick=\"cnApp.findFromHex()\"\u003eFind Name\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cp class=\"cn-notice\"\u003eEnter a 6-digit hex code (e.g. #ff6347)\u003c/p\u003e","title":"Color Name Finder"},{"content":" Pick a base color, choose a harmony mode, and instantly get a beautiful 5-color palette. Lock any color to keep it while regenerating the rest. Click any swatch to copy its HEX code.\nBase Color Harmony Mode Complementary Analogous Triadic Split-Complementary Monochromatic Generate \u0026#9684; Random CSS Variables Export Copy CSS Trending Palette Presets\nRelated: Pick individual colors → Color Picker Related Tools Color Blindness Simulator Color Contrast Checker Color Converter Related Articles Best AI Image Generators Free 2026 ","permalink":"https://productivity-works.com/tools/color-palette-generator/","summary":"\u003cdiv id=\"pal-app\"\u003e\n\u003cstyle\u003e\n#pal-app *,#pal-app *::before,#pal-app *::after{box-sizing:border-box;margin:0;padding:0}\n#pal-app{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:#1a1a2e;--accent:#6c63ff;--radius:10px;--shadow:0 4px 24px rgba(0,0,0,.10)}\n#pal-app .pa-wrap{max-width:820px;margin:0 auto;padding:0 0 48px}\n#pal-app .pa-lead{font-size:.97rem;color:#555;margin-bottom:24px;line-height:1.6}\n\n/* Controls */\n#pal-app .pa-controls{display:flex;flex-wrap:wrap;gap:12px;align-items:flex-end;margin-bottom:28px;background:#f7f7fc;border-radius:var(--radius);padding:20px}\n#pal-app .pa-group{display:flex;flex-direction:column;gap:6px}\n#pal-app .pa-group label{font-size:.78rem;font-weight:600;color:#444;text-transform:uppercase;letter-spacing:.04em}\n#pal-app .pa-color-pick{width:60px;height:44px;border:2px solid #ddd;border-radius:8px;cursor:pointer;padding:2px;background:#fff}\n#pal-app .pa-select{height:44px;padding:0 12px;border:2px solid #ddd;border-radius:8px;font-size:.9rem;background:#fff;cursor:pointer;outline:none;transition:border-color .2s}\n#pal-app .pa-select:focus{border-color:var(--accent)}\n#pal-app .pa-btn{height:44px;padding:0 22px;border:none;border-radius:8px;font-size:.9rem;font-weight:600;cursor:pointer;transition:transform .1s,opacity .2s}\n#pal-app .pa-btn:active{transform:scale(.97)}\n#pal-app .pa-btn-primary{background:var(--accent);color:#fff}\n#pal-app .pa-btn-primary:hover{opacity:.88}\n#pal-app .pa-btn-secondary{background:#fff;color:#333;border:2px solid #ddd}\n#pal-app .pa-btn-secondary:hover{border-color:#aaa}\n#pal-app .pa-btn-random{background:#ff6584;color:#fff}\n#pal-app .pa-btn-random:hover{opacity:.88}\n\n/* Strip */\n#pal-app .pa-strip{display:flex;border-radius:var(--radius);overflow:hidden;height:80px;margin-bottom:20px;box-shadow:var(--shadow)}\n#pal-app .pa-strip-swatch{flex:1;transition:flex .3s}\n\n/* Cards */\n#pal-app .pa-cards{display:grid;grid-template-columns:repeat(5,1fr);gap:12px;margin-bottom:28px}\n@media(max-width:600px){#pal-app .pa-cards{grid-template-columns:repeat(2,1fr)}}\n#pal-app .pa-card{border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow);background:#fff;transition:transform .18s}\n#pal-app .pa-card:hover{transform:translateY(-3px)}\n#pal-app .pa-card-swatch{height:120px;position:relative;cursor:pointer}\n#pal-app .pa-card-swatch:hover::after{content:'Copy';position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.22);color:#fff;font-size:.9rem;font-weight:700}\n#pal-app .pa-card-swatch::after{display:flex;align-items:center;justify-content:center;font-size:.85rem;font-weight:600;color:transparent;transition:color .2s}\n#pal-app .pa-lock-btn{position:absolute;top:8px;right:8px;width:28px;height:28px;border:none;border-radius:50%;background:rgba(255,255,255,.75);cursor:pointer;font-size:14px;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);transition:background .2s}\n#pal-app .pa-lock-btn:hover{background:rgba(255,255,255,.95)}\n#pal-app .pa-lock-btn.locked{background:rgba(255,255,255,.95)}\n#pal-app .pa-card-info{padding:10px 10px 12px;display:flex;flex-direction:column;gap:4px}\n#pal-app .pa-hex{font-size:.92rem;font-weight:700;color:#1a1a2e;letter-spacing:.02em}\n#pal-app .pa-rgb{font-size:.72rem;color:#888}\n\n/* Toast */\n#pal-app .pa-toast{position:fixed;bottom:32px;left:50%;transform:translateX(-50%) translateY(80px);background:#1a1a2e;color:#fff;padding:10px 24px;border-radius:50px;font-size:.88rem;font-weight:600;opacity:0;transition:opacity .25s,transform .25s;z-index:9999;pointer-events:none}\n#pal-app .pa-toast.show{opacity:1;transform:translateX(-50%) translateY(0)}\n\n/* Export */\n#pal-app .pa-export{margin-bottom:28px}\n#pal-app .pa-export-box{background:#1a1a2e;color:#a8ff78;font-family:'Fira Mono','Courier New',monospace;font-size:.82rem;padding:18px 20px;border-radius:var(--radius);line-height:1.7;white-space:pre;overflow-x:auto}\n#pal-app .pa-export-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}\n#pal-app .pa-export-header h3{font-size:.95rem;font-weight:700;color:#333}\n\n/* Presets */\n#pal-app .pa-presets-title{font-size:.95rem;font-weight:700;color:#333;margin-bottom:12px}\n#pal-app .pa-presets{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:32px}\n#pal-app .pa-preset{border:2px solid transparent;border-radius:8px;overflow:hidden;cursor:pointer;transition:border-color .2s,transform .18s;width:120px}\n#pal-app .pa-preset:hover{border-color:var(--accent);transform:translateY(-2px)}\n#pal-app .pa-preset-strip{display:flex;height:28px}\n#pal-app .pa-preset-swatch{flex:1}\n#pal-app .pa-preset-name{font-size:.7rem;text-align:center;padding:4px;background:#fff;color:#444;font-weight:600}\n\n/* Divider */\n#pal-app .pa-divider{border:none;border-top:1px solid #eee;margin:8px 0 24px}\n\n/* Related */\n#pal-app .pa-related{font-size:.9rem;color:#555;padding:14px 18px;background:#f7f7fc;border-radius:var(--radius);border-left:4px solid var(--accent)}\n#pal-app .pa-related a{color:var(--accent);font-weight:600;text-decoration:none}\n#pal-app .pa-related a:hover{text-decoration:underline}\n\u003c/style\u003e\n\u003cdiv class=\"pa-wrap\"\u003e\n\u003cp class=\"pa-lead\"\u003ePick a base color, choose a harmony mode, and instantly get a beautiful 5-color palette. Lock any color to keep it while regenerating the rest. Click any swatch to copy its HEX code.\u003c/p\u003e","title":"Color Palette Generator — Free Online Tool"},{"content":" Click to pick color Color Picker \u0026 Converter Pick any color and convert between HEX, RGB, and HSL instantly.\nGenerate palettes, check contrast, and copy CSS code.\nColor Values HEX Copy RGB Copy HSL Copy RGB Sliders R G B HSL Sliders H S L Palette Generator Complementary Analogous Triadic Split-Complementary Contrast Checker (WCAG) Text Color Background Color The quick brown fox jumps over the lazy dog Color History No colors picked yet. CSS Code Copy CSS How to Use This Color Picker Pick a color by clicking the color swatch in the top panel to open your browser\u0026rsquo;s native color picker, or type a HEX code directly into the HEX field. All other values update instantly.\nAdjust with sliders: Use the RGB or HSL sliders to fine-tune your color. RGB sliders control Red, Green, and Blue channels (0–255 each). HSL sliders control Hue (0–360°), Saturation (0–100%), and Lightness (0–100%).\nCopy values: Click the Copy button next to HEX, RGB, or HSL to copy that format to your clipboard. Use the Copy CSS button at the bottom to grab ready-to-paste CSS declarations.\nGenerate a palette: Select a harmony type — Complementary, Analogous, Triadic, or Split-Complementary — and click any swatch to switch your active color to that palette entry.\nCheck contrast: Enter a text color and background color in the Contrast Checker. The tool calculates the contrast ratio and shows WCAG AA and AAA pass/fail results for normal and large text.\nColor history: The last 10 colors you\u0026rsquo;ve picked appear as clickable swatches. Click any swatch to return to that color instantly.\nColor Theory Basics Color harmony is the principle of arranging colors that are pleasing to the eye based on their relationships on the color wheel. Complementary colors sit directly opposite each other (e.g., blue and orange) and create high visual tension, making them ideal for call-to-action buttons and bold design accents. Analogous colors sit adjacent on the wheel and create a cohesive, low-contrast palette suited for backgrounds and subtle UI themes. Triadic schemes use three colors equally spaced at 120° apart, offering balanced contrast with more variety. Split-complementary is a softer alternative to the true complementary pairing, using one base color and two colors flanking its complement.\nContrast and accessibility go hand in hand. WCAG (Web Content Accessibility Guidelines) defines minimum contrast ratios to ensure readable text for people with low vision or color blindness. A ratio of 4.5:1 is required for normal-sized text (Level AA), while 7:1 meets the stricter Level AAA standard. Large text (18pt+ or 14pt bold) has a more lenient threshold of 3:1 for AA. Using the contrast checker built into this tool before publishing your designs helps ensure your color choices are inclusive and meet modern accessibility standards.\nRelated Tools Format and validate JSON data → JSON Formatter Count words and characters → Word Counter Generate secure passwords → Password Generator Design with gradients → CSS Gradient Generator — create beautiful CSS gradients visually\nNeed placeholder text? → Lorem Ipsum Generator — generate filler content for mockups\n","permalink":"https://productivity-works.com/tools/color-picker/","summary":"\u003cdiv id=\"color-app\"\u003e\n\u003cstyle\u003e\n#color-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 0 0 40px 0;\n}\n\n#color-app * {\n  box-sizing: border-box;\n}\n\n.ca-hero {\n  background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%);\n  border-radius: 16px;\n  padding: 32px 24px;\n  margin-bottom: 24px;\n  display: flex;\n  align-items: center;\n  gap: 24px;\n  flex-wrap: wrap;\n}\n\n.ca-preview-wrap {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 12px;\n}\n\n.ca-preview {\n  width: 140px;\n  height: 140px;\n  border-radius: 16px;\n  border: 4px solid rgba(255,255,255,0.5);\n  box-shadow: 0 8px 32px rgba(0,0,0,0.25);\n  background: #3498db;\n  transition: background 0.15s;\n}\n\n.ca-native-input {\n  width: 60px;\n  height: 36px;\n  border: none;\n  border-radius: 8px;\n  cursor: pointer;\n  padding: 2px;\n  background: rgba(255,255,255,0.2);\n}\n\n.ca-native-label {\n  color: rgba(255,255,255,0.85);\n  font-size: 12px;\n  text-align: center;\n}\n\n.ca-hero-title {\n  color: #fff;\n}\n\n.ca-hero-title h2 {\n  margin: 0 0 6px 0;\n  font-size: 22px;\n  font-weight: 700;\n}\n\n.ca-hero-title p {\n  margin: 0;\n  font-size: 14px;\n  opacity: 0.85;\n}\n\n.ca-card {\n  background: #fff;\n  border-radius: 14px;\n  box-shadow: 0 2px 16px rgba(0,0,0,0.08);\n  padding: 22px 24px;\n  margin-bottom: 18px;\n}\n\n.ca-card h3 {\n  margin: 0 0 18px 0;\n  font-size: 15px;\n  font-weight: 700;\n  color: #2d3748;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  border-bottom: 2px solid #f0f0f0;\n  padding-bottom: 10px;\n}\n\n/* Formats */\n.ca-format-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-bottom: 14px;\n}\n\n.ca-format-label {\n  width: 44px;\n  font-size: 13px;\n  font-weight: 700;\n  color: #555;\n  flex-shrink: 0;\n}\n\n.ca-format-input {\n  flex: 1;\n  padding: 8px 12px;\n  border: 2px solid #e2e8f0;\n  border-radius: 8px;\n  font-size: 14px;\n  font-family: 'Courier New', monospace;\n  color: #2d3748;\n  transition: border-color 0.2s;\n  min-width: 0;\n}\n\n.ca-format-input:focus {\n  outline: none;\n  border-color: #667eea;\n}\n\n.ca-format-input.invalid {\n  border-color: #fc5c65;\n  background: #fff5f5;\n}\n\n.ca-copy-btn {\n  padding: 8px 14px;\n  background: linear-gradient(135deg, #667eea, #764ba2);\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  white-space: nowrap;\n  transition: opacity 0.2s, transform 0.1s;\n  flex-shrink: 0;\n}\n\n.ca-copy-btn:hover {\n  opacity: 0.9;\n}\n\n.ca-copy-btn:active {\n  transform: scale(0.96);\n}\n\n.ca-copy-btn.copied {\n  background: linear-gradient(135deg, #48bb78, #38a169);\n}\n\n/* Sliders */\n.ca-slider-group {\n  margin-bottom: 16px;\n}\n\n.ca-slider-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-bottom: 8px;\n}\n\n.ca-slider-label {\n  width: 20px;\n  font-size: 13px;\n  font-weight: 700;\n  color: #555;\n  flex-shrink: 0;\n  text-align: center;\n}\n\n.ca-slider {\n  flex: 1;\n  height: 6px;\n  border-radius: 3px;\n  -webkit-appearance: none;\n  appearance: none;\n  outline: none;\n  cursor: pointer;\n  min-width: 0;\n}\n\n.ca-slider::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 18px;\n  height: 18px;\n  border-radius: 50%;\n  background: #fff;\n  border: 2px solid #667eea;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.2);\n  cursor: pointer;\n}\n\n.ca-slider::-moz-range-thumb {\n  width: 18px;\n  height: 18px;\n  border-radius: 50%;\n  background: #fff;\n  border: 2px solid #667eea;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.2);\n  cursor: pointer;\n}\n\n.ca-slider-num {\n  width: 54px;\n  padding: 6px 8px;\n  border: 2px solid #e2e8f0;\n  border-radius: 8px;\n  font-size: 13px;\n  text-align: center;\n  color: #2d3748;\n  font-family: 'Courier New', monospace;\n  flex-shrink: 0;\n}\n\n.ca-slider-num:focus {\n  outline: none;\n  border-color: #667eea;\n}\n\n/* Slider track colors */\n#ca-r-slider { background: linear-gradient(to right, #000, #f00); }\n#ca-g-slider { background: linear-gradient(to right, #000, #0f0); }\n#ca-b-slider { background: linear-gradient(to right, #000, #00f); }\n#ca-h-slider { background: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00); }\n#ca-s-slider { background: linear-gradient(to right, #888, #f00); }\n#ca-l-slider { background: linear-gradient(to right, #000, #888, #fff); }\n\n/* Palette */\n.ca-palette-controls {\n  display: flex;\n  gap: 8px;\n  flex-wrap: wrap;\n  margin-bottom: 16px;\n}\n\n.ca-palette-btn {\n  padding: 7px 14px;\n  border: 2px solid #e2e8f0;\n  border-radius: 20px;\n  background: #fff;\n  font-size: 12px;\n  font-weight: 600;\n  color: #555;\n  cursor: pointer;\n  transition: all 0.2s;\n}\n\n.ca-palette-btn:hover,\n.ca-palette-btn.active {\n  border-color: #667eea;\n  background: #667eea;\n  color: #fff;\n}\n\n.ca-palette-swatches {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n}\n\n.ca-swatch {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 5px;\n  cursor: pointer;\n}\n\n.ca-swatch-color {\n  width: 56px;\n  height: 56px;\n  border-radius: 12px;\n  border: 2px solid rgba(0,0,0,0.08);\n  box-shadow: 0 2px 8px rgba(0,0,0,0.12);\n  transition: transform 0.15s;\n}\n\n.ca-swatch:hover .ca-swatch-color {\n  transform: scale(1.08);\n}\n\n.ca-swatch-hex {\n  font-size: 10px;\n  font-family: 'Courier New', monospace;\n  color: #666;\n}\n\n/* Contrast checker */\n.ca-contrast-inputs {\n  display: flex;\n  gap: 16px;\n  margin-bottom: 16px;\n  flex-wrap: wrap;\n}\n\n.ca-contrast-field {\n  flex: 1;\n  min-width: 180px;\n}\n\n.ca-contrast-field label {\n  display: block;\n  font-size: 12px;\n  font-weight: 700;\n  color: #555;\n  margin-bottom: 6px;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n\n.ca-contrast-field input[type=\"color\"] {\n  width: 100%;\n  height: 44px;\n  border: 2px solid #e2e8f0;\n  border-radius: 10px;\n  cursor: pointer;\n  padding: 2px 4px;\n}\n\n.ca-contrast-preview {\n  padding: 16px 20px;\n  border-radius: 12px;\n  margin-bottom: 14px;\n  font-size: 16px;\n  font-weight: 600;\n  text-align: center;\n  transition: all 0.2s;\n}\n\n.ca-contrast-results {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n}\n\n.ca-contrast-badge {\n  padding: 8px 16px;\n  border-radius: 20px;\n  font-size: 13px;\n  font-weight: 700;\n}\n\n.ca-badge-pass {\n  background: #c6f6d5;\n  color: #276749;\n}\n\n.ca-badge-fail {\n  background: #fed7d7;\n  color: #9b2c2c;\n}\n\n.ca-contrast-ratio {\n  font-size: 20px;\n  font-weight: 800;\n  color: #2d3748;\n  margin-bottom: 10px;\n}\n\n/* History */\n.ca-history-swatches {\n  display: flex;\n  gap: 8px;\n  flex-wrap: wrap;\n}\n\n.ca-history-swatch {\n  width: 40px;\n  height: 40px;\n  border-radius: 10px;\n  border: 2px solid rgba(0,0,0,0.1);\n  cursor: pointer;\n  transition: transform 0.15s;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.1);\n}\n\n.ca-history-swatch:hover {\n  transform: scale(1.12);\n}\n\n/* CSS output */\n.ca-css-output {\n  background: #1e1e3f;\n  border-radius: 10px;\n  padding: 16px 18px;\n  font-family: 'Courier New', monospace;\n  font-size: 13px;\n  color: #a8b5e8;\n  line-height: 1.8;\n  position: relative;\n}\n\n.ca-css-comment { color: #636da6; }\n.ca-css-prop { color: #7aa0e8; }\n.ca-css-value { color: #e8c57a; }\n\n.ca-css-copy-btn {\n  position: absolute;\n  top: 10px;\n  right: 12px;\n  padding: 5px 12px;\n  background: rgba(255,255,255,0.1);\n  color: #a8b5e8;\n  border: 1px solid rgba(255,255,255,0.15);\n  border-radius: 6px;\n  font-size: 11px;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n\n.ca-css-copy-btn:hover {\n  background: rgba(255,255,255,0.18);\n}\n\n.ca-empty-history {\n  color: #aaa;\n  font-size: 13px;\n  font-style: italic;\n}\n\n@media (max-width: 600px) {\n  .ca-hero {\n    padding: 22px 16px;\n    gap: 16px;\n  }\n  .ca-preview {\n    width: 100px;\n    height: 100px;\n  }\n  .ca-card {\n    padding: 18px 16px;\n  }\n  .ca-format-row {\n    flex-wrap: wrap;\n  }\n  .ca-format-input {\n    width: 100%;\n  }\n  .ca-swatch-color {\n    width: 46px;\n    height: 46px;\n  }\n  .ca-slider-row {\n    flex-wrap: wrap;\n  }\n}\n\u003c/style\u003e\n\u003c!-- Hero: Preview + Native Picker --\u003e\n\u003cdiv class=\"ca-hero\"\u003e\n  \u003cdiv class=\"ca-preview-wrap\"\u003e\n    \u003cdiv class=\"ca-preview\" id=\"ca-preview\"\u003e\u003c/div\u003e\n    \u003cinput type=\"color\" class=\"ca-native-input\" id=\"ca-native\" value=\"#3498db\" title=\"Click to open color picker\"\u003e\n    \u003cspan class=\"ca-native-label\"\u003eClick to pick color\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ca-hero-title\"\u003e\n    \u003ch2\u003eColor Picker \u0026 Converter\u003c/h2\u003e\n    \u003cp\u003ePick any color and convert between HEX, RGB, and HSL instantly.\u003cbr\u003eGenerate palettes, check contrast, and copy CSS code.\u003c/p\u003e","title":"Color Picker \u0026 Converter - Free Online Color Tool"},{"content":" Pick a base color, choose a harmony type, and instantly get a complete color scheme with HEX, RGB, HSL values, CSS variables, and WCAG contrast ratios — all in your browser, no uploads, no sign-up.\nBase Color HEX Harmony Type Complementary Analogous Triadic Split-Complementary Tetradic Monochromatic \u0026nbsp; Generate Copy entire scheme: Copy all HEX Copy CSS vars CSS Variables Copy Accessibility — Contrast Ratios (WCAG 2.1) Generate palettes → Color Palette Generator Convert colors → RGB HEX Converter ","permalink":"https://productivity-works.com/tools/color-scheme-generator/","summary":"\u003cdiv id=\"cs-app\"\u003e\n\u003cstyle\u003e\n#cs-app *,#cs-app *::before,#cs-app *::after{box-sizing:border-box;margin:0;padding:0}\n#cs-app{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:#1a1a2e;--accent:#6c63ff;--radius:10px;--shadow:0 4px 24px rgba(0,0,0,.10)}\n#cs-app a{color:var(--accent)}\n\n/* Layout */\n#cs-app .cs-wrap{max-width:900px;margin:0 auto;padding:0 0 56px}\n#cs-app .cs-lead{font-size:.96rem;color:#555;margin-bottom:24px;line-height:1.65}\n\n/* Controls */\n#cs-app .cs-controls{display:flex;flex-wrap:wrap;gap:12px;align-items:flex-end;margin-bottom:28px;background:#f7f7fc;border-radius:var(--radius);padding:20px 22px}\n#cs-app .cs-group{display:flex;flex-direction:column;gap:6px}\n#cs-app .cs-group label{font-size:.75rem;font-weight:700;color:#555;text-transform:uppercase;letter-spacing:.05em}\n#cs-app .cs-color-pick{width:64px;height:46px;border:2px solid #ddd;border-radius:8px;cursor:pointer;padding:2px;background:#fff}\n#cs-app .cs-color-pick:hover{border-color:var(--accent)}\n#cs-app .cs-select{height:46px;padding:0 14px;border:2px solid #ddd;border-radius:8px;font-size:.9rem;background:#fff;cursor:pointer;outline:none;transition:border-color .2s;min-width:190px}\n#cs-app .cs-select:focus{border-color:var(--accent)}\n#cs-app .cs-hex-input{height:46px;padding:0 12px;border:2px solid #ddd;border-radius:8px;font-size:.9rem;font-family:monospace;width:110px;outline:none;transition:border-color .2s}\n#cs-app .cs-hex-input:focus{border-color:var(--accent)}\n#cs-app .cs-btn{height:46px;padding:0 22px;border:none;border-radius:8px;font-size:.9rem;font-weight:700;cursor:pointer;transition:transform .1s,opacity .2s;white-space:nowrap}\n#cs-app .cs-btn:active{transform:scale(.97)}\n#cs-app .cs-btn-primary{background:var(--accent);color:#fff}\n#cs-app .cs-btn-primary:hover{opacity:.87}\n#cs-app .cs-btn-secondary{background:#fff;color:#333;border:2px solid #ddd}\n#cs-app .cs-btn-secondary:hover{border-color:#aaa}\n#cs-app .cs-btn-sm{height:34px;padding:0 14px;font-size:.8rem}\n\n/* Scheme label */\n#cs-app .cs-scheme-label{font-size:1rem;font-weight:700;color:#1a1a2e;margin-bottom:16px;display:flex;align-items:center;gap:10px}\n#cs-app .cs-scheme-label span{font-size:.8rem;font-weight:500;color:#888;font-style:italic}\n\n/* Swatches grid */\n#cs-app .cs-swatches{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:16px;margin-bottom:28px}\n@media(max-width:600px){#cs-app .cs-swatches{grid-template-columns:repeat(2,1fr)}}\n@media(max-width:360px){#cs-app .cs-swatches{grid-template-columns:1fr}}\n\n#cs-app .cs-swatch-card{border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow);background:#fff;transition:transform .18s}\n#cs-app .cs-swatch-card:hover{transform:translateY(-3px)}\n#cs-app .cs-swatch-block{height:140px;position:relative;cursor:pointer;display:flex;align-items:center;justify-content:center}\n#cs-app .cs-swatch-copy-hint{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.18);opacity:0;transition:opacity .2s;font-size:.82rem;font-weight:700;letter-spacing:.04em}\n#cs-app .cs-swatch-block:hover .cs-swatch-copy-hint{opacity:1}\n#cs-app .cs-swatch-role{position:absolute;top:8px;left:10px;font-size:.68rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:2px 7px;border-radius:4px;background:rgba(255,255,255,.72);backdrop-filter:blur(4px);color:#333}\n\n#cs-app .cs-swatch-info{padding:12px 12px 14px;display:flex;flex-direction:column;gap:3px}\n#cs-app .cs-swatch-hex{font-size:.95rem;font-weight:700;letter-spacing:.03em;color:#1a1a2e}\n#cs-app .cs-swatch-rgb{font-size:.72rem;color:#777;font-family:monospace}\n#cs-app .cs-swatch-hsl{font-size:.72rem;color:#777;font-family:monospace}\n#cs-app .cs-swatch-copy-btn{margin-top:6px;height:28px;padding:0 10px;font-size:.73rem;font-weight:600;border:1.5px solid #ddd;border-radius:6px;background:#fff;cursor:pointer;transition:background .15s,border-color .15s;align-self:flex-start}\n#cs-app .cs-swatch-copy-btn:hover{background:#f0f0ff;border-color:var(--accent);color:var(--accent)}\n\n/* Contrast table */\n#cs-app .cs-contrast-section{margin-bottom:28px}\n#cs-app .cs-section-title{font-size:.95rem;font-weight:700;color:#1a1a2e;margin-bottom:14px;padding-bottom:8px;border-bottom:2px solid #f0f0f5}\n#cs-app .cs-contrast-grid{display:grid;gap:8px}\n#cs-app .cs-contrast-row{display:flex;align-items:center;gap:10px;padding:10px 14px;border-radius:8px;background:#fafafa;border:1px solid #eee;flex-wrap:wrap}\n#cs-app .cs-contrast-swatch-pair{display:flex;gap:4px;flex-shrink:0}\n#cs-app .cs-contrast-dot{width:22px;height:22px;border-radius:50%;border:1.5px solid rgba(0,0,0,.12)}\n#cs-app .cs-contrast-label{font-size:.82rem;color:#444;flex:1;min-width:120px;font-family:monospace}\n#cs-app .cs-contrast-ratio{font-size:.88rem;font-weight:700;padding:3px 10px;border-radius:20px;white-space:nowrap}\n#cs-app .cs-ratio-aaa{background:#d1fae5;color:#065f46}\n#cs-app .cs-ratio-aa{background:#dbeafe;color:#1e3a8a}\n#cs-app .cs-ratio-fail{background:#fee2e2;color:#991b1b}\n#cs-app .cs-wcag-badge{font-size:.7rem;font-weight:700;padding:2px 7px;border-radius:4px;background:#e5e7eb;color:#374151}\n#cs-app .cs-wcag-aaa{background:#a7f3d0;color:#064e3b}\n#cs-app .cs-wcag-aa{background:#bfdbfe;color:#1e40af}\n#cs-app .cs-wcag-fail{background:#fecaca;color:#7f1d1d}\n\n/* CSS Variables output */\n#cs-app .cs-css-section{margin-bottom:28px}\n#cs-app .cs-css-box{position:relative;background:#1a1a2e;border-radius:var(--radius);padding:20px 20px 20px 24px;font-family:'Courier New',monospace;font-size:.82rem;line-height:1.8;color:#e2e8f0;overflow-x:auto}\n#cs-app .cs-css-box .cs-var-comment{color:#718096}\n#cs-app .cs-css-box .cs-var-name{color:#81e6d9}\n#cs-app .cs-css-box .cs-var-val{color:#fbd38d}\n#cs-app .cs-css-copy-btn{position:absolute;top:12px;right:12px;height:30px;padding:0 14px;font-size:.75rem;font-weight:700;background:rgba(255,255,255,.12);color:#e2e8f0;border:1px solid rgba(255,255,255,.2);border-radius:6px;cursor:pointer;transition:background .15s}\n#cs-app .cs-css-copy-btn:hover{background:rgba(255,255,255,.22)}\n\n/* Copy all */\n#cs-app .cs-copy-all-bar{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:32px;align-items:center}\n#cs-app .cs-copy-all-bar span{font-size:.82rem;color:#666}\n\n/* Toast */\n#cs-app .cs-toast{position:fixed;bottom:32px;left:50%;transform:translateX(-50%) translateY(80px);background:#1a1a2e;color:#fff;padding:11px 26px;border-radius:50px;font-size:.87rem;font-weight:600;opacity:0;transition:opacity .25s,transform .25s;z-index:9999;pointer-events:none}\n#cs-app .cs-toast.show{opacity:1;transform:translateX(-50%) translateY(0)}\n\n/* Scheme description */\n#cs-app .cs-desc{font-size:.84rem;color:#555;margin-bottom:20px;padding:12px 16px;background:#f7f7fc;border-left:3px solid var(--accent);border-radius:0 8px 8px 0;line-height:1.6}\n\u003c/style\u003e\n\u003cdiv class=\"cs-wrap\"\u003e\n\u003cp class=\"cs-lead\"\u003ePick a base color, choose a harmony type, and instantly get a complete color scheme with HEX, RGB, HSL values, CSS variables, and WCAG contrast ratios — all in your browser, no uploads, no sign-up.\u003c/p\u003e","title":"Color Scheme Generator — Free Online Tool"},{"content":"Convert Kelvin color temperature to RGB, HEX, and HSL values instantly. Drag the slider from warm candlelight to cool blue sky, see the color preview update in real time, and copy the values with one click.\n5500K Daylight 1000K Warm Neutral Cool 12000K Warm (red/orange) Neutral white Cool (blue) HEX #fff4e0 Copy RGB rgb(255, 244, 224) Copy HSL hsl(38, 100%, 94%) Copy Copy All Values Common Light Sources Candle (1900K) Tungsten (2700K) Halogen (3200K) Fluorescent (4000K) Daylight (5500K) Overcast (6500K) Shade (7500K) Blue Sky (10000K) Warm vs Cool Light Warm light (1000–3500K) has a reddish-orange hue, like candles or incandescent bulbs. It creates a cozy, relaxing atmosphere and is popular in bedrooms and restaurants.\nNeutral light (3500–5000K) appears white and is close to morning daylight. It's used in offices and kitchens where clarity matters without harshness.\nCool light (5000–12000K) shifts toward blue-white and mimics overcast sky or open shade. Photographers use higher values to calibrate cameras for blue-sky conditions. Screens typically display around 6500K.\nThe Kelvin scale describes the color of light emitted by a theoretical \"black body\" radiator when heated to that temperature — counterintuitively, higher Kelvin = \"cooler\" (bluer) in visual perception.\nRelated Tools\nFind color names → Color Name Finder Mix colors → Color Mixer ","permalink":"https://productivity-works.com/tools/color-temperature/","summary":"\u003cp\u003eConvert \u003cstrong\u003eKelvin color temperature\u003c/strong\u003e to \u003cstrong\u003eRGB, HEX, and HSL\u003c/strong\u003e values instantly. Drag the slider from warm candlelight to cool blue sky, see the color preview update in real time, and copy the values with one click.\u003c/p\u003e\n\u003cdiv id=\"ct-app\"\u003e\n\u003cstyle\u003e\n#ct-app *, #ct-app *::before, #ct-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#ct-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 15px;\n  color: #1a1a2e;\n  background: #f7f8fc;\n  border-radius: 12px;\n  padding: 24px;\n  max-width: 740px;\n  margin: 0 auto;\n}\n#ct-app h2 {\n  font-size: 1.2rem;\n  font-weight: 700;\n  margin-bottom: 14px;\n  color: #12122a;\n}\n#ct-app .ct-preview-block {\n  display: flex;\n  gap: 18px;\n  align-items: stretch;\n  margin-bottom: 22px;\n  flex-wrap: wrap;\n}\n#ct-app .ct-swatch {\n  width: 130px;\n  min-height: 130px;\n  border-radius: 12px;\n  border: 2px solid rgba(0,0,0,0.10);\n  flex-shrink: 0;\n  transition: background 0.15s;\n}\n#ct-app .ct-values {\n  flex: 1;\n  min-width: 200px;\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n  justify-content: center;\n}\n#ct-app .ct-value-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n#ct-app .ct-label {\n  font-size: 12px;\n  font-weight: 700;\n  color: #6b7280;\n  width: 34px;\n  flex-shrink: 0;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#ct-app .ct-value-box {\n  flex: 1;\n  background: #fff;\n  border: 1.5px solid #e5e7eb;\n  border-radius: 7px;\n  padding: 7px 10px;\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  font-size: 14px;\n  color: #111827;\n  min-width: 0;\n}\n#ct-app .ct-copy-btn {\n  background: #6366f1;\n  color: #fff;\n  border: none;\n  border-radius: 7px;\n  padding: 7px 13px;\n  font-size: 12px;\n  font-weight: 700;\n  cursor: pointer;\n  white-space: nowrap;\n  transition: background 0.15s, transform 0.1s;\n}\n#ct-app .ct-copy-btn:hover { background: #4f46e5; }\n#ct-app .ct-copy-btn:active { transform: scale(0.96); }\n#ct-app .ct-copy-btn.copied { background: #22c55e; }\n#ct-app .ct-kelvin-display {\n  text-align: center;\n  font-size: 2rem;\n  font-weight: 800;\n  color: #12122a;\n  margin-bottom: 6px;\n  letter-spacing: -0.02em;\n}\n#ct-app .ct-kelvin-display span {\n  font-size: 1rem;\n  font-weight: 500;\n  color: #6b7280;\n  margin-left: 4px;\n}\n#ct-app .ct-preset-name {\n  text-align: center;\n  font-size: 13px;\n  color: #6366f1;\n  font-weight: 600;\n  margin-bottom: 14px;\n  min-height: 20px;\n}\n#ct-app .ct-slider-wrap {\n  margin-bottom: 6px;\n  position: relative;\n}\n#ct-app .ct-slider {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 100%;\n  height: 18px;\n  border-radius: 9px;\n  outline: none;\n  cursor: pointer;\n  background: linear-gradient(\n    to right,\n    #ff4500 0%,\n    #ff6a00 5%,\n    #ff8c00 10%,\n    #ffa500 15%,\n    #ffbf00 20%,\n    #ffd27f 28%,\n    #ffecb3 35%,\n    #fff4e0 42%,\n    #fff9f0 48%,\n    #ffffff 53%,\n    #f0f4ff 58%,\n    #d6e4ff 65%,\n    #bad4f5 72%,\n    #9ec5fa 80%,\n    #74b9ff 90%,\n    #4fc3f7 100%\n  );\n  border: 1.5px solid rgba(0,0,0,0.10);\n}\n#ct-app .ct-slider::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 26px;\n  height: 26px;\n  border-radius: 50%;\n  background: #fff;\n  border: 3px solid #6366f1;\n  box-shadow: 0 2px 8px rgba(99,102,241,0.3);\n  cursor: pointer;\n  transition: border-color 0.15s;\n}\n#ct-app .ct-slider::-moz-range-thumb {\n  width: 26px;\n  height: 26px;\n  border-radius: 50%;\n  background: #fff;\n  border: 3px solid #6366f1;\n  box-shadow: 0 2px 8px rgba(99,102,241,0.3);\n  cursor: pointer;\n}\n#ct-app .ct-scale-labels {\n  display: flex;\n  justify-content: space-between;\n  font-size: 11px;\n  color: #9ca3af;\n  margin-top: 4px;\n  margin-bottom: 20px;\n}\n#ct-app .ct-gradient-bar {\n  width: 100%;\n  height: 28px;\n  border-radius: 8px;\n  margin-bottom: 8px;\n  background: linear-gradient(\n    to right,\n    #ff4500 0%,\n    #ff6a00 5%,\n    #ff8c00 10%,\n    #ffa500 15%,\n    #ffbf00 20%,\n    #ffd27f 28%,\n    #ffecb3 35%,\n    #fff4e0 42%,\n    #fff9f0 48%,\n    #ffffff 53%,\n    #f0f4ff 58%,\n    #d6e4ff 65%,\n    #bad4f5 72%,\n    #9ec5fa 80%,\n    #74b9ff 90%,\n    #4fc3f7 100%\n  );\n  border: 1.5px solid rgba(0,0,0,0.08);\n}\n#ct-app .ct-gradient-label {\n  display: flex;\n  justify-content: space-between;\n  font-size: 11px;\n  color: #9ca3af;\n  margin-bottom: 22px;\n}\n#ct-app .ct-presets {\n  margin-bottom: 22px;\n}\n#ct-app .ct-presets h2 {\n  margin-bottom: 10px;\n}\n#ct-app .ct-preset-grid {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n}\n#ct-app .ct-preset-btn {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 7px 12px;\n  border-radius: 8px;\n  border: 1.5px solid #e5e7eb;\n  background: #fff;\n  cursor: pointer;\n  font-size: 13px;\n  color: #374151;\n  transition: border-color 0.15s, box-shadow 0.15s;\n  font-weight: 500;\n}\n#ct-app .ct-preset-btn:hover {\n  border-color: #6366f1;\n  box-shadow: 0 0 0 2px rgba(99,102,241,0.12);\n}\n#ct-app .ct-preset-btn.active {\n  border-color: #6366f1;\n  background: #eef2ff;\n  color: #4338ca;\n}\n#ct-app .ct-preset-dot {\n  width: 16px;\n  height: 16px;\n  border-radius: 50%;\n  border: 1.5px solid rgba(0,0,0,0.12);\n  flex-shrink: 0;\n}\n#ct-app .ct-info-section {\n  background: #fff;\n  border-radius: 10px;\n  border: 1.5px solid #e5e7eb;\n  padding: 18px 20px;\n  margin-top: 4px;\n}\n#ct-app .ct-info-section h2 {\n  font-size: 1rem;\n  margin-bottom: 10px;\n}\n#ct-app .ct-info-section p {\n  font-size: 14px;\n  color: #374151;\n  line-height: 1.65;\n  margin-bottom: 8px;\n}\n#ct-app .ct-info-section p:last-child { margin-bottom: 0; }\n#ct-app .ct-copy-all-btn {\n  display: inline-block;\n  margin-top: 4px;\n  padding: 9px 20px;\n  background: #6366f1;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 14px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s;\n}\n#ct-app .ct-copy-all-btn:hover { background: #4f46e5; }\n#ct-app .ct-copy-all-btn:active { transform: scale(0.97); }\n#ct-app .ct-copy-all-btn.copied { background: #22c55e; }\n@media (max-width: 480px) {\n  #ct-app { padding: 16px; }\n  #ct-app .ct-swatch { width: 100%; min-height: 80px; }\n  #ct-app .ct-kelvin-display { font-size: 1.6rem; }\n}\n\u003c/style\u003e\n\u003cdiv class=\"ct-kelvin-display\" id=\"ct-kelvin-num\"\u003e5500\u003cspan\u003eK\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"ct-preset-name\" id=\"ct-preset-name\"\u003eDaylight\u003c/div\u003e\n\u003cdiv class=\"ct-slider-wrap\"\u003e\n  \u003cinput type=\"range\" class=\"ct-slider\" id=\"ct-slider\" min=\"1000\" max=\"12000\" step=\"100\" value=\"5500\"\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ct-scale-labels\"\u003e\n  \u003cspan\u003e1000K\u003c/span\u003e\n  \u003cspan\u003eWarm\u003c/span\u003e\n  \u003cspan\u003eNeutral\u003c/span\u003e\n  \u003cspan\u003eCool\u003c/span\u003e\n  \u003cspan\u003e12000K\u003c/span\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ct-gradient-bar\"\u003e\u003c/div\u003e\n\u003cdiv class=\"ct-gradient-label\"\u003e\n  \u003cspan\u003eWarm (red/orange)\u003c/span\u003e\n  \u003cspan\u003eNeutral white\u003c/span\u003e\n  \u003cspan\u003eCool (blue)\u003c/span\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ct-preview-block\"\u003e\n  \u003cdiv class=\"ct-swatch\" id=\"ct-swatch\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"ct-values\"\u003e\n    \u003cdiv class=\"ct-value-row\"\u003e\n      \u003cspan class=\"ct-label\"\u003eHEX\u003c/span\u003e\n      \u003cspan class=\"ct-value-box\" id=\"ct-hex\"\u003e#fff4e0\u003c/span\u003e\n      \u003cbutton class=\"ct-copy-btn\" onclick=\"ctCopy('ct-hex',this)\"\u003eCopy\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ct-value-row\"\u003e\n      \u003cspan class=\"ct-label\"\u003eRGB\u003c/span\u003e\n      \u003cspan class=\"ct-value-box\" id=\"ct-rgb\"\u003ergb(255, 244, 224)\u003c/span\u003e\n      \u003cbutton class=\"ct-copy-btn\" onclick=\"ctCopy('ct-rgb',this)\"\u003eCopy\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ct-value-row\"\u003e\n      \u003cspan class=\"ct-label\"\u003eHSL\u003c/span\u003e\n      \u003cspan class=\"ct-value-box\" id=\"ct-hsl\"\u003ehsl(38, 100%, 94%)\u003c/span\u003e\n      \u003cbutton class=\"ct-copy-btn\" onclick=\"ctCopy('ct-hsl',this)\"\u003eCopy\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv style=\"margin-top:4px;\"\u003e\n      \u003cbutton class=\"ct-copy-all-btn\" id=\"ct-copy-all-btn\" onclick=\"ctCopyAll()\"\u003eCopy All Values\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ct-presets\"\u003e\n  \u003ch2\u003eCommon Light Sources\u003c/h2\u003e\n  \u003cdiv class=\"ct-preset-grid\" id=\"ct-preset-grid\"\u003e\n    \u003cbutton class=\"ct-preset-btn\" data-k=\"1900\" onclick=\"ctSetPreset(1900,this)\"\u003e\n      \u003cspan class=\"ct-preset-dot\" id=\"ct-dot-1900\"\u003e\u003c/span\u003eCandle (1900K)\n    \u003c/button\u003e\n    \u003cbutton class=\"ct-preset-btn\" data-k=\"2700\" onclick=\"ctSetPreset(2700,this)\"\u003e\n      \u003cspan class=\"ct-preset-dot\" id=\"ct-dot-2700\"\u003e\u003c/span\u003eTungsten (2700K)\n    \u003c/button\u003e\n    \u003cbutton class=\"ct-preset-btn\" data-k=\"3200\" onclick=\"ctSetPreset(3200,this)\"\u003e\n      \u003cspan class=\"ct-preset-dot\" id=\"ct-dot-3200\"\u003e\u003c/span\u003eHalogen (3200K)\n    \u003c/button\u003e\n    \u003cbutton class=\"ct-preset-btn\" data-k=\"4000\" onclick=\"ctSetPreset(4000,this)\"\u003e\n      \u003cspan class=\"ct-preset-dot\" id=\"ct-dot-4000\"\u003e\u003c/span\u003eFluorescent (4000K)\n    \u003c/button\u003e\n    \u003cbutton class=\"ct-preset-btn\" data-k=\"5500\" onclick=\"ctSetPreset(5500,this)\"\u003e\n      \u003cspan class=\"ct-preset-dot\" id=\"ct-dot-5500\"\u003e\u003c/span\u003eDaylight (5500K)\n    \u003c/button\u003e\n    \u003cbutton class=\"ct-preset-btn\" data-k=\"6500\" onclick=\"ctSetPreset(6500,this)\"\u003e\n      \u003cspan class=\"ct-preset-dot\" id=\"ct-dot-6500\"\u003e\u003c/span\u003eOvercast (6500K)\n    \u003c/button\u003e\n    \u003cbutton class=\"ct-preset-btn\" data-k=\"7500\" onclick=\"ctSetPreset(7500,this)\"\u003e\n      \u003cspan class=\"ct-preset-dot\" id=\"ct-dot-7500\"\u003e\u003c/span\u003eShade (7500K)\n    \u003c/button\u003e\n    \u003cbutton class=\"ct-preset-btn\" data-k=\"10000\" onclick=\"ctSetPreset(10000,this)\"\u003e\n      \u003cspan class=\"ct-preset-dot\" id=\"ct-dot-10000\"\u003e\u003c/span\u003eBlue Sky (10000K)\n    \u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ct-info-section\"\u003e\n  \u003ch2\u003eWarm vs Cool Light\u003c/h2\u003e\n  \u003cp\u003e\u003cstrong\u003eWarm light (1000–3500K)\u003c/strong\u003e has a reddish-orange hue, like candles or incandescent bulbs. It creates a cozy, relaxing atmosphere and is popular in bedrooms and restaurants.\u003c/p\u003e","title":"Color Temperature Converter"},{"content":" Color Vision Test This Ishihara-style screening test checks how well you distinguish colors. Look at each plate and enter the number you see hidden in the pattern.\nDisclaimer: This is a screening tool only. Results are not a medical diagnosis. Consult a qualified eye care professional for a formal evaluation. Plate 1 of 5 What number do you see? Submit Your Results out of 5 correct Reminder: This is a screening tool only. Consult an eye care professional for diagnosis. Take the Test Again Related Free Tools Reaction Time Test – Measure your reflexes Reading Speed Test – Words per minute calculator Focus Timer – Pomodoro-style productivity timer Related Tools Color Blindness Simulator Color Contrast Checker Color Converter ","permalink":"https://productivity-works.com/tools/color-vision-test/","summary":"\u003cdiv id=\"cvt-app\"\u003e\n\u003cstyle\u003e\n#cvt-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 720px;\n  margin: 0 auto;\n  padding: 16px;\n  color: #1a1a2e;\n}\n#cvt-app h2 {\n  font-size: 1.5rem;\n  margin-bottom: 8px;\n  color: #1a1a2e;\n}\n#cvt-app p {\n  margin: 0 0 12px;\n  line-height: 1.6;\n}\n#cvt-app .cvt-disclaimer {\n  background: #fff8e1;\n  border-left: 4px solid #f9a825;\n  padding: 10px 14px;\n  border-radius: 4px;\n  font-size: 0.85rem;\n  margin-bottom: 20px;\n  color: #5d4037;\n}\n#cvt-app .cvt-plate-area {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 16px;\n  margin-bottom: 20px;\n}\n#cvt-app .cvt-progress {\n  font-size: 0.9rem;\n  color: #555;\n}\n#cvt-app canvas#cvt-canvas {\n  border-radius: 50%;\n  border: 3px solid #ddd;\n  display: block;\n  max-width: 100%;\n}\n#cvt-app .cvt-question {\n  font-size: 1.05rem;\n  font-weight: 600;\n  text-align: center;\n}\n#cvt-app .cvt-input-row {\n  display: flex;\n  gap: 8px;\n  align-items: center;\n  flex-wrap: wrap;\n  justify-content: center;\n}\n#cvt-app input#cvt-answer {\n  border: 2px solid #ccc;\n  border-radius: 8px;\n  padding: 10px 14px;\n  font-size: 1.1rem;\n  width: 120px;\n  text-align: center;\n  outline: none;\n  transition: border-color 0.2s;\n}\n#cvt-app input#cvt-answer:focus {\n  border-color: #5c6bc0;\n}\n#cvt-app button.cvt-btn {\n  background: #5c6bc0;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  padding: 10px 22px;\n  font-size: 1rem;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n#cvt-app button.cvt-btn:hover {\n  background: #3949ab;\n}\n#cvt-app button.cvt-btn:disabled {\n  background: #aaa;\n  cursor: default;\n}\n#cvt-app .cvt-feedback {\n  font-size: 0.95rem;\n  min-height: 22px;\n  text-align: center;\n  font-weight: 600;\n}\n#cvt-app .cvt-feedback.correct { color: #2e7d32; }\n#cvt-app .cvt-feedback.wrong   { color: #c62828; }\n#cvt-app .cvt-result {\n  background: #f3f4ff;\n  border: 2px solid #5c6bc0;\n  border-radius: 12px;\n  padding: 24px;\n  text-align: center;\n}\n#cvt-app .cvt-result h3 {\n  margin: 0 0 10px;\n  font-size: 1.3rem;\n  color: #1a1a2e;\n}\n#cvt-app .cvt-score-num {\n  font-size: 2.8rem;\n  font-weight: 700;\n  color: #5c6bc0;\n  line-height: 1;\n  margin: 10px 0;\n}\n#cvt-app .cvt-interp {\n  margin: 12px 0;\n  padding: 12px;\n  border-radius: 8px;\n  background: #e8eaf6;\n  font-size: 0.97rem;\n  line-height: 1.6;\n}\n#cvt-app .cvt-detail-list {\n  text-align: left;\n  font-size: 0.9rem;\n  margin: 12px 0;\n  padding: 0 0 0 18px;\n  color: #333;\n}\n#cvt-app .cvt-detail-list li {\n  margin-bottom: 4px;\n}\n#cvt-app button#cvt-restart {\n  margin-top: 14px;\n  background: #5c6bc0;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  padding: 10px 28px;\n  font-size: 1rem;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n#cvt-app button#cvt-restart:hover {\n  background: #3949ab;\n}\n#cvt-app .cvt-related {\n  margin-top: 28px;\n  padding-top: 18px;\n  border-top: 1px solid #ddd;\n  font-size: 0.95rem;\n}\n#cvt-app .cvt-related h4 {\n  margin: 0 0 8px;\n  color: #333;\n}\n#cvt-app .cvt-related ul {\n  padding-left: 18px;\n  margin: 0;\n}\n#cvt-app .cvt-related ul li {\n  margin-bottom: 5px;\n}\n#cvt-app .cvt-hidden { display: none; }\n\u003c/style\u003e\n\u003ch2\u003eColor Vision Test\u003c/h2\u003e\n\u003cp\u003eThis Ishihara-style screening test checks how well you distinguish colors. Look at each plate and enter the number you see hidden in the pattern.\u003c/p\u003e","title":"Color Vision Test - Check Your Color Perception"},{"content":" \u0026lt;div class=\u0026quot;cw-brightness-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cw-brightness-label\u0026quot;\u0026gt;Brightness\u0026lt;/div\u0026gt; \u0026lt;canvas id=\u0026quot;cw-brightness-canvas\u0026quot; width=\u0026quot;300\u0026quot; height=\u0026quot;28\u0026quot;\u0026gt;\u0026lt;/canvas\u0026gt; \u0026lt;div class=\u0026quot;cw-brightness-label\u0026quot;\u0026gt;Saturation\u0026lt;/div\u0026gt; \u0026lt;canvas id=\u0026quot;cw-saturation-canvas\u0026quot; width=\u0026quot;300\u0026quot; height=\u0026quot;28\u0026quot;\u0026gt;\u0026lt;/canvas\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cw-harmony-buttons\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;cw-btn active\u0026quot; data-mode=\u0026quot;complementary\u0026quot;\u0026gt;Complementary\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cw-btn\u0026quot; data-mode=\u0026quot;analogous\u0026quot;\u0026gt;Analogous\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cw-btn\u0026quot; data-mode=\u0026quot;triadic\u0026quot;\u0026gt;Triadic\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cw-btn\u0026quot; data-mode=\u0026quot;split\u0026quot;\u0026gt;Split-Comp\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cw-btn\u0026quot; data-mode=\u0026quot;tetradic\u0026quot;\u0026gt;Tetradic\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; Color Palette \u0026lt;div class=\u0026quot;cw-section-title\u0026quot;\u0026gt;WCAG Contrast\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cw-contrast-box\u0026quot; id=\u0026quot;cw-contrast-box\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cw-contrast-row\u0026quot;\u0026gt; \u0026lt;span style=\u0026quot;color:#94a3b8;font-size:13px;\u0026quot;\u0026gt;Base vs Harmony #1\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;cw-contrast-ratio\u0026quot; id=\u0026quot;cw-ratio-val\u0026quot;\u0026gt;—\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;cw-wcag-badge\u0026quot; id=\u0026quot;cw-wcag-badge\u0026quot;\u0026gt;—\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cw-contrast-preview\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cw-cp-sample\u0026quot; id=\u0026quot;cw-cp-dark\u0026quot;\u0026gt;Aa\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cw-cp-sample\u0026quot; id=\u0026quot;cw-cp-light\u0026quot;\u0026gt;Aa\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cw-section-title\u0026quot;\u0026gt;CSS Export\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cw-export-box\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cw-export-header\u0026quot;\u0026gt; \u0026lt;span style=\u0026quot;font-size:13px;color:#94a3b8;\u0026quot;\u0026gt;CSS Custom Properties\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026quot;cw-copy-css-btn\u0026quot; id=\u0026quot;cw-copy-css\u0026quot;\u0026gt;Copy CSS\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;textarea id=\u0026quot;cw-css-output\u0026quot; readonly\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cw-crosslinks\u0026quot;\u0026gt; Generate color palettes \u0026amp;rarr; \u0026lt;a href=\u0026quot;/tools/color-palette-generator/\u0026quot;\u0026gt;Color Palette Generator\u0026lt;/a\u0026gt;\u0026lt;br\u0026gt; Check color contrast \u0026amp;rarr; \u0026lt;a href=\u0026quot;/tools/color-contrast-checker/\u0026quot;\u0026gt;Color Contrast Checker\u0026lt;/a\u0026gt;\u0026lt;br\u0026gt; Simulate color blindness \u0026amp;rarr; \u0026lt;a href=\u0026quot;/tools/color-blindness-simulator/\u0026quot;\u0026gt;Color Blindness Simulator\u0026lt;/a\u0026gt; \u0026lt;/div\u0026gt; Related Tools Color Blindness Simulator Color Contrast Checker Color Converter ","permalink":"https://productivity-works.com/tools/color-wheel-picker/","summary":"\u003cdiv id=\"cw-app\"\u003e\n\u003cstyle\u003e\n#cw-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #e2e8f0;\n}\n#cw-app * {\n  box-sizing: border-box;\n}\n#cw-app .cw-layout {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 24px;\n  align-items: flex-start;\n}\n#cw-app .cw-left {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 16px;\n  flex: 0 0 auto;\n}\n#cw-app .cw-right {\n  flex: 1 1 320px;\n  display: flex;\n  flex-direction: column;\n  gap: 16px;\n}\n#cw-app .cw-wheel-wrap {\n  position: relative;\n  width: 300px;\n  height: 300px;\n}\n#cw-app #cw-wheel {\n  border-radius: 50%;\n  cursor: crosshair;\n  display: block;\n}\n#cw-app .cw-brightness-wrap {\n  width: 300px;\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n}\n#cw-app .cw-brightness-label {\n  font-size: 12px;\n  color: #94a3b8;\n  letter-spacing: 0.05em;\n  text-transform: uppercase;\n}\n#cw-app #cw-brightness-canvas {\n  width: 300px;\n  height: 28px;\n  border-radius: 6px;\n  cursor: crosshair;\n  display: block;\n}\n#cw-app #cw-saturation-canvas {\n  width: 300px;\n  height: 28px;\n  border-radius: 6px;\n  cursor: crosshair;\n  display: block;\n}\n#cw-app .cw-harmony-buttons {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  width: 300px;\n}\n#cw-app .cw-btn {\n  padding: 6px 12px;\n  border-radius: 6px;\n  border: 1px solid #475569;\n  background: #1e293b;\n  color: #cbd5e1;\n  font-size: 13px;\n  cursor: pointer;\n  transition: background 0.15s, border-color 0.15s, color 0.15s;\n  white-space: nowrap;\n}\n#cw-app .cw-btn:hover {\n  background: #334155;\n  color: #f1f5f9;\n}\n#cw-app .cw-btn.active {\n  background: #3b82f6;\n  border-color: #3b82f6;\n  color: #fff;\n}\n#cw-app .cw-section-title {\n  font-size: 13px;\n  font-weight: 600;\n  color: #94a3b8;\n  letter-spacing: 0.07em;\n  text-transform: uppercase;\n  margin-bottom: 2px;\n}\n#cw-app .cw-swatches {\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n}\n#cw-app .cw-swatch-row {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 10px;\n  padding: 10px 14px;\n}\n#cw-app .cw-swatch-box {\n  width: 48px;\n  height: 48px;\n  border-radius: 8px;\n  flex-shrink: 0;\n  border: 2px solid #475569;\n}\n#cw-app .cw-swatch-info {\n  display: flex;\n  flex-direction: column;\n  gap: 4px;\n  flex: 1;\n}\n#cw-app .cw-swatch-label {\n  font-size: 11px;\n  color: #64748b;\n  font-weight: 500;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n}\n#cw-app .cw-values {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 6px;\n}\n#cw-app .cw-val-chip {\n  font-size: 12px;\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  background: #0f172a;\n  border: 1px solid #334155;\n  border-radius: 4px;\n  padding: 2px 8px;\n  color: #93c5fd;\n  cursor: pointer;\n  transition: background 0.12s, color 0.12s;\n  user-select: none;\n}\n#cw-app .cw-val-chip:hover {\n  background: #1d4ed8;\n  color: #fff;\n  border-color: #2563eb;\n}\n#cw-app .cw-val-chip.copied {\n  background: #16a34a;\n  color: #fff;\n  border-color: #16a34a;\n}\n#cw-app .cw-contrast-box {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 10px;\n  padding: 14px;\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n}\n#cw-app .cw-contrast-row {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  font-size: 13px;\n  flex-wrap: wrap;\n  gap: 6px;\n}\n#cw-app .cw-contrast-ratio {\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  font-size: 18px;\n  font-weight: 700;\n  color: #f1f5f9;\n}\n#cw-app .cw-wcag-badge {\n  padding: 2px 10px;\n  border-radius: 20px;\n  font-size: 11px;\n  font-weight: 700;\n  letter-spacing: 0.05em;\n}\n#cw-app .badge-pass {\n  background: #14532d;\n  color: #4ade80;\n  border: 1px solid #16a34a;\n}\n#cw-app .badge-fail {\n  background: #450a0a;\n  color: #f87171;\n  border: 1px solid #dc2626;\n}\n#cw-app .badge-aa {\n  background: #1c3a6e;\n  color: #93c5fd;\n  border: 1px solid #2563eb;\n}\n#cw-app .cw-contrast-preview {\n  display: flex;\n  gap: 8px;\n}\n#cw-app .cw-cp-sample {\n  flex: 1;\n  border-radius: 6px;\n  padding: 8px 10px;\n  font-size: 13px;\n  font-weight: 600;\n  text-align: center;\n  border: 1px solid #334155;\n}\n#cw-app .cw-export-box {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 10px;\n  padding: 14px;\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n}\n#cw-app .cw-export-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n}\n#cw-app #cw-css-output {\n  background: #0f172a;\n  color: #7dd3fc;\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  font-size: 12px;\n  border: 1px solid #334155;\n  border-radius: 6px;\n  padding: 10px;\n  resize: vertical;\n  min-height: 90px;\n  width: 100%;\n  white-space: pre;\n  overflow-x: auto;\n}\n#cw-app .cw-copy-css-btn {\n  padding: 6px 16px;\n  border-radius: 6px;\n  border: 1px solid #3b82f6;\n  background: #1d4ed8;\n  color: #fff;\n  font-size: 13px;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n#cw-app .cw-copy-css-btn:hover {\n  background: #2563eb;\n}\n#cw-app .cw-copy-css-btn.copied {\n  background: #16a34a;\n  border-color: #16a34a;\n}\n#cw-app .cw-crosslinks {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 10px;\n  padding: 14px 18px;\n  font-size: 13px;\n  color: #94a3b8;\n  line-height: 1.8;\n}\n#cw-app .cw-crosslinks a {\n  color: #60a5fa;\n  text-decoration: none;\n}\n#cw-app .cw-crosslinks a:hover {\n  text-decoration: underline;\n}\n@media (max-width: 640px) {\n  #cw-app .cw-layout {\n    flex-direction: column;\n    align-items: center;\n  }\n  #cw-app .cw-right {\n    width: 100%;\n  }\n  #cw-app .cw-wheel-wrap,\n  #cw-app #cw-wheel,\n  #cw-app .cw-brightness-wrap,\n  #cw-app #cw-brightness-canvas,\n  #cw-app #cw-saturation-canvas,\n  #cw-app .cw-harmony-buttons {\n    width: 280px;\n  }\n  #cw-app .cw-wheel-wrap {\n    height: 280px;\n  }\n  #cw-app #cw-wheel {\n    width: 280px;\n    height: 280px;\n  }\n}\n\u003c/style\u003e\n\u003cdiv class=\"cw-layout\"\u003e\n  \u003c!-- LEFT: Wheel + sliders + harmony buttons --\u003e\n  \u003cdiv class=\"cw-left\"\u003e\n    \u003cdiv class=\"cw-wheel-wrap\"\u003e\n      \u003ccanvas id=\"cw-wheel\" width=\"300\" height=\"300\"\u003e\u003c/canvas\u003e\n    \u003c/div\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;div class=\u0026quot;cw-brightness-wrap\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;cw-brightness-label\u0026quot;\u0026gt;Brightness\u0026lt;/div\u0026gt;\n  \u0026lt;canvas id=\u0026quot;cw-brightness-canvas\u0026quot; width=\u0026quot;300\u0026quot; height=\u0026quot;28\u0026quot;\u0026gt;\u0026lt;/canvas\u0026gt;\n  \u0026lt;div class=\u0026quot;cw-brightness-label\u0026quot;\u0026gt;Saturation\u0026lt;/div\u0026gt;\n  \u0026lt;canvas id=\u0026quot;cw-saturation-canvas\u0026quot; width=\u0026quot;300\u0026quot; height=\u0026quot;28\u0026quot;\u0026gt;\u0026lt;/canvas\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;cw-harmony-buttons\u0026quot;\u0026gt;\n  \u0026lt;button class=\u0026quot;cw-btn active\u0026quot; data-mode=\u0026quot;complementary\u0026quot;\u0026gt;Complementary\u0026lt;/button\u0026gt;\n  \u0026lt;button class=\u0026quot;cw-btn\u0026quot; data-mode=\u0026quot;analogous\u0026quot;\u0026gt;Analogous\u0026lt;/button\u0026gt;\n  \u0026lt;button class=\u0026quot;cw-btn\u0026quot; data-mode=\u0026quot;triadic\u0026quot;\u0026gt;Triadic\u0026lt;/button\u0026gt;\n  \u0026lt;button class=\u0026quot;cw-btn\u0026quot; data-mode=\u0026quot;split\u0026quot;\u0026gt;Split-Comp\u0026lt;/button\u0026gt;\n  \u0026lt;button class=\u0026quot;cw-btn\u0026quot; data-mode=\u0026quot;tetradic\u0026quot;\u0026gt;Tetradic\u0026lt;/button\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n  \u003c!-- RIGHT: Swatches + contrast + export --\u003e\n  \u003cdiv class=\"cw-right\"\u003e\n    \u003cdiv class=\"cw-section-title\"\u003eColor Palette\u003c/div\u003e\n    \u003cdiv class=\"cw-swatches\" id=\"cw-swatches\"\u003e\u003c/div\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;div class=\u0026quot;cw-section-title\u0026quot;\u0026gt;WCAG Contrast\u0026lt;/div\u0026gt;\n\u0026lt;div class=\u0026quot;cw-contrast-box\u0026quot; id=\u0026quot;cw-contrast-box\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;cw-contrast-row\u0026quot;\u0026gt;\n    \u0026lt;span style=\u0026quot;color:#94a3b8;font-size:13px;\u0026quot;\u0026gt;Base vs Harmony #1\u0026lt;/span\u0026gt;\n    \u0026lt;span class=\u0026quot;cw-contrast-ratio\u0026quot; id=\u0026quot;cw-ratio-val\u0026quot;\u0026gt;—\u0026lt;/span\u0026gt;\n    \u0026lt;span class=\u0026quot;cw-wcag-badge\u0026quot; id=\u0026quot;cw-wcag-badge\u0026quot;\u0026gt;—\u0026lt;/span\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cw-contrast-preview\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;cw-cp-sample\u0026quot; id=\u0026quot;cw-cp-dark\u0026quot;\u0026gt;Aa\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;cw-cp-sample\u0026quot; id=\u0026quot;cw-cp-light\u0026quot;\u0026gt;Aa\u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;cw-section-title\u0026quot;\u0026gt;CSS Export\u0026lt;/div\u0026gt;\n\u0026lt;div class=\u0026quot;cw-export-box\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;cw-export-header\u0026quot;\u0026gt;\n    \u0026lt;span style=\u0026quot;font-size:13px;color:#94a3b8;\u0026quot;\u0026gt;CSS Custom Properties\u0026lt;/span\u0026gt;\n    \u0026lt;button class=\u0026quot;cw-copy-css-btn\u0026quot; id=\u0026quot;cw-copy-css\u0026quot;\u0026gt;Copy CSS\u0026lt;/button\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;textarea id=\u0026quot;cw-css-output\u0026quot; readonly\u0026gt;\u0026lt;/textarea\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;cw-crosslinks\u0026quot;\u0026gt;\n  Generate color palettes \u0026amp;rarr; \u0026lt;a href=\u0026quot;/tools/color-palette-generator/\u0026quot;\u0026gt;Color Palette Generator\u0026lt;/a\u0026gt;\u0026lt;br\u0026gt;\n  Check color contrast \u0026amp;rarr; \u0026lt;a href=\u0026quot;/tools/color-contrast-checker/\u0026quot;\u0026gt;Color Contrast Checker\u0026lt;/a\u0026gt;\u0026lt;br\u0026gt;\n  Simulate color blindness \u0026amp;rarr; \u0026lt;a href=\u0026quot;/tools/color-blindness-simulator/\u0026quot;\u0026gt;Color Blindness Simulator\u0026lt;/a\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  \"use strict\";\n\n  // ── State ──────────────────────────────────────────────────────────────────\n  var state = {\n    hue: 200,\n    sat: 80,\n    bri: 55,\n    mode: \"complementary\",\n  };\n\n  // ── Canvas references ──────────────────────────────────────────────────────\n  var wheelCanvas = document.getElementById(\"cw-wheel\");\n  var wCtx = wheelCanvas.getContext(\"2d\");\n  var brCanvas = document.getElementById(\"cw-brightness-canvas\");\n  var brCtx = brCanvas.getContext(\"2d\");\n  var saCanvas = document.getElementById(\"cw-saturation-canvas\");\n  var saCtx = saCanvas.getContext(\"2d\");\n\n  var WHEEL_R = 150; // radius of wheel canvas\n\n  // ── Color math ────────────────────────────────────────────────────────────\n  function hslToRgb(h, s, l) {\n    s /= 100; l /= 100;\n    var c = (1 - Math.abs(2 * l - 1)) * s;\n    var x = c * (1 - Math.abs((h / 60) % 2 - 1));\n    var m = l - c / 2;\n    var r, g, b;\n    if (h \u003c 60)       { r=c; g=x; b=0; }\n    else if (h \u003c 120) { r=x; g=c; b=0; }\n    else if (h \u003c 180) { r=0; g=c; b=x; }\n    else if (h \u003c 240) { r=0; g=x; b=c; }\n    else if (h \u003c 300) { r=x; g=0; b=c; }\n    else              { r=c; g=0; b=x; }\n    return [\n      Math.round((r + m) * 255),\n      Math.round((g + m) * 255),\n      Math.round((b + m) * 255),\n    ];\n  }\n\n  function rgbToHex(r, g, b) {\n    return \"#\" + [r, g, b].map(function(v) {\n      return v.toString(16).padStart(2, \"0\");\n    }).join(\"\");\n  }\n\n  function colorInfo(h, s, l) {\n    var rgb = hslToRgb(h, s, l);\n    return {\n      hsl: \"hsl(\" + Math.round(h) + \", \" + Math.round(s) + \"%, \" + Math.round(l) + \"%)\",\n      hex: rgbToHex(rgb[0], rgb[1], rgb[2]),\n      rgb: \"rgb(\" + rgb[0] + \", \" + rgb[1] + \", \" + rgb[2] + \")\",\n      r: rgb[0], g: rgb[1], b: rgb[2],\n    };\n  }\n\n  // Harmony offsets (degrees) for each mode\n  function harmonyOffsets(mode) {\n    switch (mode) {\n      case \"complementary\":    return [180];\n      case \"analogous\":        return [-30, 30];\n      case \"triadic\":          return [120, 240];\n      case \"split\":            return [150, 210];\n      case \"tetradic\":         return [90, 180, 270];\n      default:                 return [180];\n    }\n  }\n\n  function buildPalette() {\n    var offsets = harmonyOffsets(state.mode);\n    var colors = [colorInfo(state.hue, state.sat, state.bri)];\n    offsets.forEach(function(off) {\n      var h = ((state.hue + off) % 360 + 360) % 360;\n      colors.push(colorInfo(h, state.sat, state.bri));\n    });\n    return colors;\n  }\n\n  // ── Draw wheel ────────────────────────────────────────────────────────────\n  function drawWheel() {\n    var size = wheelCanvas.width;\n    var cx = size / 2, cy = size / 2, r = size / 2 - 2;\n    wCtx.clearRect(0, 0, size, size);\n\n    // HSL hue ring\n    for (var angle = 0; angle \u003c 360; angle += 1) {\n      var startAngle = (angle - 1) * Math.PI / 180;\n      var endAngle   = (angle + 1) * Math.PI / 180;\n      var grad = wCtx.createRadialGradient(cx, cy, 0, cx, cy, r);\n      grad.addColorStop(0,   \"hsl(\" + angle + \",0%,50%)\");\n      grad.addColorStop(0.5, \"hsl(\" + angle + \",\" + state.sat + \"%,\" + state.bri + \"%)\");\n      grad.addColorStop(1,   \"hsl(\" + angle + \",\" + state.sat + \"%,\" + state.bri + \"%)\");\n      wCtx.beginPath();\n      wCtx.moveTo(cx, cy);\n      wCtx.arc(cx, cy, r, startAngle, endAngle);\n      wCtx.closePath();\n      wCtx.fillStyle = grad;\n      wCtx.fill();\n    }\n\n    // White radial overlay for inner white fade\n    var fade = wCtx.createRadialGradient(cx, cy, 0, cx, cy, r);\n    fade.addColorStop(0,   \"rgba(255,255,255,0.7)\");\n    fade.addColorStop(0.4, \"rgba(255,255,255,0.1)\");\n    fade.addColorStop(1,   \"rgba(255,255,255,0)\");\n    wCtx.beginPath();\n    wCtx.arc(cx, cy, r, 0, Math.PI * 2);\n    wCtx.fillStyle = fade;\n    wCtx.fill();\n\n    // Clip to circle\n    wCtx.save();\n    wCtx.beginPath();\n    wCtx.arc(cx, cy, r, 0, Math.PI * 2);\n    wCtx.clip();\n\n    // Draw harmony dots + base dot\n    var offsets = harmonyOffsets(state.mode);\n    var allHues = [state.hue].concat(offsets.map(function(o) {\n      return ((state.hue + o) % 360 + 360) % 360;\n    }));\n    var dotColors = [\"#ffffff\"].concat([\"#fbbf24\", \"#34d399\", \"#f472b6\", \"#a78bfa\"]);\n\n    allHues.forEach(function(h, i) {\n      var rad = (h - 90) * Math.PI / 180;\n      var dotR = r * 0.82;\n      var dx = cx + dotR * Math.cos(rad);\n      var dy = cy + dotR * Math.sin(rad);\n      wCtx.beginPath();\n      wCtx.arc(dx, dy, 9, 0, Math.PI * 2);\n      wCtx.fillStyle = \"hsl(\" + h + \",\" + state.sat + \"%,\" + state.bri + \"%)\";\n      wCtx.fill();\n      wCtx.strokeStyle = dotColors[i] || \"#fff\";\n      wCtx.lineWidth = 2.5;\n      wCtx.stroke();\n    });\n\n    wCtx.restore();\n  }\n\n  // ── Draw brightness strip ─────────────────────────────────────────────────\n  function drawBrightnessStrip() {\n    var w = brCanvas.width, h = brCanvas.height;\n    var grad = brCtx.createLinearGradient(0, 0, w, 0);\n    grad.addColorStop(0,   \"hsl(\" + state.hue + \",\" + state.sat + \"%,0%)\");\n    grad.addColorStop(0.5, \"hsl(\" + state.hue + \",\" + state.sat + \"%,50%)\");\n    grad.addColorStop(1,   \"hsl(\" + state.hue + \",\" + state.sat + \"%,100%)\");\n    brCtx.clearRect(0, 0, w, h);\n    brCtx.fillStyle = grad;\n    brCtx.fillRect(0, 0, w, h);\n    // Indicator\n    var x = Math.round((state.bri / 100) * w);\n    brCtx.beginPath();\n    brCtx.arc(x, h / 2, 10, 0, Math.PI * 2);\n    brCtx.fillStyle = \"hsl(\" + state.hue + \",\" + state.sat + \"%,\" + state.bri + \"%)\";\n    brCtx.fill();\n    brCtx.strokeStyle = \"#fff\";\n    brCtx.lineWidth = 2;\n    brCtx.stroke();\n  }\n\n  // ── Draw saturation strip ─────────────────────────────────────────────────\n  function drawSaturationStrip() {\n    var w = saCanvas.width, h = saCanvas.height;\n    var grad = saCtx.createLinearGradient(0, 0, w, 0);\n    grad.addColorStop(0, \"hsl(\" + state.hue + \",0%,\" + state.bri + \"%)\");\n    grad.addColorStop(1, \"hsl(\" + state.hue + \",100%,\" + state.bri + \"%)\");\n    saCtx.clearRect(0, 0, w, h);\n    saCtx.fillStyle = grad;\n    saCtx.fillRect(0, 0, w, h);\n    var x = Math.round((state.sat / 100) * w);\n    saCtx.beginPath();\n    saCtx.arc(x, h / 2, 10, 0, Math.PI * 2);\n    saCtx.fillStyle = \"hsl(\" + state.hue + \",\" + state.sat + \"%,\" + state.bri + \"%)\";\n    saCtx.fill();\n    saCtx.strokeStyle = \"#fff\";\n    saCtx.lineWidth = 2;\n    saCtx.stroke();\n  }\n\n  // ── WCAG contrast ─────────────────────────────────────────────────────────\n  function relativeLuminance(r, g, b) {\n    var sRGB = [r, g, b].map(function(c) {\n      c /= 255;\n      return c \u003c= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n    });\n    return 0.2126 * sRGB[0] + 0.7152 * sRGB[1] + 0.0722 * sRGB[2];\n  }\n\n  function contrastRatio(c1, c2) {\n    var L1 = relativeLuminance(c1.r, c1.g, c1.b);\n    var L2 = relativeLuminance(c2.r, c2.g, c2.b);\n    var lighter = Math.max(L1, L2);\n    var darker  = Math.min(L1, L2);\n    return (lighter + 0.05) / (darker + 0.05);\n  }\n\n  // ── Render swatches ────────────────────────────────────────────────────────\n  var labels = [\"Base\", \"Harmony 1\", \"Harmony 2\", \"Harmony 3\", \"Harmony 4\"];\n\n  function copyText(text, chip) {\n    navigator.clipboard.writeText(text).then(function() {\n      chip.classList.add(\"copied\");\n      var orig = chip.textContent;\n      chip.textContent = \"Copied!\";\n      setTimeout(function() {\n        chip.classList.remove(\"copied\");\n        chip.textContent = orig;\n      }, 1200);\n    }).catch(function() {\n      // fallback\n      var ta = document.createElement(\"textarea\");\n      ta.value = text;\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand(\"copy\");\n      document.body.removeChild(ta);\n      chip.classList.add(\"copied\");\n      var orig = chip.textContent;\n      chip.textContent = \"Copied!\";\n      setTimeout(function() {\n        chip.classList.remove(\"copied\");\n        chip.textContent = orig;\n      }, 1200);\n    });\n  }\n\n  function renderSwatches(palette) {\n    var container = document.getElementById(\"cw-swatches\");\n    container.innerHTML = \"\";\n    palette.forEach(function(color, i) {\n      var row = document.createElement(\"div\");\n      row.className = \"cw-swatch-row\";\n\n      var box = document.createElement(\"div\");\n      box.className = \"cw-swatch-box\";\n      box.style.background = color.hex;\n\n      var info = document.createElement(\"div\");\n      info.className = \"cw-swatch-info\";\n\n      var lbl = document.createElement(\"div\");\n      lbl.className = \"cw-swatch-label\";\n      lbl.textContent = labels[i] || (\"Color \" + (i + 1));\n\n      var vals = document.createElement(\"div\");\n      vals.className = \"cw-values\";\n\n      [color.hex, color.rgb, color.hsl].forEach(function(val) {\n        var chip = document.createElement(\"span\");\n        chip.className = \"cw-val-chip\";\n        chip.textContent = val;\n        chip.title = \"Click to copy\";\n        chip.addEventListener(\"click\", function() { copyText(val, chip); });\n        vals.appendChild(chip);\n      });\n\n      info.appendChild(lbl);\n      info.appendChild(vals);\n      row.appendChild(box);\n      row.appendChild(info);\n      container.appendChild(row);\n    });\n  }\n\n  // ── Render contrast ────────────────────────────────────────────────────────\n  function renderContrast(palette) {\n    if (palette.length \u003c 2) return;\n    var c1 = palette[0], c2 = palette[1];\n    var ratio = contrastRatio(c1, c2);\n    var ratioStr = ratio.toFixed(2) + \":1\";\n\n    document.getElementById(\"cw-ratio-val\").textContent = ratioStr;\n\n    var badge = document.getElementById(\"cw-wcag-badge\");\n    if (ratio \u003e= 7) {\n      badge.textContent = \"AAA Pass\";\n      badge.className = \"cw-wcag-badge badge-pass\";\n    } else if (ratio \u003e= 4.5) {\n      badge.textContent = \"AA Pass\";\n      badge.className = \"cw-wcag-badge badge-aa\";\n    } else if (ratio \u003e= 3) {\n      badge.textContent = \"AA Large\";\n      badge.className = \"cw-wcag-badge badge-aa\";\n    } else {\n      badge.textContent = \"Fail\";\n      badge.className = \"cw-wcag-badge badge-fail\";\n    }\n\n    // Preview samples\n    var dark = document.getElementById(\"cw-cp-dark\");\n    var light = document.getElementById(\"cw-cp-light\");\n    dark.style.background = c1.hex;\n    dark.style.color = c2.hex;\n    dark.textContent = \"Aa — \" + ratioStr;\n    light.style.background = c2.hex;\n    light.style.color = c1.hex;\n    light.textContent = \"Aa — \" + ratioStr;\n  }\n\n  // ── Render CSS export ──────────────────────────────────────────────────────\n  function renderCSS(palette) {\n    var lines = [\":root {\"];\n    palette.forEach(function(color, i) {\n      var name = i === 0 ? \"base\" : (\"harmony-\" + i);\n      lines.push(\"  --color-\" + name + \": \" + color.hex + \";\");\n      lines.push(\"  --color-\" + name + \"-rgb: \" + color.r + \", \" + color.g + \", \" + color.b + \";\");\n      lines.push(\"  --color-\" + name + \"-hsl: \" + color.hsl + \";\");\n    });\n    lines.push(\"}\");\n    document.getElementById(\"cw-css-output\").value = lines.join(\"\\n\");\n  }\n\n  // ── Full render ────────────────────────────────────────────────────────────\n  function render() {\n    drawWheel();\n    drawBrightnessStrip();\n    drawSaturationStrip();\n    var palette = buildPalette();\n    renderSwatches(palette);\n    renderContrast(palette);\n    renderCSS(palette);\n  }\n\n  // ── Wheel interaction ──────────────────────────────────────────────────────\n  function wheelHueFromEvent(e) {\n    var rect = wheelCanvas.getBoundingClientRect();\n    var clientX = e.touches ? e.touches[0].clientX : e.clientX;\n    var clientY = e.touches ? e.touches[0].clientY : e.clientY;\n    var scaleX = wheelCanvas.width / rect.width;\n    var scaleY = wheelCanvas.height / rect.height;\n    var x = (clientX - rect.left) * scaleX - WHEEL_R;\n    var y = (clientY - rect.top)  * scaleY - WHEEL_R;\n    var angle = Math.atan2(y, x) * 180 / Math.PI + 90;\n    return ((angle % 360) + 360) % 360;\n  }\n\n  var wheelDragging = false;\n  wheelCanvas.addEventListener(\"mousedown\", function(e) {\n    wheelDragging = true;\n    state.hue = wheelHueFromEvent(e);\n    render();\n  });\n  window.addEventListener(\"mousemove\", function(e) {\n    if (!wheelDragging) return;\n    state.hue = wheelHueFromEvent(e);\n    render();\n  });\n  window.addEventListener(\"mouseup\", function() { wheelDragging = false; });\n\n  wheelCanvas.addEventListener(\"touchstart\", function(e) {\n    e.preventDefault();\n    wheelDragging = true;\n    state.hue = wheelHueFromEvent(e);\n    render();\n  }, { passive: false });\n  wheelCanvas.addEventListener(\"touchmove\", function(e) {\n    e.preventDefault();\n    if (!wheelDragging) return;\n    state.hue = wheelHueFromEvent(e);\n    render();\n  }, { passive: false });\n  wheelCanvas.addEventListener(\"touchend\", function() { wheelDragging = false; });\n\n  // ── Brightness strip interaction ───────────────────────────────────────────\n  function briFromEvent(e) {\n    var rect = brCanvas.getBoundingClientRect();\n    var clientX = e.touches ? e.touches[0].clientX : e.clientX;\n    var x = clientX - rect.left;\n    return Math.max(0, Math.min(100, Math.round((x / rect.width) * 100)));\n  }\n  var briDragging = false;\n  brCanvas.addEventListener(\"mousedown\", function(e) {\n    briDragging = true;\n    state.bri = briFromEvent(e);\n    render();\n  });\n  window.addEventListener(\"mousemove\", function(e) {\n    if (!briDragging) return;\n    state.bri = briFromEvent(e);\n    render();\n  });\n  window.addEventListener(\"mouseup\", function() { briDragging = false; });\n  brCanvas.addEventListener(\"touchstart\", function(e) {\n    e.preventDefault(); briDragging = true;\n    state.bri = briFromEvent(e); render();\n  }, { passive: false });\n  brCanvas.addEventListener(\"touchmove\", function(e) {\n    e.preventDefault();\n    if (!briDragging) return;\n    state.bri = briFromEvent(e); render();\n  }, { passive: false });\n  brCanvas.addEventListener(\"touchend\", function() { briDragging = false; });\n\n  // ── Saturation strip interaction ───────────────────────────────────────────\n  function satFromEvent(e) {\n    var rect = saCanvas.getBoundingClientRect();\n    var clientX = e.touches ? e.touches[0].clientX : e.clientX;\n    var x = clientX - rect.left;\n    return Math.max(0, Math.min(100, Math.round((x / rect.width) * 100)));\n  }\n  var satDragging = false;\n  saCanvas.addEventListener(\"mousedown\", function(e) {\n    satDragging = true;\n    state.sat = satFromEvent(e);\n    render();\n  });\n  window.addEventListener(\"mousemove\", function(e) {\n    if (!satDragging) return;\n    state.sat = satFromEvent(e);\n    render();\n  });\n  window.addEventListener(\"mouseup\", function() { satDragging = false; });\n  saCanvas.addEventListener(\"touchstart\", function(e) {\n    e.preventDefault(); satDragging = true;\n    state.sat = satFromEvent(e); render();\n  }, { passive: false });\n  saCanvas.addEventListener(\"touchmove\", function(e) {\n    e.preventDefault();\n    if (!satDragging) return;\n    state.sat = satFromEvent(e); render();\n  }, { passive: false });\n  saCanvas.addEventListener(\"touchend\", function() { satDragging = false; });\n\n  // ── Harmony mode buttons ───────────────────────────────────────────────────\n  document.querySelectorAll(\"#cw-app .cw-btn[data-mode]\").forEach(function(btn) {\n    btn.addEventListener(\"click\", function() {\n      document.querySelectorAll(\"#cw-app .cw-btn[data-mode]\").forEach(function(b) {\n        b.classList.remove(\"active\");\n      });\n      btn.classList.add(\"active\");\n      state.mode = btn.getAttribute(\"data-mode\");\n      render();\n    });\n  });\n\n  // ── Copy CSS button ────────────────────────────────────────────────────────\n  document.getElementById(\"cw-copy-css\").addEventListener(\"click\", function() {\n    var btn = document.getElementById(\"cw-copy-css\");\n    var text = document.getElementById(\"cw-css-output\").value;\n    navigator.clipboard.writeText(text).then(function() {\n      btn.textContent = \"Copied!\";\n      btn.classList.add(\"copied\");\n      setTimeout(function() {\n        btn.textContent = \"Copy CSS\";\n        btn.classList.remove(\"copied\");\n      }, 1400);\n    }).catch(function() {\n      var ta = document.getElementById(\"cw-css-output\");\n      ta.select();\n      document.execCommand(\"copy\");\n      btn.textContent = \"Copied!\";\n      btn.classList.add(\"copied\");\n      setTimeout(function() {\n        btn.textContent = \"Copy CSS\";\n        btn.classList.remove(\"copied\");\n      }, 1400);\n    });\n  });\n\n  // ── Initial render ─────────────────────────────────────────────────────────\n  render();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"https://productivity-works.com/tools/color-blindness-simulator/\"\u003eColor Blindness Simulator\u003c/a\u003e\n\n\u003ca href=\"https://productivity-works.com/tools/color-contrast-checker/\"\u003eColor Contrast Checker\u003c/a\u003e\n\n\u003ca href=\"https://productivity-works.com/tools/color-converter/\"\u003eColor Converter\u003c/a\u003e\n\u003c/p\u003e","title":"Color Wheel Picker"},{"content":" Cooking Unit Converter Volume Weight By Ingredient Recipe Scaler Quick Reference Amount \u0026#8644; Result From Unit Cup (US) Tablespoon (tbsp) Teaspoon (tsp) Milliliter (ml) Liter (L) Fluid Ounce (fl oz) To Unit Cup (US) Tablespoon (tbsp) Teaspoon (tsp) Milliliter (ml) Liter (L) Fluid Ounce (fl oz) 236.59 ml 1 Cup (US) = 236.59 Milliliter (ml) Tip: 1 US Cup = 16 tablespoons = 48 teaspoons = 236.59 ml = 8 fl oz Amount \u0026#8644; Result From Unit Gram (g) Kilogram (kg) Ounce (oz) Pound (lb) To Unit Gram (g) Kilogram (kg) Ounce (oz) Pound (lb) 0.04 oz 1 Gram (g) = 0.04 Ounce (oz) Tip: 1 oz = 28.35 g \u0026nbsp;|\u0026nbsp; 1 lb = 16 oz = 453.59 g \u0026nbsp;|\u0026nbsp; 1 kg = 2.205 lb Convert volume to weight (or vice versa) using ingredient density.\nIngredient Water Milk (whole) All-Purpose Flour White Sugar (granulated) Brown Sugar (packed) Powdered Sugar Butter Vegetable Oil Honey Rice (uncooked) Oats (rolled) Cocoa Powder Table Salt Baking Powder Amount From Unit Cup Tablespoon Teaspoon ml fl oz Gram Ounce -- Select an ingredient and amount Scale any recipe up or down by entering your ingredients below.\nOriginal Servings Desired Servings Ingredients Cup Tablespoon Teaspoon ml gram oz lb piece(s) \u0026#10005; Cup Tablespoon Teaspoon ml gram oz lb piece(s) \u0026#10005; + Add Ingredient Scale Recipe Original Scaled Amount Volume Quick Reference Measurement Cups Tbsp Tsp ml fl oz 1 Cup11648236.68 3/4 Cup0.751236177.46 2/3 Cup0.66710.6732157.75.33 1/2 Cup0.5824118.34 1/3 Cup0.3335.331678.92.67 1/4 Cup0.2541259.12 1 Tbsp0.06251314.790.5 1 Tsp0.02080.33314.930.167 1 fl oz0.1252629.571 1 Liter4.22767.63202.9100033.81 Common Ingredient Weights (per 1 Cup) Ingredient Grams Ounces All-Purpose Flour125 g4.4 oz White Sugar (granulated)200 g7.1 oz Brown Sugar (packed)220 g7.8 oz Powdered Sugar120 g4.2 oz Butter227 g8.0 oz Vegetable Oil218 g7.7 oz Milk (whole)244 g8.6 oz Water237 g8.4 oz Honey340 g12.0 oz Rice (uncooked)185 g6.5 oz Rolled Oats90 g3.2 oz Cocoa Powder85 g3.0 oz Weight Quick Reference From Grams Kilograms Ounces Pounds 1 gram10.0010.0350.0022 1 kilogram1000135.272.205 1 ounce28.350.02810.0625 1 pound453.60.454161 Related Free Tools BMI Calculator — Body Mass Index calculator for adults Calorie Calculator — Daily calorie needs estimator Tip Calculator — Split bills and calculate tips easily Unit Converter — Length, area, temperature, and more Related Tools Carbon Footprint Calculator Electricity Cost Calculator Fuel Cost Calculator ","permalink":"https://productivity-works.com/tools/cooking-unit-converter/","summary":"\u003cdiv id=\"cu-app\"\u003e\n\u003cstyle\u003e\n#cu-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 820px;\n  margin: 0 auto;\n  color: #1a1a2e;\n  line-height: 1.6;\n}\n#cu-app h2 {\n  font-size: 1.4rem;\n  font-weight: 700;\n  color: #1a1a2e;\n  margin: 1.8rem 0 1rem;\n  padding-bottom: 0.4rem;\n  border-bottom: 2px solid #f0a500;\n}\n#cu-app .tabs {\n  display: flex;\n  gap: 0.5rem;\n  margin-bottom: 1.5rem;\n  flex-wrap: wrap;\n}\n#cu-app .tab-btn {\n  padding: 0.55rem 1.2rem;\n  border: 2px solid #e0e0e0;\n  border-radius: 2rem;\n  background: #fff;\n  color: #555;\n  font-size: 0.9rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.2s;\n}\n#cu-app .tab-btn.active,\n#cu-app .tab-btn:hover {\n  background: #f0a500;\n  border-color: #f0a500;\n  color: #fff;\n}\n#cu-app .panel {\n  display: none;\n  background: #fff;\n  border: 1px solid #e8e8e8;\n  border-radius: 12px;\n  padding: 1.5rem;\n  box-shadow: 0 2px 8px rgba(0,0,0,0.06);\n}\n#cu-app .panel.active {\n  display: block;\n}\n#cu-app .converter-grid {\n  display: grid;\n  grid-template-columns: 1fr auto 1fr;\n  gap: 1rem;\n  align-items: end;\n  margin-bottom: 1rem;\n}\n#cu-app .swap-btn {\n  background: #f0a500;\n  color: #fff;\n  border: none;\n  border-radius: 50%;\n  width: 40px;\n  height: 40px;\n  font-size: 1.1rem;\n  cursor: pointer;\n  transition: background 0.2s;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  margin-bottom: 0.2rem;\n}\n#cu-app .swap-btn:hover { background: #d4920a; }\n#cu-app label {\n  display: block;\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #666;\n  margin-bottom: 0.3rem;\n  text-transform: uppercase;\n  letter-spacing: 0.03em;\n}\n#cu-app input[type=\"number\"],\n#cu-app select {\n  width: 100%;\n  padding: 0.65rem 0.8rem;\n  border: 1.5px solid #ddd;\n  border-radius: 8px;\n  font-size: 1rem;\n  background: #fafafa;\n  box-sizing: border-box;\n  transition: border-color 0.2s;\n}\n#cu-app input[type=\"number\"]:focus,\n#cu-app select:focus {\n  outline: none;\n  border-color: #f0a500;\n  background: #fff;\n}\n#cu-app .result-box {\n  background: linear-gradient(135deg, #fff7e6, #fff);\n  border: 1.5px solid #f0a500;\n  border-radius: 10px;\n  padding: 1rem 1.2rem;\n  margin-top: 1rem;\n  text-align: center;\n}\n#cu-app .result-box .result-value {\n  font-size: 2rem;\n  font-weight: 800;\n  color: #f0a500;\n}\n#cu-app .result-box .result-label {\n  font-size: 0.9rem;\n  color: #888;\n  margin-top: 0.2rem;\n}\n#cu-app .ingredient-row {\n  display: flex;\n  gap: 0.8rem;\n  margin-bottom: 1rem;\n  align-items: end;\n  flex-wrap: wrap;\n}\n#cu-app .ingredient-row \u003e div {\n  flex: 1;\n  min-width: 120px;\n}\n#cu-app .scaler-row {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1rem;\n  margin-bottom: 1rem;\n}\n#cu-app .scaler-ingredients {\n  margin-top: 1rem;\n}\n#cu-app .scaler-ingredient-item {\n  display: grid;\n  grid-template-columns: 1fr 1fr auto;\n  gap: 0.6rem;\n  margin-bottom: 0.5rem;\n  align-items: center;\n}\n#cu-app .scaler-ingredient-item input,\n#cu-app .scaler-ingredient-item select {\n  padding: 0.5rem 0.6rem;\n  font-size: 0.9rem;\n}\n#cu-app .remove-btn {\n  background: #ff5555;\n  color: #fff;\n  border: none;\n  border-radius: 6px;\n  padding: 0.5rem 0.7rem;\n  cursor: pointer;\n  font-size: 0.85rem;\n}\n#cu-app .remove-btn:hover { background: #cc3333; }\n#cu-app .add-btn {\n  background: #e8f5e9;\n  color: #2e7d32;\n  border: 1.5px solid #a5d6a7;\n  border-radius: 8px;\n  padding: 0.5rem 1rem;\n  cursor: pointer;\n  font-size: 0.88rem;\n  font-weight: 600;\n  margin-right: 0.5rem;\n}\n#cu-app .add-btn:hover { background: #c8e6c9; }\n#cu-app .calc-btn {\n  background: #f0a500;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  padding: 0.55rem 1.2rem;\n  cursor: pointer;\n  font-size: 0.9rem;\n  font-weight: 700;\n}\n#cu-app .calc-btn:hover { background: #d4920a; }\n#cu-app .scaler-result {\n  background: #f8f9fa;\n  border-radius: 10px;\n  padding: 1rem;\n  margin-top: 1rem;\n}\n#cu-app .scaler-result table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.92rem;\n}\n#cu-app .scaler-result th {\n  text-align: left;\n  padding: 0.4rem 0.6rem;\n  background: #f0a500;\n  color: #fff;\n  border-radius: 4px;\n}\n#cu-app .scaler-result td {\n  padding: 0.4rem 0.6rem;\n  border-bottom: 1px solid #eee;\n}\n#cu-app .scaler-result tr:last-child td { border-bottom: none; }\n#cu-app .ref-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.88rem;\n  margin-top: 0.5rem;\n}\n#cu-app .ref-table th {\n  background: #1a1a2e;\n  color: #fff;\n  padding: 0.55rem 0.8rem;\n  text-align: left;\n}\n#cu-app .ref-table td {\n  padding: 0.5rem 0.8rem;\n  border-bottom: 1px solid #eee;\n}\n#cu-app .ref-table tr:nth-child(even) td { background: #f9f9f9; }\n#cu-app .tip-box {\n  background: #e8f4fd;\n  border-left: 4px solid #2196f3;\n  border-radius: 0 8px 8px 0;\n  padding: 0.8rem 1rem;\n  margin: 1rem 0;\n  font-size: 0.88rem;\n  color: #1565c0;\n}\n#cu-app .related-tools {\n  background: #f8f9fa;\n  border-radius: 10px;\n  padding: 1.2rem;\n  margin-top: 2rem;\n}\n#cu-app .related-tools h3 {\n  margin: 0 0 0.8rem;\n  font-size: 1rem;\n  color: #1a1a2e;\n}\n#cu-app .related-tools ul {\n  margin: 0;\n  padding-left: 1.2rem;\n}\n#cu-app .related-tools li {\n  margin-bottom: 0.4rem;\n  font-size: 0.9rem;\n}\n#cu-app .related-tools a {\n  color: #f0a500;\n  text-decoration: none;\n  font-weight: 600;\n}\n#cu-app .related-tools a:hover { text-decoration: underline; }\n@media (max-width: 600px) {\n  #cu-app .converter-grid {\n    grid-template-columns: 1fr;\n  }\n  #cu-app .swap-btn {\n    margin: 0 auto;\n    transform: rotate(90deg);\n  }\n  #cu-app .scaler-row {\n    grid-template-columns: 1fr;\n  }\n  #cu-app .scaler-ingredient-item {\n    grid-template-columns: 1fr 1fr auto;\n  }\n}\n\u003c/style\u003e\n\u003ch2\u003eCooking Unit Converter\u003c/h2\u003e\n\u003cdiv class=\"tabs\"\u003e\n  \u003cbutton class=\"tab-btn active\" onclick=\"cuShowTab('volume')\"\u003eVolume\u003c/button\u003e\n  \u003cbutton class=\"tab-btn\" onclick=\"cuShowTab('weight')\"\u003eWeight\u003c/button\u003e\n  \u003cbutton class=\"tab-btn\" onclick=\"cuShowTab('ingredient')\"\u003eBy Ingredient\u003c/button\u003e\n  \u003cbutton class=\"tab-btn\" onclick=\"cuShowTab('scaler')\"\u003eRecipe Scaler\u003c/button\u003e\n  \u003cbutton class=\"tab-btn\" onclick=\"cuShowTab('reference')\"\u003eQuick Reference\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- VOLUME TAB --\u003e\n\u003cdiv id=\"cu-tab-volume\" class=\"panel active\"\u003e\n  \u003cdiv class=\"converter-grid\"\u003e\n    \u003cdiv\u003e\n      \u003clabel\u003eAmount\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"vol-amount\" value=\"1\" min=\"0\" step=\"any\" oninput=\"cuConvertVolume()\"\u003e\n    \u003c/div\u003e\n    \u003cdiv style=\"display:flex;align-items:flex-end;padding-bottom:0.2rem;\"\u003e\n      \u003cbutton class=\"swap-btn\" onclick=\"cuSwapVolume()\" title=\"Swap units\"\u003e\u0026#8644;\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003clabel\u003eResult\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"vol-result\" value=\"\" min=\"0\" step=\"any\" oninput=\"cuConvertVolumeReverse()\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin-bottom:1rem;\"\u003e\n    \u003cdiv\u003e\n      \u003clabel\u003eFrom Unit\u003c/label\u003e\n      \u003cselect id=\"vol-from\" onchange=\"cuConvertVolume()\"\u003e\n        \u003coption value=\"cup\"\u003eCup (US)\u003c/option\u003e\n        \u003coption value=\"tbsp\"\u003eTablespoon (tbsp)\u003c/option\u003e\n        \u003coption value=\"tsp\"\u003eTeaspoon (tsp)\u003c/option\u003e\n        \u003coption value=\"ml\"\u003eMilliliter (ml)\u003c/option\u003e\n        \u003coption value=\"liter\"\u003eLiter (L)\u003c/option\u003e\n        \u003coption value=\"floz\"\u003eFluid Ounce (fl oz)\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003clabel\u003eTo Unit\u003c/label\u003e\n      \u003cselect id=\"vol-to\" onchange=\"cuConvertVolume()\"\u003e\n        \u003coption value=\"cup\"\u003eCup (US)\u003c/option\u003e\n        \u003coption value=\"tbsp\"\u003eTablespoon (tbsp)\u003c/option\u003e\n        \u003coption value=\"tsp\"\u003eTeaspoon (tsp)\u003c/option\u003e\n        \u003coption value=\"ml\" selected\u003eMilliliter (ml)\u003c/option\u003e\n        \u003coption value=\"liter\"\u003eLiter (L)\u003c/option\u003e\n        \u003coption value=\"floz\"\u003eFluid Ounce (fl oz)\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"result-box\" id=\"vol-result-box\"\u003e\n    \u003cdiv class=\"result-value\" id=\"vol-result-display\"\u003e236.59 ml\u003c/div\u003e\n    \u003cdiv class=\"result-label\"\u003e1 Cup (US) = 236.59 Milliliter (ml)\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"tip-box\"\u003e\n    Tip: 1 US Cup = 16 tablespoons = 48 teaspoons = 236.59 ml = 8 fl oz\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- WEIGHT TAB --\u003e\n\u003cdiv id=\"cu-tab-weight\" class=\"panel\"\u003e\n  \u003cdiv class=\"converter-grid\"\u003e\n    \u003cdiv\u003e\n      \u003clabel\u003eAmount\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"wt-amount\" value=\"1\" min=\"0\" step=\"any\" oninput=\"cuConvertWeight()\"\u003e\n    \u003c/div\u003e\n    \u003cdiv style=\"display:flex;align-items:flex-end;padding-bottom:0.2rem;\"\u003e\n      \u003cbutton class=\"swap-btn\" onclick=\"cuSwapWeight()\" title=\"Swap units\"\u003e\u0026#8644;\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003clabel\u003eResult\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"wt-result\" value=\"\" min=\"0\" step=\"any\" oninput=\"cuConvertWeightReverse()\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv style=\"display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin-bottom:1rem;\"\u003e\n    \u003cdiv\u003e\n      \u003clabel\u003eFrom Unit\u003c/label\u003e\n      \u003cselect id=\"wt-from\" onchange=\"cuConvertWeight()\"\u003e\n        \u003coption value=\"g\"\u003eGram (g)\u003c/option\u003e\n        \u003coption value=\"kg\"\u003eKilogram (kg)\u003c/option\u003e\n        \u003coption value=\"oz\"\u003eOunce (oz)\u003c/option\u003e\n        \u003coption value=\"lb\"\u003ePound (lb)\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003clabel\u003eTo Unit\u003c/label\u003e\n      \u003cselect id=\"wt-to\" onchange=\"cuConvertWeight()\"\u003e\n        \u003coption value=\"g\"\u003eGram (g)\u003c/option\u003e\n        \u003coption value=\"kg\"\u003eKilogram (kg)\u003c/option\u003e\n        \u003coption value=\"oz\" selected\u003eOunce (oz)\u003c/option\u003e\n        \u003coption value=\"lb\"\u003ePound (lb)\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"result-box\" id=\"wt-result-box\"\u003e\n    \u003cdiv class=\"result-value\" id=\"wt-result-display\"\u003e0.04 oz\u003c/div\u003e\n    \u003cdiv class=\"result-label\"\u003e1 Gram (g) = 0.04 Ounce (oz)\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"tip-box\"\u003e\n    Tip: 1 oz = 28.35 g \u0026nbsp;|\u0026nbsp; 1 lb = 16 oz = 453.59 g \u0026nbsp;|\u0026nbsp; 1 kg = 2.205 lb\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- INGREDIENT TAB --\u003e\n\u003cdiv id=\"cu-tab-ingredient\" class=\"panel\"\u003e\n  \u003cp style=\"font-size:0.9rem;color:#555;margin-top:0;\"\u003eConvert volume to weight (or vice versa) using ingredient density.\u003c/p\u003e","title":"Cooking Unit Converter - Kitchen Measurement Calculator"},{"content":" \u0026#8987; Countdown Timer Countdown Time Since Event Name (optional) Date Time Start Countdown + Add to List Reset Quick Presets \u0026#127881; New Year 2027 \u0026#127876; Christmas 2026 \u0026#9728;\u0026#65039; Summer 2026 \u0026#127875; Halloween 2026 ","permalink":"https://productivity-works.com/tools/countdown-timer/","summary":"\u003cdiv id=\"countdown-app\"\u003e\n\u003cstyle\u003e\n/* =============================================\n   COUNTDOWN TIMER - Deep Purple + Gold Theme\n   ============================================= */\n\n#countdown-app {\n  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #1e1b4b;\n}\n\n/* --- Input Panel --- */\n.cd-panel {\n  background: linear-gradient(135deg, #6d28d9 0%, #4c1d95 100%);\n  border-radius: 16px;\n  padding: 28px 28px 24px;\n  color: #fff;\n  margin-bottom: 24px;\n  box-shadow: 0 8px 32px rgba(109,40,217,0.28);\n}\n\n.cd-panel h2 {\n  margin: 0 0 20px;\n  font-size: 1.3rem;\n  font-weight: 700;\n  letter-spacing: .02em;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n\n.cd-form-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 12px;\n  align-items: flex-end;\n}\n\n.cd-form-group {\n  display: flex;\n  flex-direction: column;\n  gap: 5px;\n  flex: 1;\n  min-width: 160px;\n}\n\n.cd-form-group label {\n  font-size: .78rem;\n  font-weight: 600;\n  letter-spacing: .06em;\n  text-transform: uppercase;\n  opacity: .85;\n}\n\n.cd-form-group input[type=\"text\"],\n.cd-form-group input[type=\"date\"],\n.cd-form-group input[type=\"time\"] {\n  background: rgba(255,255,255,0.15);\n  border: 1.5px solid rgba(255,255,255,0.35);\n  border-radius: 8px;\n  padding: 9px 13px;\n  color: #fff;\n  font-size: .97rem;\n  outline: none;\n  transition: border-color .2s, background .2s;\n  width: 100%;\n  box-sizing: border-box;\n}\n\n.cd-form-group input[type=\"text\"]::placeholder {\n  color: rgba(255,255,255,0.55);\n}\n\n.cd-form-group input:focus {\n  border-color: #fbbf24;\n  background: rgba(255,255,255,0.22);\n}\n\n.cd-form-group input[type=\"date\"]::-webkit-calendar-picker-indicator,\n.cd-form-group input[type=\"time\"]::-webkit-calendar-picker-indicator {\n  filter: invert(1);\n  opacity: .7;\n  cursor: pointer;\n}\n\n.cd-btn-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  margin-top: 16px;\n  align-items: center;\n}\n\n.cd-btn {\n  padding: 9px 20px;\n  border-radius: 8px;\n  border: none;\n  font-size: .9rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: transform .12s, box-shadow .12s, background .2s;\n}\n\n.cd-btn:active { transform: scale(.97); }\n\n.cd-btn-primary {\n  background: #fbbf24;\n  color: #1e1b4b;\n  box-shadow: 0 3px 10px rgba(251,191,36,.35);\n}\n\n.cd-btn-primary:hover { background: #f59e0b; box-shadow: 0 4px 14px rgba(251,191,36,.5); }\n\n.cd-btn-secondary {\n  background: rgba(255,255,255,0.15);\n  color: #fff;\n  border: 1.5px solid rgba(255,255,255,0.3);\n}\n\n.cd-btn-secondary:hover { background: rgba(255,255,255,0.25); }\n\n/* Mode toggle */\n.cd-mode-toggle {\n  display: flex;\n  gap: 0;\n  background: rgba(0,0,0,0.2);\n  border-radius: 8px;\n  padding: 3px;\n  margin-bottom: 16px;\n  width: fit-content;\n}\n\n.cd-mode-btn {\n  padding: 6px 16px;\n  border: none;\n  border-radius: 6px;\n  font-size: .85rem;\n  font-weight: 600;\n  cursor: pointer;\n  background: transparent;\n  color: rgba(255,255,255,0.65);\n  transition: background .2s, color .2s;\n}\n\n.cd-mode-btn.active {\n  background: #fbbf24;\n  color: #1e1b4b;\n}\n\n/* --- Presets --- */\n.cd-presets {\n  margin-top: 16px;\n  border-top: 1px solid rgba(255,255,255,0.15);\n  padding-top: 16px;\n}\n\n.cd-presets-label {\n  font-size: .75rem;\n  font-weight: 600;\n  letter-spacing: .07em;\n  text-transform: uppercase;\n  opacity: .7;\n  margin-bottom: 10px;\n}\n\n.cd-preset-chips {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n}\n\n.cd-chip {\n  padding: 5px 13px;\n  border-radius: 20px;\n  border: 1.5px solid rgba(255,255,255,0.35);\n  background: rgba(255,255,255,0.1);\n  color: #fff;\n  font-size: .82rem;\n  font-weight: 500;\n  cursor: pointer;\n  transition: background .2s, border-color .2s;\n}\n\n.cd-chip:hover {\n  background: rgba(251,191,36,0.25);\n  border-color: #fbbf24;\n}\n\n/* --- Main Countdown Display --- */\n.cd-display {\n  background: #fff;\n  border-radius: 16px;\n  padding: 32px 24px 28px;\n  box-shadow: 0 4px 20px rgba(109,40,217,0.10);\n  border: 1.5px solid #ede9fe;\n  margin-bottom: 20px;\n  text-align: center;\n}\n\n.cd-event-label {\n  font-size: 1.05rem;\n  color: #6d28d9;\n  font-weight: 700;\n  margin-bottom: 6px;\n  letter-spacing: .01em;\n  min-height: 1.4em;\n}\n\n.cd-mode-label {\n  font-size: .8rem;\n  color: #9ca3af;\n  margin-bottom: 22px;\n  text-transform: uppercase;\n  letter-spacing: .08em;\n}\n\n/* Flip card numbers */\n.cd-units {\n  display: flex;\n  justify-content: center;\n  gap: 16px;\n  flex-wrap: wrap;\n  margin-bottom: 24px;\n}\n\n.cd-unit {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 8px;\n}\n\n.cd-flip-card {\n  width: 90px;\n  height: 90px;\n  position: relative;\n  perspective: 400px;\n}\n\n@media (max-width: 500px) {\n  .cd-flip-card { width: 68px; height: 68px; }\n  .cd-num { font-size: 2.1rem !important; }\n}\n\n.cd-num {\n  width: 100%;\n  height: 100%;\n  background: linear-gradient(160deg, #6d28d9 0%, #4c1d95 100%);\n  border-radius: 12px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 2.8rem;\n  font-weight: 800;\n  color: #fff;\n  box-shadow: 0 4px 16px rgba(109,40,217,0.30);\n  position: relative;\n  overflow: hidden;\n  transition: transform .1s;\n  letter-spacing: -.02em;\n  line-height: 1;\n}\n\n.cd-num::after {\n  content: '';\n  position: absolute;\n  left: 0; right: 0;\n  top: 50%;\n  height: 1.5px;\n  background: rgba(0,0,0,0.18);\n}\n\n.cd-num.flip-anim {\n  animation: cdFlip .3s ease-in-out;\n}\n\n@keyframes cdFlip {\n  0%   { transform: rotateX(0deg); }\n  40%  { transform: rotateX(-25deg); }\n  100% { transform: rotateX(0deg); }\n}\n\n.cd-unit-label {\n  font-size: .72rem;\n  font-weight: 700;\n  letter-spacing: .1em;\n  text-transform: uppercase;\n  color: #7c3aed;\n}\n\n/* Separator */\n.cd-sep {\n  display: flex;\n  align-items: center;\n  padding-bottom: 32px;\n  font-size: 2rem;\n  font-weight: 800;\n  color: #c4b5fd;\n  line-height: 1;\n}\n\n/* Progress bar */\n.cd-progress-wrap {\n  margin: 0 auto 18px;\n  max-width: 520px;\n}\n\n.cd-progress-label {\n  display: flex;\n  justify-content: space-between;\n  font-size: .78rem;\n  color: #9ca3af;\n  margin-bottom: 6px;\n}\n\n.cd-progress-track {\n  background: #ede9fe;\n  border-radius: 99px;\n  height: 8px;\n  overflow: hidden;\n}\n\n.cd-progress-bar {\n  height: 100%;\n  border-radius: 99px;\n  background: linear-gradient(90deg, #6d28d9, #fbbf24);\n  transition: width .6s ease;\n}\n\n/* Fun facts */\n.cd-facts {\n  background: #faf5ff;\n  border-radius: 10px;\n  padding: 14px 18px;\n  font-size: .87rem;\n  color: #5b21b6;\n  line-height: 1.7;\n  max-width: 520px;\n  margin: 0 auto;\n  text-align: left;\n}\n\n.cd-facts strong { color: #6d28d9; }\n\n/* --- Finished state --- */\n.cd-finished {\n  display: none;\n  flex-direction: column;\n  align-items: center;\n  gap: 12px;\n  padding: 20px 0;\n}\n\n.cd-finished.show { display: flex; }\n\n.cd-finished-emoji {\n  font-size: 3.5rem;\n  animation: cdBounce .6s ease infinite alternate;\n}\n\n@keyframes cdBounce {\n  from { transform: translateY(0); }\n  to   { transform: translateY(-12px); }\n}\n\n.cd-finished-msg {\n  font-size: 1.4rem;\n  font-weight: 800;\n  color: #6d28d9;\n}\n\n/* Confetti canvas */\n#cd-confetti {\n  position: fixed;\n  top: 0; left: 0;\n  width: 100%; height: 100%;\n  pointer-events: none;\n  z-index: 9999;\n  display: none;\n}\n\n/* --- Multiple Countdowns --- */\n.cd-list-section {\n  background: #fff;\n  border-radius: 16px;\n  padding: 22px 24px;\n  box-shadow: 0 4px 20px rgba(109,40,217,0.08);\n  border: 1.5px solid #ede9fe;\n  margin-bottom: 20px;\n}\n\n.cd-list-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 16px;\n}\n\n.cd-list-title {\n  font-size: 1rem;\n  font-weight: 700;\n  color: #4c1d95;\n}\n\n.cd-list-empty {\n  text-align: center;\n  color: #a78bfa;\n  font-size: .9rem;\n  padding: 16px 0;\n}\n\n.cd-event-cards {\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n}\n\n.cd-event-card {\n  background: linear-gradient(90deg, #faf5ff 0%, #f5f3ff 100%);\n  border: 1.5px solid #ddd6fe;\n  border-radius: 10px;\n  padding: 13px 16px;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 12px;\n  flex-wrap: wrap;\n}\n\n.cd-event-card-name {\n  font-weight: 700;\n  color: #5b21b6;\n  font-size: .95rem;\n  flex: 1;\n  min-width: 120px;\n}\n\n.cd-event-card-date {\n  font-size: .8rem;\n  color: #9ca3af;\n}\n\n.cd-event-card-time {\n  font-size: 1.1rem;\n  font-weight: 800;\n  color: #6d28d9;\n  white-space: nowrap;\n}\n\n.cd-event-card-remove {\n  background: none;\n  border: none;\n  color: #c4b5fd;\n  font-size: 1.1rem;\n  cursor: pointer;\n  padding: 2px 6px;\n  border-radius: 5px;\n  transition: color .2s, background .2s;\n  line-height: 1;\n}\n\n.cd-event-card-remove:hover { color: #ef4444; background: #fef2f2; }\n\n/* --- Responsive --- */\n@media (max-width: 600px) {\n  .cd-panel { padding: 20px 16px 18px; }\n  .cd-display { padding: 22px 12px 20px; }\n  .cd-units { gap: 6px; }\n  .cd-sep { padding-bottom: 24px; font-size: 1.5rem; }\n  .cd-list-section { padding: 18px 14px; }\n}\n\u003c/style\u003e\n\u003c!-- Confetti Canvas --\u003e\n\u003cp\u003e\u003ccanvas id=\"cd-confetti\"\u003e\u003c/canvas\u003e\u003c/p\u003e","title":"Countdown Timer - Free Event Countdown \u0026 Date Calculator"},{"content":" Enter any credit card number to validate its format using the Luhn algorithm and identify the card network. This tool works entirely in your browser — no data is ever sent to a server. Card Network •••• •••• •••• •••• VALID THRU ••/•• —— Card Number Validate Clear Card Network — Number Length — Luhn Check — IIN / BIN — Generate Test Card Numbers These are mathematically valid numbers used for testing only. They are not linked to real accounts. Generate All Types How the Luhn Algorithm Works Enter a card number above to see a step-by-step breakdown. Important Notice: This tool validates number format only. It cannot verify if a card exists, is active, or has available funds. Do not use real card numbers — for testing, use the generated test numbers above. Related Tools IBAN Validator Password Generator BMI Calculator Compound Interest Calculator Unit Converter ","permalink":"https://productivity-works.com/tools/credit-card-validator/","summary":"\u003cdiv id=\"ccv-app\"\u003e\n\u003cstyle\u003e\n#ccv-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 760px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#ccv-app * { box-sizing: border-box; }\n\n#ccv-app .ccv-intro {\n  background: #f0f4ff;\n  border-left: 4px solid #4a6cf7;\n  padding: 14px 18px;\n  border-radius: 0 8px 8px 0;\n  margin-bottom: 28px;\n  font-size: 0.95rem;\n  color: #444;\n}\n\n/* Card Visual */\n#ccv-app .ccv-card-visual {\n  width: 340px;\n  height: 200px;\n  border-radius: 16px;\n  margin: 0 auto 28px;\n  position: relative;\n  padding: 24px 28px;\n  color: #fff;\n  background: linear-gradient(135deg, #1a1a2e 0%, #16213e 60%, #0f3460 100%);\n  box-shadow: 0 10px 40px rgba(0,0,0,0.25);\n  overflow: hidden;\n  transition: background 0.4s ease;\n}\n#ccv-app .ccv-card-visual::before {\n  content: '';\n  position: absolute;\n  top: -40px; right: -40px;\n  width: 180px; height: 180px;\n  border-radius: 50%;\n  background: rgba(255,255,255,0.05);\n}\n#ccv-app .ccv-card-visual::after {\n  content: '';\n  position: absolute;\n  bottom: -60px; right: 40px;\n  width: 200px; height: 200px;\n  border-radius: 50%;\n  background: rgba(255,255,255,0.04);\n}\n#ccv-app .ccv-card-visual.visa-card   { background: linear-gradient(135deg, #1a1f71 0%, #1565c0 100%); }\n#ccv-app .ccv-card-visual.mc-card     { background: linear-gradient(135deg, #eb001b 0%, #f79e1b 100%); }\n#ccv-app .ccv-card-visual.amex-card   { background: linear-gradient(135deg, #007b5e 0%, #00b09b 100%); }\n#ccv-app .ccv-card-visual.discover-card { background: linear-gradient(135deg, #f7941d 0%, #ffd200 100%); }\n#ccv-app .ccv-card-visual.jcb-card    { background: linear-gradient(135deg, #003087 0%, #009246 100%); }\n#ccv-app .ccv-card-visual.diners-card { background: linear-gradient(135deg, #4a4a4a 0%, #888 100%); }\n\n#ccv-app .ccv-card-brand {\n  font-size: 1.1rem;\n  font-weight: 700;\n  letter-spacing: 2px;\n  text-transform: uppercase;\n  opacity: 0.9;\n  margin-bottom: 30px;\n}\n#ccv-app .ccv-card-number-display {\n  font-size: 1.3rem;\n  letter-spacing: 4px;\n  font-family: 'Courier New', monospace;\n  margin-bottom: 20px;\n  word-spacing: 8px;\n}\n#ccv-app .ccv-card-footer {\n  display: flex;\n  justify-content: space-between;\n  font-size: 0.75rem;\n  opacity: 0.7;\n  text-transform: uppercase;\n  letter-spacing: 1px;\n}\n\n/* Input Area */\n#ccv-app .ccv-input-section {\n  background: #fff;\n  border: 2px solid #e0e7ff;\n  border-radius: 12px;\n  padding: 24px;\n  margin-bottom: 20px;\n}\n#ccv-app .ccv-label {\n  display: block;\n  font-weight: 600;\n  margin-bottom: 8px;\n  color: #333;\n  font-size: 0.95rem;\n}\n#ccv-app .ccv-input-row {\n  display: flex;\n  gap: 12px;\n  align-items: center;\n  flex-wrap: wrap;\n}\n#ccv-app .ccv-number-input {\n  flex: 1;\n  min-width: 200px;\n  padding: 14px 18px;\n  font-size: 1.2rem;\n  letter-spacing: 3px;\n  font-family: 'Courier New', monospace;\n  border: 2px solid #d0d8ff;\n  border-radius: 10px;\n  outline: none;\n  transition: border-color 0.2s;\n  background: #fafbff;\n}\n#ccv-app .ccv-number-input:focus { border-color: #4a6cf7; background: #fff; }\n#ccv-app .ccv-number-input.valid   { border-color: #22c55e; background: #f0fff4; }\n#ccv-app .ccv-number-input.invalid { border-color: #ef4444; background: #fff5f5; }\n\n#ccv-app .ccv-btn {\n  padding: 14px 22px;\n  border: none;\n  border-radius: 10px;\n  font-size: 0.95rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.2s;\n}\n#ccv-app .ccv-btn-primary {\n  background: #4a6cf7;\n  color: #fff;\n}\n#ccv-app .ccv-btn-primary:hover { background: #3a5ce6; transform: translateY(-1px); }\n#ccv-app .ccv-btn-secondary {\n  background: #f0f4ff;\n  color: #4a6cf7;\n  border: 2px solid #d0d8ff;\n}\n#ccv-app .ccv-btn-secondary:hover { background: #e0e7ff; }\n\n/* Result Badge */\n#ccv-app .ccv-result-badge {\n  display: none;\n  align-items: center;\n  gap: 10px;\n  padding: 14px 20px;\n  border-radius: 10px;\n  font-weight: 600;\n  font-size: 1rem;\n  margin-top: 16px;\n}\n#ccv-app .ccv-result-badge.show { display: flex; }\n#ccv-app .ccv-result-badge.valid-badge {\n  background: #f0fff4;\n  border: 2px solid #22c55e;\n  color: #15803d;\n}\n#ccv-app .ccv-result-badge.invalid-badge {\n  background: #fff5f5;\n  border: 2px solid #ef4444;\n  color: #b91c1c;\n}\n#ccv-app .ccv-result-icon { font-size: 1.4rem; }\n\n/* Card Details */\n#ccv-app .ccv-details-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n  gap: 12px;\n  margin-top: 16px;\n}\n#ccv-app .ccv-detail-box {\n  background: #f8faff;\n  border: 1px solid #e0e7ff;\n  border-radius: 10px;\n  padding: 12px 16px;\n  text-align: center;\n}\n#ccv-app .ccv-detail-label {\n  font-size: 0.72rem;\n  color: #888;\n  text-transform: uppercase;\n  letter-spacing: 1px;\n  margin-bottom: 6px;\n}\n#ccv-app .ccv-detail-value {\n  font-size: 1rem;\n  font-weight: 700;\n  color: #1a1a2e;\n}\n\n/* Generate Section */\n#ccv-app .ccv-generate-section {\n  background: #fff;\n  border: 2px solid #e0e7ff;\n  border-radius: 12px;\n  padding: 20px 24px;\n  margin-bottom: 20px;\n}\n#ccv-app .ccv-generate-section h3 {\n  margin: 0 0 6px;\n  font-size: 1rem;\n  color: #333;\n}\n#ccv-app .ccv-generate-note {\n  font-size: 0.8rem;\n  color: #888;\n  margin-bottom: 14px;\n}\n#ccv-app .ccv-test-cards {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-top: 12px;\n}\n#ccv-app .ccv-test-chip {\n  padding: 6px 14px;\n  background: #f0f4ff;\n  border: 1px solid #d0d8ff;\n  border-radius: 20px;\n  font-size: 0.85rem;\n  font-family: 'Courier New', monospace;\n  cursor: pointer;\n  transition: all 0.15s;\n  color: #4a6cf7;\n}\n#ccv-app .ccv-test-chip:hover { background: #e0e7ff; transform: translateY(-1px); }\n#ccv-app .ccv-test-chip-label {\n  font-size: 0.75rem;\n  color: #888;\n  font-family: sans-serif;\n  margin-right: 4px;\n}\n\n/* Luhn Explanation */\n#ccv-app .ccv-luhn-section {\n  background: #fff;\n  border: 2px solid #e0e7ff;\n  border-radius: 12px;\n  padding: 20px 24px;\n  margin-bottom: 20px;\n}\n#ccv-app .ccv-luhn-section h3 {\n  margin: 0 0 12px;\n  font-size: 1rem;\n  color: #333;\n}\n#ccv-app .ccv-luhn-steps {\n  display: none;\n}\n#ccv-app .ccv-luhn-steps.show { display: block; }\n#ccv-app .ccv-luhn-step {\n  background: #f8faff;\n  border-radius: 8px;\n  padding: 12px 16px;\n  margin-bottom: 10px;\n  font-size: 0.88rem;\n  border-left: 3px solid #4a6cf7;\n}\n#ccv-app .ccv-luhn-step-title {\n  font-weight: 700;\n  color: #4a6cf7;\n  margin-bottom: 4px;\n}\n#ccv-app .ccv-digit-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 4px;\n  margin-top: 6px;\n}\n#ccv-app .ccv-digit-cell {\n  width: 32px;\n  height: 32px;\n  border-radius: 6px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 0.8rem;\n  font-weight: 600;\n  font-family: 'Courier New', monospace;\n}\n#ccv-app .ccv-digit-cell.orig    { background: #e0e7ff; color: #4a6cf7; }\n#ccv-app .ccv-digit-cell.doubled { background: #fef3c7; color: #92400e; }\n#ccv-app .ccv-digit-cell.check   { background: #dcfce7; color: #15803d; }\n\n#ccv-app .ccv-luhn-placeholder {\n  color: #aaa;\n  font-size: 0.88rem;\n  font-style: italic;\n}\n\n/* Disclaimer */\n#ccv-app .ccv-disclaimer {\n  background: #fffbeb;\n  border: 1px solid #fcd34d;\n  border-radius: 10px;\n  padding: 14px 18px;\n  font-size: 0.85rem;\n  color: #78350f;\n  margin-bottom: 24px;\n}\n#ccv-app .ccv-disclaimer strong { color: #92400e; }\n\n/* Related Tools */\n#ccv-app .ccv-related {\n  background: #f8faff;\n  border-radius: 12px;\n  padding: 20px 24px;\n}\n#ccv-app .ccv-related h3 { margin: 0 0 14px; font-size: 1rem; color: #333; }\n#ccv-app .ccv-related-links {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n}\n#ccv-app .ccv-related-link {\n  padding: 8px 16px;\n  background: #fff;\n  border: 2px solid #e0e7ff;\n  border-radius: 8px;\n  color: #4a6cf7;\n  text-decoration: none;\n  font-size: 0.88rem;\n  font-weight: 600;\n  transition: all 0.15s;\n}\n#ccv-app .ccv-related-link:hover {\n  background: #e0e7ff;\n  text-decoration: none;\n}\n\n@media (max-width: 480px) {\n  #ccv-app .ccv-card-visual { width: 100%; }\n  #ccv-app .ccv-input-row { flex-direction: column; align-items: stretch; }\n  #ccv-app .ccv-btn { width: 100%; }\n}\n\u003c/style\u003e\n\u003cdiv class=\"ccv-intro\"\u003e\nEnter any credit card number to validate its format using the Luhn algorithm and identify the card network. This tool works entirely in your browser — no data is ever sent to a server.\n\u003c/div\u003e\n\u003c!-- Card Visual --\u003e\n\u003cdiv class=\"ccv-card-visual\" id=\"ccvCardVisual\"\u003e\n  \u003cdiv class=\"ccv-card-brand\" id=\"ccvCardBrand\"\u003eCard Network\u003c/div\u003e\n  \u003cdiv class=\"ccv-card-number-display\" id=\"ccvCardDisplay\"\u003e•••• •••• •••• ••••\u003c/div\u003e\n  \u003cdiv class=\"ccv-card-footer\"\u003e\n    \u003cspan\u003eVALID THRU ••/••\u003c/span\u003e\n    \u003cspan id=\"ccvCardFooterBrand\"\u003e——\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Input Section --\u003e\n\u003cdiv class=\"ccv-input-section\"\u003e\n  \u003clabel class=\"ccv-label\" for=\"ccvInput\"\u003eCard Number\u003c/label\u003e\n  \u003cdiv class=\"ccv-input-row\"\u003e\n    \u003cinput\n      type=\"text\"\n      id=\"ccvInput\"\n      class=\"ccv-number-input\"\n      placeholder=\"4111 1111 1111 1111\"\n      maxlength=\"23\"\n      inputmode=\"numeric\"\n      autocomplete=\"off\"\n    /\u003e\n    \u003cbutton class=\"ccv-btn ccv-btn-primary\" onclick=\"ccvValidate()\"\u003eValidate\u003c/button\u003e\n    \u003cbutton class=\"ccv-btn ccv-btn-secondary\" onclick=\"ccvClear()\"\u003eClear\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ccv-result-badge\" id=\"ccvResultBadge\"\u003e\n    \u003cspan class=\"ccv-result-icon\" id=\"ccvResultIcon\"\u003e\u003c/span\u003e\n    \u003cspan id=\"ccvResultText\"\u003e\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ccv-details-grid\" id=\"ccvDetailsGrid\" style=\"display:none;\"\u003e\n    \u003cdiv class=\"ccv-detail-box\"\u003e\n      \u003cdiv class=\"ccv-detail-label\"\u003eCard Network\u003c/div\u003e\n      \u003cdiv class=\"ccv-detail-value\" id=\"ccvDetailNetwork\"\u003e—\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ccv-detail-box\"\u003e\n      \u003cdiv class=\"ccv-detail-label\"\u003eNumber Length\u003c/div\u003e\n      \u003cdiv class=\"ccv-detail-value\" id=\"ccvDetailLength\"\u003e—\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ccv-detail-box\"\u003e\n      \u003cdiv class=\"ccv-detail-label\"\u003eLuhn Check\u003c/div\u003e\n      \u003cdiv class=\"ccv-detail-value\" id=\"ccvDetailLuhn\"\u003e—\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ccv-detail-box\"\u003e\n      \u003cdiv class=\"ccv-detail-label\"\u003eIIN / BIN\u003c/div\u003e\n      \u003cdiv class=\"ccv-detail-value\" id=\"ccvDetailBin\"\u003e—\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Generate Test Numbers --\u003e\n\u003cdiv class=\"ccv-generate-section\"\u003e\n  \u003ch3\u003eGenerate Test Card Numbers\u003c/h3\u003e\n  \u003cdiv class=\"ccv-generate-note\"\u003eThese are mathematically valid numbers used for testing only. They are not linked to real accounts.\u003c/div\u003e\n  \u003cdiv style=\"display:flex; flex-wrap:wrap; gap:10px;\"\u003e\n    \u003cbutton class=\"ccv-btn ccv-btn-secondary\" onclick=\"ccvGenerateAll()\"\u003eGenerate All Types\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ccv-test-cards\" id=\"ccvTestCards\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Luhn Algorithm Explanation --\u003e\n\u003cdiv class=\"ccv-luhn-section\"\u003e\n  \u003ch3\u003eHow the Luhn Algorithm Works\u003c/h3\u003e\n  \u003cdiv class=\"ccv-luhn-placeholder\" id=\"ccvLuhnPlaceholder\"\u003eEnter a card number above to see a step-by-step breakdown.\u003c/div\u003e\n  \u003cdiv class=\"ccv-luhn-steps\" id=\"ccvLuhnSteps\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Disclaimer --\u003e\n\u003cdiv class=\"ccv-disclaimer\"\u003e\n  \u003cstrong\u003eImportant Notice:\u003c/strong\u003e This tool validates number format only. It cannot verify if a card exists, is active, or has available funds. Do not use real card numbers — for testing, use the generated test numbers above.\n\u003c/div\u003e\n\u003c!-- Related Tools --\u003e\n\u003cdiv class=\"ccv-related\"\u003e\n  \u003ch3\u003eRelated Tools\u003c/h3\u003e\n  \u003cdiv class=\"ccv-related-links\"\u003e\n    \u003ca href=\"/tools/iban-validator/\" class=\"ccv-related-link\"\u003eIBAN Validator\u003c/a\u003e\n    \u003ca href=\"/tools/password-generator/\" class=\"ccv-related-link\"\u003ePassword Generator\u003c/a\u003e\n    \u003ca href=\"/tools/bmi-calculator/\" class=\"ccv-related-link\"\u003eBMI Calculator\u003c/a\u003e\n    \u003ca href=\"/tools/compound-interest-calculator/\" class=\"ccv-related-link\"\u003eCompound Interest Calculator\u003c/a\u003e\n    \u003ca href=\"/tools/unit-converter/\" class=\"ccv-related-link\"\u003eUnit Converter\u003c/a\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  // Card network definitions\n  var NETWORKS = [\n    { name: 'Visa',       cssClass: 'visa-card',    pattern: /^4/,                          lengths: [13,16,19] },\n    { name: 'Mastercard', cssClass: 'mc-card',      pattern: /^5[1-5]|^2(2[2-9]|[3-6]\\d|7[01])/,  lengths: [16] },\n    { name: 'Amex',       cssClass: 'amex-card',    pattern: /^3[47]/,                      lengths: [15] },\n    { name: 'Discover',   cssClass: 'discover-card',pattern: /^6(?:011|5)/,                 lengths: [16,19] },\n    { name: 'JCB',        cssClass: 'jcb-card',     pattern: /^35(?:2[89]|[3-8])/,          lengths: [16,17,18,19] },\n    { name: 'Diners',     cssClass: 'diners-card',  pattern: /^3(?:0[0-5]|[68])/,           lengths: [14] },\n  ];\n\n  // Test number seeds for each network (valid Luhn numbers)\n  var TEST_SEEDS = [\n    { net: 'Visa',        num: '4111111111111111' },\n    { net: 'Visa',        num: '4000056655665556' },\n    { net: 'Mastercard',  num: '5555555555554444' },\n    { net: 'Mastercard',  num: '5200828282828210' },\n    { net: 'Amex',        num: '378282246310005' },\n    { net: 'Amex',        num: '371449635398431' },\n    { net: 'Discover',    num: '6011111111111117' },\n    { net: 'JCB',         num: '3530111333300000' },\n    { net: 'Diners',      num: '30569309025904' },\n  ];\n\n  function detectNetwork(digits) {\n    for (var i = 0; i \u003c NETWORKS.length; i++) {\n      if (NETWORKS[i].pattern.test(digits)) return NETWORKS[i];\n    }\n    return null;\n  }\n\n  function luhnCheck(digits) {\n    var sum = 0;\n    var alternate = false;\n    for (var i = digits.length - 1; i \u003e= 0; i--) {\n      var n = parseInt(digits[i], 10);\n      if (alternate) {\n        n *= 2;\n        if (n \u003e 9) n -= 9;\n      }\n      sum += n;\n      alternate = !alternate;\n    }\n    return sum % 10 === 0;\n  }\n\n  function luhnSteps(digits) {\n    // Returns detailed steps\n    var origArr = digits.split('').map(Number);\n    var doubled = [];\n    var alternate = false;\n    for (var i = origArr.length - 1; i \u003e= 0; i--) {\n      var n = origArr[i];\n      if (alternate) {\n        n *= 2;\n        if (n \u003e 9) n -= 9;\n      }\n      doubled[i] = n;\n      alternate = !alternate;\n    }\n    var sum = doubled.reduce(function(a, b) { return a + b; }, 0);\n    return { orig: origArr, doubled: doubled, sum: sum, valid: sum % 10 === 0 };\n  }\n\n  function formatDisplay(digits, net) {\n    if (!digits) return '•••• •••• •••• ••••';\n    var len = digits.length;\n    // Amex: 4-6-5, Diners: 4-6-4, others: 4-4-4-4\n    if (net \u0026\u0026 net.name === 'Amex') {\n      var p1 = digits.slice(0,4).padEnd(4,'•');\n      var p2 = digits.slice(4,10).padEnd(6,'•');\n      var p3 = digits.slice(10,15).padEnd(5,'•');\n      return p1 + ' ' + p2 + ' ' + p3;\n    }\n    if (net \u0026\u0026 net.name === 'Diners') {\n      var p1 = digits.slice(0,4).padEnd(4,'•');\n      var p2 = digits.slice(4,10).padEnd(6,'•');\n      var p3 = digits.slice(10,14).padEnd(4,'•');\n      return p1 + ' ' + p2 + ' ' + p3;\n    }\n    var groups = [];\n    for (var i = 0; i \u003c 16; i += 4) {\n      groups.push(digits.slice(i, i+4).padEnd(4,'•'));\n    }\n    return groups.join(' ');\n  }\n\n  function updateCard(digits, net) {\n    var visual = document.getElementById('ccvCardVisual');\n    var brandEl = document.getElementById('ccvCardBrand');\n    var dispEl  = document.getElementById('ccvCardDisplay');\n    var footEl  = document.getElementById('ccvCardFooterBrand');\n\n    // Reset classes\n    visual.className = 'ccv-card-visual';\n    if (net) visual.classList.add(net.cssClass);\n\n    brandEl.textContent = net ? net.name : 'Card Network';\n    dispEl.textContent  = formatDisplay(digits, net);\n    footEl.textContent  = net ? net.name.toUpperCase() : '——';\n  }\n\n  // Auto-format input\n  var inp = document.getElementById('ccvInput');\n  inp.addEventListener('input', function(e) {\n    var raw = inp.value.replace(/\\D/g,'');\n    var net = detectNetwork(raw);\n    // Format with spaces\n    var formatted;\n    if (net \u0026\u0026 net.name === 'Amex') {\n      formatted = raw.slice(0,4);\n      if (raw.length \u003e 4) formatted += ' ' + raw.slice(4,10);\n      if (raw.length \u003e 10) formatted += ' ' + raw.slice(10,15);\n    } else if (net \u0026\u0026 net.name === 'Diners') {\n      formatted = raw.slice(0,4);\n      if (raw.length \u003e 4) formatted += ' ' + raw.slice(4,10);\n      if (raw.length \u003e 10) formatted += ' ' + raw.slice(10,14);\n    } else {\n      formatted = raw.replace(/(.{4})/g, '$1 ').trim();\n    }\n    inp.value = formatted;\n    updateCard(raw, net);\n\n    // Live border feedback if we have enough digits\n    if (raw.length \u003e= 13) {\n      var ok = luhnCheck(raw);\n      inp.className = 'ccv-number-input ' + (ok ? 'valid' : 'invalid');\n    } else {\n      inp.className = 'ccv-number-input';\n    }\n  });\n\n  window.ccvValidate = function() {\n    var raw = inp.value.replace(/\\D/g,'');\n    var net = detectNetwork(raw);\n    var badge = document.getElementById('ccvResultBadge');\n    var icon  = document.getElementById('ccvResultIcon');\n    var text  = document.getElementById('ccvResultText');\n    var grid  = document.getElementById('ccvDetailsGrid');\n\n    if (raw.length \u003c 8) {\n      badge.className = 'ccv-result-badge show invalid-badge';\n      icon.textContent = '✗';\n      text.textContent = 'Please enter a valid card number (at least 8 digits).';\n      grid.style.display = 'none';\n      showLuhnSteps(null);\n      return;\n    }\n\n    var luhnOk = luhnCheck(raw);\n    var netOk  = net \u0026\u0026 net.lengths.indexOf(raw.length) !== -1;\n\n    updateCard(raw, net);\n    inp.className = 'ccv-number-input ' + (luhnOk ? 'valid' : 'invalid');\n\n    if (luhnOk) {\n      badge.className = 'ccv-result-badge show valid-badge';\n      icon.textContent = '✓';\n      text.textContent = 'Luhn check passed! This is a structurally valid card number.' + (netOk ? '' : ' (Length is unusual for this network.)');\n    } else {\n      badge.className = 'ccv-result-badge show invalid-badge';\n      icon.textContent = '✗';\n      text.textContent = 'Luhn check failed. This number is not a valid card number format.';\n    }\n\n    // Details\n    grid.style.display = 'grid';\n    document.getElementById('ccvDetailNetwork').textContent = net ? net.name : 'Unknown';\n    document.getElementById('ccvDetailLength').textContent  = raw.length + ' digits';\n    document.getElementById('ccvDetailLuhn').textContent    = luhnOk ? 'Pass ✓' : 'Fail ✗';\n    document.getElementById('ccvDetailBin').textContent     = raw.slice(0,6);\n\n    showLuhnSteps(raw);\n  };\n\n  window.ccvClear = function() {\n    inp.value = '';\n    inp.className = 'ccv-number-input';\n    document.getElementById('ccvResultBadge').className = 'ccv-result-badge';\n    document.getElementById('ccvDetailsGrid').style.display = 'none';\n    updateCard('', null);\n    showLuhnSteps(null);\n  };\n\n  function showLuhnSteps(digits) {\n    var placeholder = document.getElementById('ccvLuhnPlaceholder');\n    var stepsEl     = document.getElementById('ccvLuhnSteps');\n\n    if (!digits) {\n      placeholder.style.display = '';\n      stepsEl.className = 'ccv-luhn-steps';\n      stepsEl.innerHTML = '';\n      return;\n    }\n\n    placeholder.style.display = 'none';\n    stepsEl.className = 'ccv-luhn-steps show';\n\n    var data = luhnSteps(digits);\n    var origArr = data.orig;\n    var doubledArr = data.doubled;\n\n    // Which positions are doubled (from right, every other starting at position 1)\n    var isDoubled = origArr.map(function(_, i) {\n      return (origArr.length - 1 - i) % 2 === 1;\n    });\n\n    function makeRow(arr, colorFn) {\n      return '\u003cdiv class=\"ccv-digit-row\"\u003e' + arr.map(function(v, i) {\n        return '\u003cdiv class=\"ccv-digit-cell ' + colorFn(i) + '\"\u003e' + v + '\u003c/div\u003e';\n      }).join('') + '\u003c/div\u003e';\n    }\n\n    var html = '';\n\n    // Step 1: Original digits\n    html += '\u003cdiv class=\"ccv-luhn-step\"\u003e';\n    html += '\u003cdiv class=\"ccv-luhn-step-title\"\u003eStep 1 — Original digits (right to left, every 2nd is doubled)\u003c/div\u003e';\n    html += makeRow(origArr, function(i) { return isDoubled[i] ? 'doubled' : 'orig'; });\n    html += '\u003cdiv style=\"margin-top:6px;font-size:0.8rem;color:#666;\"\u003eBlue = keep as-is \u0026nbsp;|\u0026nbsp; Yellow = will be doubled\u003c/div\u003e';\n    html += '\u003c/div\u003e';\n\n    // Step 2: After doubling\n    html += '\u003cdiv class=\"ccv-luhn-step\"\u003e';\n    html += '\u003cdiv class=\"ccv-luhn-step-title\"\u003eStep 2 — After doubling (if result \u003e 9, subtract 9)\u003c/div\u003e';\n    html += makeRow(doubledArr, function(i) { return isDoubled[i] ? 'doubled' : 'orig'; });\n    html += '\u003c/div\u003e';\n\n    // Step 3: Sum\n    html += '\u003cdiv class=\"ccv-luhn-step\"\u003e';\n    html += '\u003cdiv class=\"ccv-luhn-step-title\"\u003eStep 3 — Sum all digits\u003c/div\u003e';\n    html += '\u003cdiv style=\"font-size:0.9rem;margin-top:4px;\"\u003e';\n    html += doubledArr.join(' + ') + ' = \u003cstrong\u003e' + data.sum + '\u003c/strong\u003e';\n    html += '\u003c/div\u003e';\n    html += '\u003c/div\u003e';\n\n    // Step 4: Check\n    html += '\u003cdiv class=\"ccv-luhn-step\"\u003e';\n    html += '\u003cdiv class=\"ccv-luhn-step-title\"\u003eStep 4 — Check if sum is divisible by 10\u003c/div\u003e';\n    html += '\u003cdiv style=\"font-size:0.9rem;margin-top:4px;\"\u003e';\n    html += '\u003cstrong\u003e' + data.sum + '\u003c/strong\u003e mod 10 = \u003cstrong\u003e' + (data.sum % 10) + '\u003c/strong\u003e → ';\n    html += data.valid\n      ? '\u003cspan style=\"color:#15803d;font-weight:700;\"\u003eValid! (remainder is 0)\u003c/span\u003e'\n      : '\u003cspan style=\"color:#b91c1c;font-weight:700;\"\u003eInvalid (remainder is not 0)\u003c/span\u003e';\n    html += '\u003c/div\u003e';\n    html += '\u003c/div\u003e';\n\n    stepsEl.innerHTML = html;\n  }\n\n  window.ccvGenerateAll = function() {\n    var container = document.getElementById('ccvTestCards');\n    container.innerHTML = '';\n    TEST_SEEDS.forEach(function(seed) {\n      var chip = document.createElement('span');\n      chip.className = 'ccv-test-chip';\n      chip.innerHTML = '\u003cspan class=\"ccv-test-chip-label\"\u003e' + seed.net + ':\u003c/span\u003e' + seed.num;\n      chip.addEventListener('click', function() {\n        var net = detectNetwork(seed.num);\n        var formatted;\n        if (net \u0026\u0026 net.name === 'Amex') {\n          formatted = seed.num.slice(0,4) + ' ' + seed.num.slice(4,10) + ' ' + seed.num.slice(10);\n        } else {\n          formatted = seed.num.replace(/(.{4})/g, '$1 ').trim();\n        }\n        inp.value = formatted;\n        inp.className = 'ccv-number-input valid';\n        updateCard(seed.num, net);\n        ccvValidate();\n      });\n      container.appendChild(chip);\n    });\n  };\n})();\n\u003c/script\u003e\n\u003c/div\u003e","title":"Credit Card Validator - Luhn Algorithm Checker"},{"content":" Common Presets Cron Expression Expression: Copy Human-Readable Description Every minute Visual Builder Next 10 Run Times Related Tools Convert Unix timestamps \u0026rarr; Unix Timestamp Converter Generate Git commands \u0026rarr; Git Command Generator Calculate file permissions \u0026rarr; Chmod Calculator Related Articles How to Automate Tasks with AI Step by Step ","permalink":"https://productivity-works.com/tools/cron-expression-builder/","summary":"\u003cdiv id=\"cr-app\"\u003e\n\u003cstyle\u003e\n#cr-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #e2e8f0;\n}\n\n#cr-app * {\n  box-sizing: border-box;\n}\n\n#cr-app .cr-section {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 10px;\n  padding: 20px 24px;\n  margin-bottom: 18px;\n}\n\n#cr-app .cr-section-title {\n  font-size: 13px;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #94a3b8;\n  margin: 0 0 16px 0;\n}\n\n/* Presets */\n#cr-app .cr-presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n}\n\n#cr-app .cr-preset-btn {\n  background: #0f172a;\n  border: 1px solid #475569;\n  border-radius: 6px;\n  color: #cbd5e1;\n  padding: 6px 12px;\n  font-size: 13px;\n  cursor: pointer;\n  transition: background 0.15s, border-color 0.15s, color 0.15s;\n}\n\n#cr-app .cr-preset-btn:hover {\n  background: #334155;\n  border-color: #64748b;\n  color: #f1f5f9;\n}\n\n/* Expression display */\n#cr-app .cr-expr-row {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  flex-wrap: wrap;\n}\n\n#cr-app .cr-expr-label {\n  font-size: 13px;\n  color: #94a3b8;\n  white-space: nowrap;\n}\n\n#cr-app .cr-expr-input {\n  flex: 1;\n  min-width: 200px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 20px;\n  font-weight: 700;\n  background: #0f172a;\n  border: 2px solid #334155;\n  border-radius: 8px;\n  color: #38bdf8;\n  padding: 10px 16px;\n  outline: none;\n  transition: border-color 0.15s;\n  letter-spacing: 0.05em;\n}\n\n#cr-app .cr-expr-input:focus {\n  border-color: #38bdf8;\n}\n\n#cr-app .cr-expr-input.cr-invalid {\n  border-color: #ef4444;\n  color: #ef4444;\n}\n\n#cr-app .cr-copy-btn {\n  background: #0369a1;\n  border: none;\n  border-radius: 6px;\n  color: #f0f9ff;\n  padding: 10px 18px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s;\n  white-space: nowrap;\n}\n\n#cr-app .cr-copy-btn:hover {\n  background: #0284c7;\n}\n\n#cr-app .cr-copy-btn.cr-copied {\n  background: #16a34a;\n}\n\n/* Error message */\n#cr-app .cr-error {\n  display: none;\n  color: #f87171;\n  font-size: 13px;\n  margin-top: 8px;\n  padding: 8px 12px;\n  background: #450a0a;\n  border: 1px solid #7f1d1d;\n  border-radius: 6px;\n}\n\n#cr-app .cr-error.cr-visible {\n  display: block;\n}\n\n/* Description */\n#cr-app .cr-description {\n  font-size: 16px;\n  color: #a5f3fc;\n  font-weight: 500;\n  min-height: 24px;\n}\n\n/* Fields grid */\n#cr-app .cr-fields-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));\n  gap: 16px;\n}\n\n#cr-app .cr-field-card {\n  background: #0f172a;\n  border: 1px solid #334155;\n  border-radius: 8px;\n  padding: 14px 16px;\n}\n\n#cr-app .cr-field-name {\n  font-size: 12px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #7dd3fc;\n  margin-bottom: 10px;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n}\n\n#cr-app .cr-field-range {\n  font-weight: 400;\n  font-size: 11px;\n  color: #64748b;\n  text-transform: none;\n  letter-spacing: 0;\n}\n\n#cr-app .cr-field-mode {\n  display: flex;\n  gap: 4px;\n  margin-bottom: 10px;\n  flex-wrap: wrap;\n}\n\n#cr-app .cr-mode-btn {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 4px;\n  color: #94a3b8;\n  padding: 3px 9px;\n  font-size: 11px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.12s, color 0.12s, border-color 0.12s;\n}\n\n#cr-app .cr-mode-btn:hover {\n  background: #334155;\n  color: #e2e8f0;\n}\n\n#cr-app .cr-mode-btn.cr-active {\n  background: #0369a1;\n  border-color: #0284c7;\n  color: #f0f9ff;\n}\n\n#cr-app .cr-field-controls {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  align-items: center;\n}\n\n#cr-app .cr-select,\n#cr-app .cr-input {\n  background: #1e293b;\n  border: 1px solid #475569;\n  border-radius: 5px;\n  color: #e2e8f0;\n  padding: 5px 10px;\n  font-size: 13px;\n  outline: none;\n  transition: border-color 0.12s;\n}\n\n#cr-app .cr-select:focus,\n#cr-app .cr-input:focus {\n  border-color: #38bdf8;\n}\n\n#cr-app .cr-input {\n  width: 70px;\n  text-align: center;\n}\n\n#cr-app .cr-input-wide {\n  width: 140px;\n  text-align: left;\n}\n\n#cr-app .cr-field-label {\n  font-size: 12px;\n  color: #64748b;\n}\n\n/* Field value preview */\n#cr-app .cr-field-preview {\n  margin-top: 8px;\n  font-family: monospace;\n  font-size: 13px;\n  color: #38bdf8;\n  background: #1e293b;\n  border-radius: 4px;\n  padding: 3px 8px;\n  display: inline-block;\n}\n\n/* Next runs */\n#cr-app .cr-runs-list {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));\n  gap: 6px;\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\n\n#cr-app .cr-run-item {\n  background: #0f172a;\n  border: 1px solid #1e293b;\n  border-radius: 6px;\n  padding: 7px 12px;\n  font-size: 13px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  gap: 8px;\n}\n\n#cr-app .cr-run-index {\n  color: #475569;\n  font-size: 11px;\n  min-width: 20px;\n  font-weight: 700;\n}\n\n#cr-app .cr-run-time {\n  font-family: monospace;\n  color: #e2e8f0;\n  flex: 1;\n}\n\n#cr-app .cr-run-rel {\n  font-size: 11px;\n  color: #64748b;\n  white-space: nowrap;\n}\n\n/* Cross-links */\n#cr-app .cr-links {\n  margin-top: 10px;\n  padding: 0;\n}\n\n#cr-app .cr-links li {\n  list-style: none;\n  margin-bottom: 6px;\n  font-size: 14px;\n  color: #94a3b8;\n}\n\n#cr-app .cr-links a {\n  color: #38bdf8;\n  text-decoration: none;\n}\n\n#cr-app .cr-links a:hover {\n  text-decoration: underline;\n}\n\n@media (max-width: 600px) {\n  #cr-app .cr-expr-input {\n    font-size: 16px;\n  }\n  #cr-app .cr-fields-grid {\n    grid-template-columns: 1fr;\n  }\n}\n\u003c/style\u003e\n\u003c!-- Presets --\u003e\n\u003cdiv class=\"cr-section\"\u003e\n  \u003cdiv class=\"cr-section-title\"\u003eCommon Presets\u003c/div\u003e\n  \u003cdiv class=\"cr-presets\" id=\"cr-presets\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Expression --\u003e\n\u003cdiv class=\"cr-section\"\u003e\n  \u003cdiv class=\"cr-section-title\"\u003eCron Expression\u003c/div\u003e\n  \u003cdiv class=\"cr-expr-row\"\u003e\n    \u003cspan class=\"cr-expr-label\"\u003eExpression:\u003c/span\u003e\n    \u003cinput class=\"cr-expr-input\" id=\"cr-expr-input\" type=\"text\" value=\"* * * * *\" spellcheck=\"false\" autocomplete=\"off\" /\u003e\n    \u003cbutton class=\"cr-copy-btn\" id=\"cr-copy-btn\"\u003eCopy\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cr-error\" id=\"cr-error\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Description --\u003e\n\u003cdiv class=\"cr-section\"\u003e\n  \u003cdiv class=\"cr-section-title\"\u003eHuman-Readable Description\u003c/div\u003e\n  \u003cdiv class=\"cr-description\" id=\"cr-description\"\u003eEvery minute\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Visual Builder --\u003e\n\u003cdiv class=\"cr-section\"\u003e\n  \u003cdiv class=\"cr-section-title\"\u003eVisual Builder\u003c/div\u003e\n  \u003cdiv class=\"cr-fields-grid\" id=\"cr-fields-grid\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Next Runs --\u003e\n\u003cdiv class=\"cr-section\"\u003e\n  \u003cdiv class=\"cr-section-title\"\u003eNext 10 Run Times\u003c/div\u003e\n  \u003cul class=\"cr-runs-list\" id=\"cr-runs-list\"\u003e\u003c/ul\u003e\n\u003c/div\u003e\n\u003c!-- Cross-links --\u003e\n\u003cdiv class=\"cr-section\"\u003e\n  \u003cdiv class=\"cr-section-title\"\u003eRelated Tools\u003c/div\u003e\n  \u003cul class=\"cr-links\"\u003e\n    \u003cli\u003eConvert Unix timestamps \u0026rarr; \u003ca href=\"/tools/timestamp-converter/\"\u003eUnix Timestamp Converter\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003eGenerate Git commands \u0026rarr; \u003ca href=\"/tools/git-command-generator/\"\u003eGit Command Generator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003eCalculate file permissions \u0026rarr; \u003ca href=\"/tools/chmod-calculator/\"\u003eChmod Calculator\u003c/a\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  \"use strict\";\n\n  /* ── Field definitions ── */\n  const FIELDS = [\n    { id: \"minute\",  label: \"Minute\",       min: 0,  max: 59, extras: [] },\n    { id: \"hour\",    label: \"Hour\",          min: 0,  max: 23, extras: [] },\n    { id: \"dom\",     label: \"Day of Month\",  min: 1,  max: 31, extras: [\"L\", \"W\"] },\n    { id: \"month\",   label: \"Month\",         min: 1,  max: 12, extras: [] },\n    { id: \"dow\",     label: \"Day of Week\",   min: 0,  max: 6,  extras: [] },\n  ];\n\n  const MONTH_NAMES = [\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"];\n  const DOW_NAMES   = [\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"];\n\n  /* Mode: every | specific | range | interval */\n  const state = {\n    minute: { mode: \"every\", specific: \"0\", rangeFrom: \"0\", rangeTo: \"59\", interval: \"5\", list: \"0\" },\n    hour:   { mode: \"every\", specific: \"0\", rangeFrom: \"0\", rangeTo: \"23\", interval: \"1\", list: \"0\" },\n    dom:    { mode: \"every\", specific: \"1\", rangeFrom: \"1\", rangeTo: \"31\", interval: \"1\", list: \"1\", special: \"L\" },\n    month:  { mode: \"every\", specific: \"1\", rangeFrom: \"1\", rangeTo: \"12\", interval: \"1\", list: \"1\" },\n    dow:    { mode: \"every\", specific: \"0\", rangeFrom: \"0\", rangeTo:  \"6\", interval: \"1\", list: \"0\" },\n  };\n\n  /* ── Presets ── */\n  const PRESETS = [\n    { label: \"Every minute\",        expr: \"* * * * *\" },\n    { label: \"Every 5 minutes\",     expr: \"*/5 * * * *\" },\n    { label: \"Every 15 minutes\",    expr: \"*/15 * * * *\" },\n    { label: \"Every hour\",          expr: \"0 * * * *\" },\n    { label: \"Daily at midnight\",   expr: \"0 0 * * *\" },\n    { label: \"Weekly Mon 9am\",      expr: \"0 9 * * 1\" },\n    { label: \"Monthly 1st midnight\",expr: \"0 0 1 * *\" },\n    { label: \"Every weekday 9am\",   expr: \"0 9 * * 1-5\" },\n    { label: \"Every Sunday noon\",   expr: \"0 12 * * 0\" },\n  ];\n\n  /* ── Utility ── */\n  function clamp(v, min, max) { return Math.max(min, Math.min(max, v)); }\n\n  function fieldValue(fid) {\n    const f = FIELDS.find(x =\u003e x.id === fid);\n    const s = state[fid];\n    switch (s.mode) {\n      case \"every\":    return \"*\";\n      case \"specific\":\n        if (fid === \"dom\" \u0026\u0026 (s.specific === \"L\" || s.specific === \"W\" || s.specific === \"LW\")) return s.specific;\n        return s.specific;\n      case \"range\":    return s.rangeFrom + \"-\" + s.rangeTo;\n      case \"interval\": return \"*/\" + s.interval;\n      case \"list\":     return s.list;\n      default:         return \"*\";\n    }\n  }\n\n  function buildExpr() {\n    return [\"minute\",\"hour\",\"dom\",\"month\",\"dow\"].map(fieldValue).join(\" \");\n  }\n\n  /* ── Parse expression back to state ── */\n  function parseExpr(expr) {\n    const parts = expr.trim().split(/\\s+/);\n    if (parts.length !== 5) return false;\n    const ids = [\"minute\",\"hour\",\"dom\",\"month\",\"dow\"];\n    for (let i = 0; i \u003c 5; i++) {\n      const p = parts[i];\n      const s = state[ids[i]];\n      if (p === \"*\") {\n        s.mode = \"every\";\n      } else if (/^\\*\\/\\d+$/.test(p)) {\n        s.mode = \"interval\";\n        s.interval = p.slice(2);\n      } else if (/^\\d+-\\d+$/.test(p)) {\n        s.mode = \"range\";\n        const [a, b] = p.split(\"-\");\n        s.rangeFrom = a;\n        s.rangeTo = b;\n      } else if (/^[\\d,]+$/.test(p)) {\n        if (p.indexOf(\",\") === -1) {\n          s.mode = \"specific\";\n          s.specific = p;\n        } else {\n          s.mode = \"list\";\n          s.list = p;\n        }\n      } else if (/^[LW]+$/.test(p) \u0026\u0026 ids[i] === \"dom\") {\n        s.mode = \"specific\";\n        s.specific = p;\n      } else {\n        // Complex: treat as list\n        s.mode = \"list\";\n        s.list = p;\n      }\n    }\n    return true;\n  }\n\n  /* ── Validation ── */\n  function validateExpr(expr) {\n    const parts = expr.trim().split(/\\s+/);\n    if (parts.length !== 5) return \"Expression must have exactly 5 fields (minute hour dom month dow).\";\n    const ranges = [[0,59],[0,23],[1,31],[1,12],[0,6]];\n    const names  = [\"Minute\",\"Hour\",\"Day of Month\",\"Month\",\"Day of Week\"];\n    for (let i = 0; i \u003c 5; i++) {\n      const p = parts[i];\n      const [lo, hi] = ranges[i];\n      if (p === \"*\") continue;\n      if (/^\\*\\/(\\d+)$/.test(p)) {\n        const n = parseInt(RegExp.$1);\n        if (n \u003c 1) return names[i] + \": interval must be \u003e= 1.\";\n        if (n \u003e hi) return names[i] + \": interval \" + n + \" exceeds max \" + hi + \".\";\n        continue;\n      }\n      if (/^(\\d+)-(\\d+)$/.test(p)) {\n        const a = parseInt(RegExp.$1), b = parseInt(RegExp.$2);\n        if (a \u003c lo || a \u003e hi) return names[i] + \": range start \" + a + \" out of bounds [\" + lo + \"-\" + hi + \"].\";\n        if (b \u003c lo || b \u003e hi) return names[i] + \": range end \" + b + \" out of bounds [\" + lo + \"-\" + hi + \"].\";\n        if (a \u003e= b) return names[i] + \": range start must be less than end.\";\n        continue;\n      }\n      if (i === 2 \u0026\u0026 /^[LW]+$/.test(p)) continue;\n      if (/^[\\d,]+$/.test(p)) {\n        const vals = p.split(\",\").map(Number);\n        for (const v of vals) {\n          if (v \u003c lo || v \u003e hi) return names[i] + \": value \" + v + \" out of bounds [\" + lo + \"-\" + hi + \"].\";\n        }\n        continue;\n      }\n      // allow step ranges like 1-5/2\n      if (/^\\d+-\\d+\\/\\d+$/.test(p)) continue;\n      return names[i] + \": unrecognized pattern \\\"\" + p + \"\\\".\";\n    }\n    return null;\n  }\n\n  /* ── Human-readable description ── */\n  function describe(expr) {\n    const parts = expr.trim().split(/\\s+/);\n    if (parts.length !== 5) return \"Invalid expression\";\n    const [min, hr, dom, mon, dow] = parts;\n\n    function descField(p, unit, names) {\n      if (p === \"*\") return null;\n      if (p.startsWith(\"*/\")) return \"every \" + p.slice(2) + \" \" + unit + (p.slice(2) === \"1\" ? \"\" : \"s\");\n      if (/^\\d+-\\d+$/.test(p)) {\n        const [a,b] = p.split(\"-\").map(Number);\n        if (names) return names[a] + \" through \" + names[b];\n        return unit + \" \" + a + \" through \" + b;\n      }\n      if (/^[\\d,]+$/.test(p)) {\n        const vals = p.split(\",\").map(Number);\n        if (names) return vals.map(v =\u003e names[v]).join(\", \");\n        return \"at \" + unit + \" \" + vals.join(\", \");\n      }\n      return p;\n    }\n\n    let parts2 = [];\n\n    // Time part\n    if (min === \"*\" \u0026\u0026 hr === \"*\") {\n      parts2.push(\"every minute\");\n    } else if (min.startsWith(\"*/\") \u0026\u0026 hr === \"*\") {\n      parts2.push(\"every \" + min.slice(2) + \" minutes\");\n    } else if (hr.startsWith(\"*/\") \u0026\u0026 min === \"0\") {\n      parts2.push(\"every \" + hr.slice(2) + \" hours\");\n    } else if (hr.startsWith(\"*/\")) {\n      parts2.push(\"every \" + hr.slice(2) + \" hours at minute \" + min);\n    } else if (min === \"*\") {\n      const h = parseInt(hr);\n      if (!isNaN(h)) {\n        const ampm = h \u003c 12 ? \"AM\" : \"PM\";\n        const h12 = h === 0 ? 12 : h \u003e 12 ? h - 12 : h;\n        parts2.push(\"every minute between \" + h12 + \":00 \" + ampm + \" and \" + h12 + \":59 \" + ampm);\n      } else {\n        parts2.push(\"every minute of hour \" + hr);\n      }\n    } else {\n      const h = parseInt(hr), m = parseInt(min);\n      if (!isNaN(h) \u0026\u0026 !isNaN(m)) {\n        const ampm = h \u003c 12 ? \"AM\" : \"PM\";\n        const h12 = h === 0 ? 12 : h \u003e 12 ? h - 12 : h;\n        const mm = m \u003c 10 ? \"0\" + m : \"\" + m;\n        parts2.push(\"at \" + h12 + \":\" + mm + \" \" + ampm);\n      } else {\n        const md = descField(min, \"minute\", null);\n        const hd = descField(hr, \"hour\", null);\n        if (md) parts2.push(md);\n        if (hd) parts2.push(hd);\n      }\n    }\n\n    // Day of week\n    if (dow !== \"*\") {\n      if (dow === \"1-5\") parts2.push(\"on weekdays\");\n      else if (dow === \"6-0\" || dow === \"0,6\" || dow === \"6,0\") parts2.push(\"on weekends\");\n      else {\n        const d = descField(dow, \"day\", DOW_NAMES);\n        if (d) parts2.push(\"on \" + d);\n      }\n    }\n\n    // Day of month\n    if (dom !== \"*\") {\n      if (dom === \"L\") parts2.push(\"on the last day of the month\");\n      else if (dom === \"LW\") parts2.push(\"on the last weekday of the month\");\n      else {\n        const d = descField(dom, \"day\", null);\n        if (d) parts2.push(\"on day \" + dom + \" of the month\");\n      }\n    }\n\n    // Month\n    if (mon !== \"*\") {\n      const d = descField(mon, \"month\", MONTH_NAMES);\n      if (d) parts2.push(\"in \" + d);\n    }\n\n    if (parts2.length === 0) return \"Every minute\";\n    return parts2.join(\", \").replace(/^./, c =\u003e c.toUpperCase());\n  }\n\n  /* ── Next run times ── */\n  function matchesField(val, p, lo) {\n    if (p === \"*\") return true;\n    if (p === \"L\") return true; // simplified\n    if (p === \"W\") return true;\n    if (p.startsWith(\"*/\")) {\n      const step = parseInt(p.slice(2));\n      return (val - lo) % step === 0;\n    }\n    if (/^\\d+-\\d+$/.test(p)) {\n      const [a, b] = p.split(\"-\").map(Number);\n      return val \u003e= a \u0026\u0026 val \u003c= b;\n    }\n    if (/^\\d+-\\d+\\/\\d+$/.test(p)) {\n      const m = p.match(/^(\\d+)-(\\d+)\\/(\\d+)$/);\n      const a = parseInt(m[1]), b = parseInt(m[2]), step = parseInt(m[3]);\n      if (val \u003c a || val \u003e b) return false;\n      return (val - a) % step === 0;\n    }\n    if (/^[\\d,]+$/.test(p)) {\n      return p.split(\",\").map(Number).includes(val);\n    }\n    return false;\n  }\n\n  function nextRuns(expr, count) {\n    const parts = expr.trim().split(/\\s+/);\n    if (parts.length !== 5) return [];\n    const [minP, hrP, domP, monP, dowP] = parts;\n\n    const results = [];\n    const now = new Date();\n    // Start from the next minute\n    let cur = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes() + 1, 0, 0);\n\n    const MAX_ITER = 366 * 24 * 60 * 2; // 2 years of minutes\n    let iter = 0;\n\n    while (results.length \u003c count \u0026\u0026 iter \u003c MAX_ITER) {\n      iter++;\n      const mn = cur.getMinutes();\n      const hr = cur.getHours();\n      const dom = cur.getDate();\n      const mon = cur.getMonth() + 1; // 1-12\n      const dow = cur.getDay();       // 0-6\n\n      if (\n        matchesField(mon, monP, 1) \u0026\u0026\n        matchesField(dom, domP, 1) \u0026\u0026\n        matchesField(dow, dowP, 0) \u0026\u0026\n        matchesField(hr,  hrP,  0) \u0026\u0026\n        matchesField(mn,  minP, 0)\n      ) {\n        results.push(new Date(cur));\n      }\n\n      cur = new Date(cur.getTime() + 60000);\n    }\n\n    return results;\n  }\n\n  function relTime(d) {\n    const diff = d - Date.now();\n    const sec = Math.round(diff / 1000);\n    if (sec \u003c 60) return \"in \" + sec + \"s\";\n    const min = Math.round(sec / 60);\n    if (min \u003c 60) return \"in \" + min + \"m\";\n    const hr = Math.round(min / 60);\n    if (hr \u003c 24) return \"in \" + hr + \"h\";\n    const day = Math.round(hr / 24);\n    return \"in \" + day + \"d\";\n  }\n\n  function formatDate(d) {\n    const pad = n =\u003e n \u003c 10 ? \"0\" + n : \"\" + n;\n    const days = [\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"];\n    return days[d.getDay()] + \" \" +\n      d.getFullYear() + \"-\" + pad(d.getMonth()+1) + \"-\" + pad(d.getDate()) +\n      \" \" + pad(d.getHours()) + \":\" + pad(d.getMinutes());\n  }\n\n  /* ── Render ── */\n  function renderPresets() {\n    const container = document.getElementById(\"cr-presets\");\n    container.innerHTML = \"\";\n    PRESETS.forEach(p =\u003e {\n      const btn = document.createElement(\"button\");\n      btn.className = \"cr-preset-btn\";\n      btn.textContent = p.label;\n      btn.addEventListener(\"click\", () =\u003e {\n        const input = document.getElementById(\"cr-expr-input\");\n        input.value = p.expr;\n        onExprInput(p.expr);\n      });\n      container.appendChild(btn);\n    });\n  }\n\n  function renderFields() {\n    const grid = document.getElementById(\"cr-fields-grid\");\n    grid.innerHTML = \"\";\n\n    FIELDS.forEach(f =\u003e {\n      const s = state[f.id];\n      const card = document.createElement(\"div\");\n      card.className = \"cr-field-card\";\n      card.id = \"cr-field-\" + f.id;\n\n      // Header\n      const header = document.createElement(\"div\");\n      header.className = \"cr-field-name\";\n      header.innerHTML = f.label + ' \u003cspan class=\"cr-field-range\"\u003e' + f.min + '–' + f.max + '\u003c/span\u003e';\n      card.appendChild(header);\n\n      // Mode buttons\n      const modeRow = document.createElement(\"div\");\n      modeRow.className = \"cr-field-mode\";\n      const modes = [\"every\",\"specific\",\"range\",\"interval\",\"list\"];\n      modes.forEach(m =\u003e {\n        const btn = document.createElement(\"button\");\n        btn.className = \"cr-mode-btn\" + (s.mode === m ? \" cr-active\" : \"\");\n        btn.textContent = m.charAt(0).toUpperCase() + m.slice(1);\n        btn.dataset.field = f.id;\n        btn.dataset.mode = m;\n        btn.addEventListener(\"click\", () =\u003e {\n          state[f.id].mode = m;\n          renderFields();\n          updateAll();\n        });\n        modeRow.appendChild(btn);\n      });\n      card.appendChild(modeRow);\n\n      // Controls\n      const controls = document.createElement(\"div\");\n      controls.className = \"cr-field-controls\";\n\n      if (s.mode === \"every\") {\n        const lbl = document.createElement(\"span\");\n        lbl.className = \"cr-field-label\";\n        lbl.textContent = \"Every \" + f.label.toLowerCase();\n        controls.appendChild(lbl);\n      }\n\n      if (s.mode === \"specific\") {\n        if (f.extras \u0026\u0026 f.extras.length \u003e 0) {\n          // Show a select with numbers + special values\n          const sel = document.createElement(\"select\");\n          sel.className = \"cr-select\";\n          for (let v = f.min; v \u003c= f.max; v++) {\n            const o = document.createElement(\"option\");\n            o.value = v;\n            o.textContent = formatFieldVal(f.id, v);\n            if (String(v) === s.specific) o.selected = true;\n            sel.appendChild(o);\n          }\n          f.extras.forEach(e =\u003e {\n            const o = document.createElement(\"option\");\n            o.value = e;\n            o.textContent = e;\n            if (e === s.specific) o.selected = true;\n            sel.appendChild(o);\n          });\n          sel.addEventListener(\"change\", () =\u003e {\n            state[f.id].specific = sel.value;\n            updateAll();\n          });\n          controls.appendChild(sel);\n        } else {\n          const sel = document.createElement(\"select\");\n          sel.className = \"cr-select\";\n          for (let v = f.min; v \u003c= f.max; v++) {\n            const o = document.createElement(\"option\");\n            o.value = v;\n            o.textContent = formatFieldVal(f.id, v);\n            if (String(v) === s.specific) o.selected = true;\n            sel.appendChild(o);\n          }\n          sel.addEventListener(\"change\", () =\u003e {\n            state[f.id].specific = sel.value;\n            updateAll();\n          });\n          controls.appendChild(sel);\n        }\n      }\n\n      if (s.mode === \"range\") {\n        const lblFrom = document.createElement(\"span\");\n        lblFrom.className = \"cr-field-label\";\n        lblFrom.textContent = \"From\";\n        controls.appendChild(lblFrom);\n\n        const selFrom = document.createElement(\"select\");\n        selFrom.className = \"cr-select\";\n        for (let v = f.min; v \u003c= f.max; v++) {\n          const o = document.createElement(\"option\");\n          o.value = v;\n          o.textContent = formatFieldVal(f.id, v);\n          if (String(v) === s.rangeFrom) o.selected = true;\n          selFrom.appendChild(o);\n        }\n        selFrom.addEventListener(\"change\", () =\u003e {\n          state[f.id].rangeFrom = selFrom.value;\n          updateAll();\n        });\n        controls.appendChild(selFrom);\n\n        const lblTo = document.createElement(\"span\");\n        lblTo.className = \"cr-field-label\";\n        lblTo.textContent = \"to\";\n        controls.appendChild(lblTo);\n\n        const selTo = document.createElement(\"select\");\n        selTo.className = \"cr-select\";\n        for (let v = f.min; v \u003c= f.max; v++) {\n          const o = document.createElement(\"option\");\n          o.value = v;\n          o.textContent = formatFieldVal(f.id, v);\n          if (String(v) === s.rangeTo) o.selected = true;\n          selTo.appendChild(o);\n        }\n        selTo.addEventListener(\"change\", () =\u003e {\n          state[f.id].rangeTo = selTo.value;\n          updateAll();\n        });\n        controls.appendChild(selTo);\n      }\n\n      if (s.mode === \"interval\") {\n        const lbl = document.createElement(\"span\");\n        lbl.className = \"cr-field-label\";\n        lbl.textContent = \"Every\";\n        controls.appendChild(lbl);\n\n        const inp = document.createElement(\"input\");\n        inp.className = \"cr-input\";\n        inp.type = \"number\";\n        inp.min = 1;\n        inp.max = f.max;\n        inp.value = s.interval;\n        inp.addEventListener(\"input\", () =\u003e {\n          state[f.id].interval = inp.value;\n          updateAll();\n        });\n        controls.appendChild(inp);\n\n        const lbl2 = document.createElement(\"span\");\n        lbl2.className = \"cr-field-label\";\n        lbl2.textContent = f.label.toLowerCase() + \"(s)\";\n        controls.appendChild(lbl2);\n      }\n\n      if (s.mode === \"list\") {\n        const lbl = document.createElement(\"span\");\n        lbl.className = \"cr-field-label\";\n        lbl.textContent = \"Values (comma-separated):\";\n        controls.appendChild(lbl);\n\n        const inp = document.createElement(\"input\");\n        inp.className = \"cr-input cr-input-wide\";\n        inp.type = \"text\";\n        inp.value = s.list;\n        inp.placeholder = f.min + \",\" + (f.min+1);\n        inp.addEventListener(\"input\", () =\u003e {\n          state[f.id].list = inp.value;\n          updateAll();\n        });\n        controls.appendChild(inp);\n      }\n\n      card.appendChild(controls);\n\n      // Preview\n      const preview = document.createElement(\"div\");\n      preview.className = \"cr-field-preview\";\n      preview.textContent = fieldValue(f.id);\n      preview.id = \"cr-preview-\" + f.id;\n      card.appendChild(preview);\n\n      grid.appendChild(card);\n    });\n  }\n\n  function formatFieldVal(fid, v) {\n    if (fid === \"month\") return v + \" – \" + MONTH_NAMES[v-1];\n    if (fid === \"dow\")   return v + \" – \" + DOW_NAMES[v];\n    return String(v);\n  }\n\n  function updateAll() {\n    const expr = buildExpr();\n    const input = document.getElementById(\"cr-expr-input\");\n    input.value = expr;\n    refreshOutput(expr);\n    updatePreviews();\n  }\n\n  function updatePreviews() {\n    FIELDS.forEach(f =\u003e {\n      const el = document.getElementById(\"cr-preview-\" + f.id);\n      if (el) el.textContent = fieldValue(f.id);\n    });\n  }\n\n  function refreshOutput(expr) {\n    const errEl  = document.getElementById(\"cr-error\");\n    const descEl = document.getElementById(\"cr-description\");\n    const input  = document.getElementById(\"cr-expr-input\");\n    const runsList = document.getElementById(\"cr-runs-list\");\n\n    const err = validateExpr(expr);\n    if (err) {\n      errEl.textContent = err;\n      errEl.classList.add(\"cr-visible\");\n      input.classList.add(\"cr-invalid\");\n      descEl.textContent = \"—\";\n      runsList.innerHTML = \"\";\n      return;\n    }\n\n    errEl.classList.remove(\"cr-visible\");\n    input.classList.remove(\"cr-invalid\");\n    descEl.textContent = describe(expr);\n\n    // Next runs\n    const runs = nextRuns(expr, 10);\n    runsList.innerHTML = \"\";\n    runs.forEach((d, i) =\u003e {\n      const li = document.createElement(\"li\");\n      li.className = \"cr-run-item\";\n      li.innerHTML =\n        '\u003cspan class=\"cr-run-index\"\u003e' + (i+1) + '\u003c/span\u003e' +\n        '\u003cspan class=\"cr-run-time\"\u003e' + formatDate(d) + '\u003c/span\u003e' +\n        '\u003cspan class=\"cr-run-rel\"\u003e' + relTime(d) + '\u003c/span\u003e';\n      runsList.appendChild(li);\n    });\n\n    if (runs.length === 0) {\n      const li = document.createElement(\"li\");\n      li.className = \"cr-run-item\";\n      li.innerHTML = '\u003cspan style=\"color:#64748b;\"\u003eCould not compute next runs for this expression.\u003c/span\u003e';\n      runsList.appendChild(li);\n    }\n  }\n\n  function onExprInput(expr) {\n    const ok = parseExpr(expr);\n    if (ok) {\n      renderFields();\n    }\n    refreshOutput(expr);\n  }\n\n  /* ── Copy button ── */\n  function initCopy() {\n    const btn = document.getElementById(\"cr-copy-btn\");\n    btn.addEventListener(\"click\", () =\u003e {\n      const expr = document.getElementById(\"cr-expr-input\").value;\n      navigator.clipboard.writeText(expr).then(() =\u003e {\n        btn.textContent = \"Copied!\";\n        btn.classList.add(\"cr-copied\");\n        setTimeout(() =\u003e {\n          btn.textContent = \"Copy\";\n          btn.classList.remove(\"cr-copied\");\n        }, 1500);\n      }).catch(() =\u003e {\n        // Fallback\n        const ta = document.createElement(\"textarea\");\n        ta.value = expr;\n        ta.style.position = \"fixed\";\n        ta.style.opacity = \"0\";\n        document.body.appendChild(ta);\n        ta.select();\n        document.execCommand(\"copy\");\n        document.body.removeChild(ta);\n        btn.textContent = \"Copied!\";\n        btn.classList.add(\"cr-copied\");\n        setTimeout(() =\u003e {\n          btn.textContent = \"Copy\";\n          btn.classList.remove(\"cr-copied\");\n        }, 1500);\n      });\n    });\n  }\n\n  /* ── Expression input (bidirectional) ── */\n  function initExprInput() {\n    const input = document.getElementById(\"cr-expr-input\");\n    input.addEventListener(\"input\", () =\u003e {\n      onExprInput(input.value);\n    });\n    input.addEventListener(\"keydown\", e =\u003e {\n      if (e.key === \"Enter\") onExprInput(input.value);\n    });\n  }\n\n  /* ── Init ── */\n  function init() {\n    renderPresets();\n    renderFields();\n    updateAll();\n    initCopy();\n    initExprInput();\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\"DOMContentLoaded\", init);\n  } else {\n    init();\n  }\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003ch2 id=\"related-articles\"\u003eRelated Articles\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://productivity-works.com/posts/how-to-automate-tasks-with-ai-step-by-step/\"\u003eHow to Automate Tasks with AI Step by Step\u003c/a\u003e\n\u003c/li\u003e\n\u003c/ul\u003e","title":"Cron Expression Builder"},{"content":" Cron Expression Generator Common Presets Every minute Every hour Daily at midnight Daily at 9 AM Weekly (Mon midnight) Weekly (Mon 9 AM) Monthly 1st Yearly (Jan 1st) Every 5 min Every 15 min Every 30 min Hourly (9–5 weekdays) Manual Entry / Parse Parse Visual Builder Minute Every minute (*) Every N minutes Specific minute(s) Range Hour Every hour (*) Every N hours Specific hour(s) Range Day of Month Every day (*) Specific day(s) Range Month Every month (*) Specific month(s) Range Weekday Any day (*) Specific day(s) Range * * * * * Copied! Copy Runs every minute. Next 5 Execution Times Related: Format code with JSON Formatter ","permalink":"https://productivity-works.com/tools/cron-generator/","summary":"\u003cstyle\u003e\n#cron-app *,\n#cron-app *::before,\n#cron-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\n#cron-app {\n  font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n  background: #0f172a;\n  color: #e2e8f0;\n  border-radius: 12px;\n  padding: 28px;\n  max-width: 860px;\n  margin: 0 auto;\n}\n\n#cron-app h2 {\n  font-family: ui-sans-serif, system-ui, sans-serif;\n  font-size: 1.4rem;\n  font-weight: 700;\n  color: #84cc16;\n  margin-bottom: 20px;\n  letter-spacing: -0.02em;\n}\n\n#cron-app .ca-section-label {\n  font-family: ui-sans-serif, system-ui, sans-serif;\n  font-size: 0.7rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.1em;\n  color: #64748b;\n  margin-bottom: 10px;\n}\n\n/* Presets */\n#cron-app .ca-presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 24px;\n}\n\n#cron-app .ca-preset-btn {\n  background: #1e293b;\n  border: 1px solid #334155;\n  color: #94a3b8;\n  font-size: 0.75rem;\n  font-family: inherit;\n  padding: 6px 12px;\n  border-radius: 6px;\n  cursor: pointer;\n  transition: all 0.15s;\n  white-space: nowrap;\n}\n\n#cron-app .ca-preset-btn:hover {\n  background: #253347;\n  border-color: #84cc16;\n  color: #84cc16;\n}\n\n/* Manual input */\n#cron-app .ca-manual-row {\n  display: flex;\n  gap: 10px;\n  margin-bottom: 24px;\n  align-items: center;\n}\n\n#cron-app .ca-manual-input {\n  flex: 1;\n  background: #1e293b;\n  border: 1px solid #334155;\n  color: #f1f5f9;\n  font-family: inherit;\n  font-size: 1rem;\n  padding: 10px 14px;\n  border-radius: 8px;\n  outline: none;\n  transition: border-color 0.15s;\n}\n\n#cron-app .ca-manual-input:focus {\n  border-color: #84cc16;\n}\n\n#cron-app .ca-parse-btn,\n#cron-app .ca-copy-btn {\n  background: #84cc16;\n  border: none;\n  color: #0f172a;\n  font-family: ui-sans-serif, system-ui, sans-serif;\n  font-weight: 700;\n  font-size: 0.8rem;\n  padding: 10px 16px;\n  border-radius: 8px;\n  cursor: pointer;\n  transition: background 0.15s;\n  white-space: nowrap;\n}\n\n#cron-app .ca-parse-btn:hover,\n#cron-app .ca-copy-btn:hover {\n  background: #a3e635;\n}\n\n/* Builder grid */\n#cron-app .ca-builder {\n  display: grid;\n  grid-template-columns: repeat(5, 1fr);\n  gap: 12px;\n  margin-bottom: 24px;\n}\n\n@media (max-width: 600px) {\n  #cron-app .ca-builder {\n    grid-template-columns: repeat(2, 1fr);\n  }\n}\n\n#cron-app .ca-field {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 10px;\n  padding: 14px 12px;\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n}\n\n#cron-app .ca-field-label {\n  font-family: ui-sans-serif, system-ui, sans-serif;\n  font-size: 0.65rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.1em;\n  color: #84cc16;\n}\n\n#cron-app .ca-field select,\n#cron-app .ca-field input[type=\"text\"] {\n  background: #0f172a;\n  border: 1px solid #334155;\n  color: #f1f5f9;\n  font-family: inherit;\n  font-size: 0.8rem;\n  padding: 6px 8px;\n  border-radius: 6px;\n  outline: none;\n  width: 100%;\n  transition: border-color 0.15s;\n  cursor: pointer;\n}\n\n#cron-app .ca-field select:focus,\n#cron-app .ca-field input[type=\"text\"]:focus {\n  border-color: #84cc16;\n}\n\n#cron-app .ca-field input[type=\"text\"] {\n  cursor: text;\n}\n\n#cron-app .ca-field .ca-field-hint {\n  font-size: 0.65rem;\n  color: #475569;\n  font-family: ui-sans-serif, system-ui, sans-serif;\n}\n\n/* Result */\n#cron-app .ca-result-box {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 10px;\n  padding: 18px 20px;\n  margin-bottom: 16px;\n}\n\n#cron-app .ca-expr-row {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 12px;\n  margin-bottom: 12px;\n}\n\n#cron-app .ca-expr-display {\n  font-size: 1.35rem;\n  font-weight: 700;\n  color: #84cc16;\n  letter-spacing: 0.08em;\n  word-break: break-all;\n}\n\n#cron-app .ca-description {\n  font-family: ui-sans-serif, system-ui, sans-serif;\n  font-size: 0.9rem;\n  color: #cbd5e1;\n  line-height: 1.5;\n  padding-top: 10px;\n  border-top: 1px solid #334155;\n}\n\n#cron-app .ca-error {\n  color: #f87171;\n  font-family: ui-sans-serif, system-ui, sans-serif;\n  font-size: 0.85rem;\n  padding-top: 10px;\n  border-top: 1px solid #334155;\n}\n\n/* Next runs */\n#cron-app .ca-next-box {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 10px;\n  padding: 16px 20px;\n}\n\n#cron-app .ca-next-list {\n  list-style: none;\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n  margin-top: 10px;\n}\n\n#cron-app .ca-next-list li {\n  font-size: 0.82rem;\n  color: #94a3b8;\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n\n#cron-app .ca-next-list li::before {\n  content: '';\n  display: inline-block;\n  width: 6px;\n  height: 6px;\n  border-radius: 50%;\n  background: #84cc16;\n  flex-shrink: 0;\n}\n\n#cron-app .ca-copy-feedback {\n  font-size: 0.72rem;\n  color: #84cc16;\n  font-family: ui-sans-serif, system-ui, sans-serif;\n  opacity: 0;\n  transition: opacity 0.3s;\n  white-space: nowrap;\n}\n\n#cron-app .ca-copy-feedback.visible {\n  opacity: 1;\n}\n\n#cron-app .ca-related {\n  margin-top: 20px;\n  padding-top: 16px;\n  border-top: 1px solid #1e293b;\n  font-family: ui-sans-serif, system-ui, sans-serif;\n  font-size: 0.82rem;\n  color: #64748b;\n}\n\n#cron-app .ca-related a {\n  color: #84cc16;\n  text-decoration: none;\n}\n\n#cron-app .ca-related a:hover {\n  text-decoration: underline;\n}\n\u003c/style\u003e\n\u003cdiv id=\"cron-app\"\u003e\n\u003ch2\u003eCron Expression Generator\u003c/h2\u003e\n\u003cdiv class=\"ca-section-label\"\u003eCommon Presets\u003c/div\u003e\n\u003cdiv class=\"ca-presets\"\u003e\n  \u003cbutton class=\"ca-preset-btn\" data-expr=\"* * * * *\"\u003eEvery minute\u003c/button\u003e\n  \u003cbutton class=\"ca-preset-btn\" data-expr=\"0 * * * *\"\u003eEvery hour\u003c/button\u003e\n  \u003cbutton class=\"ca-preset-btn\" data-expr=\"0 0 * * *\"\u003eDaily at midnight\u003c/button\u003e\n  \u003cbutton class=\"ca-preset-btn\" data-expr=\"0 9 * * *\"\u003eDaily at 9 AM\u003c/button\u003e\n  \u003cbutton class=\"ca-preset-btn\" data-expr=\"0 0 * * 1\"\u003eWeekly (Mon midnight)\u003c/button\u003e\n  \u003cbutton class=\"ca-preset-btn\" data-expr=\"0 9 * * 1\"\u003eWeekly (Mon 9 AM)\u003c/button\u003e\n  \u003cbutton class=\"ca-preset-btn\" data-expr=\"0 0 1 * *\"\u003eMonthly 1st\u003c/button\u003e\n  \u003cbutton class=\"ca-preset-btn\" data-expr=\"0 0 1 1 *\"\u003eYearly (Jan 1st)\u003c/button\u003e\n  \u003cbutton class=\"ca-preset-btn\" data-expr=\"*/5 * * * *\"\u003eEvery 5 min\u003c/button\u003e\n  \u003cbutton class=\"ca-preset-btn\" data-expr=\"*/15 * * * *\"\u003eEvery 15 min\u003c/button\u003e\n  \u003cbutton class=\"ca-preset-btn\" data-expr=\"*/30 * * * *\"\u003eEvery 30 min\u003c/button\u003e\n  \u003cbutton class=\"ca-preset-btn\" data-expr=\"0 9-17 * * 1-5\"\u003eHourly (9–5 weekdays)\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ca-section-label\"\u003eManual Entry / Parse\u003c/div\u003e\n\u003cdiv class=\"ca-manual-row\"\u003e\n  \u003cinput class=\"ca-manual-input\" id=\"ca-manual\" type=\"text\" placeholder=\"e.g.  */5 * * * *\" spellcheck=\"false\" /\u003e\n  \u003cbutton class=\"ca-parse-btn\" id=\"ca-parse-btn\"\u003eParse\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ca-section-label\"\u003eVisual Builder\u003c/div\u003e\n\u003cdiv class=\"ca-builder\"\u003e\n  \u003cdiv class=\"ca-field\"\u003e\n    \u003cdiv class=\"ca-field-label\"\u003eMinute\u003c/div\u003e\n    \u003cselect id=\"ca-min-mode\"\u003e\n      \u003coption value=\"any\"\u003eEvery minute (*)\u003c/option\u003e\n      \u003coption value=\"every\"\u003eEvery N minutes\u003c/option\u003e\n      \u003coption value=\"specific\"\u003eSpecific minute(s)\u003c/option\u003e\n      \u003coption value=\"range\"\u003eRange\u003c/option\u003e\n    \u003c/select\u003e\n    \u003cinput type=\"text\" id=\"ca-min-val\" placeholder=\"e.g. 0,30\" style=\"display:none\" /\u003e\n    \u003cdiv class=\"ca-field-hint\" id=\"ca-min-hint\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ca-field\"\u003e\n    \u003cdiv class=\"ca-field-label\"\u003eHour\u003c/div\u003e\n    \u003cselect id=\"ca-hr-mode\"\u003e\n      \u003coption value=\"any\"\u003eEvery hour (*)\u003c/option\u003e\n      \u003coption value=\"every\"\u003eEvery N hours\u003c/option\u003e\n      \u003coption value=\"specific\"\u003eSpecific hour(s)\u003c/option\u003e\n      \u003coption value=\"range\"\u003eRange\u003c/option\u003e\n    \u003c/select\u003e\n    \u003cinput type=\"text\" id=\"ca-hr-val\" placeholder=\"e.g. 9,17\" style=\"display:none\" /\u003e\n    \u003cdiv class=\"ca-field-hint\" id=\"ca-hr-hint\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ca-field\"\u003e\n    \u003cdiv class=\"ca-field-label\"\u003eDay of Month\u003c/div\u003e\n    \u003cselect id=\"ca-dom-mode\"\u003e\n      \u003coption value=\"any\"\u003eEvery day (*)\u003c/option\u003e\n      \u003coption value=\"specific\"\u003eSpecific day(s)\u003c/option\u003e\n      \u003coption value=\"range\"\u003eRange\u003c/option\u003e\n    \u003c/select\u003e\n    \u003cinput type=\"text\" id=\"ca-dom-val\" placeholder=\"e.g. 1,15\" style=\"display:none\" /\u003e\n    \u003cdiv class=\"ca-field-hint\" id=\"ca-dom-hint\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ca-field\"\u003e\n    \u003cdiv class=\"ca-field-label\"\u003eMonth\u003c/div\u003e\n    \u003cselect id=\"ca-mon-mode\"\u003e\n      \u003coption value=\"any\"\u003eEvery month (*)\u003c/option\u003e\n      \u003coption value=\"specific\"\u003eSpecific month(s)\u003c/option\u003e\n      \u003coption value=\"range\"\u003eRange\u003c/option\u003e\n    \u003c/select\u003e\n    \u003cinput type=\"text\" id=\"ca-mon-val\" placeholder=\"e.g. 1,6,12\" style=\"display:none\" /\u003e\n    \u003cdiv class=\"ca-field-hint\" id=\"ca-mon-hint\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ca-field\"\u003e\n    \u003cdiv class=\"ca-field-label\"\u003eWeekday\u003c/div\u003e\n    \u003cselect id=\"ca-dow-mode\"\u003e\n      \u003coption value=\"any\"\u003eAny day (*)\u003c/option\u003e\n      \u003coption value=\"specific\"\u003eSpecific day(s)\u003c/option\u003e\n      \u003coption value=\"range\"\u003eRange\u003c/option\u003e\n    \u003c/select\u003e\n    \u003cinput type=\"text\" id=\"ca-dow-val\" placeholder=\"0=Sun … 6=Sat\" style=\"display:none\" /\u003e\n    \u003cdiv class=\"ca-field-hint\" id=\"ca-dow-hint\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ca-result-box\"\u003e\n  \u003cdiv class=\"ca-expr-row\"\u003e\n    \u003cdiv class=\"ca-expr-display\" id=\"ca-expr-display\"\u003e* * * * *\u003c/div\u003e\n    \u003cdiv style=\"display:flex;align-items:center;gap:10px\"\u003e\n      \u003cspan class=\"ca-copy-feedback\" id=\"ca-copy-feedback\"\u003eCopied!\u003c/span\u003e\n      \u003cbutton class=\"ca-copy-btn\" id=\"ca-copy-btn\"\u003eCopy\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ca-description\" id=\"ca-description\"\u003eRuns every minute.\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ca-next-box\"\u003e\n  \u003cdiv class=\"ca-section-label\"\u003eNext 5 Execution Times\u003c/div\u003e\n  \u003cul class=\"ca-next-list\" id=\"ca-next-list\"\u003e\u003c/ul\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ca-related\"\u003e\n  Related: Format code with \u003ca href=\"/tools/json-formatter/\"\u003eJSON Formatter\u003c/a\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  'use strict';\n\n  // ── DOM refs ─────────────────────────────────────────────────────────────\n  const manual      = document.getElementById('ca-manual');\n  const parseBtn    = document.getElementById('ca-parse-btn');\n  const exprDisplay = document.getElementById('ca-expr-display');\n  const descEl      = document.getElementById('ca-description');\n  const nextList    = document.getElementById('ca-next-list');\n  const copyBtn     = document.getElementById('ca-copy-btn');\n  const copyFb      = document.getElementById('ca-copy-feedback');\n\n  const fields = ['min', 'hr', 'dom', 'mon', 'dow'];\n  const modes  = {};\n  const vals   = {};\n  const hints  = {};\n\n  fields.forEach(f =\u003e {\n    modes[f] = document.getElementById('ca-' + f + '-mode');\n    vals[f]  = document.getElementById('ca-' + f + '-val');\n    hints[f] = document.getElementById('ca-' + f + '-hint');\n  });\n\n  // ── Mode → show/hide value input ────────────────────────────────────────\n  function updateFieldUI(f) {\n    const m = modes[f].value;\n    const needVal = m !== 'any';\n    vals[f].style.display = needVal ? 'block' : 'none';\n\n    const hintMap = {\n      any:      '',\n      every:    { min: 'N (1–59)', hr: 'N (1–23)', dom: '', mon: '', dow: '' },\n      specific: { min: 'e.g. 0,15,30,45', hr: 'e.g. 9,12,18', dom: 'e.g. 1,15', mon: 'e.g. 1,6,12', dow: '0=Sun 1=Mon … 6=Sat' },\n      range:    { min: 'start-end e.g. 0-29', hr: 'e.g. 9-17', dom: 'e.g. 1-15', mon: 'e.g. 1-6', dow: 'e.g. 1-5' },\n    };\n    hints[f].textContent = (m === 'any') ? '' : (hintMap[m][f] || '');\n    if (m !== 'any' \u0026\u0026 !vals[f].value) {\n      const defaults = {\n        every:    { min: '5', hr: '2', dom: '', mon: '', dow: '' },\n        specific: { min: '0', hr: '9', dom: '1', mon: '1', dow: '1' },\n        range:    { min: '0-29', hr: '9-17', dom: '1-15', mon: '1-6', dow: '1-5' },\n      };\n      vals[f].value = defaults[m] ? (defaults[m][f] || '') : '';\n    }\n    rebuild();\n  }\n\n  fields.forEach(f =\u003e {\n    modes[f].addEventListener('change', () =\u003e updateFieldUI(f));\n    vals[f].addEventListener('input', rebuild);\n  });\n\n  // ── Build expression from UI ─────────────────────────────────────────────\n  function fieldExpr(f) {\n    const m = modes[f].value;\n    const v = vals[f].value.trim();\n    if (m === 'any') return '*';\n    if (m === 'every') {\n      const n = parseInt(v, 10);\n      if (!v || isNaN(n) || n \u003c 1) return '*';\n      return '*/' + n;\n    }\n    if (m === 'specific' || m === 'range') {\n      return v || '*';\n    }\n    return '*';\n  }\n\n  function rebuild() {\n    const parts = fields.map(f =\u003e fieldExpr(f));\n    const expr = parts.join(' ');\n    setExpression(expr);\n  }\n\n  // ── Parse \u0026 describe ─────────────────────────────────────────────────────\n  function setExpression(expr) {\n    expr = expr.trim();\n    exprDisplay.textContent = expr;\n    manual.value = expr;\n\n    const result = parseExpr(expr);\n    if (result.error) {\n      descEl.className = 'ca-error';\n      descEl.textContent = result.error;\n      nextList.innerHTML = '';\n    } else {\n      descEl.className = 'ca-description';\n      descEl.textContent = result.description;\n      renderNext(expr);\n    }\n  }\n\n  function parseExpr(expr) {\n    const parts = expr.trim().split(/\\s+/);\n    if (parts.length !== 5) return { error: 'Cron expression must have exactly 5 fields: minute hour day month weekday' };\n\n    const [min, hr, dom, mon, dow] = parts;\n\n    // Basic validation\n    const ranges = [\n      [min, 0, 59, 'minute'],\n      [hr,  0, 23, 'hour'],\n      [dom, 1, 31, 'day-of-month'],\n      [mon, 1, 12, 'month'],\n      [dow, 0,  7, 'weekday'],\n    ];\n    for (const [f, lo, hi, name] of ranges) {\n      const err = validateField(f, lo, hi);\n      if (err) return { error: 'Invalid ' + name + ': ' + err };\n    }\n\n    return { description: describe(min, hr, dom, mon, dow) };\n  }\n\n  function validateField(f, lo, hi) {\n    if (f === '*') return null;\n    if (/^\\*\\/\\d+$/.test(f)) {\n      const n = parseInt(f.slice(2), 10);\n      if (n \u003c 1) return 'step must be \u003e= 1';\n      return null;\n    }\n    const parts = f.split(',');\n    for (const p of parts) {\n      if (p.includes('-')) {\n        const [a, b] = p.split('-').map(Number);\n        if (isNaN(a) || isNaN(b) || a \u003c lo || b \u003e hi || a \u003e b)\n          return '\"' + p + '\" is not a valid range (' + lo + '-' + hi + ')';\n      } else {\n        const n = Number(p);\n        if (isNaN(n) || n \u003c lo || n \u003e hi)\n          return '\"' + p + '\" out of range (' + lo + '-' + hi + ')';\n      }\n    }\n    return null;\n  }\n\n  // ── Human-readable description ───────────────────────────────────────────\n  const MONTHS = ['','January','February','March','April','May','June','July','August','September','October','November','December'];\n  const DAYS   = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'];\n\n  function fmtField(f, type) {\n    if (f === '*') return null; // \"any\"\n    if (f.startsWith('*/')) {\n      const n = f.slice(2);\n      const label = { min: 'minute', hr: 'hour', dom: 'day', mon: 'month', dow: 'day' }[type];\n      return 'every ' + n + ' ' + label + (n === '1' ? '' : 's');\n    }\n    // specific / range / list\n    const parts = f.split(',');\n    const fmted = parts.map(p =\u003e {\n      if (p.includes('-')) {\n        const [a, b] = p.split('-');\n        if (type === 'mon') return MONTHS[+a] + ' through ' + MONTHS[+b];\n        if (type === 'dow') return DAYS[+a] + ' through ' + DAYS[+b];\n        return p;\n      }\n      if (type === 'mon') return MONTHS[+p] || p;\n      if (type === 'dow') return DAYS[+p] || p;\n      return p;\n    });\n    return fmted.join(', ');\n  }\n\n  function ordinal(n) {\n    const s = ['th','st','nd','rd'];\n    const v = n % 100;\n    return n + (s[(v-20)%10] || s[v] || s[0]);\n  }\n\n  function describe(min, hr, dom, mon, dow) {\n    // Time description\n    let time = '';\n    if (min === '*' \u0026\u0026 hr === '*') {\n      time = 'every minute';\n    } else if (min.startsWith('*/') \u0026\u0026 hr === '*') {\n      time = 'every ' + min.slice(2) + ' minutes';\n    } else if (hr.startsWith('*/') \u0026\u0026 min === '0') {\n      time = 'every ' + hr.slice(2) + ' hours (at minute 0)';\n    } else if (hr.startsWith('*/')) {\n      time = 'every ' + hr.slice(2) + ' hours at minute ' + min;\n    } else {\n      // specific times\n      const hrParts = hr.split(',');\n      const minParts = min === '*' ? ['every minute'] : min.split(',').map(m =\u003e m.padStart(2,'0'));\n      const times = hrParts.map(h =\u003e {\n        if (h.includes('-')) return 'hours ' + h + ':' + (min === '*' ? 'xx' : min.padStart(2,'0'));\n        return h.padStart(2,'0') + ':' + (min === '*' ? 'xx' : (min.includes(',') ? '[' + min + ']' : min.padStart(2,'0')));\n      });\n      time = 'at ' + times.join(', ');\n    }\n\n    // Day/Month description\n    const domStr = fmtField(dom, 'dom');\n    const monStr = fmtField(mon, 'mon');\n    const dowStr = fmtField(dow, 'dow');\n\n    let schedule = 'Runs ' + time;\n\n    if (dowStr) {\n      schedule += ' on ' + dowStr;\n    } else if (domStr) {\n      // check if all specific days\n      const domParts = dom.split(',');\n      const ordinals = domParts.map(p =\u003e {\n        if (p.includes('-')) return 'days ' + p;\n        return ordinal(+p);\n      });\n      schedule += ' on the ' + ordinals.join(', ') + ' of the month';\n    }\n\n    if (monStr) {\n      schedule += ' in ' + monStr;\n    }\n\n    schedule += '.';\n    return schedule;\n  }\n\n  // ── Next 5 execution times ────────────────────────────────────────────────\n  function renderNext(expr) {\n    const parts = expr.trim().split(/\\s+/);\n    if (parts.length !== 5) { nextList.innerHTML = ''; return; }\n    const [min, hr, dom, mon, dow] = parts;\n\n    const times = nextRuns(min, hr, dom, mon, dow, 5);\n    nextList.innerHTML = times.map(d =\u003e '\u003cli\u003e' + formatDate(d) + '\u003c/li\u003e').join('');\n  }\n\n  function expandField(f, lo, hi) {\n    if (f === '*') {\n      const r = [];\n      for (let i = lo; i \u003c= hi; i++) r.push(i);\n      return r;\n    }\n    if (f.startsWith('*/')) {\n      const step = parseInt(f.slice(2), 10);\n      const r = [];\n      for (let i = lo; i \u003c= hi; i += step) r.push(i);\n      return r;\n    }\n    const result = new Set();\n    f.split(',').forEach(p =\u003e {\n      if (p.includes('-')) {\n        const [a, b] = p.split('-').map(Number);\n        for (let i = a; i \u003c= b; i++) result.add(i);\n      } else {\n        result.add(Number(p));\n      }\n    });\n    return [...result].sort((a,b) =\u003e a-b);\n  }\n\n  function nextRuns(minF, hrF, domF, monF, dowF, count) {\n    const mins = expandField(minF, 0, 59);\n    const hrs  = expandField(hrF,  0, 23);\n    const doms = expandField(domF, 1, 31);\n    const mons = expandField(monF, 1, 12);\n    const dows = dowF === '*' ? null : expandField(dowF.replace('7','0'), 0, 6);\n\n    const results = [];\n    const now = new Date();\n    // Start from next minute\n    const start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes() + 1, 0, 0);\n\n    let d = new Date(start);\n    const limit = new Date(start.getTime() + 366 * 24 * 60 * 60 * 1000); // max 1 year ahead\n\n    while (results.length \u003c count \u0026\u0026 d \u003c limit) {\n      const m  = d.getMonth() + 1; // 1-based\n      const dy = d.getDate();\n      const h  = d.getHours();\n      const mn = d.getMinutes();\n      const wd = d.getDay();\n\n      if (!mons.includes(m)) {\n        // advance to 1st of next valid month\n        const nextMon = mons.find(x =\u003e x \u003e m);\n        if (nextMon) {\n          d = new Date(d.getFullYear(), nextMon - 1, 1, 0, 0, 0, 0);\n        } else {\n          d = new Date(d.getFullYear() + 1, mons[0] - 1, 1, 0, 0, 0, 0);\n        }\n        continue;\n      }\n\n      const domOk = doms.includes(dy);\n      const dowOk = dows === null || dows.includes(wd);\n      if (!domOk \u0026\u0026 !dowOk) {\n        d = new Date(d.getFullYear(), d.getMonth(), d.getDate() + 1, 0, 0, 0, 0);\n        continue;\n      }\n\n      if (!hrs.includes(h)) {\n        const nextHr = hrs.find(x =\u003e x \u003e h);\n        if (nextHr !== undefined) {\n          d = new Date(d.getFullYear(), d.getMonth(), d.getDate(), nextHr, 0, 0, 0);\n        } else {\n          d = new Date(d.getFullYear(), d.getMonth(), d.getDate() + 1, 0, 0, 0, 0);\n        }\n        continue;\n      }\n\n      if (!mins.includes(mn)) {\n        const nextMin = mins.find(x =\u003e x \u003e mn);\n        if (nextMin !== undefined) {\n          d = new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), nextMin, 0, 0);\n        } else {\n          const nextHr = hrs.find(x =\u003e x \u003e h);\n          if (nextHr !== undefined) {\n            d = new Date(d.getFullYear(), d.getMonth(), d.getDate(), nextHr, 0, 0, 0);\n          } else {\n            d = new Date(d.getFullYear(), d.getMonth(), d.getDate() + 1, 0, 0, 0, 0);\n          }\n        }\n        continue;\n      }\n\n      results.push(new Date(d));\n      d = new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes() + 1, 0, 0);\n    }\n\n    return results;\n  }\n\n  function formatDate(d) {\n    const days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];\n    const pad = n =\u003e String(n).padStart(2, '0');\n    return days[d.getDay()] + ' ' +\n      d.getFullYear() + '-' + pad(d.getMonth()+1) + '-' + pad(d.getDate()) +\n      '  ' + pad(d.getHours()) + ':' + pad(d.getMinutes());\n  }\n\n  // ── Presets ──────────────────────────────────────────────────────────────\n  document.querySelectorAll('#cron-app .ca-preset-btn').forEach(btn =\u003e {\n    btn.addEventListener('click', () =\u003e {\n      const expr = btn.dataset.expr;\n      loadExprIntoBuilder(expr);\n      setExpression(expr);\n    });\n  });\n\n  function loadExprIntoBuilder(expr) {\n    const parts = expr.trim().split(/\\s+/);\n    if (parts.length !== 5) return;\n    const [minV, hrV, domV, monV, dowV] = parts;\n\n    function detectMode(f) {\n      if (f === '*') return 'any';\n      if (f.startsWith('*/')) return 'every';\n      if (f.includes('-')) return 'range';\n      return 'specific';\n    }\n\n    const fieldMap = [\n      ['min', minV],\n      ['hr',  hrV],\n      ['dom', domV],\n      ['mon', monV],\n      ['dow', dowV],\n    ];\n\n    fieldMap.forEach(([f, v]) =\u003e {\n      const m = detectMode(v);\n      modes[f].value = m;\n      if (m === 'any') {\n        vals[f].value = '';\n        vals[f].style.display = 'none';\n      } else if (m === 'every') {\n        vals[f].value = v.slice(2);\n        vals[f].style.display = 'block';\n      } else {\n        vals[f].value = v;\n        vals[f].style.display = 'block';\n      }\n      hints[f].textContent = '';\n    });\n  }\n\n  // ── Manual parse ─────────────────────────────────────────────────────────\n  parseBtn.addEventListener('click', () =\u003e {\n    const expr = manual.value.trim();\n    if (!expr) return;\n    loadExprIntoBuilder(expr);\n    setExpression(expr);\n  });\n\n  manual.addEventListener('keydown', e =\u003e {\n    if (e.key === 'Enter') parseBtn.click();\n  });\n\n  // ── Copy ─────────────────────────────────────────────────────────────────\n  copyBtn.addEventListener('click', () =\u003e {\n    const expr = exprDisplay.textContent;\n    navigator.clipboard.writeText(expr).then(() =\u003e {\n      copyFb.classList.add('visible');\n      setTimeout(() =\u003e copyFb.classList.remove('visible'), 1800);\n    }).catch(() =\u003e {\n      // fallback\n      const ta = document.createElement('textarea');\n      ta.value = expr;\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      copyFb.classList.add('visible');\n      setTimeout(() =\u003e copyFb.classList.remove('visible'), 1800);\n    });\n  });\n\n  // ── Init ─────────────────────────────────────────────────────────────────\n  setExpression('* * * * *');\n\n})();\n\u003c/script\u003e","title":"Cron Expression Generator"},{"content":" Build Your Cron Expression Every minute Every hour Daily at midnight Daily at 9 AM Weekly (Sun) Monthly (1st) Weekdays 9 AM Every 5 min Every 15 min Yearly (Jan 1) Minute Hour Day (Month) Month Day (Week) * * * * * Copy Description Every minute Next 5 Execution Times Validate an Existing Expression Validate Clear Build cron expressions → Cron Expression Builder ","permalink":"https://productivity-works.com/tools/crontab-generator/","summary":"\u003cdiv id=\"cg-app\"\u003e\n\u003cstyle\u003e\n#cg-app {\n  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;\n  background: #0f0f13;\n  color: #e2e8f0;\n  border-radius: 12px;\n  padding: 24px;\n  margin: 0 auto;\n  max-width: 900px;\n  box-sizing: border-box;\n}\n#cg-app * { box-sizing: border-box; }\n\n#cg-app h2 {\n  font-size: 1.05rem;\n  font-weight: 600;\n  color: #f1f5f9;\n  margin: 0 0 14px 0;\n  letter-spacing: 0.02em;\n}\n#cg-app h3 {\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #94a3b8;\n  margin: 0 0 12px 0;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n}\n\n/* ── Presets ── */\n.cg-presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 22px;\n}\n.cg-preset-btn {\n  padding: 6px 14px;\n  border-radius: 20px;\n  border: 1px solid #2d2d4d;\n  background: #1a1a24;\n  color: #a5b4fc;\n  font-size: 0.82rem;\n  font-weight: 600;\n  cursor: pointer;\n  font-family: inherit;\n  transition: background 0.15s, border-color 0.15s;\n}\n.cg-preset-btn:hover { background: #22224a; border-color: #6366f1; color: #c7d2fe; }\n.cg-preset-btn.active { background: #3730a3; border-color: #6366f1; color: #fff; }\n\n/* ── Field Builder ── */\n.cg-builder {\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 10px;\n  padding: 18px;\n  margin-bottom: 18px;\n}\n.cg-fields {\n  display: grid;\n  grid-template-columns: repeat(5, 1fr);\n  gap: 12px;\n}\n.cg-field-wrap { }\n.cg-field-label {\n  font-size: 0.72rem;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 6px;\n  display: block;\n}\n.cg-field-select {\n  width: 100%;\n  background: #0f0f18;\n  border: 1px solid #2d2d3d;\n  border-radius: 6px;\n  color: #e2e8f0;\n  font-size: 0.84rem;\n  padding: 7px 10px;\n  outline: none;\n  font-family: inherit;\n  cursor: pointer;\n  transition: border-color 0.2s;\n  appearance: none;\n  -webkit-appearance: none;\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2364748b' d='M6 8L1 3h10z'/%3E%3C/svg%3E\");\n  background-repeat: no-repeat;\n  background-position: right 8px center;\n  padding-right: 26px;\n}\n.cg-field-select:focus { border-color: #6366f1; }\n\n/* ── Expression Display ── */\n.cg-expr-wrap {\n  background: #12192e;\n  border: 1px solid #1e2a4a;\n  border-radius: 8px;\n  padding: 14px 16px;\n  margin-bottom: 16px;\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  flex-wrap: wrap;\n}\n.cg-expr-code {\n  font-family: 'Courier New', 'Consolas', monospace;\n  font-size: 1.4rem;\n  font-weight: 700;\n  color: #7dd3fc;\n  letter-spacing: 0.06em;\n  flex: 1;\n  min-width: 200px;\n}\n.cg-btn {\n  padding: 7px 16px;\n  border-radius: 6px;\n  border: none;\n  font-size: 0.84rem;\n  font-weight: 600;\n  cursor: pointer;\n  font-family: inherit;\n  transition: background 0.15s, transform 0.1s;\n  white-space: nowrap;\n}\n.cg-btn:active { transform: scale(0.97); }\n.cg-btn-copy { background: #1a1a24; color: #a5b4fc; border: 1px solid #2d2d4d; }\n.cg-btn-copy:hover { background: #22224a; }\n.cg-btn-primary { background: #6366f1; color: #fff; }\n.cg-btn-primary:hover { background: #4f46e5; }\n.cg-btn-danger { background: #1a1a24; color: #f87171; border: 1px solid #3d2020; }\n.cg-btn-danger:hover { background: #2d1a1a; }\n\n/* ── Description ── */\n.cg-desc-wrap {\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  padding: 14px 16px;\n  margin-bottom: 18px;\n}\n.cg-desc-text {\n  font-size: 0.95rem;\n  color: #c7d2fe;\n  font-weight: 500;\n  line-height: 1.5;\n}\n\n/* ── Next Run Times ── */\n.cg-runs-wrap {\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  padding: 14px 16px;\n  margin-bottom: 18px;\n}\n.cg-run-list {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\n.cg-run-item {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  padding: 6px 0;\n  border-bottom: 1px solid #1e1e2c;\n  font-size: 0.88rem;\n}\n.cg-run-item:last-child { border-bottom: none; }\n.cg-run-num {\n  font-size: 0.72rem;\n  color: #475569;\n  width: 20px;\n  text-align: right;\n  flex-shrink: 0;\n}\n.cg-run-time {\n  font-family: 'Courier New', monospace;\n  color: #7dd3fc;\n  font-weight: 600;\n}\n.cg-run-rel {\n  color: #64748b;\n  font-size: 0.78rem;\n}\n\n/* ── Validator ── */\n.cg-validate-wrap {\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  padding: 14px 16px;\n  margin-bottom: 10px;\n}\n.cg-validate-row {\n  display: flex;\n  gap: 8px;\n  align-items: center;\n  flex-wrap: wrap;\n}\n.cg-validate-input {\n  flex: 1;\n  min-width: 200px;\n  background: #0f0f18;\n  border: 1px solid #2d2d3d;\n  border-radius: 6px;\n  color: #e2e8f0;\n  font-size: 0.9rem;\n  font-family: 'Courier New', monospace;\n  padding: 8px 12px;\n  outline: none;\n  transition: border-color 0.2s;\n}\n.cg-validate-input:focus { border-color: #6366f1; }\n.cg-validate-input::placeholder { color: #3a3a5a; font-family: inherit; }\n.cg-validate-result {\n  margin-top: 10px;\n  font-size: 0.88rem;\n  min-height: 1.2em;\n  line-height: 1.5;\n}\n.cg-valid { color: #4ade80; }\n.cg-invalid { color: #f87171; }\n\n@media (max-width: 640px) {\n  #cg-app { padding: 16px 12px; }\n  .cg-fields { grid-template-columns: repeat(3, 1fr); }\n  .cg-expr-code { font-size: 1.1rem; }\n}\n@media (max-width: 400px) {\n  .cg-fields { grid-template-columns: repeat(2, 1fr); }\n}\n\u003c/style\u003e\n\u003ch2\u003eBuild Your Cron Expression\u003c/h2\u003e\n\u003c!-- Presets --\u003e\n\u003cdiv class=\"cg-presets\" id=\"cg-presets\"\u003e\n  \u003cbutton class=\"cg-preset-btn\" data-expr=\"* * * * *\" onclick=\"cgApplyPreset(this)\"\u003eEvery minute\u003c/button\u003e\n  \u003cbutton class=\"cg-preset-btn\" data-expr=\"0 * * * *\" onclick=\"cgApplyPreset(this)\"\u003eEvery hour\u003c/button\u003e\n  \u003cbutton class=\"cg-preset-btn\" data-expr=\"0 0 * * *\" onclick=\"cgApplyPreset(this)\"\u003eDaily at midnight\u003c/button\u003e\n  \u003cbutton class=\"cg-preset-btn\" data-expr=\"0 9 * * *\" onclick=\"cgApplyPreset(this)\"\u003eDaily at 9 AM\u003c/button\u003e\n  \u003cbutton class=\"cg-preset-btn\" data-expr=\"0 0 * * 0\" onclick=\"cgApplyPreset(this)\"\u003eWeekly (Sun)\u003c/button\u003e\n  \u003cbutton class=\"cg-preset-btn\" data-expr=\"0 0 1 * *\" onclick=\"cgApplyPreset(this)\"\u003eMonthly (1st)\u003c/button\u003e\n  \u003cbutton class=\"cg-preset-btn\" data-expr=\"0 9 * * 1-5\" onclick=\"cgApplyPreset(this)\"\u003eWeekdays 9 AM\u003c/button\u003e\n  \u003cbutton class=\"cg-preset-btn\" data-expr=\"*/5 * * * *\" onclick=\"cgApplyPreset(this)\"\u003eEvery 5 min\u003c/button\u003e\n  \u003cbutton class=\"cg-preset-btn\" data-expr=\"*/15 * * * *\" onclick=\"cgApplyPreset(this)\"\u003eEvery 15 min\u003c/button\u003e\n  \u003cbutton class=\"cg-preset-btn\" data-expr=\"0 0 1 1 *\" onclick=\"cgApplyPreset(this)\"\u003eYearly (Jan 1)\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Field Builder --\u003e\n\u003cdiv class=\"cg-builder\"\u003e\n  \u003cdiv class=\"cg-fields\" id=\"cg-fields\"\u003e\n    \u003c!-- Minute --\u003e\n    \u003cdiv class=\"cg-field-wrap\"\u003e\n      \u003clabel class=\"cg-field-label\" for=\"cg-min\"\u003eMinute\u003c/label\u003e\n      \u003cselect class=\"cg-field-select\" id=\"cg-min\" onchange=\"cgUpdate()\"\u003e\u003c/select\u003e\n    \u003c/div\u003e\n    \u003c!-- Hour --\u003e\n    \u003cdiv class=\"cg-field-wrap\"\u003e\n      \u003clabel class=\"cg-field-label\" for=\"cg-hr\"\u003eHour\u003c/label\u003e\n      \u003cselect class=\"cg-field-select\" id=\"cg-hr\" onchange=\"cgUpdate()\"\u003e\u003c/select\u003e\n    \u003c/div\u003e\n    \u003c!-- Day of Month --\u003e\n    \u003cdiv class=\"cg-field-wrap\"\u003e\n      \u003clabel class=\"cg-field-label\" for=\"cg-dom\"\u003eDay (Month)\u003c/label\u003e\n      \u003cselect class=\"cg-field-select\" id=\"cg-dom\" onchange=\"cgUpdate()\"\u003e\u003c/select\u003e\n    \u003c/div\u003e\n    \u003c!-- Month --\u003e\n    \u003cdiv class=\"cg-field-wrap\"\u003e\n      \u003clabel class=\"cg-field-label\" for=\"cg-mon\"\u003eMonth\u003c/label\u003e\n      \u003cselect class=\"cg-field-select\" id=\"cg-mon\" onchange=\"cgUpdate()\"\u003e\u003c/select\u003e\n    \u003c/div\u003e\n    \u003c!-- Day of Week --\u003e\n    \u003cdiv class=\"cg-field-wrap\"\u003e\n      \u003clabel class=\"cg-field-label\" for=\"cg-dow\"\u003eDay (Week)\u003c/label\u003e\n      \u003cselect class=\"cg-field-select\" id=\"cg-dow\" onchange=\"cgUpdate()\"\u003e\u003c/select\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Expression Output --\u003e\n\u003cdiv class=\"cg-expr-wrap\"\u003e\n  \u003cspan class=\"cg-expr-code\" id=\"cg-expr\"\u003e* * * * *\u003c/span\u003e\n  \u003cbutton class=\"cg-btn cg-btn-copy\" id=\"cg-copy-btn\" onclick=\"cgCopyExpr()\"\u003eCopy\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Human-readable description --\u003e\n\u003cdiv class=\"cg-desc-wrap\"\u003e\n  \u003ch3\u003eDescription\u003c/h3\u003e\n  \u003cdiv class=\"cg-desc-text\" id=\"cg-desc\"\u003eEvery minute\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Next 5 execution times --\u003e\n\u003cdiv class=\"cg-runs-wrap\"\u003e\n  \u003ch3\u003eNext 5 Execution Times\u003c/h3\u003e\n  \u003cul class=\"cg-run-list\" id=\"cg-run-list\"\u003e\u003c/ul\u003e\n\u003c/div\u003e\n\u003c!-- Validator --\u003e\n\u003cdiv class=\"cg-validate-wrap\"\u003e\n  \u003ch3\u003eValidate an Existing Expression\u003c/h3\u003e\n  \u003cdiv class=\"cg-validate-row\"\u003e\n    \u003cinput class=\"cg-validate-input\" id=\"cg-val-input\" placeholder=\"e.g. 0 9 * * 1-5\" /\u003e\n    \u003cbutton class=\"cg-btn cg-btn-primary\" onclick=\"cgValidate()\"\u003eValidate\u003c/button\u003e\n    \u003cbutton class=\"cg-btn cg-btn-danger\" onclick=\"cgClearValidate()\"\u003eClear\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cg-validate-result\" id=\"cg-val-result\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n\n  /* ── Field Definitions ── */\n  var FIELDS = {\n    min: {\n      el: null,\n      any: '*',\n      options: (function(){\n        var o = [{ v: '*', l: 'Every minute (*)' }];\n        for (var i = 0; i \u003c= 59; i++) o.push({ v: String(i), l: 'At minute ' + i });\n        o.push({ v: '*/2', l: 'Every 2 min (*/2)' });\n        o.push({ v: '*/5', l: 'Every 5 min (*/5)' });\n        o.push({ v: '*/10', l: 'Every 10 min (*/10)' });\n        o.push({ v: '*/15', l: 'Every 15 min (*/15)' });\n        o.push({ v: '*/30', l: 'Every 30 min (*/30)' });\n        return o;\n      })()\n    },\n    hr: {\n      el: null,\n      any: '*',\n      options: (function(){\n        var o = [{ v: '*', l: 'Every hour (*)' }];\n        for (var i = 0; i \u003c= 23; i++) {\n          var ampm = i === 0 ? '12 AM' : i \u003c 12 ? i + ' AM' : i === 12 ? '12 PM' : (i-12) + ' PM';\n          o.push({ v: String(i), l: 'At ' + ampm + ' (' + i + ')' });\n        }\n        o.push({ v: '*/2', l: 'Every 2 hrs (*/2)' });\n        o.push({ v: '*/3', l: 'Every 3 hrs (*/3)' });\n        o.push({ v: '*/6', l: 'Every 6 hrs (*/6)' });\n        o.push({ v: '*/12', l: 'Every 12 hrs (*/12)' });\n        return o;\n      })()\n    },\n    dom: {\n      el: null,\n      any: '*',\n      options: (function(){\n        var o = [{ v: '*', l: 'Every day (*)' }];\n        for (var i = 1; i \u003c= 31; i++) o.push({ v: String(i), l: 'On day ' + i });\n        o.push({ v: '*/2', l: 'Every 2 days' });\n        o.push({ v: '1,15', l: '1st and 15th' });\n        o.push({ v: 'L', l: 'Last day of month' });\n        return o;\n      })()\n    },\n    mon: {\n      el: null,\n      any: '*',\n      options: [\n        { v: '*', l: 'Every month (*)' },\n        { v: '1', l: 'January' }, { v: '2', l: 'February' }, { v: '3', l: 'March' },\n        { v: '4', l: 'April' }, { v: '5', l: 'May' }, { v: '6', l: 'June' },\n        { v: '7', l: 'July' }, { v: '8', l: 'August' }, { v: '9', l: 'September' },\n        { v: '10', l: 'October' }, { v: '11', l: 'November' }, { v: '12', l: 'December' },\n        { v: '1-3', l: 'Q1 (Jan–Mar)' }, { v: '4-6', l: 'Q2 (Apr–Jun)' },\n        { v: '7-9', l: 'Q3 (Jul–Sep)' }, { v: '10-12', l: 'Q4 (Oct–Dec)' }\n      ]\n    },\n    dow: {\n      el: null,\n      any: '*',\n      options: [\n        { v: '*', l: 'Every day of week (*)' },\n        { v: '0', l: 'Sunday (0)' }, { v: '1', l: 'Monday (1)' }, { v: '2', l: 'Tuesday (2)' },\n        { v: '3', l: 'Wednesday (3)' }, { v: '4', l: 'Thursday (4)' }, { v: '5', l: 'Friday (5)' },\n        { v: '6', l: 'Saturday (6)' },\n        { v: '1-5', l: 'Weekdays (Mon–Fri)' }, { v: '0,6', l: 'Weekends (Sat–Sun)' },\n        { v: '1,3,5', l: 'Mon, Wed, Fri' }, { v: '2,4', l: 'Tue, Thu' }\n      ]\n    }\n  };\n\n  var MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];\n  var DAYS_FULL = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];\n  var DAYS_SHORT = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];\n\n  /* Populate selects */\n  function buildSelects() {\n    ['min','hr','dom','mon','dow'].forEach(function(key) {\n      var sel = document.getElementById('cg-' + key);\n      FIELDS[key].el = sel;\n      FIELDS[key].options.forEach(function(opt) {\n        var o = document.createElement('option');\n        o.value = opt.v; o.textContent = opt.l;\n        sel.appendChild(o);\n      });\n    });\n  }\n\n  function getExpr() {\n    return ['min','hr','dom','mon','dow'].map(function(k){ return FIELDS[k].el.value; }).join(' ');\n  }\n\n  /* ── Description Generator ── */\n  function describe(expr) {\n    var parts = expr.trim().split(/\\s+/);\n    if (parts.length !== 5) return 'Invalid expression';\n    var min = parts[0], hr = parts[1], dom = parts[2], mon = parts[3], dow = parts[4];\n\n    // Common presets\n    if (expr === '* * * * *') return 'Every minute';\n    if (expr === '0 * * * *') return 'At the start of every hour';\n    if (expr === '0 0 * * *') return 'Every day at midnight (12:00 AM)';\n    if (expr === '0 0 * * 0') return 'Every Sunday at midnight';\n    if (expr === '0 0 1 * *') return 'On the 1st of every month at midnight';\n    if (expr === '0 0 1 1 *') return 'On January 1st at midnight (Yearly)';\n    if (expr === '0 9 * * 1-5') return 'Every weekday (Monday–Friday) at 9:00 AM';\n\n    var parts_desc = [];\n\n    // Minute\n    if (min === '*') parts_desc.push('every minute');\n    else if (min.startsWith('*/')) parts_desc.push('every ' + min.slice(2) + ' minutes');\n    else parts_desc.push('at minute ' + min);\n\n    // Hour\n    if (hr === '*') { /* omit \"every hour\" if minute already says something */ }\n    else if (hr.startsWith('*/')) parts_desc.push('every ' + hr.slice(2) + ' hours');\n    else {\n      var h = parseInt(hr);\n      var ampm = h === 0 ? '12:00 AM' : h \u003c 12 ? h + ':' + (min === '*' ? '00' : (min.length===1?'0':'')+min) + ' AM'\n        : h === 12 ? '12:' + (min === '*' ? '00' : (min.length===1?'0':'')+min) + ' PM'\n        : (h-12) + ':' + (min === '*' ? '00' : (min.length===1?'0':'')+min) + ' PM';\n      parts_desc = ['at ' + ampm];\n    }\n\n    // Day of month\n    if (dom !== '*' \u0026\u0026 dom !== '*/1') {\n      if (dom === 'L') parts_desc.push('on the last day of the month');\n      else if (dom.includes('-')) parts_desc.push('on days ' + dom + ' of the month');\n      else if (dom.includes(',')) parts_desc.push('on days ' + dom + ' of the month');\n      else if (dom.startsWith('*/')) parts_desc.push('every ' + dom.slice(2) + ' days');\n      else parts_desc.push('on day ' + dom + ' of the month');\n    }\n\n    // Month\n    if (mon !== '*') {\n      if (mon.includes('-')) {\n        var sp = mon.split('-');\n        parts_desc.push('in ' + (MONTHS[parseInt(sp[0])-1]||sp[0]) + '–' + (MONTHS[parseInt(sp[1])-1]||sp[1]));\n      } else if (mon.includes(',')) {\n        var ms = mon.split(',').map(function(m){ return MONTHS[parseInt(m)-1]||m; });\n        parts_desc.push('in ' + ms.join(', '));\n      } else {\n        parts_desc.push('in ' + (MONTHS[parseInt(mon)-1] || mon));\n      }\n    }\n\n    // Day of week\n    if (dow !== '*') {\n      if (dow === '1-5') parts_desc.push('on weekdays (Mon–Fri)');\n      else if (dow === '0,6' || dow === '6,0') parts_desc.push('on weekends');\n      else if (dow.includes('-')) {\n        var sp2 = dow.split('-');\n        parts_desc.push('on ' + (DAYS_SHORT[parseInt(sp2[0])]||sp2[0]) + '–' + (DAYS_SHORT[parseInt(sp2[1])]||sp2[1]));\n      } else if (dow.includes(',')) {\n        var ds = dow.split(',').map(function(d){ return DAYS_SHORT[parseInt(d)]||d; });\n        parts_desc.push('on ' + ds.join(', '));\n      } else {\n        parts_desc.push('every ' + (DAYS_FULL[parseInt(dow)] || 'day ' + dow));\n      }\n    }\n\n    return parts_desc.length ? parts_desc.join(', ') : 'Custom schedule';\n  }\n\n  /* ── Next Run Times ── */\n  function nextRuns(expr, count) {\n    // Parse standard 5-field cron (no seconds, no 'L', no special chars beyond */n, ranges, lists)\n    var parts = expr.trim().split(/\\s+/);\n    if (parts.length !== 5) return [];\n\n    function parseField(str, min, max) {\n      var values = [];\n      if (str === '*') {\n        for (var i = min; i \u003c= max; i++) values.push(i);\n        return values;\n      }\n      // Handle comma-separated\n      var segments = str.split(',');\n      segments.forEach(function(seg) {\n        seg = seg.trim();\n        if (seg === '*') {\n          for (var i = min; i \u003c= max; i++) values.push(i);\n        } else if (seg.startsWith('*/')) {\n          var step = parseInt(seg.slice(2));\n          for (var i = min; i \u003c= max; i += step) values.push(i);\n        } else if (seg.includes('-')) {\n          var rng = seg.split('-');\n          var from = parseInt(rng[0]), to = parseInt(rng[1]);\n          for (var i = from; i \u003c= to; i++) values.push(i);\n        } else {\n          var n = parseInt(seg);\n          if (!isNaN(n)) values.push(n);\n        }\n      });\n      return values.filter(function(v,i,a){ return a.indexOf(v)===i; }).sort(function(a,b){return a-b;});\n    }\n\n    // Skip 'L' or unsupported special chars for next-run calc\n    try {\n      var mins  = parseField(parts[0], 0, 59);\n      var hrs   = parseField(parts[1], 0, 23);\n      var doms  = parts[2] === '*' ? null : parseField(parts[2], 1, 31);\n      var mons  = parseField(parts[3], 1, 12);\n      var dows  = parts[4] === '*' ? null : parseField(parts[4], 0, 6);\n\n      var runs = [];\n      var now = new Date();\n      var cur = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes() + 1, 0);\n      var limit = 50000; // iteration guard\n\n      while (runs.length \u003c count \u0026\u0026 limit-- \u003e 0) {\n        var m = cur.getMonth() + 1; // 1-12\n        var dom = cur.getDate();\n        var dow = cur.getDay(); // 0=Sun\n        var h = cur.getHours();\n        var mi = cur.getMinutes();\n\n        if (mons.indexOf(m) \u003e= 0\n          \u0026\u0026 (doms === null || doms.indexOf(dom) \u003e= 0)\n          \u0026\u0026 (dows === null || dows.indexOf(dow) \u003e= 0)\n          \u0026\u0026 hrs.indexOf(h) \u003e= 0\n          \u0026\u0026 mins.indexOf(mi) \u003e= 0) {\n          runs.push(new Date(cur));\n        }\n        cur = new Date(cur.getTime() + 60000);\n      }\n      return runs;\n    } catch(e) {\n      return [];\n    }\n  }\n\n  function relTime(d) {\n    var diff = Math.round((d - Date.now()) / 1000);\n    if (diff \u003c 60) return 'in ' + diff + 's';\n    if (diff \u003c 3600) return 'in ' + Math.round(diff/60) + 'min';\n    if (diff \u003c 86400) return 'in ' + Math.round(diff/3600) + 'h';\n    return 'in ' + Math.round(diff/86400) + 'd';\n  }\n\n  function padZ(n) { return n \u003c 10 ? '0' + n : String(n); }\n\n  function fmtDate(d) {\n    return d.getFullYear() + '-' + padZ(d.getMonth()+1) + '-' + padZ(d.getDate())\n      + ' ' + padZ(d.getHours()) + ':' + padZ(d.getMinutes());\n  }\n\n  function renderRuns(expr) {\n    var list = document.getElementById('cg-run-list');\n    var runs = nextRuns(expr, 5);\n    if (!runs.length) {\n      list.innerHTML = '\u003cli class=\"cg-run-item\"\u003e\u003cspan style=\"color:#64748b;\"\u003eCould not compute next run times for this expression.\u003c/span\u003e\u003c/li\u003e';\n      return;\n    }\n    list.innerHTML = runs.map(function(d, i) {\n      return '\u003cli class=\"cg-run-item\"\u003e'\n        + '\u003cspan class=\"cg-run-num\"\u003e' + (i+1) + '\u003c/span\u003e'\n        + '\u003cspan class=\"cg-run-time\"\u003e' + fmtDate(d) + '\u003c/span\u003e'\n        + '\u003cspan class=\"cg-run-rel\"\u003e' + relTime(d) + '\u003c/span\u003e'\n        + '\u003c/li\u003e';\n    }).join('');\n  }\n\n  /* ── Main Update ── */\n  function cgUpdate() {\n    var expr = getExpr();\n    document.getElementById('cg-expr').textContent = expr;\n    document.getElementById('cg-desc').textContent = describe(expr);\n    renderRuns(expr);\n    // Clear active preset highlights\n    document.querySelectorAll('.cg-preset-btn').forEach(function(b){\n      b.classList.toggle('active', b.getAttribute('data-expr') === expr);\n    });\n  }\n\n  /* ── Presets ── */\n  window.cgApplyPreset = function(btn) {\n    var expr = btn.getAttribute('data-expr');\n    var parts = expr.split(' ');\n    var keys = ['min','hr','dom','mon','dow'];\n    keys.forEach(function(k, i) {\n      var sel = FIELDS[k].el;\n      // Find exact match\n      var found = false;\n      for (var j = 0; j \u003c sel.options.length; j++) {\n        if (sel.options[j].value === parts[i]) {\n          sel.selectedIndex = j; found = true; break;\n        }\n      }\n      if (!found) sel.selectedIndex = 0; // fallback to *\n    });\n    cgUpdate();\n  };\n\n  /* ── Copy ── */\n  window.cgCopyExpr = function() {\n    var expr = getExpr();\n    var btn = document.getElementById('cg-copy-btn');\n    try {\n      if (navigator.clipboard) {\n        navigator.clipboard.writeText(expr).then(function(){\n          btn.textContent = 'Copied!';\n          setTimeout(function(){ btn.textContent = 'Copy'; }, 1600);\n        });\n      } else {\n        var ta = document.createElement('textarea');\n        ta.value = expr;\n        document.body.appendChild(ta);\n        ta.select();\n        document.execCommand('copy');\n        document.body.removeChild(ta);\n        btn.textContent = 'Copied!';\n        setTimeout(function(){ btn.textContent = 'Copy'; }, 1600);\n      }\n    } catch(e) {}\n  };\n\n  /* ── Validator ── */\n  function validateExpr(expr) {\n    var parts = expr.trim().split(/\\s+/);\n    if (parts.length !== 5) return { ok: false, msg: 'A cron expression must have exactly 5 fields (minute hour day month weekday).' };\n    var ranges = [[0,59],[0,23],[1,31],[1,12],[0,7]];\n    var names  = ['minute','hour','day-of-month','month','day-of-week'];\n    for (var i = 0; i \u003c 5; i++) {\n      var f = parts[i];\n      if (f === '*') continue;\n      if (/^\\*\\/\\d+$/.test(f)) {\n        var step = parseInt(f.slice(2));\n        if (step \u003c 1) return { ok: false, msg: 'Field \"' + names[i] + '\": step value must be ≥ 1.' };\n        continue;\n      }\n      // Split by comma\n      var segs = f.split(',');\n      for (var j = 0; j \u003c segs.length; j++) {\n        var seg = segs[j].trim();\n        if (/^\\d+$/.test(seg)) {\n          var n = parseInt(seg);\n          if (n \u003c ranges[i][0] || n \u003e ranges[i][1]) {\n            return { ok: false, msg: 'Field \"' + names[i] + '\": value ' + n + ' is out of range (' + ranges[i][0] + '–' + ranges[i][1] + ').' };\n          }\n        } else if (/^\\d+-\\d+$/.test(seg)) {\n          var rp = seg.split('-').map(Number);\n          if (rp[0] \u003e rp[1]) return { ok: false, msg: 'Field \"' + names[i] + '\": range start must be ≤ end.' };\n          if (rp[0] \u003c ranges[i][0] || rp[1] \u003e ranges[i][1]) {\n            return { ok: false, msg: 'Field \"' + names[i] + '\": range ' + seg + ' is out of allowed range (' + ranges[i][0] + '–' + ranges[i][1] + ').' };\n          }\n        } else if (/^\\d+-\\d+\\/\\d+$/.test(seg)) {\n          continue; // range/step — basic pass\n        } else if (seg === 'L' \u0026\u0026 i === 2) {\n          continue; // last day\n        } else {\n          return { ok: false, msg: 'Field \"' + names[i] + '\": unrecognised value \"' + seg + '\".' };\n        }\n      }\n    }\n    return { ok: true, msg: 'Valid cron expression: ' + describe(parts.join(' ')) };\n  }\n\n  window.cgValidate = function() {\n    var val = document.getElementById('cg-val-input').value.trim();\n    var res = document.getElementById('cg-val-result');\n    if (!val) { res.textContent = 'Please enter a cron expression to validate.'; res.className = 'cg-validate-result'; return; }\n    var r = validateExpr(val);\n    res.textContent = r.msg;\n    res.className = 'cg-validate-result ' + (r.ok ? 'cg-valid' : 'cg-invalid');\n    if (r.ok) {\n      // Load into builder\n      var parts = val.trim().split(/\\s+/);\n      var keys = ['min','hr','dom','mon','dow'];\n      keys.forEach(function(k, i) {\n        var sel = FIELDS[k].el;\n        for (var j = 0; j \u003c sel.options.length; j++) {\n          if (sel.options[j].value === parts[i]) { sel.selectedIndex = j; break; }\n        }\n      });\n      cgUpdate();\n    }\n  };\n\n  window.cgClearValidate = function() {\n    document.getElementById('cg-val-input').value = '';\n    document.getElementById('cg-val-result').textContent = '';\n    document.getElementById('cg-val-result').className = 'cg-validate-result';\n  };\n\n  /* ── Init ── */\n  buildSelects();\n  cgUpdate();\n\n  // Enter key in validator\n  document.getElementById('cg-val-input').addEventListener('keydown', function(e){\n    if (e.key === 'Enter') cgValidate();\n  });\n\n})();\n\u003c/script\u003e\n\u003cblockquote\u003e\n\u003cp\u003eBuild cron expressions → \u003ca href=\"https://productivity-works.com/tools/cron-expression-builder/\"\u003eCron Expression Builder\u003c/a\u003e\n\u003c/p\u003e","title":"Crontab Generator"},{"content":" Presets Fade In Fade Out Slide In Left Slide In Right Slide In Up Slide In Down Bounce Pulse Shake Spin Flip Swing Animation Properties Duration (s) 1s Delay (s) 0s Timing Function ease ease-in ease-out ease-in-out linear cubic-bezier (back) steps(4, end) Iteration Count 1 2 3 5 infinite Direction normal reverse alternate alternate-reverse Fill Mode none forwards backwards both Live Preview CSS \u0026lt;div class=\u0026quot;speed-row\u0026quot;\u0026gt; \u0026lt;span\u0026gt;Speed\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;speed-ctrl\u0026quot; min=\u0026quot;0.25\u0026quot; max=\u0026quot;4\u0026quot; step=\u0026quot;0.25\u0026quot; value=\u0026quot;1\u0026quot;\u0026gt; \u0026lt;span id=\u0026quot;lbl-speed\u0026quot;\u0026gt;1×\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;preview-controls\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;ctrl-btn btn-play\u0026quot; id=\u0026quot;btn-play\u0026quot;\u0026gt;\u0026amp;#9654; Play\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;ctrl-btn btn-pause\u0026quot; id=\u0026quot;btn-pause\u0026quot;\u0026gt;\u0026amp;#10074;\u0026amp;#10074; Pause\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;ctrl-btn btn-reset\u0026quot; id=\u0026quot;btn-reset\u0026quot;\u0026gt;\u0026amp;#8635; Reset\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; Keyframe Editor + Add Keyframe Generated CSS Copy Related Tools Build Flexbox layouts → CSS Flexbox Generator Generate gradients → CSS Gradient Generator Create box shadows → CSS Box Shadow Generator ","permalink":"https://productivity-works.com/tools/css-animation-generator/","summary":"\u003cdiv id=\"anim-app\"\u003e\n\u003cstyle\u003e\n/* ── Scoped styles — all selectors prefixed with #anim-app ── */\n#anim-app *,\n#anim-app *::before,\n#anim-app *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n#anim-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 14px;\n  color: #1a1a2e;\n  background: #f4f6fb;\n  border-radius: 12px;\n  padding: 24px;\n  max-width: 960px;\n  margin: 0 auto;\n}\n\n/* ── Layout grid ── */\n#anim-app .anim-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 20px;\n}\n@media (max-width: 680px) {\n  #anim-app .anim-grid { grid-template-columns: 1fr; }\n}\n\n/* ── Cards ── */\n#anim-app .anim-card {\n  background: #ffffff;\n  border: 1px solid #e0e4f0;\n  border-radius: 10px;\n  padding: 18px;\n}\n#anim-app .anim-card h3 {\n  font-size: 13px;\n  font-weight: 700;\n  letter-spacing: .05em;\n  text-transform: uppercase;\n  color: #5b6af0;\n  margin-bottom: 14px;\n}\n\n/* ── Form controls ── */\n#anim-app label {\n  display: flex;\n  flex-direction: column;\n  gap: 4px;\n  font-size: 12px;\n  font-weight: 600;\n  color: #444;\n  margin-bottom: 10px;\n}\n#anim-app input[type=\"text\"],\n#anim-app input[type=\"number\"],\n#anim-app input[type=\"color\"],\n#anim-app select {\n  font-size: 13px;\n  padding: 7px 10px;\n  border: 1px solid #d0d5e8;\n  border-radius: 6px;\n  background: #f9faff;\n  color: #1a1a2e;\n  width: 100%;\n  outline: none;\n  transition: border-color .2s;\n}\n#anim-app input[type=\"color\"] {\n  padding: 2px 4px;\n  height: 34px;\n  cursor: pointer;\n}\n#anim-app input:focus,\n#anim-app select:focus { border-color: #5b6af0; }\n\n#anim-app input[type=\"range\"] {\n  width: 100%;\n  accent-color: #5b6af0;\n  cursor: pointer;\n}\n#anim-app .range-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n#anim-app .range-row span {\n  font-size: 12px;\n  font-weight: 700;\n  color: #5b6af0;\n  min-width: 38px;\n  text-align: right;\n}\n\n/* ── Preset buttons ── */\n#anim-app .preset-grid {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 7px;\n  margin-bottom: 4px;\n}\n#anim-app .preset-btn {\n  font-size: 12px;\n  font-weight: 600;\n  padding: 5px 11px;\n  border: 1.5px solid #d0d5e8;\n  border-radius: 20px;\n  background: #f4f6fb;\n  color: #444;\n  cursor: pointer;\n  transition: background .15s, border-color .15s, color .15s;\n}\n#anim-app .preset-btn:hover,\n#anim-app .preset-btn.active {\n  background: #5b6af0;\n  border-color: #5b6af0;\n  color: #fff;\n}\n\n/* ── Keyframe editor ── */\n#anim-app .kf-list {\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n}\n#anim-app .kf-item {\n  border: 1px solid #e0e4f0;\n  border-radius: 8px;\n  padding: 10px 12px;\n  background: #f9faff;\n}\n#anim-app .kf-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 8px;\n}\n#anim-app .kf-pct {\n  font-size: 13px;\n  font-weight: 700;\n  color: #5b6af0;\n}\n#anim-app .kf-props {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 6px 10px;\n}\n#anim-app .kf-props label {\n  margin-bottom: 0;\n  font-size: 11px;\n}\n#anim-app .kf-del {\n  background: none;\n  border: none;\n  color: #c0392b;\n  font-size: 16px;\n  cursor: pointer;\n  line-height: 1;\n  padding: 0 2px;\n}\n#anim-app .kf-del:hover { color: #e74c3c; }\n#anim-app .add-kf-row {\n  display: flex;\n  gap: 8px;\n  align-items: center;\n  margin-top: 10px;\n}\n#anim-app .add-kf-row input { width: 80px; flex-shrink: 0; }\n#anim-app .btn-add {\n  padding: 7px 14px;\n  font-size: 12px;\n  font-weight: 700;\n  border-radius: 6px;\n  border: none;\n  background: #5b6af0;\n  color: #fff;\n  cursor: pointer;\n  transition: background .2s;\n}\n#anim-app .btn-add:hover { background: #4454d8; }\n\n/* ── Preview area ── */\n#anim-app .preview-area {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 16px;\n}\n#anim-app .preview-stage {\n  width: 100%;\n  height: 180px;\n  background: linear-gradient(135deg, #e8ecff 0%, #f4f6fb 100%);\n  border-radius: 10px;\n  border: 1px solid #d0d5e8;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  overflow: hidden;\n  position: relative;\n}\n#anim-app #preview-box {\n  width: 72px;\n  height: 72px;\n  background: #5b6af0;\n  border-radius: 10px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: #fff;\n  font-size: 11px;\n  font-weight: 700;\n  letter-spacing: .03em;\n  user-select: none;\n  will-change: transform, opacity;\n}\n#anim-app .preview-controls {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n  justify-content: center;\n}\n#anim-app .ctrl-btn {\n  padding: 8px 20px;\n  font-size: 13px;\n  font-weight: 700;\n  border-radius: 8px;\n  border: none;\n  cursor: pointer;\n  transition: opacity .2s;\n}\n#anim-app .ctrl-btn:hover { opacity: .85; }\n#anim-app .btn-play  { background: #27ae60; color: #fff; }\n#anim-app .btn-pause { background: #e67e22; color: #fff; }\n#anim-app .btn-reset { background: #636e72; color: #fff; }\n\n/* ── Speed slider ── */\n#anim-app .speed-row {\n  width: 100%;\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  font-size: 12px;\n  font-weight: 600;\n  color: #444;\n}\n#anim-app .speed-row input { flex: 1; }\n#anim-app .speed-row span { min-width: 34px; color: #5b6af0; font-weight: 700; }\n\n/* ── Output CSS ── */\n#anim-app .output-wrap {\n  grid-column: 1 / -1;\n}\n#anim-app .output-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 8px;\n}\n#anim-app .output-header h3 { margin-bottom: 0; }\n#anim-app .btn-copy {\n  padding: 6px 16px;\n  font-size: 12px;\n  font-weight: 700;\n  border-radius: 6px;\n  border: none;\n  background: #5b6af0;\n  color: #fff;\n  cursor: pointer;\n  transition: background .2s;\n}\n#anim-app .btn-copy:hover  { background: #4454d8; }\n#anim-app .btn-copy.copied { background: #27ae60; }\n#anim-app #css-output {\n  width: 100%;\n  min-height: 160px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 12px;\n  line-height: 1.6;\n  padding: 14px;\n  border: 1px solid #d0d5e8;\n  border-radius: 8px;\n  background: #1a1a2e;\n  color: #a0d8ef;\n  resize: vertical;\n  outline: none;\n}\n\n/* ── Divider ── */\n#anim-app .full-row { grid-column: 1 / -1; }\n\u003c/style\u003e\n\u003c!-- ══════════════════════════════\n     PRESETS\n══════════════════════════════ --\u003e\n\u003cdiv class=\"anim-card full-row\" style=\"margin-bottom:0\"\u003e\n  \u003ch3\u003ePresets\u003c/h3\u003e\n  \u003cdiv class=\"preset-grid\" id=\"preset-grid\"\u003e\n    \u003cbutton class=\"preset-btn\" data-preset=\"fadeIn\"\u003eFade In\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" data-preset=\"fadeOut\"\u003eFade Out\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" data-preset=\"slideInLeft\"\u003eSlide In Left\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" data-preset=\"slideInRight\"\u003eSlide In Right\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" data-preset=\"slideInUp\"\u003eSlide In Up\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" data-preset=\"slideInDown\"\u003eSlide In Down\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" data-preset=\"bounce\"\u003eBounce\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" data-preset=\"pulse\"\u003ePulse\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" data-preset=\"shake\"\u003eShake\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" data-preset=\"spin\"\u003eSpin\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" data-preset=\"flip\"\u003eFlip\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" data-preset=\"swing\"\u003eSwing\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"anim-grid\" style=\"margin-top:20px\"\u003e\n\u003c!-- ══════════════════════════════\n     ANIMATION PROPERTIES\n══════════════════════════════ --\u003e\n\u003cdiv class=\"anim-card\"\u003e\n  \u003ch3\u003eAnimation Properties\u003c/h3\u003e\n\u003cp\u003e\u003clabel\u003eDuration (s)\n\u003cdiv class=\"range-row\"\u003e\n\u003cinput type=\"range\" id=\"prop-duration\" min=\"0.1\" max=\"10\" step=\"0.1\" value=\"1\"\u003e\n\u003cspan id=\"lbl-duration\"\u003e1s\u003c/span\u003e\n\u003c/div\u003e\n\u003c/label\u003e\u003c/p\u003e","title":"CSS Animation Generator — Keyframe Builder"},{"content":"Generate CSS aspect-ratio property values for perfectly proportioned, responsive containers — no JavaScript required.\nCSS Aspect Ratio Generator 16:9 4:3 1:1 21:9 9:16 3:2 2:3 Width : Height Max-width 100% Live Preview 16 : 9 Modern CSS — aspect-ratio Recommended Copy Padding-bottom Hack Legacy Fallback Copy How It Works The modern aspect-ratio CSS property tells the browser to maintain a fixed width-to-height ratio as the element resizes. Set it on any block element and pair with width: 100% for a fully responsive container.\n.video-container { width: 100%; aspect-ratio: 16 / 9; } The padding-bottom hack achieves the same result in older browsers (pre-2021) by exploiting how percentage padding is always calculated relative to the parent\u0026rsquo;s width, not height.\nCommon Ratios Reference Ratio Use Case 16:9 HD video, YouTube embeds, widescreen 4:3 Classic TV, older presentations 1:1 Square images, Instagram posts 21:9 Cinematic/ultrawide hero banners 9:16 Mobile video, Stories, Reels 3:2 DSLR photos, card thumbnails Browser Support aspect-ratio is supported by all modern browsers (Chrome 88+, Firefox 89+, Safari 15+, Edge 88+). For IE11 or older Safari, use the padding-bottom fallback generated above.\nRelated Tools:\nAspect Ratio Calculator — Find the missing dimension from any ratio CSS Flexbox Generator — Build flexbox layouts visually Image Resizer — Resize images while preserving aspect ratio ","permalink":"https://productivity-works.com/tools/css-aspect-ratio/","summary":"\u003cp\u003eGenerate CSS \u003ccode\u003easpect-ratio\u003c/code\u003e property values for perfectly proportioned, responsive containers — no JavaScript required.\u003c/p\u003e\n\u003cdiv id=\"ar-app\"\u003e\n\u003cstyle\u003e\n  #ar-app *,\n  #ar-app *::before,\n  #ar-app *::after {\n    box-sizing: border-box;\n    margin: 0;\n    padding: 0;\n  }\n  #ar-app {\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n    font-size: 15px;\n    color: #1a1a2e;\n    background: #f8f9fc;\n    border-radius: 12px;\n    padding: 28px 24px;\n    max-width: 760px;\n    margin: 32px auto;\n  }\n  #ar-app h2 {\n    font-size: 1.25rem;\n    font-weight: 700;\n    margin-bottom: 20px;\n    color: #111827;\n  }\n  #ar-app .ar-presets {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 8px;\n    margin-bottom: 20px;\n  }\n  #ar-app .ar-preset-btn {\n    background: #fff;\n    border: 2px solid #d1d5db;\n    border-radius: 8px;\n    padding: 7px 14px;\n    font-size: 0.85rem;\n    font-weight: 600;\n    cursor: pointer;\n    color: #374151;\n    transition: border-color 0.15s, background 0.15s, color 0.15s;\n  }\n  #ar-app .ar-preset-btn:hover {\n    border-color: #6366f1;\n    color: #6366f1;\n  }\n  #ar-app .ar-preset-btn.active {\n    background: #6366f1;\n    border-color: #6366f1;\n    color: #fff;\n  }\n  #ar-app .ar-inputs {\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    margin-bottom: 20px;\n    flex-wrap: wrap;\n  }\n  #ar-app .ar-inputs label {\n    font-size: 0.85rem;\n    font-weight: 600;\n    color: #6b7280;\n    white-space: nowrap;\n  }\n  #ar-app .ar-inputs input[type=\"number\"] {\n    width: 90px;\n    padding: 8px 10px;\n    border: 2px solid #d1d5db;\n    border-radius: 8px;\n    font-size: 1rem;\n    font-weight: 700;\n    color: #111827;\n    text-align: center;\n    outline: none;\n    transition: border-color 0.15s;\n  }\n  #ar-app .ar-inputs input[type=\"number\"]:focus {\n    border-color: #6366f1;\n  }\n  #ar-app .ar-sep {\n    font-size: 1.3rem;\n    font-weight: 700;\n    color: #9ca3af;\n    user-select: none;\n  }\n  #ar-app .ar-maxwidth-row {\n    display: flex;\n    align-items: center;\n    gap: 12px;\n    margin-bottom: 24px;\n    flex-wrap: wrap;\n  }\n  #ar-app .ar-maxwidth-row label {\n    font-size: 0.85rem;\n    font-weight: 600;\n    color: #6b7280;\n    white-space: nowrap;\n  }\n  #ar-app .ar-maxwidth-row input[type=\"range\"] {\n    flex: 1;\n    min-width: 120px;\n    max-width: 260px;\n    accent-color: #6366f1;\n    cursor: pointer;\n  }\n  #ar-app .ar-maxwidth-val {\n    font-size: 0.9rem;\n    font-weight: 700;\n    color: #6366f1;\n    min-width: 48px;\n  }\n  #ar-app .ar-preview-area {\n    margin-bottom: 24px;\n  }\n  #ar-app .ar-preview-label {\n    font-size: 0.8rem;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 0.07em;\n    color: #9ca3af;\n    margin-bottom: 10px;\n  }\n  #ar-app .ar-preview-wrapper {\n    background: #e5e7eb;\n    border-radius: 10px;\n    padding: 16px;\n    display: flex;\n    justify-content: center;\n    align-items: flex-start;\n    min-height: 80px;\n  }\n  #ar-app .ar-preview-box {\n    background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #a78bfa 100%);\n    border-radius: 8px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    color: #fff;\n    font-weight: 700;\n    font-size: 1rem;\n    letter-spacing: 0.03em;\n    transition: aspect-ratio 0.25s, max-width 0.25s;\n  }\n  #ar-app .ar-code-section {\n    display: flex;\n    flex-direction: column;\n    gap: 16px;\n  }\n  #ar-app .ar-code-block {\n    background: #1e1b2e;\n    border-radius: 10px;\n    padding: 18px 18px 14px;\n    position: relative;\n  }\n  #ar-app .ar-code-title {\n    font-size: 0.75rem;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 0.08em;\n    color: #a78bfa;\n    margin-bottom: 10px;\n  }\n  #ar-app .ar-code-title .ar-badge {\n    display: inline-block;\n    background: #312e81;\n    color: #c4b5fd;\n    border-radius: 4px;\n    font-size: 0.7rem;\n    padding: 1px 6px;\n    margin-left: 6px;\n    font-weight: 600;\n    vertical-align: middle;\n  }\n  #ar-app .ar-code-title .ar-badge-old {\n    background: #3b2a1a;\n    color: #fbbf24;\n  }\n  #ar-app pre {\n    margin: 0;\n    font-family: \"Fira Mono\", \"Consolas\", \"Monaco\", monospace;\n    font-size: 0.85rem;\n    line-height: 1.7;\n    color: #e2e8f0;\n    white-space: pre-wrap;\n    word-break: break-all;\n    overflow-x: auto;\n  }\n  #ar-app .ar-prop   { color: #93c5fd; }\n  #ar-app .ar-val    { color: #6ee7b7; }\n  #ar-app .ar-unit   { color: #fcd34d; }\n  #ar-app .ar-sel    { color: #f9a8d4; }\n  #ar-app .ar-comment{ color: #6b7280; font-style: italic; }\n  #ar-app .ar-copy-btn {\n    position: absolute;\n    top: 14px;\n    right: 14px;\n    background: #312e81;\n    border: none;\n    border-radius: 6px;\n    color: #c4b5fd;\n    font-size: 0.78rem;\n    font-weight: 700;\n    padding: 5px 12px;\n    cursor: pointer;\n    transition: background 0.15s, color 0.15s;\n  }\n  #ar-app .ar-copy-btn:hover {\n    background: #6366f1;\n    color: #fff;\n  }\n  #ar-app .ar-copy-btn.copied {\n    background: #065f46;\n    color: #6ee7b7;\n  }\n  @media (max-width: 520px) {\n    #ar-app {\n      padding: 18px 12px;\n    }\n    #ar-app .ar-inputs input[type=\"number\"] {\n      width: 72px;\n    }\n    #ar-app .ar-maxwidth-row input[type=\"range\"] {\n      min-width: 80px;\n    }\n    #ar-app pre {\n      font-size: 0.78rem;\n    }\n  }\n\u003c/style\u003e\n\u003ch2\u003eCSS Aspect Ratio Generator\u003c/h2\u003e\n\u003cdiv class=\"ar-presets\" id=\"ar-presets\"\u003e\n  \u003cbutton class=\"ar-preset-btn active\" data-w=\"16\" data-h=\"9\"\u003e16:9\u003c/button\u003e\n  \u003cbutton class=\"ar-preset-btn\" data-w=\"4\" data-h=\"3\"\u003e4:3\u003c/button\u003e\n  \u003cbutton class=\"ar-preset-btn\" data-w=\"1\" data-h=\"1\"\u003e1:1\u003c/button\u003e\n  \u003cbutton class=\"ar-preset-btn\" data-w=\"21\" data-h=\"9\"\u003e21:9\u003c/button\u003e\n  \u003cbutton class=\"ar-preset-btn\" data-w=\"9\" data-h=\"16\"\u003e9:16\u003c/button\u003e\n  \u003cbutton class=\"ar-preset-btn\" data-w=\"3\" data-h=\"2\"\u003e3:2\u003c/button\u003e\n  \u003cbutton class=\"ar-preset-btn\" data-w=\"2\" data-h=\"3\"\u003e2:3\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ar-inputs\"\u003e\n  \u003clabel for=\"ar-width\"\u003eWidth\u003c/label\u003e\n  \u003cinput type=\"number\" id=\"ar-width\" min=\"1\" max=\"9999\" value=\"16\" /\u003e\n  \u003cspan class=\"ar-sep\"\u003e:\u003c/span\u003e\n  \u003clabel for=\"ar-height\"\u003eHeight\u003c/label\u003e\n  \u003cinput type=\"number\" id=\"ar-height\" min=\"1\" max=\"9999\" value=\"9\" /\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ar-maxwidth-row\"\u003e\n  \u003clabel for=\"ar-maxwidth\"\u003eMax-width\u003c/label\u003e\n  \u003cinput type=\"range\" id=\"ar-maxwidth\" min=\"100\" max=\"100\" value=\"100\" step=\"5\" /\u003e\n  \u003cspan class=\"ar-maxwidth-val\" id=\"ar-maxwidth-val\"\u003e100%\u003c/span\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ar-preview-area\"\u003e\n  \u003cdiv class=\"ar-preview-label\"\u003eLive Preview\u003c/div\u003e\n  \u003cdiv class=\"ar-preview-wrapper\" id=\"ar-preview-wrapper\"\u003e\n    \u003cdiv class=\"ar-preview-box\" id=\"ar-preview-box\"\u003e16 : 9\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ar-code-section\"\u003e\n  \u003cdiv class=\"ar-code-block\" id=\"ar-block-modern\"\u003e\n    \u003cdiv class=\"ar-code-title\"\u003e\n      Modern CSS — aspect-ratio\n      \u003cspan class=\"ar-badge\"\u003eRecommended\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cbutton class=\"ar-copy-btn\" id=\"ar-copy-modern\" onclick=\"arCopy('modern')\"\u003eCopy\u003c/button\u003e\n    \u003cpre id=\"ar-pre-modern\"\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ar-code-block\" id=\"ar-block-hack\"\u003e\n    \u003cdiv class=\"ar-code-title\"\u003e\n      Padding-bottom Hack\n      \u003cspan class=\"ar-badge ar-badge-old\"\u003eLegacy Fallback\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cbutton class=\"ar-copy-btn\" id=\"ar-copy-hack\" onclick=\"arCopy('hack')\"\u003eCopy\u003c/button\u003e\n    \u003cpre id=\"ar-pre-hack\"\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var wInput  = document.getElementById('ar-width');\n  var hInput  = document.getElementById('ar-height');\n  var mwRange = document.getElementById('ar-maxwidth');\n  var mwVal   = document.getElementById('ar-maxwidth-val');\n  var preBox  = document.getElementById('ar-preview-box');\n  var preWrap = document.getElementById('ar-preview-wrapper');\n  var preModern = document.getElementById('ar-pre-modern');\n  var preHack   = document.getElementById('ar-pre-hack');\n  var presets   = document.querySelectorAll('#ar-presets .ar-preset-btn');\n\n  function gcd(a, b) { return b === 0 ? a : gcd(b, a % b); }\n\n  function simplify(w, h) {\n    var g = gcd(w, h);\n    return [w / g, h / g];\n  }\n\n  function hl(cls, text) {\n    return '\u003cspan class=\"' + cls + '\"\u003e' + text + '\u003c/span\u003e';\n  }\n\n  function render() {\n    var w  = Math.max(1, parseInt(wInput.value) || 16);\n    var h  = Math.max(1, parseInt(hInput.value) || 9);\n    var mw = parseInt(mwRange.value) || 100;\n    var sw = simplify(w, h);\n    var ratio = w + ' / ' + h;\n    var pct   = ((h / w) * 100).toFixed(4).replace(/\\.?0+$/, '') + '%';\n\n    // Update preview box\n    preBox.style.aspectRatio = ratio;\n    preBox.style.width = mw + '%';\n    preBox.style.maxWidth = '100%';\n    preBox.textContent = sw[0] + ' : ' + sw[1];\n\n    // Modern CSS output\n    var modern = [\n      hl('ar-sel', '.responsive-container') + ' {',\n      '  ' + hl('ar-prop', 'width') + ': ' + hl('ar-val', '100') + hl('ar-unit', '%') + ';',\n      '  ' + hl('ar-prop', 'max-width') + ': ' + hl('ar-val', mw) + hl('ar-unit', '%') + ';',\n      '  ' + hl('ar-prop', 'aspect-ratio') + ': ' + hl('ar-val', w + ' / ' + h) + ';',\n      '  ' + hl('ar-comment', '/* overflow: hidden; — add if needed */'),\n      '}'\n    ].join('\\n');\n\n    preModern.innerHTML = modern;\n\n    // Padding-bottom hack output\n    var hack = [\n      hl('ar-comment', '/* Wrapper — padding-bottom trick */'),\n      hl('ar-sel', '.ar-wrapper') + ' {',\n      '  ' + hl('ar-prop', 'position') + ': ' + hl('ar-val', 'relative') + ';',\n      '  ' + hl('ar-prop', 'width') + ': ' + hl('ar-val', mw) + hl('ar-unit', '%') + ';',\n      '  ' + hl('ar-prop', 'max-width') + ': ' + hl('ar-val', '100') + hl('ar-unit', '%') + ';',\n      '  ' + hl('ar-prop', 'padding-bottom') + ': ' + hl('ar-val', pct) + ';',\n      '  ' + hl('ar-prop', 'height') + ': ' + hl('ar-val', '0') + ';',\n      '}',\n      '',\n      hl('ar-comment', '/* Inner content — positioned absolutely */'),\n      hl('ar-sel', '.ar-wrapper \u003e *') + ' {',\n      '  ' + hl('ar-prop', 'position') + ': ' + hl('ar-val', 'absolute') + ';',\n      '  ' + hl('ar-prop', 'top') + ': ' + hl('ar-val', '0') + ';',\n      '  ' + hl('ar-prop', 'left') + ': ' + hl('ar-val', '0') + ';',\n      '  ' + hl('ar-prop', 'width') + ': ' + hl('ar-val', '100') + hl('ar-unit', '%') + ';',\n      '  ' + hl('ar-prop', 'height') + ': ' + hl('ar-val', '100') + hl('ar-unit', '%') + ';',\n      '}'\n    ].join('\\n');\n\n    preHack.innerHTML = hack;\n  }\n\n  function updatePresetButtons(w, h) {\n    presets.forEach(function(btn) {\n      btn.classList.toggle('active',\n        parseInt(btn.dataset.w) === w \u0026\u0026 parseInt(btn.dataset.h) === h);\n    });\n  }\n\n  presets.forEach(function(btn) {\n    btn.addEventListener('click', function() {\n      wInput.value = btn.dataset.w;\n      hInput.value = btn.dataset.h;\n      updatePresetButtons(parseInt(btn.dataset.w), parseInt(btn.dataset.h));\n      render();\n    });\n  });\n\n  wInput.addEventListener('input', function() {\n    updatePresetButtons(parseInt(wInput.value), parseInt(hInput.value));\n    render();\n  });\n  hInput.addEventListener('input', function() {\n    updatePresetButtons(parseInt(wInput.value), parseInt(hInput.value));\n    render();\n  });\n\n  mwRange.addEventListener('input', function() {\n    mwVal.textContent = mwRange.value + '%';\n    render();\n  });\n\n  // Responsive range max\n  function setRangeMax() {\n    var ww = preWrap.clientWidth || 600;\n    mwRange.max = 100;\n    mwRange.value = Math.min(parseInt(mwRange.value), 100);\n    mwVal.textContent = mwRange.value + '%';\n  }\n\n  window.addEventListener('resize', function() { setRangeMax(); render(); });\n  setRangeMax();\n  render();\n\n  window.arCopy = function(which) {\n    var pre = which === 'modern' ? preModern : preHack;\n    var text = pre.textContent;\n    var btn  = document.getElementById('ar-copy-' + which);\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(text).then(function() {\n        btn.textContent = 'Copied!';\n        btn.classList.add('copied');\n        setTimeout(function() { btn.textContent = 'Copy'; btn.classList.remove('copied'); }, 1800);\n      });\n    } else {\n      var ta = document.createElement('textarea');\n      ta.value = text;\n      ta.style.position = 'fixed';\n      ta.style.opacity = '0';\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      btn.textContent = 'Copied!';\n      btn.classList.add('copied');\n      setTimeout(function() { btn.textContent = 'Copy'; btn.classList.remove('copied'); }, 1800);\n    }\n  };\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003ch2 id=\"how-it-works\"\u003eHow It Works\u003c/h2\u003e\n\u003cp\u003eThe modern \u003ccode\u003easpect-ratio\u003c/code\u003e CSS property tells the browser to maintain a fixed width-to-height ratio as the element resizes. Set it on any block element and pair with \u003ccode\u003ewidth: 100%\u003c/code\u003e for a fully responsive container.\u003c/p\u003e","title":"CSS Aspect Ratio Generator — Responsive Containers"},{"content":"Visually design any CSS border-radius shape — from simple rounded corners to pills, circles, blobs, and organic forms. Adjust individual corners, toggle elliptical (horizontal/vertical) values, and copy the ready-to-use CSS in one click.\nPresets None Pill Circle Blob Squircle Leaf Drop Triangle-ish Preview Width (px) Height (px) Color Controls \u0026lt;!-- Unit toggle --\u0026gt; \u0026lt;div class=\u0026quot;br-unit-toggle\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;br-unit-btn active\u0026quot; data-unit=\u0026quot;px\u0026quot;\u0026gt;px\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;br-unit-btn\u0026quot; data-unit=\u0026quot;%\u0026quot;\u0026gt;%\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Link toggle --\u0026gt; \u0026lt;div class=\u0026quot;br-link-row\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;br-link-btn active\u0026quot; id=\u0026quot;br-link-btn\u0026quot;\u0026gt; \u0026lt;svg viewBox=\u0026quot;0 0 20 20\u0026quot;\u0026gt;\u0026lt;path d=\u0026quot;M10.293 3.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L12.586 9H5a1 1 0 110-2h7.586l-2.293-2.293a1 1 0 010-1.414zM3 14a1 1 0 100 2h14a1 1 0 100-2H3z\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt; Link corners \u0026lt;/button\u0026gt; \u0026lt;span id=\u0026quot;br-link-label\u0026quot; style=\u0026quot;font-size:12px;color:#6b7280;\u0026quot;\u0026gt;All corners move together\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Corner sliders --\u0026gt; \u0026lt;div class=\u0026quot;br-corner-grid\u0026quot; id=\u0026quot;br-corner-grid\u0026quot;\u0026gt; \u0026lt;!-- Top-Left --\u0026gt; \u0026lt;div class=\u0026quot;br-corner-block\u0026quot; id=\u0026quot;br-block-tl\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Top-Left\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;br-slider-row\u0026quot;\u0026gt; \u0026lt;span\u0026gt;H\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;br-tl-h\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;br-tl-h-n\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;br-slider-row\u0026quot; id=\u0026quot;br-tl-v-row\u0026quot; style=\u0026quot;display:none;\u0026quot;\u0026gt; \u0026lt;span\u0026gt;V\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;br-tl-v\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;br-tl-v-n\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;label class=\u0026quot;br-elliptic-toggle\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;br-tl-ell\u0026quot;\u0026gt; Elliptical\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Top-Right --\u0026gt; \u0026lt;div class=\u0026quot;br-corner-block\u0026quot; id=\u0026quot;br-block-tr\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Top-Right\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;br-slider-row\u0026quot;\u0026gt; \u0026lt;span\u0026gt;H\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;br-tr-h\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;br-tr-h-n\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;br-slider-row\u0026quot; id=\u0026quot;br-tr-v-row\u0026quot; style=\u0026quot;display:none;\u0026quot;\u0026gt; \u0026lt;span\u0026gt;V\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;br-tr-v\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;br-tr-v-n\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;label class=\u0026quot;br-elliptic-toggle\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;br-tr-ell\u0026quot;\u0026gt; Elliptical\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Bottom-Right --\u0026gt; \u0026lt;div class=\u0026quot;br-corner-block\u0026quot; id=\u0026quot;br-block-br\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Bottom-Right\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;br-slider-row\u0026quot;\u0026gt; \u0026lt;span\u0026gt;H\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;br-br-h\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;br-br-h-n\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;br-slider-row\u0026quot; id=\u0026quot;br-br-v-row\u0026quot; style=\u0026quot;display:none;\u0026quot;\u0026gt; \u0026lt;span\u0026gt;V\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;br-br-v\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;br-br-v-n\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;label class=\u0026quot;br-elliptic-toggle\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;br-br-ell\u0026quot;\u0026gt; Elliptical\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Bottom-Left --\u0026gt; \u0026lt;div class=\u0026quot;br-corner-block\u0026quot; id=\u0026quot;br-block-bl\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Bottom-Left\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;br-slider-row\u0026quot;\u0026gt; \u0026lt;span\u0026gt;H\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;br-bl-h\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;br-bl-h-n\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;br-slider-row\u0026quot; id=\u0026quot;br-bl-v-row\u0026quot; style=\u0026quot;display:none;\u0026quot;\u0026gt; \u0026lt;span\u0026gt;V\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;br-bl-v\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;br-bl-v-n\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;label class=\u0026quot;br-elliptic-toggle\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;br-bl-ell\u0026quot;\u0026gt; Elliptical\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt;\u0026lt;!-- /.br-corner-grid --\u0026gt; \u0026lt;/div\u0026gt;\u0026lt;!-- /.br-panel --\u0026gt; Generated CSS Copy border-radius: 0; How to Use Choose a preset to start from a known shape, or drag the sliders to build from scratch. Toggle px / % — percentage values are relative to the element\u0026rsquo;s dimensions; pixels are absolute. Unlink corners to set each corner independently. Enable Elliptical on any corner to separate horizontal and vertical radii for organic shapes. Resize the preview box to see how your radius looks at different aspect ratios. Copy the CSS and paste it directly into your stylesheet. Quick Reference: border-radius Syntax Value Meaning border-radius: 8px All corners equal border-radius: 8px 16px TL+BR / TR+BL border-radius: 8px 16px 24px 32px TL TR BR BL (clockwise) border-radius: 40% 60% / 60% 40% Elliptical (H / V) Related Tools Generate box shadows → CSS Box Shadow Generator Build gradients → CSS Gradient Generator Create Flexbox layouts → CSS Flexbox Generator ","permalink":"https://productivity-works.com/tools/border-radius-generator/","summary":"\u003cp\u003eVisually design any CSS border-radius shape — from simple rounded corners to pills, circles, blobs, and organic forms. Adjust individual corners, toggle elliptical (horizontal/vertical) values, and copy the ready-to-use CSS in one click.\u003c/p\u003e\n\u003cdiv id=\"br-app\"\u003e\n\u003cstyle\u003e\n/* ── Reset \u0026 Base ─────────────────────────────────────────── */\n#br-app *,\n#br-app *::before,\n#br-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#br-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  font-size: 14px;\n  color: #1a1a2e;\n  line-height: 1.5;\n}\n\n/* ── Layout ───────────────────────────────────────────────── */\n#br-app .br-layout {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 24px;\n  margin-top: 24px;\n}\n@media (max-width: 720px) {\n  #br-app .br-layout {\n    grid-template-columns: 1fr;\n  }\n}\n\n/* ── Panel ────────────────────────────────────────────────── */\n#br-app .br-panel {\n  background: #f8f9fc;\n  border: 1px solid #e2e6f0;\n  border-radius: 12px;\n  padding: 20px;\n}\n#br-app .br-panel h3 {\n  font-size: 13px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: .06em;\n  color: #6b7280;\n  margin-bottom: 16px;\n}\n\n/* ── Preview ──────────────────────────────────────────────── */\n#br-app .br-preview-wrap {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  min-height: 260px;\n  background: repeating-conic-gradient(#e5e7eb 0% 25%, #f9fafb 0% 50%) 0 0 / 20px 20px;\n  border-radius: 8px;\n  padding: 20px;\n  margin-bottom: 16px;\n}\n#br-app #br-preview-box {\n  width: 160px;\n  height: 160px;\n  background: #6366f1;\n  transition: border-radius .15s ease, width .15s ease, height .15s ease, background .15s ease;\n}\n\n/* ── Preview Controls ─────────────────────────────────────── */\n#br-app .br-preview-controls {\n  display: grid;\n  grid-template-columns: 1fr 1fr 1fr;\n  gap: 10px;\n  align-items: end;\n}\n#br-app .br-preview-controls label {\n  font-size: 12px;\n  color: #6b7280;\n  display: block;\n  margin-bottom: 4px;\n}\n#br-app .br-preview-controls input[type=\"number\"] {\n  width: 100%;\n  padding: 6px 8px;\n  border: 1px solid #d1d5db;\n  border-radius: 6px;\n  font-size: 13px;\n  background: #fff;\n}\n#br-app .br-preview-controls input[type=\"color\"] {\n  width: 100%;\n  height: 34px;\n  padding: 2px 4px;\n  border: 1px solid #d1d5db;\n  border-radius: 6px;\n  cursor: pointer;\n  background: #fff;\n}\n\n/* ── Presets ──────────────────────────────────────────────── */\n#br-app .br-presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 0;\n}\n#br-app .br-preset-btn {\n  padding: 6px 14px;\n  border: 1px solid #d1d5db;\n  border-radius: 6px;\n  background: #fff;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background .12s, border-color .12s, color .12s;\n}\n#br-app .br-preset-btn:hover,\n#br-app .br-preset-btn.active {\n  background: #6366f1;\n  border-color: #6366f1;\n  color: #fff;\n}\n\n/* ── Unit Toggle ──────────────────────────────────────────── */\n#br-app .br-unit-toggle {\n  display: flex;\n  gap: 0;\n  border: 1px solid #d1d5db;\n  border-radius: 6px;\n  overflow: hidden;\n  width: fit-content;\n  margin-bottom: 16px;\n}\n#br-app .br-unit-btn {\n  padding: 5px 16px;\n  border: none;\n  background: #fff;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background .12s, color .12s;\n}\n#br-app .br-unit-btn.active {\n  background: #6366f1;\n  color: #fff;\n}\n\n/* ── Corner Grid ──────────────────────────────────────────── */\n#br-app .br-corner-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 16px;\n  margin-bottom: 16px;\n}\n#br-app .br-corner-block {\n  background: #fff;\n  border: 1px solid #e2e6f0;\n  border-radius: 8px;\n  padding: 12px;\n}\n#br-app .br-corner-block label {\n  font-size: 11px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: .05em;\n  color: #6366f1;\n  display: block;\n  margin-bottom: 8px;\n}\n#br-app .br-slider-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin-bottom: 6px;\n}\n#br-app .br-slider-row span {\n  font-size: 11px;\n  color: #9ca3af;\n  width: 10px;\n  flex-shrink: 0;\n}\n#br-app .br-slider-row input[type=\"range\"] {\n  flex: 1;\n  accent-color: #6366f1;\n  cursor: pointer;\n}\n#br-app .br-slider-row input[type=\"number\"] {\n  width: 52px;\n  padding: 3px 6px;\n  border: 1px solid #d1d5db;\n  border-radius: 5px;\n  font-size: 12px;\n  text-align: right;\n}\n#br-app .br-elliptic-toggle {\n  font-size: 11px;\n  color: #6b7280;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  gap: 4px;\n  margin-top: 4px;\n  user-select: none;\n}\n#br-app .br-elliptic-toggle input { accent-color: #6366f1; cursor: pointer; }\n\n/* ── Link Toggle ──────────────────────────────────────────── */\n#br-app .br-link-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin-bottom: 16px;\n}\n#br-app .br-link-btn {\n  padding: 6px 14px;\n  border: 1px solid #d1d5db;\n  border-radius: 6px;\n  background: #fff;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background .12s, border-color .12s, color .12s;\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n#br-app .br-link-btn.active {\n  background: #6366f1;\n  border-color: #6366f1;\n  color: #fff;\n}\n#br-app .br-link-btn svg {\n  width: 14px; height: 14px; fill: currentColor;\n}\n\n/* ── Output ───────────────────────────────────────────────── */\n#br-app .br-output-box {\n  background: #1a1a2e;\n  border-radius: 8px;\n  padding: 16px;\n  margin-top: 8px;\n  position: relative;\n}\n#br-app .br-output-code {\n  font-family: 'Fira Mono', 'Cascadia Code', monospace;\n  font-size: 13px;\n  color: #a5f3fc;\n  word-break: break-all;\n  white-space: pre-wrap;\n  line-height: 1.7;\n}\n#br-app .br-copy-btn {\n  position: absolute;\n  top: 10px;\n  right: 10px;\n  padding: 5px 12px;\n  background: #6366f1;\n  color: #fff;\n  border: none;\n  border-radius: 5px;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background .12s;\n}\n#br-app .br-copy-btn:hover { background: #4f46e5; }\n#br-app .br-copy-btn.copied { background: #10b981; }\n\u003c/style\u003e\n\u003c!-- ── Presets ── --\u003e\n\u003cdiv class=\"br-panel\" style=\"margin-top:24px;\"\u003e\n  \u003ch3\u003ePresets\u003c/h3\u003e\n  \u003cdiv class=\"br-presets\"\u003e\n    \u003cbutton class=\"br-preset-btn\" data-preset=\"none\"\u003eNone\u003c/button\u003e\n    \u003cbutton class=\"br-preset-btn\" data-preset=\"pill\"\u003ePill\u003c/button\u003e\n    \u003cbutton class=\"br-preset-btn\" data-preset=\"circle\"\u003eCircle\u003c/button\u003e\n    \u003cbutton class=\"br-preset-btn\" data-preset=\"blob\"\u003eBlob\u003c/button\u003e\n    \u003cbutton class=\"br-preset-btn\" data-preset=\"squircle\"\u003eSquircle\u003c/button\u003e\n    \u003cbutton class=\"br-preset-btn\" data-preset=\"leaf\"\u003eLeaf\u003c/button\u003e\n    \u003cbutton class=\"br-preset-btn\" data-preset=\"drop\"\u003eDrop\u003c/button\u003e\n    \u003cbutton class=\"br-preset-btn\" data-preset=\"triangle\"\u003eTriangle-ish\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"br-layout\"\u003e\n  \u003c!-- Left: Preview --\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"br-panel\"\u003e\n      \u003ch3\u003ePreview\u003c/h3\u003e\n      \u003cdiv class=\"br-preview-wrap\"\u003e\n        \u003cdiv id=\"br-preview-box\"\u003e\u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"br-preview-controls\"\u003e\n        \u003cdiv\u003e\n          \u003clabel for=\"br-pw\"\u003eWidth (px)\u003c/label\u003e\n          \u003cinput type=\"number\" id=\"br-pw\" value=\"160\" min=\"40\" max=\"400\" step=\"4\"\u003e\n        \u003c/div\u003e\n        \u003cdiv\u003e\n          \u003clabel for=\"br-ph\"\u003eHeight (px)\u003c/label\u003e\n          \u003cinput type=\"number\" id=\"br-ph\" value=\"160\" min=\"40\" max=\"400\" step=\"4\"\u003e\n        \u003c/div\u003e\n        \u003cdiv\u003e\n          \u003clabel for=\"br-pbg\"\u003eColor\u003c/label\u003e\n          \u003cinput type=\"color\" id=\"br-pbg\" value=\"#6366f1\"\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Right: Controls --\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"br-panel\"\u003e\n      \u003ch3\u003eControls\u003c/h3\u003e\n\u003cpre\u003e\u003ccode\u003e  \u0026lt;!-- Unit toggle --\u0026gt;\n  \u0026lt;div class=\u0026quot;br-unit-toggle\u0026quot;\u0026gt;\n    \u0026lt;button class=\u0026quot;br-unit-btn active\u0026quot; data-unit=\u0026quot;px\u0026quot;\u0026gt;px\u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;br-unit-btn\u0026quot; data-unit=\u0026quot;%\u0026quot;\u0026gt;%\u0026lt;/button\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;!-- Link toggle --\u0026gt;\n  \u0026lt;div class=\u0026quot;br-link-row\u0026quot;\u0026gt;\n    \u0026lt;button class=\u0026quot;br-link-btn active\u0026quot; id=\u0026quot;br-link-btn\u0026quot;\u0026gt;\n      \u0026lt;svg viewBox=\u0026quot;0 0 20 20\u0026quot;\u0026gt;\u0026lt;path d=\u0026quot;M10.293 3.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L12.586 9H5a1 1 0 110-2h7.586l-2.293-2.293a1 1 0 010-1.414zM3 14a1 1 0 100 2h14a1 1 0 100-2H3z\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt;\n      Link corners\n    \u0026lt;/button\u0026gt;\n    \u0026lt;span id=\u0026quot;br-link-label\u0026quot; style=\u0026quot;font-size:12px;color:#6b7280;\u0026quot;\u0026gt;All corners move together\u0026lt;/span\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;!-- Corner sliders --\u0026gt;\n  \u0026lt;div class=\u0026quot;br-corner-grid\u0026quot; id=\u0026quot;br-corner-grid\u0026quot;\u0026gt;\n\n    \u0026lt;!-- Top-Left --\u0026gt;\n    \u0026lt;div class=\u0026quot;br-corner-block\u0026quot; id=\u0026quot;br-block-tl\u0026quot;\u0026gt;\n      \u0026lt;label\u0026gt;Top-Left\u0026lt;/label\u0026gt;\n      \u0026lt;div class=\u0026quot;br-slider-row\u0026quot;\u0026gt;\n        \u0026lt;span\u0026gt;H\u0026lt;/span\u0026gt;\n        \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;br-tl-h\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt;\n        \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;br-tl-h-n\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;br-slider-row\u0026quot; id=\u0026quot;br-tl-v-row\u0026quot; style=\u0026quot;display:none;\u0026quot;\u0026gt;\n        \u0026lt;span\u0026gt;V\u0026lt;/span\u0026gt;\n        \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;br-tl-v\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt;\n        \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;br-tl-v-n\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;label class=\u0026quot;br-elliptic-toggle\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;br-tl-ell\u0026quot;\u0026gt; Elliptical\u0026lt;/label\u0026gt;\n    \u0026lt;/div\u0026gt;\n\n    \u0026lt;!-- Top-Right --\u0026gt;\n    \u0026lt;div class=\u0026quot;br-corner-block\u0026quot; id=\u0026quot;br-block-tr\u0026quot;\u0026gt;\n      \u0026lt;label\u0026gt;Top-Right\u0026lt;/label\u0026gt;\n      \u0026lt;div class=\u0026quot;br-slider-row\u0026quot;\u0026gt;\n        \u0026lt;span\u0026gt;H\u0026lt;/span\u0026gt;\n        \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;br-tr-h\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt;\n        \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;br-tr-h-n\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;br-slider-row\u0026quot; id=\u0026quot;br-tr-v-row\u0026quot; style=\u0026quot;display:none;\u0026quot;\u0026gt;\n        \u0026lt;span\u0026gt;V\u0026lt;/span\u0026gt;\n        \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;br-tr-v\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt;\n        \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;br-tr-v-n\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;label class=\u0026quot;br-elliptic-toggle\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;br-tr-ell\u0026quot;\u0026gt; Elliptical\u0026lt;/label\u0026gt;\n    \u0026lt;/div\u0026gt;\n\n    \u0026lt;!-- Bottom-Right --\u0026gt;\n    \u0026lt;div class=\u0026quot;br-corner-block\u0026quot; id=\u0026quot;br-block-br\u0026quot;\u0026gt;\n      \u0026lt;label\u0026gt;Bottom-Right\u0026lt;/label\u0026gt;\n      \u0026lt;div class=\u0026quot;br-slider-row\u0026quot;\u0026gt;\n        \u0026lt;span\u0026gt;H\u0026lt;/span\u0026gt;\n        \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;br-br-h\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt;\n        \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;br-br-h-n\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;br-slider-row\u0026quot; id=\u0026quot;br-br-v-row\u0026quot; style=\u0026quot;display:none;\u0026quot;\u0026gt;\n        \u0026lt;span\u0026gt;V\u0026lt;/span\u0026gt;\n        \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;br-br-v\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt;\n        \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;br-br-v-n\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;label class=\u0026quot;br-elliptic-toggle\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;br-br-ell\u0026quot;\u0026gt; Elliptical\u0026lt;/label\u0026gt;\n    \u0026lt;/div\u0026gt;\n\n    \u0026lt;!-- Bottom-Left --\u0026gt;\n    \u0026lt;div class=\u0026quot;br-corner-block\u0026quot; id=\u0026quot;br-block-bl\u0026quot;\u0026gt;\n      \u0026lt;label\u0026gt;Bottom-Left\u0026lt;/label\u0026gt;\n      \u0026lt;div class=\u0026quot;br-slider-row\u0026quot;\u0026gt;\n        \u0026lt;span\u0026gt;H\u0026lt;/span\u0026gt;\n        \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;br-bl-h\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt;\n        \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;br-bl-h-n\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;br-slider-row\u0026quot; id=\u0026quot;br-bl-v-row\u0026quot; style=\u0026quot;display:none;\u0026quot;\u0026gt;\n        \u0026lt;span\u0026gt;V\u0026lt;/span\u0026gt;\n        \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;br-bl-v\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt;\n        \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;br-bl-v-n\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;label class=\u0026quot;br-elliptic-toggle\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;br-bl-ell\u0026quot;\u0026gt; Elliptical\u0026lt;/label\u0026gt;\n    \u0026lt;/div\u0026gt;\n\n  \u0026lt;/div\u0026gt;\u0026lt;!-- /.br-corner-grid --\u0026gt;\n\u0026lt;/div\u0026gt;\u0026lt;!-- /.br-panel --\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\u003c!-- /.br-layout --\u003e\n\u003c!-- Output --\u003e\n\u003cdiv class=\"br-panel\" style=\"margin-top:0;\"\u003e\n  \u003ch3\u003eGenerated CSS\u003c/h3\u003e\n  \u003cdiv class=\"br-output-box\"\u003e\n    \u003cbutton class=\"br-copy-btn\" id=\"br-copy-btn\"\u003eCopy\u003c/button\u003e\n    \u003cpre class=\"br-output-code\" id=\"br-output-code\"\u003eborder-radius: 0;\u003c/pre\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  'use strict';\n\n  /* ── State ── */\n  var unit = 'px';\n  var linked = true;\n\n  var corners = {\n    tl: { h: 0, v: 0, elliptic: false },\n    tr: { h: 0, v: 0, elliptic: false },\n    br: { h: 0, v: 0, elliptic: false },\n    bl: { h: 0, v: 0, elliptic: false }\n  };\n\n  /* ── Presets (px-based; % presets use special flag) ── */\n  var PRESETS = {\n    none:     { tl:{h:0,v:0,e:false}, tr:{h:0,v:0,e:false}, br:{h:0,v:0,e:false}, bl:{h:0,v:0,e:false}, unit:'px' },\n    pill:     { tl:{h:999,v:999,e:false}, tr:{h:999,v:999,e:false}, br:{h:999,v:999,e:false}, bl:{h:999,v:999,e:false}, unit:'px' },\n    circle:   { tl:{h:50,v:50,e:false}, tr:{h:50,v:50,e:false}, br:{h:50,v:50,e:false}, bl:{h:50,v:50,e:false}, unit:'%' },\n    blob:     { tl:{h:60,v:40,e:true}, tr:{h:40,v:70,e:true}, br:{h:70,v:50,e:true}, bl:{h:50,v:60,e:true}, unit:'%' },\n    squircle: { tl:{h:30,v:30,e:false}, tr:{h:30,v:30,e:false}, br:{h:30,v:30,e:false}, bl:{h:30,v:30,e:false}, unit:'%' },\n    leaf:     { tl:{h:0,v:0,e:false}, tr:{h:50,v:50,e:false}, br:{h:0,v:0,e:false}, bl:{h:50,v:50,e:false}, unit:'%' },\n    drop:     { tl:{h:50,v:50,e:false}, tr:{h:50,v:50,e:false}, br:{h:50,v:50,e:false}, bl:{h:0,v:0,e:false}, unit:'%' },\n    triangle: { tl:{h:0,v:0,e:false}, tr:{h:100,v:0,e:true}, br:{h:100,v:100,e:true}, bl:{h:0,v:100,e:true}, unit:'%' }\n  };\n\n  /* ── DOM refs ── */\n  var preview   = document.getElementById('br-preview-box');\n  var outputEl  = document.getElementById('br-output-code');\n  var copyBtn   = document.getElementById('br-copy-btn');\n  var linkBtn   = document.getElementById('br-link-btn');\n  var linkLabel = document.getElementById('br-link-label');\n  var pwInput   = document.getElementById('br-pw');\n  var phInput   = document.getElementById('br-ph');\n  var pbgInput  = document.getElementById('br-pbg');\n\n  var cornerKeys = ['tl','tr','br','bl'];\n\n  function el(id) { return document.getElementById(id); }\n\n  /* ── Build CSS value ── */\n  function cornerVal(c) {\n    var u = unit;\n    var hv = Math.min(c.h, u === '%' ? 50 : 200);\n    if (c.elliptic) {\n      var vv = Math.min(c.v, u === '%' ? 50 : 200);\n      return hv + u + ' / ' + vv + u;\n    }\n    return hv + u;\n  }\n\n  function buildCSS() {\n    var c = corners;\n    var vals = [cornerVal(c.tl), cornerVal(c.tr), cornerVal(c.br), cornerVal(c.bl)];\n\n    // Check if any corner is elliptic\n    var anyElliptic = cornerKeys.some(function(k){ return corners[k].elliptic; });\n\n    var css;\n    if (anyElliptic) {\n      // Full 4-value horizontal / 4-value vertical syntax\n      var hParts = cornerKeys.map(function(k){\n        return Math.min(corners[k].h, unit === '%' ? 50 : 200) + unit;\n      });\n      var vParts = cornerKeys.map(function(k){\n        return Math.min(corners[k].v, unit === '%' ? 50 : 200) + unit;\n      });\n      css = 'border-radius: ' + hParts.join(' ') + ' / ' + vParts.join(' ') + ';';\n    } else {\n      // Compact shorthand\n      var tl = Math.min(corners.tl.h, unit === '%' ? 50 : 200) + unit;\n      var tr = Math.min(corners.tr.h, unit === '%' ? 50 : 200) + unit;\n      var brv= Math.min(corners.br.h, unit === '%' ? 50 : 200) + unit;\n      var bl = Math.min(corners.bl.h, unit === '%' ? 50 : 200) + unit;\n      // Collapse shorthand when possible\n      if (tl === tr \u0026\u0026 tr === brv \u0026\u0026 brv === bl) {\n        css = 'border-radius: ' + tl + ';';\n      } else if (tl === brv \u0026\u0026 tr === bl) {\n        css = 'border-radius: ' + tl + ' ' + tr + ';';\n      } else if (tr === bl) {\n        css = 'border-radius: ' + tl + ' ' + tr + ' ' + brv + ';';\n      } else {\n        css = 'border-radius: ' + tl + ' ' + tr + ' ' + brv + ' ' + bl + ';';\n      }\n    }\n    return css;\n  }\n\n  function applyPreview() {\n    var css = buildCSS();\n    // Apply border-radius to preview box\n    var hParts = cornerKeys.map(function(k){\n      return Math.min(corners[k].h, unit === '%' ? 50 : 200) + unit;\n    });\n    var vParts = cornerKeys.map(function(k){\n      return Math.min(corners[k].v, unit === '%' ? 50 : 200) + unit;\n    });\n    preview.style.borderRadius = hParts.join(' ') + ' / ' + vParts.join(' ');\n    outputEl.textContent = css;\n  }\n\n  function syncInputs() {\n    cornerKeys.forEach(function(k) {\n      var c = corners[k];\n      var maxVal = unit === '%' ? 50 : 200;\n      el('br-' + k + '-h').max = maxVal;\n      el('br-' + k + '-h-n').max = maxVal;\n      el('br-' + k + '-v').max = maxVal;\n      el('br-' + k + '-v-n').max = maxVal;\n      el('br-' + k + '-h').value = Math.min(c.h, maxVal);\n      el('br-' + k + '-h-n').value = Math.min(c.h, maxVal);\n      el('br-' + k + '-v').value = Math.min(c.v, maxVal);\n      el('br-' + k + '-v-n').value = Math.min(c.v, maxVal);\n      el('br-' + k + '-ell').checked = c.elliptic;\n      el('br-' + k + '-v-row').style.display = c.elliptic ? 'flex' : 'none';\n    });\n  }\n\n  /* ── Wire slider ↔ number input ── */\n  function wireCorner(k, axis) {\n    var sliderEl = el('br-' + k + '-' + axis);\n    var numEl    = el('br-' + k + '-' + axis + '-n');\n\n    function onChange(val) {\n      val = Math.max(0, Math.min(parseInt(val) || 0, unit === '%' ? 50 : 200));\n      corners[k][axis] = val;\n\n      if (linked \u0026\u0026 axis === 'h') {\n        cornerKeys.forEach(function(ck) {\n          corners[ck].h = val;\n          if (!corners[ck].elliptic) corners[ck].v = val;\n        });\n        syncInputs();\n      } else {\n        sliderEl.value = val;\n        numEl.value = val;\n      }\n      applyPreview();\n    }\n\n    sliderEl.addEventListener('input', function() { onChange(this.value); });\n    numEl.addEventListener('input', function() { onChange(this.value); });\n  }\n\n  cornerKeys.forEach(function(k) {\n    wireCorner(k, 'h');\n    wireCorner(k, 'v');\n\n    // Elliptic toggle\n    el('br-' + k + '-ell').addEventListener('change', function() {\n      corners[k].elliptic = this.checked;\n      el('br-' + k + '-v-row').style.display = this.checked ? 'flex' : 'none';\n      applyPreview();\n    });\n  });\n\n  /* ── Link button ── */\n  linkBtn.addEventListener('click', function() {\n    linked = !linked;\n    linkBtn.classList.toggle('active', linked);\n    linkLabel.textContent = linked ? 'All corners move together' : 'Corners are independent';\n  });\n\n  /* ── Unit buttons ── */\n  document.querySelectorAll('#br-app .br-unit-btn').forEach(function(btn) {\n    btn.addEventListener('click', function() {\n      document.querySelectorAll('#br-app .br-unit-btn').forEach(function(b){ b.classList.remove('active'); });\n      this.classList.add('active');\n      unit = this.dataset.unit;\n      // Scale values when switching unit\n      var maxVal = unit === '%' ? 50 : 200;\n      cornerKeys.forEach(function(k) {\n        corners[k].h = Math.min(corners[k].h, maxVal);\n        corners[k].v = Math.min(corners[k].v, maxVal);\n      });\n      syncInputs();\n      applyPreview();\n    });\n  });\n\n  /* ── Preset buttons ── */\n  document.querySelectorAll('#br-app .br-preset-btn').forEach(function(btn) {\n    btn.addEventListener('click', function() {\n      document.querySelectorAll('#br-app .br-preset-btn').forEach(function(b){ b.classList.remove('active'); });\n      this.classList.add('active');\n\n      var p = PRESETS[this.dataset.preset];\n      if (!p) return;\n\n      // Switch unit\n      unit = p.unit;\n      document.querySelectorAll('#br-app .br-unit-btn').forEach(function(b){\n        b.classList.toggle('active', b.dataset.unit === unit);\n      });\n\n      // Apply corner values\n      var maxVal = unit === '%' ? 50 : 200;\n      cornerKeys.forEach(function(k) {\n        corners[k].h = Math.min(p[k].h, maxVal);\n        corners[k].v = Math.min(p[k].v, maxVal);\n        corners[k].elliptic = p[k].e;\n      });\n\n      // Pill uses px 999 — clamp displayed but let CSS do the work\n      if (this.dataset.preset === 'pill') {\n        cornerKeys.forEach(function(k) { corners[k].h = 200; });\n      }\n\n      linked = false;\n      linkBtn.classList.remove('active');\n      linkLabel.textContent = 'Corners are independent';\n\n      syncInputs();\n      applyPreview();\n    });\n  });\n\n  /* ── Preview box size / color ── */\n  pwInput.addEventListener('input', function() {\n    preview.style.width = Math.max(40, parseInt(this.value) || 160) + 'px';\n  });\n  phInput.addEventListener('input', function() {\n    preview.style.height = Math.max(40, parseInt(this.value) || 160) + 'px';\n  });\n  pbgInput.addEventListener('input', function() {\n    preview.style.background = this.value;\n  });\n\n  /* ── Copy button ── */\n  copyBtn.addEventListener('click', function() {\n    var text = outputEl.textContent;\n    if (navigator.clipboard) {\n      navigator.clipboard.writeText(text).then(function() { flash(); });\n    } else {\n      var ta = document.createElement('textarea');\n      ta.value = text;\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      flash();\n    }\n    function flash() {\n      copyBtn.textContent = 'Copied!';\n      copyBtn.classList.add('copied');\n      setTimeout(function() {\n        copyBtn.textContent = 'Copy';\n        copyBtn.classList.remove('copied');\n      }, 1800);\n    }\n  });\n\n  /* ── Init ── */\n  syncInputs();\n  applyPreview();\n\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch3 id=\"how-to-use\"\u003eHow to Use\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eChoose a preset\u003c/strong\u003e to start from a known shape, or drag the sliders to build from scratch.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eToggle px / %\u003c/strong\u003e — percentage values are relative to the element\u0026rsquo;s dimensions; pixels are absolute.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eUnlink corners\u003c/strong\u003e to set each corner independently.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eEnable Elliptical\u003c/strong\u003e on any corner to separate horizontal and vertical radii for organic shapes.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eResize the preview\u003c/strong\u003e box to see how your radius looks at different aspect ratios.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eCopy the CSS\u003c/strong\u003e and paste it directly into your stylesheet.\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch3 id=\"quick-reference-border-radius-syntax\"\u003eQuick Reference: border-radius Syntax\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eValue\u003c/th\u003e\n          \u003cth\u003eMeaning\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eborder-radius: 8px\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eAll corners equal\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eborder-radius: 8px 16px\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eTL+BR / TR+BL\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eborder-radius: 8px 16px 24px 32px\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eTL TR BR BL (clockwise)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eborder-radius: 40% 60% / 60% 40%\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eElliptical (H / V)\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003ch3 id=\"related-tools\"\u003eRelated Tools\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003eGenerate box shadows → \u003ca href=\"https://productivity-works.com/tools/box-shadow-generator/\"\u003eCSS Box Shadow Generator\u003c/a\u003e\n\nBuild gradients → \u003ca href=\"https://productivity-works.com/tools/css-gradient-generator/\"\u003eCSS Gradient Generator\u003c/a\u003e\n\nCreate Flexbox layouts → \u003ca href=\"https://productivity-works.com/tools/flexbox-generator/\"\u003eCSS Flexbox Generator\u003c/a\u003e\n\u003c/p\u003e","title":"CSS Border Radius Generator — Free Visual Tool"},{"content":"Adjust margin, border, padding, and content dimensions in real time. The diagram updates instantly — just like Chrome DevTools — and the ready-to-copy CSS output updates alongside it.\nSettings \u0026lt;!-- Presets --\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div class=\u0026quot;bm-section-header\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;bm-section-label\u0026quot; style=\u0026quot;background:#e8eaff;color:#3a3a6e;\u0026quot;\u0026gt;Presets\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bm-presets\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;bm-preset-btn active\u0026quot; onclick=\u0026quot;bmApplyPreset('card',this)\u0026quot;\u0026gt;Card\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;bm-preset-btn\u0026quot; onclick=\u0026quot;bmApplyPreset('button',this)\u0026quot;\u0026gt;Button\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;bm-preset-btn\u0026quot; onclick=\u0026quot;bmApplyPreset('input',this)\u0026quot;\u0026gt;Input\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;bm-preset-btn\u0026quot; onclick=\u0026quot;bmApplyPreset('container',this)\u0026quot;\u0026gt;Container\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;bm-preset-btn\u0026quot; onclick=\u0026quot;bmApplyPreset('reset',this)\u0026quot;\u0026gt;Reset\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Box-sizing --\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div class=\u0026quot;bm-section-header\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;bm-section-label\u0026quot; style=\u0026quot;background:#e8eaff;color:#3a3a6e;\u0026quot;\u0026gt;box-sizing\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bm-sizing-toggle\u0026quot;\u0026gt; \u0026lt;label\u0026gt;\u0026lt;input type=\u0026quot;radio\u0026quot; name=\u0026quot;bm-sizing\u0026quot; value=\u0026quot;content-box\u0026quot; onchange=\u0026quot;bmUpdate()\u0026quot;\u0026gt; content-box\u0026lt;/label\u0026gt; \u0026lt;label\u0026gt;\u0026lt;input type=\u0026quot;radio\u0026quot; name=\u0026quot;bm-sizing\u0026quot; value=\u0026quot;border-box\u0026quot; onchange=\u0026quot;bmUpdate()\u0026quot; checked\u0026gt; border-box\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Content --\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div class=\u0026quot;bm-section-header\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;bm-section-label bm-label-content\u0026quot;\u0026gt;Content\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bm-four-grid\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Width\u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;bm-cw\u0026quot; value=\u0026quot;200\u0026quot; min=\u0026quot;20\u0026quot; max=\u0026quot;800\u0026quot; oninput=\u0026quot;bmUpdate()\u0026quot;\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;label\u0026gt;Height\u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;bm-ch\u0026quot; value=\u0026quot;100\u0026quot; min=\u0026quot;20\u0026quot; max=\u0026quot;800\u0026quot; oninput=\u0026quot;bmUpdate()\u0026quot;\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Padding --\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div class=\u0026quot;bm-section-header\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;bm-section-label bm-label-padding\u0026quot;\u0026gt;Padding\u0026lt;/span\u0026gt; \u0026lt;label class=\u0026quot;bm-link-toggle\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;bm-link-padding\u0026quot; checked onchange=\u0026quot;bmLinkChanged('padding')\u0026quot;\u0026gt; Link sides\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bm-four-grid\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Top\u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;bm-pt\u0026quot; value=\u0026quot;16\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; oninput=\u0026quot;bmSideInput('padding','t')\u0026quot;\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;label\u0026gt;Right\u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;bm-pr\u0026quot; value=\u0026quot;16\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; oninput=\u0026quot;bmSideInput('padding','r')\u0026quot;\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;label\u0026gt;Bottom\u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;bm-pb\u0026quot; value=\u0026quot;16\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; oninput=\u0026quot;bmSideInput('padding','b')\u0026quot;\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;label\u0026gt;Left\u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;bm-pl\u0026quot; value=\u0026quot;16\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; oninput=\u0026quot;bmSideInput('padding','l')\u0026quot;\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Border --\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div class=\u0026quot;bm-section-header\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;bm-section-label bm-label-border\u0026quot;\u0026gt;Border\u0026lt;/span\u0026gt; \u0026lt;label class=\u0026quot;bm-link-toggle\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;bm-link-border\u0026quot; checked onchange=\u0026quot;bmLinkChanged('border')\u0026quot;\u0026gt; Link sides\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bm-four-grid\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Top\u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;bm-bt\u0026quot; value=\u0026quot;1\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;50\u0026quot; oninput=\u0026quot;bmSideInput('border','t')\u0026quot;\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;label\u0026gt;Right\u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;bm-br\u0026quot; value=\u0026quot;1\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;50\u0026quot; oninput=\u0026quot;bmSideInput('border','r')\u0026quot;\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;label\u0026gt;Bottom\u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;bm-bb\u0026quot; value=\u0026quot;1\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;50\u0026quot; oninput=\u0026quot;bmSideInput('border','b')\u0026quot;\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;label\u0026gt;Left\u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;bm-bl\u0026quot; value=\u0026quot;1\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;50\u0026quot; oninput=\u0026quot;bmSideInput('border','l')\u0026quot;\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bm-border-style-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Border style\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;bm-bstyle\u0026quot; onchange=\u0026quot;bmUpdate()\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;solid\u0026quot; selected\u0026gt;solid\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;dashed\u0026quot;\u0026gt;dashed\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;dotted\u0026quot;\u0026gt;dotted\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;double\u0026quot;\u0026gt;double\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;groove\u0026quot;\u0026gt;groove\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;none\u0026quot;\u0026gt;none\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Margin --\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div class=\u0026quot;bm-section-header\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;bm-section-label bm-label-margin\u0026quot;\u0026gt;Margin\u0026lt;/span\u0026gt; \u0026lt;label class=\u0026quot;bm-link-toggle\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;bm-link-margin\u0026quot; checked onchange=\u0026quot;bmLinkChanged('margin')\u0026quot;\u0026gt; Link sides\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bm-four-grid\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Top\u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;bm-mt\u0026quot; value=\u0026quot;16\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; oninput=\u0026quot;bmSideInput('margin','t')\u0026quot;\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;label\u0026gt;Right\u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;bm-mr\u0026quot; value=\u0026quot;16\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; oninput=\u0026quot;bmSideInput('margin','r')\u0026quot;\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;label\u0026gt;Bottom\u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;bm-mb\u0026quot; value=\u0026quot;16\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; oninput=\u0026quot;bmSideInput('margin','b')\u0026quot;\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;label\u0026gt;Left\u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;bm-ml\u0026quot; value=\u0026quot;16\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; oninput=\u0026quot;bmSideInput('margin','l')\u0026quot;\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Box Model Diagram margin border padding 200 × 100 content \u0026lt;!-- Computed size --\u0026gt; \u0026lt;div class=\u0026quot;bm-totals\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;bm-total-box\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;bm-total-val\u0026quot; id=\u0026quot;bm-total-w\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bm-total-lbl\u0026quot;\u0026gt;Total width (px)\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bm-total-box\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;bm-total-val\u0026quot; id=\u0026quot;bm-total-h\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bm-total-lbl\u0026quot;\u0026gt;Total height (px)\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Generated CSS Copy CSS How to Use Choose a preset — Card, Button, Input, or Container loads sensible defaults instantly. Adjust values — Each area (margin, border, padding, content) has independent top/right/bottom/left fields. Enable Link sides to keep all four sides equal. Toggle box-sizing — Switch between content-box and border-box to see how the computed total changes. Read the diagram — Colors match Chrome DevTools: orange = margin, yellow = border, green = padding, blue = content. Copy CSS — Hit Copy CSS and paste directly into your stylesheet. Understanding the CSS Box Model Every HTML element is a rectangular box made of four concentric layers:\nLayer Color Description Content Blue Where text and child elements live. Set by width / height. Padding Green Space between content and border. Background color fills this area. Border Yellow The visible edge of the element. Can have width, style, and color. Margin Orange Invisible space outside the border. Separates elements from each other. content-box vs border-box With content-box (the CSS default) width refers to the content area only — padding and border are added on top. With border-box width includes content, padding, and border, making layouts far more predictable. Most modern projects use box-sizing: border-box globally.\n*, *::before, *::after { box-sizing: border-box; } Margin shorthand reference Syntax Meaning margin: 16px All four sides = 16 px margin: 8px 16px Top/Bottom = 8 px, Left/Right = 16 px margin: 8px 16px 24px Top = 8, Left/Right = 16, Bottom = 24 margin: 8px 12px 16px 20px Top / Right / Bottom / Left Related Tools CSS Spacing Generator — Generate consistent spacing scales CSS Grid Generator — Build grid layouts visually Flexbox Generator — Design flex containers and items ","permalink":"https://productivity-works.com/tools/box-model-visualizer/","summary":"\u003cp\u003eAdjust margin, border, padding, and content dimensions in real time. The diagram updates instantly — just like Chrome DevTools — and the ready-to-copy CSS output updates alongside it.\u003c/p\u003e\n\u003cdiv id=\"bm-app\"\u003e\n\u003cstyle\u003e\n  #bm-app *,\n  #bm-app *::before,\n  #bm-app *::after {\n    box-sizing: border-box;\n    margin: 0;\n    padding: 0;\n  }\n\u003cp\u003e#bm-app {\nfont-family: -apple-system, BlinkMacSystemFont, \u0026ldquo;Segoe UI\u0026rdquo;, Roboto, sans-serif;\nfont-size: 14px;\ncolor: #1a1a2e;\nbackground: #f5f7ff;\nborder-radius: 12px;\npadding: 24px;\nmax-width: 960px;\nmargin: 0 auto;\n}\u003c/p\u003e\n\u003cp\u003e/* ── Layout ─────────────────────────────────────────────── */\n#bm-app .bm-layout {\ndisplay: grid;\ngrid-template-columns: 1fr 1fr;\ngap: 24px;\n}\n@media (max-width: 700px) {\n#bm-app .bm-layout { grid-template-columns: 1fr; }\n}\u003c/p\u003e","title":"CSS Box Model Visualizer"},{"content":" CSS Box Shadow Generator Live Preview \u0026lt;!-- Presets --\u0026gt; \u0026lt;div class=\u0026quot;sa-presets\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;sa-preview-label\u0026quot;\u0026gt;Presets\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sa-preset-grid\u0026quot; id=\u0026quot;sa-preset-grid\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; + Add Shadow Layer Generated CSS Copy CSS Tailwind equivalent: — Related Tools Generate gradients → CSS Gradient Generator Build color palettes → Color Palette Generator Create meta tags → Meta Tag Generator ","permalink":"https://productivity-works.com/tools/box-shadow-generator/","summary":"\u003cdiv id=\"shadow-app\"\u003e\n\u003cstyle\u003e\n#shadow-app *,\n#shadow-app *::before,\n#shadow-app *::after {\n  box-sizing: border-box;\n}\n\n#shadow-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  color: #f3f4f6;\n  background: #374151;\n  border-radius: 12px;\n  padding: 24px;\n  max-width: 900px;\n  margin: 0 auto;\n}\n\n#shadow-app h2 {\n  margin: 0 0 20px;\n  font-size: 1.25rem;\n  font-weight: 600;\n  color: #f9fafb;\n  border-bottom: 1px solid #4b5563;\n  padding-bottom: 10px;\n}\n\n#shadow-app .sa-layout {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 24px;\n}\n\n@media (max-width: 680px) {\n  #shadow-app .sa-layout {\n    grid-template-columns: 1fr;\n  }\n}\n\n/* Preview */\n#shadow-app .sa-preview-area {\n  background: #4b5563;\n  border-radius: 10px;\n  padding: 20px;\n  display: flex;\n  flex-direction: column;\n  gap: 16px;\n}\n\n#shadow-app .sa-preview-label {\n  font-size: 0.75rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #9ca3af;\n}\n\n#shadow-app .sa-preview-box-wrap {\n  flex: 1;\n  min-height: 180px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  background: #374151;\n  border-radius: 8px;\n  padding: 24px;\n}\n\n#shadow-app .sa-preview-box {\n  width: 120px;\n  height: 120px;\n  background: #ffffff;\n  border-radius: 8px;\n  transition: box-shadow 0.2s ease;\n}\n\n/* Controls panel */\n#shadow-app .sa-controls {\n  display: flex;\n  flex-direction: column;\n  gap: 0;\n}\n\n/* Presets */\n#shadow-app .sa-presets {\n  background: #4b5563;\n  border-radius: 10px;\n  padding: 16px;\n  margin-bottom: 16px;\n}\n\n#shadow-app .sa-preset-grid {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 6px;\n  margin-top: 8px;\n}\n\n#shadow-app .sa-preset-btn {\n  background: #374151;\n  border: 1px solid #6b7280;\n  color: #d1d5db;\n  padding: 5px 10px;\n  border-radius: 6px;\n  font-size: 0.75rem;\n  cursor: pointer;\n  transition: background 0.15s, border-color 0.15s, color 0.15s;\n}\n\n#shadow-app .sa-preset-btn:hover,\n#shadow-app .sa-preset-btn.sa-active {\n  background: #6366f1;\n  border-color: #6366f1;\n  color: #fff;\n}\n\n/* Shadow list */\n#shadow-app .sa-shadow-list {\n  display: flex;\n  flex-direction: column;\n  gap: 12px;\n  margin-bottom: 12px;\n}\n\n#shadow-app .sa-shadow-item {\n  background: #4b5563;\n  border-radius: 10px;\n  padding: 14px;\n  position: relative;\n}\n\n#shadow-app .sa-shadow-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 12px;\n}\n\n#shadow-app .sa-shadow-title {\n  font-size: 0.8rem;\n  font-weight: 600;\n  color: #e5e7eb;\n}\n\n#shadow-app .sa-shadow-actions {\n  display: flex;\n  gap: 6px;\n  align-items: center;\n}\n\n#shadow-app .sa-toggle-btn {\n  background: none;\n  border: 1px solid #6b7280;\n  color: #9ca3af;\n  padding: 3px 8px;\n  border-radius: 5px;\n  font-size: 0.7rem;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n\n#shadow-app .sa-toggle-btn.sa-active {\n  background: #818cf8;\n  border-color: #818cf8;\n  color: #fff;\n}\n\n#shadow-app .sa-remove-btn {\n  background: none;\n  border: 1px solid #6b7280;\n  color: #9ca3af;\n  width: 22px;\n  height: 22px;\n  border-radius: 5px;\n  font-size: 0.85rem;\n  line-height: 1;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  transition: all 0.15s;\n}\n\n#shadow-app .sa-remove-btn:hover {\n  background: #ef4444;\n  border-color: #ef4444;\n  color: #fff;\n}\n\n/* Sliders */\n#shadow-app .sa-field {\n  display: grid;\n  grid-template-columns: 90px 1fr 44px;\n  align-items: center;\n  gap: 8px;\n  margin-bottom: 8px;\n}\n\n#shadow-app .sa-field:last-child {\n  margin-bottom: 0;\n}\n\n#shadow-app .sa-field label {\n  font-size: 0.72rem;\n  color: #9ca3af;\n  white-space: nowrap;\n}\n\n#shadow-app .sa-field input[type=\"range\"] {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 100%;\n  height: 4px;\n  border-radius: 2px;\n  background: #6b7280;\n  outline: none;\n  cursor: pointer;\n}\n\n#shadow-app .sa-field input[type=\"range\"]::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 14px;\n  height: 14px;\n  border-radius: 50%;\n  background: #6366f1;\n  cursor: pointer;\n}\n\n#shadow-app .sa-field input[type=\"range\"]::-moz-range-thumb {\n  width: 14px;\n  height: 14px;\n  border-radius: 50%;\n  background: #6366f1;\n  cursor: pointer;\n  border: none;\n}\n\n#shadow-app .sa-field .sa-val {\n  font-size: 0.72rem;\n  color: #d1d5db;\n  text-align: right;\n  font-variant-numeric: tabular-nums;\n  min-width: 40px;\n}\n\n/* Color row */\n#shadow-app .sa-color-row {\n  display: grid;\n  grid-template-columns: 90px 28px 1fr 44px;\n  align-items: center;\n  gap: 8px;\n  margin-bottom: 8px;\n}\n\n#shadow-app .sa-color-row label {\n  font-size: 0.72rem;\n  color: #9ca3af;\n}\n\n#shadow-app .sa-color-row input[type=\"color\"] {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 28px;\n  height: 22px;\n  border: none;\n  background: none;\n  padding: 0;\n  cursor: pointer;\n  border-radius: 4px;\n}\n\n#shadow-app .sa-color-row input[type=\"range\"] {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 100%;\n  height: 4px;\n  border-radius: 2px;\n  background: #6b7280;\n  outline: none;\n  cursor: pointer;\n}\n\n#shadow-app .sa-color-row input[type=\"range\"]::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 14px;\n  height: 14px;\n  border-radius: 50%;\n  background: #6366f1;\n  cursor: pointer;\n}\n\n#shadow-app .sa-color-row input[type=\"range\"]::-moz-range-thumb {\n  width: 14px;\n  height: 14px;\n  border-radius: 50%;\n  background: #6366f1;\n  cursor: pointer;\n  border: none;\n}\n\n#shadow-app .sa-color-row .sa-val {\n  font-size: 0.72rem;\n  color: #d1d5db;\n  text-align: right;\n  font-variant-numeric: tabular-nums;\n}\n\n/* Add shadow button */\n#shadow-app .sa-add-btn {\n  width: 100%;\n  background: #374151;\n  border: 1px dashed #6b7280;\n  color: #9ca3af;\n  padding: 8px;\n  border-radius: 8px;\n  font-size: 0.8rem;\n  cursor: pointer;\n  transition: all 0.15s;\n  margin-bottom: 16px;\n}\n\n#shadow-app .sa-add-btn:hover {\n  border-color: #6366f1;\n  color: #a5b4fc;\n}\n\n#shadow-app .sa-add-btn:disabled {\n  opacity: 0.4;\n  cursor: not-allowed;\n}\n\n/* Output */\n#shadow-app .sa-output {\n  background: #4b5563;\n  border-radius: 10px;\n  padding: 16px;\n  grid-column: 1 / -1;\n}\n\n#shadow-app .sa-output-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 10px;\n}\n\n#shadow-app .sa-copy-btn {\n  background: #6366f1;\n  border: none;\n  color: #fff;\n  padding: 5px 14px;\n  border-radius: 6px;\n  font-size: 0.78rem;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n\n#shadow-app .sa-copy-btn:hover {\n  background: #4f46e5;\n}\n\n#shadow-app .sa-copy-btn.sa-copied {\n  background: #10b981;\n}\n\n#shadow-app code.sa-code-block {\n  display: block;\n  background: #1f2937;\n  border-radius: 6px;\n  padding: 12px 14px;\n  font-family: \"Fira Code\", \"Cascadia Code\", Consolas, monospace;\n  font-size: 0.82rem;\n  color: #86efac;\n  white-space: pre-wrap;\n  word-break: break-all;\n  margin-bottom: 10px;\n  border: 1px solid #374151;\n}\n\n#shadow-app .sa-tailwind-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  flex-wrap: wrap;\n  margin-top: 6px;\n}\n\n#shadow-app .sa-tw-label {\n  font-size: 0.72rem;\n  color: #9ca3af;\n  white-space: nowrap;\n}\n\n#shadow-app code.sa-tw-code {\n  background: #1f2937;\n  border: 1px solid #374151;\n  border-radius: 5px;\n  padding: 3px 8px;\n  font-family: \"Fira Code\", Consolas, monospace;\n  font-size: 0.78rem;\n  color: #7dd3fc;\n}\n\u003c/style\u003e\n\u003ch2\u003eCSS Box Shadow Generator\u003c/h2\u003e\n\u003cdiv class=\"sa-layout\"\u003e\n  \u003c!-- Left: preview + presets + shadows --\u003e\n  \u003cdiv\u003e\n    \u003c!-- Preview --\u003e\n    \u003cdiv class=\"sa-preview-area\" style=\"margin-bottom:16px;\"\u003e\n      \u003cdiv class=\"sa-preview-label\"\u003eLive Preview\u003c/div\u003e\n      \u003cdiv class=\"sa-preview-box-wrap\"\u003e\n        \u003cdiv class=\"sa-preview-box\" id=\"sa-preview-box\"\u003e\u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;!-- Presets --\u0026gt;\n\u0026lt;div class=\u0026quot;sa-presets\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;sa-preview-label\u0026quot;\u0026gt;Presets\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;sa-preset-grid\u0026quot; id=\u0026quot;sa-preset-grid\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n  \u003c!-- Right: shadow controls --\u003e\n  \u003cdiv class=\"sa-controls\"\u003e\n    \u003cdiv class=\"sa-shadow-list\" id=\"sa-shadow-list\"\u003e\u003c/div\u003e\n    \u003cbutton class=\"sa-add-btn\" id=\"sa-add-btn\"\u003e+ Add Shadow Layer\u003c/button\u003e\n  \u003c/div\u003e\n  \u003c!-- Output --\u003e\n  \u003cdiv class=\"sa-output\"\u003e\n    \u003cdiv class=\"sa-output-header\"\u003e\n      \u003cdiv class=\"sa-preview-label\"\u003eGenerated CSS\u003c/div\u003e\n      \u003cbutton class=\"sa-copy-btn\" id=\"sa-copy-btn\"\u003eCopy CSS\u003c/button\u003e\n    \u003c/div\u003e\n    \u003ccode class=\"sa-code-block\" id=\"sa-css-output\"\u003e\u003c/code\u003e\n    \u003cdiv class=\"sa-tailwind-row\"\u003e\n      \u003cspan class=\"sa-tw-label\"\u003eTailwind equivalent:\u003c/span\u003e\n      \u003ccode class=\"sa-tw-code\" id=\"sa-tw-output\"\u003e—\u003c/code\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  \"use strict\";\n\n  /* ── Presets ─────────────────────────────────────────────── */\n  const PRESETS = [\n    {\n      name: \"Subtle\",\n      shadows: [{ x: 0, y: 1, blur: 3, spread: 0, color: \"#000000\", opacity: 0.12, inset: false }],\n    },\n    {\n      name: \"Medium\",\n      shadows: [{ x: 0, y: 4, blur: 12, spread: 0, color: \"#000000\", opacity: 0.18, inset: false }],\n    },\n    {\n      name: \"Large\",\n      shadows: [{ x: 0, y: 10, blur: 30, spread: -5, color: \"#000000\", opacity: 0.25, inset: false }],\n    },\n    {\n      name: \"Sharp\",\n      shadows: [{ x: 4, y: 4, blur: 0, spread: 0, color: \"#000000\", opacity: 0.35, inset: false }],\n    },\n    {\n      name: \"Soft\",\n      shadows: [\n        { x: 0, y: 2, blur: 8, spread: 0, color: \"#000000\", opacity: 0.08, inset: false },\n        { x: 0, y: 8, blur: 24, spread: 0, color: \"#000000\", opacity: 0.12, inset: false },\n      ],\n    },\n    {\n      name: \"Neon\",\n      shadows: [{ x: 0, y: 0, blur: 20, spread: 4, color: \"#6366f1\", opacity: 0.85, inset: false }],\n    },\n    {\n      name: \"Inset\",\n      shadows: [{ x: 0, y: 2, blur: 8, spread: 0, color: \"#000000\", opacity: 0.25, inset: true }],\n    },\n    {\n      name: \"Floating\",\n      shadows: [\n        { x: 0, y: 1, blur: 2, spread: 0, color: \"#000000\", opacity: 0.06, inset: false },\n        { x: 0, y: 4, blur: 6, spread: -1, color: \"#000000\", opacity: 0.1, inset: false },\n        { x: 0, y: 20, blur: 40, spread: -8, color: \"#000000\", opacity: 0.18, inset: false },\n      ],\n    },\n    {\n      name: \"Brutalist\",\n      shadows: [{ x: 6, y: 6, blur: 0, spread: 0, color: \"#111827\", opacity: 1, inset: false }],\n    },\n  ];\n\n  /* ── Tailwind map ─────────────────────────────────────────── */\n  const TW_MAP = [\n    { cls: \"shadow-sm\",  css: \"0 1px 2px 0 rgb(0 0 0 / 0.05)\" },\n    { cls: \"shadow\",     css: \"0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)\" },\n    { cls: \"shadow-md\",  css: \"0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)\" },\n    { cls: \"shadow-lg\",  css: \"0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)\" },\n    { cls: \"shadow-xl\",  css: \"0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)\" },\n    { cls: \"shadow-2xl\", css: \"0 25px 50px -12px rgb(0 0 0 / 0.25)\" },\n    { cls: \"shadow-inner\", css: \"inset 0 2px 4px 0 rgb(0 0 0 / 0.05)\" },\n    { cls: \"shadow-none\", css: \"0 0 #0000\" },\n  ];\n\n  /* ── State ────────────────────────────────────────────────── */\n  let shadows = [\n    { x: 0, y: 4, blur: 12, spread: 0, color: \"#000000\", opacity: 0.18, inset: false },\n  ];\n\n  /* ── Helpers ──────────────────────────────────────────────── */\n  function hexToRgb(hex) {\n    const r = parseInt(hex.slice(1, 3), 16);\n    const g = parseInt(hex.slice(3, 5), 16);\n    const b = parseInt(hex.slice(5, 7), 16);\n    return { r, g, b };\n  }\n\n  function shadowToCSS(s) {\n    const { r, g, b } = hexToRgb(s.color);\n    const alpha = Math.round(s.opacity * 100) / 100;\n    return `${s.inset ? \"inset \" : \"\"}${s.x}px ${s.y}px ${s.blur}px ${s.spread}px rgba(${r}, ${g}, ${b}, ${alpha})`;\n  }\n\n  function buildFullCSS() {\n    const val = shadows.map(shadowToCSS).join(\",\\n             \");\n    return `box-shadow: ${val};`;\n  }\n\n  function guessTailwind() {\n    if (shadows.length !== 1) return null;\n    const s = shadows[0];\n    if (s.inset \u0026\u0026 Math.abs(s.x) \u003c= 1 \u0026\u0026 Math.abs(s.y) \u003c= 3 \u0026\u0026 s.blur \u003c= 6) return \"shadow-inner\";\n    if (!s.inset) {\n      if (s.blur === 0 \u0026\u0026 s.spread === 0 \u0026\u0026 s.opacity === 0) return \"shadow-none\";\n      if (s.blur \u003c= 3 \u0026\u0026 Math.abs(s.y) \u003c= 2) return \"shadow-sm\";\n      if (s.blur \u003c= 6 \u0026\u0026 Math.abs(s.y) \u003c= 4) return \"shadow\";\n      if (s.blur \u003c= 10 \u0026\u0026 Math.abs(s.y) \u003c= 6) return \"shadow-md\";\n      if (s.blur \u003c= 18 \u0026\u0026 Math.abs(s.y) \u003c= 12) return \"shadow-lg\";\n      if (s.blur \u003c= 28 \u0026\u0026 Math.abs(s.y) \u003c= 22) return \"shadow-xl\";\n      if (s.blur \u003c= 55 \u0026\u0026 Math.abs(s.y) \u003c= 28) return \"shadow-2xl\";\n    }\n    return null;\n  }\n\n  /* ── Render ───────────────────────────────────────────────── */\n  function render() {\n    const preview = document.getElementById(\"sa-preview-box\");\n    const cssOut = document.getElementById(\"sa-css-output\");\n    const twOut = document.getElementById(\"sa-tw-output\");\n\n    const shadowCSS = shadows.map(shadowToCSS).join(\", \");\n    preview.style.boxShadow = shadowCSS;\n\n    cssOut.textContent = buildFullCSS();\n\n    const tw = guessTailwind();\n    twOut.textContent = tw ? tw : \"No direct Tailwind equivalent (use arbitrary value)\";\n\n    renderShadowList();\n    updateAddBtn();\n  }\n\n  function updateAddBtn() {\n    const btn = document.getElementById(\"sa-add-btn\");\n    btn.disabled = shadows.length \u003e= 4;\n  }\n\n  function renderShadowList() {\n    const list = document.getElementById(\"sa-shadow-list\");\n    list.innerHTML = \"\";\n\n    shadows.forEach((s, i) =\u003e {\n      const item = document.createElement(\"div\");\n      item.className = \"sa-shadow-item\";\n\n      item.innerHTML = `\n        \u003cdiv class=\"sa-shadow-header\"\u003e\n          \u003cspan class=\"sa-shadow-title\"\u003eShadow ${i + 1}\u003c/span\u003e\n          \u003cdiv class=\"sa-shadow-actions\"\u003e\n            \u003cbutton class=\"sa-toggle-btn${s.inset ? \" sa-active\" : \"\"}\" data-i=\"${i}\" data-action=\"inset\"\u003eInset\u003c/button\u003e\n            ${shadows.length \u003e 1 ? `\u003cbutton class=\"sa-remove-btn\" data-i=\"${i}\" data-action=\"remove\" title=\"Remove\"\u003e×\u003c/button\u003e` : \"\"}\n          \u003c/div\u003e\n        \u003c/div\u003e\n\n        \u003cdiv class=\"sa-field\"\u003e\n          \u003clabel\u003eHorizontal\u003c/label\u003e\n          \u003cinput type=\"range\" min=\"-60\" max=\"60\" value=\"${s.x}\" data-i=\"${i}\" data-prop=\"x\"\u003e\n          \u003cspan class=\"sa-val\"\u003e${s.x}px\u003c/span\u003e\n        \u003c/div\u003e\n        \u003cdiv class=\"sa-field\"\u003e\n          \u003clabel\u003eVertical\u003c/label\u003e\n          \u003cinput type=\"range\" min=\"-60\" max=\"60\" value=\"${s.y}\" data-i=\"${i}\" data-prop=\"y\"\u003e\n          \u003cspan class=\"sa-val\"\u003e${s.y}px\u003c/span\u003e\n        \u003c/div\u003e\n        \u003cdiv class=\"sa-field\"\u003e\n          \u003clabel\u003eBlur\u003c/label\u003e\n          \u003cinput type=\"range\" min=\"0\" max=\"100\" value=\"${s.blur}\" data-i=\"${i}\" data-prop=\"blur\"\u003e\n          \u003cspan class=\"sa-val\"\u003e${s.blur}px\u003c/span\u003e\n        \u003c/div\u003e\n        \u003cdiv class=\"sa-field\"\u003e\n          \u003clabel\u003eSpread\u003c/label\u003e\n          \u003cinput type=\"range\" min=\"-30\" max=\"30\" value=\"${s.spread}\" data-i=\"${i}\" data-prop=\"spread\"\u003e\n          \u003cspan class=\"sa-val\"\u003e${s.spread}px\u003c/span\u003e\n        \u003c/div\u003e\n\n        \u003cdiv class=\"sa-color-row\"\u003e\n          \u003clabel\u003eColor / Opacity\u003c/label\u003e\n          \u003cinput type=\"color\" value=\"${s.color}\" data-i=\"${i}\" data-prop=\"color\"\u003e\n          \u003cinput type=\"range\" min=\"0\" max=\"1\" step=\"0.01\" value=\"${s.opacity}\" data-i=\"${i}\" data-prop=\"opacity\"\u003e\n          \u003cspan class=\"sa-val\"\u003e${Math.round(s.opacity * 100)}%\u003c/span\u003e\n        \u003c/div\u003e\n      `;\n\n      /* Events */\n      item.querySelectorAll(\"input[type='range'][data-prop]\").forEach((el) =\u003e {\n        el.addEventListener(\"input\", (e) =\u003e {\n          const idx = +e.target.dataset.i;\n          const prop = e.target.dataset.prop;\n          shadows[idx][prop] = parseFloat(e.target.value);\n          render();\n        });\n      });\n\n      item.querySelectorAll(\"input[type='color']\").forEach((el) =\u003e {\n        el.addEventListener(\"input\", (e) =\u003e {\n          shadows[+e.target.dataset.i].color = e.target.value;\n          render();\n        });\n      });\n\n      item.querySelectorAll(\"button[data-action]\").forEach((el) =\u003e {\n        el.addEventListener(\"click\", (e) =\u003e {\n          const idx = +e.target.dataset.i;\n          const action = e.target.dataset.action;\n          if (action === \"inset\") {\n            shadows[idx].inset = !shadows[idx].inset;\n            render();\n          } else if (action === \"remove\") {\n            shadows.splice(idx, 1);\n            render();\n          }\n        });\n      });\n\n      list.appendChild(item);\n    });\n  }\n\n  /* ── Presets ──────────────────────────────────────────────── */\n  function renderPresets() {\n    const grid = document.getElementById(\"sa-preset-grid\");\n    PRESETS.forEach((p) =\u003e {\n      const btn = document.createElement(\"button\");\n      btn.className = \"sa-preset-btn\";\n      btn.textContent = p.name;\n      btn.addEventListener(\"click\", () =\u003e {\n        shadows = p.shadows.map((s) =\u003e ({ ...s }));\n        document.querySelectorAll(\"#sa-preset-grid .sa-preset-btn\").forEach((b) =\u003e\n          b.classList.remove(\"sa-active\")\n        );\n        btn.classList.add(\"sa-active\");\n        render();\n      });\n      grid.appendChild(btn);\n    });\n  }\n\n  /* ── Add shadow ───────────────────────────────────────────── */\n  document.getElementById(\"sa-add-btn\").addEventListener(\"click\", () =\u003e {\n    if (shadows.length \u003c 4) {\n      shadows.push({ x: 0, y: 4, blur: 12, spread: 0, color: \"#000000\", opacity: 0.15, inset: false });\n      render();\n    }\n  });\n\n  /* ── Copy CSS ─────────────────────────────────────────────── */\n  document.getElementById(\"sa-copy-btn\").addEventListener(\"click\", () =\u003e {\n    const css = buildFullCSS();\n    navigator.clipboard.writeText(css).then(() =\u003e {\n      const btn = document.getElementById(\"sa-copy-btn\");\n      btn.textContent = \"Copied!\";\n      btn.classList.add(\"sa-copied\");\n      setTimeout(() =\u003e {\n        btn.textContent = \"Copy CSS\";\n        btn.classList.remove(\"sa-copied\");\n      }, 1800);\n    });\n  });\n\n  /* ── Init ─────────────────────────────────────────────────── */\n  renderPresets();\n  render();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch3 id=\"related-tools\"\u003eRelated Tools\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003eGenerate gradients → \u003ca href=\"https://productivity-works.com/tools/css-gradient-generator/\"\u003eCSS Gradient Generator\u003c/a\u003e\n\u003c/p\u003e","title":"CSS Box Shadow Generator — Free Online Tool"},{"content":"Design professional CSS buttons visually — no coding required. Adjust every style property in real time, pick from built-in presets, and copy the finished CSS and HTML with one click.\n\u0026lt;div\u0026gt; \u0026lt;div class=\u0026quot;bgen-section-title\u0026quot;\u0026gt;Presets\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-presets\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;bgen-preset-btn\u0026quot; data-preset=\u0026quot;primary\u0026quot;\u0026gt;Primary\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;bgen-preset-btn\u0026quot; data-preset=\u0026quot;secondary\u0026quot;\u0026gt;Secondary\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;bgen-preset-btn\u0026quot; data-preset=\u0026quot;outline\u0026quot;\u0026gt;Outline\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;bgen-preset-btn\u0026quot; data-preset=\u0026quot;ghost\u0026quot;\u0026gt;Ghost\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;bgen-preset-btn\u0026quot; data-preset=\u0026quot;gradient\u0026quot;\u0026gt;Gradient\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;bgen-preset-btn\u0026quot; data-preset=\u0026quot;threed\u0026quot;\u0026gt;3D\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;bgen-preset-btn\u0026quot; data-preset=\u0026quot;pill\u0026quot;\u0026gt;Pill\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;bgen-preset-btn\u0026quot; data-preset=\u0026quot;icon\u0026quot;\u0026gt;Icon Button\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;hr class=\u0026quot;bgen-divider\u0026quot;\u0026gt; \u0026lt;!-- Button Text --\u0026gt; \u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Button Text\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bgen-text\u0026quot; class=\u0026quot;bgen-select\u0026quot; value=\u0026quot;Click Me\u0026quot; style=\u0026quot;padding:5px 8px;\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Colors --\u0026gt; \u0026lt;div class=\u0026quot;bgen-section-title\u0026quot;\u0026gt;Colors\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Background Color\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;bgen-color-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;bgen-bg\u0026quot; value=\u0026quot;#4f46e5\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bgen-bg-hex\u0026quot; value=\u0026quot;#4f46e5\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Text Color\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;bgen-color-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;bgen-color\u0026quot; value=\u0026quot;#ffffff\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bgen-color-hex\u0026quot; value=\u0026quot;#ffffff\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Border Color\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;bgen-color-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;bgen-border-color\u0026quot; value=\u0026quot;#4f46e5\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bgen-border-color-hex\u0026quot; value=\u0026quot;#4f46e5\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;hr class=\u0026quot;bgen-divider\u0026quot;\u0026gt; \u0026lt;!-- Typography \u0026amp; Size --\u0026gt; \u0026lt;div class=\u0026quot;bgen-section-title\u0026quot;\u0026gt;Size \u0026amp; Typography\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Font Size \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-fontsize-val\u0026quot;\u0026gt;16px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-fontsize\u0026quot; min=\u0026quot;10\u0026quot; max=\u0026quot;36\u0026quot; value=\u0026quot;16\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Padding H \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-padh-val\u0026quot;\u0026gt;20px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-padh\u0026quot; min=\u0026quot;4\u0026quot; max=\u0026quot;80\u0026quot; value=\u0026quot;20\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Padding V \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-padv-val\u0026quot;\u0026gt;12px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-padv\u0026quot; min=\u0026quot;2\u0026quot; max=\u0026quot;40\u0026quot; value=\u0026quot;12\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Border Radius \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-radius-val\u0026quot;\u0026gt;8px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-radius\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;60\u0026quot; value=\u0026quot;8\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;hr class=\u0026quot;bgen-divider\u0026quot;\u0026gt; \u0026lt;!-- Border --\u0026gt; \u0026lt;div class=\u0026quot;bgen-section-title\u0026quot;\u0026gt;Border\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Border Width \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-bwidth-val\u0026quot;\u0026gt;0px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-bwidth\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;8\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Border Style\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;bgen-bstyle\u0026quot; class=\u0026quot;bgen-select\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;solid\u0026quot;\u0026gt;Solid\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;dashed\u0026quot;\u0026gt;Dashed\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;dotted\u0026quot;\u0026gt;Dotted\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;double\u0026quot;\u0026gt;Double\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;hr class=\u0026quot;bgen-divider\u0026quot;\u0026gt; \u0026lt;!-- Box Shadow --\u0026gt; \u0026lt;div class=\u0026quot;bgen-section-title\u0026quot;\u0026gt;Box Shadow\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Shadow X \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-sx-val\u0026quot;\u0026gt;0px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-sx\u0026quot; min=\u0026quot;-20\u0026quot; max=\u0026quot;20\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Shadow Y \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-sy-val\u0026quot;\u0026gt;4px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-sy\u0026quot; min=\u0026quot;-20\u0026quot; max=\u0026quot;20\u0026quot; value=\u0026quot;4\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Shadow Blur \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-sblur-val\u0026quot;\u0026gt;14px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-sblur\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;60\u0026quot; value=\u0026quot;14\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Shadow Spread \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-sspread-val\u0026quot;\u0026gt;0px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-sspread\u0026quot; min=\u0026quot;-10\u0026quot; max=\u0026quot;20\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Shadow Color\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;bgen-color-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;bgen-scolor\u0026quot; value=\u0026quot;#4f46e5\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bgen-scolor-hex\u0026quot; value=\u0026quot;#4f46e5\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Shadow Opacity \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-sopacity-val\u0026quot;\u0026gt;30%\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-sopacity\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;100\u0026quot; value=\u0026quot;30\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;hr class=\u0026quot;bgen-divider\u0026quot;\u0026gt; \u0026lt;!-- Hover Effects --\u0026gt; \u0026lt;div class=\u0026quot;bgen-section-title\u0026quot;\u0026gt;Hover Effects\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Hover Background\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;bgen-color-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;bgen-hbg\u0026quot; value=\u0026quot;#4338ca\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bgen-hbg-hex\u0026quot; value=\u0026quot;#4338ca\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-toggle-row\u0026quot;\u0026gt; \u0026lt;label style=\u0026quot;font-size:13px;font-weight:500;color:#333;\u0026quot;\u0026gt;Scale on Hover\u0026lt;/label\u0026gt; \u0026lt;label class=\u0026quot;bgen-toggle\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;bgen-hover-scale\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;bgen-toggle-slider\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Hover Scale \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-hscale-val\u0026quot;\u0026gt;1.05\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-hscale\u0026quot; min=\u0026quot;100\u0026quot; max=\u0026quot;120\u0026quot; value=\u0026quot;105\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-toggle-row\u0026quot;\u0026gt; \u0026lt;label style=\u0026quot;font-size:13px;font-weight:500;color:#333;\u0026quot;\u0026gt;Hover Shadow\u0026lt;/label\u0026gt; \u0026lt;label class=\u0026quot;bgen-toggle\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;bgen-hover-shadow\u0026quot; checked\u0026gt; \u0026lt;span class=\u0026quot;bgen-toggle-slider\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Transition Duration \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-trans-val\u0026quot;\u0026gt;300ms\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-trans\u0026quot; min=\u0026quot;50\u0026quot; max=\u0026quot;1000\u0026quot; step=\u0026quot;50\u0026quot; value=\u0026quot;300\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-preview-box light\u0026quot; id=\u0026quot;bgen-preview-box\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;bgen-preview-label\u0026quot;\u0026gt;Live Preview\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;bgen-theme-toggle\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;bgen-theme-btn active\u0026quot; data-theme=\u0026quot;light\u0026quot;\u0026gt;Light\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;bgen-theme-btn\u0026quot; data-theme=\u0026quot;dark\u0026quot;\u0026gt;Dark\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button id=\u0026quot;bgen-live-btn\u0026quot;\u0026gt;Click Me\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;bgen-code-block\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;bgen-code-tabs\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;bgen-code-tab active\u0026quot; data-tab=\u0026quot;css\u0026quot;\u0026gt;CSS\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;bgen-code-tab\u0026quot; data-tab=\u0026quot;html\u0026quot;\u0026gt;HTML\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;bgen-copy-btn\u0026quot; id=\u0026quot;bgen-copy-btn\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;div class=\u0026quot;bgen-code-output\u0026quot; id=\u0026quot;bgen-code-output\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; How to Use the CSS Button Generator Choose a preset — start with Primary, Outline, Gradient, or any of the 8 presets. Adjust controls — tweak colors, size, border, shadow, and hover effects live. Toggle preview theme — check how the button looks on light and dark backgrounds. Copy CSS or HTML — switch tabs and click Copy to grab the finished code. Paste into your project — rename .my-button to match your class name. CSS Properties Explained Property What it does background-color Fill color of the button color Text color font-size Text size in pixels padding Inner spacing (vertical / horizontal) border-radius Corner roundness; 9999px = pill shape border Width, style, and color of the border box-shadow Drop or glow shadow with full offset control transition Animation speed for hover effects transform: scale() Scale-up effect on hover Preset Reference Primary — solid indigo fill, subtle shadow, standard border-radius Secondary — muted gray, minimal shadow Outline — transparent background, colored border and text Ghost — no border, no background; background appears on hover only Gradient — linear-gradient background with glow shadow and scale hover 3D — solid bottom shadow creates a raised, pressable look Pill — border-radius: 9999px for fully rounded ends Icon Button — compact square with equal padding, suitable for icon-only actions Tips for Accessible Buttons Maintain at least 4.5:1 contrast ratio between text and background. Never rely on color alone — add a visible focus ring with :focus-visible. Keep tap targets at least 44×44 px for mobile users. Use \u0026lt;button\u0026gt; for actions and \u0026lt;a\u0026gt; for navigation links. Related Free Tools CSS Box Shadow Generator — build and preview multi-layer shadows CSS Gradient Generator — linear, radial, and conic gradients with live preview CSS Animation Generator — keyframe animations with timing and easing controls ","permalink":"https://productivity-works.com/tools/css-button-generator/","summary":"\u003cp\u003eDesign professional CSS buttons visually — no coding required. Adjust every style property in real time, pick from built-in presets, and copy the finished CSS and HTML with one click.\u003c/p\u003e\n\u003cdiv id=\"btn-app\"\u003e\n\u003cstyle\u003e\n#btn-app *,\n#btn-app *::before,\n#btn-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#btn-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 14px;\n  color: #1a1a2e;\n  line-height: 1.5;\n}\n#btn-app .bgen-wrap {\n  display: grid;\n  grid-template-columns: 320px 1fr;\n  gap: 24px;\n  margin: 24px 0;\n}\n@media (max-width: 768px) {\n  #btn-app .bgen-wrap {\n    grid-template-columns: 1fr;\n  }\n}\n/* Panel */\n#btn-app .bgen-panel {\n  background: #f8f9fa;\n  border: 1px solid #e0e0e0;\n  border-radius: 12px;\n  padding: 20px;\n  display: flex;\n  flex-direction: column;\n  gap: 16px;\n}\n#btn-app .bgen-section-title {\n  font-size: 11px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #888;\n  margin-bottom: 4px;\n}\n#btn-app .bgen-field {\n  display: flex;\n  flex-direction: column;\n  gap: 4px;\n}\n#btn-app .bgen-field label {\n  font-size: 13px;\n  font-weight: 500;\n  color: #333;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n#btn-app .bgen-field label span.bgen-val {\n  font-weight: 400;\n  color: #666;\n  font-size: 12px;\n}\n#btn-app .bgen-field input[type=\"range\"] {\n  width: 100%;\n  accent-color: #4f46e5;\n  cursor: pointer;\n}\n#btn-app .bgen-field input[type=\"color\"] {\n  width: 40px;\n  height: 28px;\n  border: 1px solid #ccc;\n  border-radius: 6px;\n  padding: 2px;\n  cursor: pointer;\n  background: none;\n}\n#btn-app .bgen-color-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n#btn-app .bgen-color-row input[type=\"text\"] {\n  flex: 1;\n  border: 1px solid #ccc;\n  border-radius: 6px;\n  padding: 4px 8px;\n  font-size: 12px;\n  font-family: monospace;\n  color: #333;\n}\n#btn-app .bgen-select {\n  width: 100%;\n  border: 1px solid #ccc;\n  border-radius: 6px;\n  padding: 5px 8px;\n  font-size: 13px;\n  background: #fff;\n  color: #333;\n  cursor: pointer;\n}\n#btn-app .bgen-toggle-row {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n}\n#btn-app .bgen-toggle {\n  position: relative;\n  width: 36px;\n  height: 20px;\n}\n#btn-app .bgen-toggle input {\n  opacity: 0;\n  width: 0;\n  height: 0;\n}\n#btn-app .bgen-toggle-slider {\n  position: absolute;\n  inset: 0;\n  background: #ccc;\n  border-radius: 20px;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n#btn-app .bgen-toggle-slider::before {\n  content: \"\";\n  position: absolute;\n  width: 14px;\n  height: 14px;\n  left: 3px;\n  top: 3px;\n  background: #fff;\n  border-radius: 50%;\n  transition: transform 0.2s;\n}\n#btn-app .bgen-toggle input:checked + .bgen-toggle-slider {\n  background: #4f46e5;\n}\n#btn-app .bgen-toggle input:checked + .bgen-toggle-slider::before {\n  transform: translateX(16px);\n}\n#btn-app .bgen-divider {\n  border: none;\n  border-top: 1px solid #e0e0e0;\n}\n/* Presets */\n#btn-app .bgen-presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 6px;\n}\n#btn-app .bgen-preset-btn {\n  border: 1px solid #ccc;\n  border-radius: 6px;\n  padding: 4px 10px;\n  font-size: 12px;\n  cursor: pointer;\n  background: #fff;\n  color: #333;\n  transition: all 0.15s;\n}\n#btn-app .bgen-preset-btn:hover {\n  border-color: #4f46e5;\n  color: #4f46e5;\n}\n/* Preview area */\n#btn-app .bgen-right {\n  display: flex;\n  flex-direction: column;\n  gap: 16px;\n}\n#btn-app .bgen-preview-box {\n  border: 1px solid #e0e0e0;\n  border-radius: 12px;\n  min-height: 180px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  transition: background 0.3s;\n  padding: 32px;\n  position: relative;\n}\n#btn-app .bgen-preview-box.dark {\n  background: #1a1a2e;\n  border-color: #333;\n}\n#btn-app .bgen-preview-box.light {\n  background: #ffffff;\n}\n#btn-app .bgen-preview-label {\n  position: absolute;\n  top: 10px;\n  left: 14px;\n  font-size: 11px;\n  color: #aaa;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n}\n#btn-app .bgen-theme-toggle {\n  position: absolute;\n  top: 8px;\n  right: 12px;\n  display: flex;\n  gap: 6px;\n}\n#btn-app .bgen-theme-btn {\n  border: 1px solid #ccc;\n  border-radius: 6px;\n  padding: 2px 8px;\n  font-size: 11px;\n  cursor: pointer;\n  background: #fff;\n  color: #555;\n}\n#btn-app .bgen-theme-btn.active {\n  background: #4f46e5;\n  color: #fff;\n  border-color: #4f46e5;\n}\n/* Generated button */\n#btn-app #bgen-live-btn {\n  display: inline-block;\n  cursor: pointer;\n  text-decoration: none;\n  border: 2px solid transparent;\n  transition: all 0.3s ease;\n}\n/* Code output */\n#btn-app .bgen-code-block {\n  background: #1e1e2e;\n  border-radius: 10px;\n  padding: 16px;\n  position: relative;\n}\n#btn-app .bgen-code-tabs {\n  display: flex;\n  gap: 4px;\n  margin-bottom: 12px;\n}\n#btn-app .bgen-code-tab {\n  padding: 4px 12px;\n  border-radius: 6px;\n  font-size: 12px;\n  cursor: pointer;\n  background: #2d2d44;\n  color: #aaa;\n  border: none;\n}\n#btn-app .bgen-code-tab.active {\n  background: #4f46e5;\n  color: #fff;\n}\n#btn-app .bgen-code-output {\n  font-family: \"Fira Code\", \"Cascadia Code\", monospace;\n  font-size: 12px;\n  color: #cdd6f4;\n  white-space: pre-wrap;\n  word-break: break-all;\n  line-height: 1.6;\n  max-height: 200px;\n  overflow-y: auto;\n}\n#btn-app .bgen-copy-btn {\n  position: absolute;\n  top: 12px;\n  right: 12px;\n  background: #4f46e5;\n  color: #fff;\n  border: none;\n  border-radius: 6px;\n  padding: 4px 12px;\n  font-size: 12px;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n#btn-app .bgen-copy-btn:hover {\n  background: #4338ca;\n}\n#btn-app .bgen-copy-btn.copied {\n  background: #16a34a;\n}\n\u003c/style\u003e\n\u003cdiv class=\"bgen-wrap\"\u003e\n  \u003c!-- LEFT: Controls --\u003e\n  \u003cdiv class=\"bgen-panel\"\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;div\u0026gt;\n  \u0026lt;div class=\u0026quot;bgen-section-title\u0026quot;\u0026gt;Presets\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;bgen-presets\u0026quot;\u0026gt;\n    \u0026lt;button class=\u0026quot;bgen-preset-btn\u0026quot; data-preset=\u0026quot;primary\u0026quot;\u0026gt;Primary\u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;bgen-preset-btn\u0026quot; data-preset=\u0026quot;secondary\u0026quot;\u0026gt;Secondary\u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;bgen-preset-btn\u0026quot; data-preset=\u0026quot;outline\u0026quot;\u0026gt;Outline\u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;bgen-preset-btn\u0026quot; data-preset=\u0026quot;ghost\u0026quot;\u0026gt;Ghost\u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;bgen-preset-btn\u0026quot; data-preset=\u0026quot;gradient\u0026quot;\u0026gt;Gradient\u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;bgen-preset-btn\u0026quot; data-preset=\u0026quot;threed\u0026quot;\u0026gt;3D\u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;bgen-preset-btn\u0026quot; data-preset=\u0026quot;pill\u0026quot;\u0026gt;Pill\u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;bgen-preset-btn\u0026quot; data-preset=\u0026quot;icon\u0026quot;\u0026gt;Icon Button\u0026lt;/button\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;hr class=\u0026quot;bgen-divider\u0026quot;\u0026gt;\n\n\u0026lt;!-- Button Text --\u0026gt;\n\u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Button Text\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bgen-text\u0026quot; class=\u0026quot;bgen-select\u0026quot; value=\u0026quot;Click Me\u0026quot; style=\u0026quot;padding:5px 8px;\u0026quot;\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Colors --\u0026gt;\n\u0026lt;div class=\u0026quot;bgen-section-title\u0026quot;\u0026gt;Colors\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Background Color\u0026lt;/label\u0026gt;\n  \u0026lt;div class=\u0026quot;bgen-color-row\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;bgen-bg\u0026quot; value=\u0026quot;#4f46e5\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bgen-bg-hex\u0026quot; value=\u0026quot;#4f46e5\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Text Color\u0026lt;/label\u0026gt;\n  \u0026lt;div class=\u0026quot;bgen-color-row\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;bgen-color\u0026quot; value=\u0026quot;#ffffff\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bgen-color-hex\u0026quot; value=\u0026quot;#ffffff\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Border Color\u0026lt;/label\u0026gt;\n  \u0026lt;div class=\u0026quot;bgen-color-row\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;bgen-border-color\u0026quot; value=\u0026quot;#4f46e5\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bgen-border-color-hex\u0026quot; value=\u0026quot;#4f46e5\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;hr class=\u0026quot;bgen-divider\u0026quot;\u0026gt;\n\n\u0026lt;!-- Typography \u0026amp; Size --\u0026gt;\n\u0026lt;div class=\u0026quot;bgen-section-title\u0026quot;\u0026gt;Size \u0026amp; Typography\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Font Size \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-fontsize-val\u0026quot;\u0026gt;16px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-fontsize\u0026quot; min=\u0026quot;10\u0026quot; max=\u0026quot;36\u0026quot; value=\u0026quot;16\u0026quot;\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Padding H \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-padh-val\u0026quot;\u0026gt;20px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-padh\u0026quot; min=\u0026quot;4\u0026quot; max=\u0026quot;80\u0026quot; value=\u0026quot;20\u0026quot;\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Padding V \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-padv-val\u0026quot;\u0026gt;12px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-padv\u0026quot; min=\u0026quot;2\u0026quot; max=\u0026quot;40\u0026quot; value=\u0026quot;12\u0026quot;\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Border Radius \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-radius-val\u0026quot;\u0026gt;8px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-radius\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;60\u0026quot; value=\u0026quot;8\u0026quot;\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;hr class=\u0026quot;bgen-divider\u0026quot;\u0026gt;\n\n\u0026lt;!-- Border --\u0026gt;\n\u0026lt;div class=\u0026quot;bgen-section-title\u0026quot;\u0026gt;Border\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Border Width \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-bwidth-val\u0026quot;\u0026gt;0px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-bwidth\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;8\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Border Style\u0026lt;/label\u0026gt;\n  \u0026lt;select id=\u0026quot;bgen-bstyle\u0026quot; class=\u0026quot;bgen-select\u0026quot;\u0026gt;\n    \u0026lt;option value=\u0026quot;solid\u0026quot;\u0026gt;Solid\u0026lt;/option\u0026gt;\n    \u0026lt;option value=\u0026quot;dashed\u0026quot;\u0026gt;Dashed\u0026lt;/option\u0026gt;\n    \u0026lt;option value=\u0026quot;dotted\u0026quot;\u0026gt;Dotted\u0026lt;/option\u0026gt;\n    \u0026lt;option value=\u0026quot;double\u0026quot;\u0026gt;Double\u0026lt;/option\u0026gt;\n  \u0026lt;/select\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;hr class=\u0026quot;bgen-divider\u0026quot;\u0026gt;\n\n\u0026lt;!-- Box Shadow --\u0026gt;\n\u0026lt;div class=\u0026quot;bgen-section-title\u0026quot;\u0026gt;Box Shadow\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Shadow X \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-sx-val\u0026quot;\u0026gt;0px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-sx\u0026quot; min=\u0026quot;-20\u0026quot; max=\u0026quot;20\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt;\n\u0026lt;/div\u0026gt;\n\u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Shadow Y \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-sy-val\u0026quot;\u0026gt;4px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-sy\u0026quot; min=\u0026quot;-20\u0026quot; max=\u0026quot;20\u0026quot; value=\u0026quot;4\u0026quot;\u0026gt;\n\u0026lt;/div\u0026gt;\n\u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Shadow Blur \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-sblur-val\u0026quot;\u0026gt;14px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-sblur\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;60\u0026quot; value=\u0026quot;14\u0026quot;\u0026gt;\n\u0026lt;/div\u0026gt;\n\u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Shadow Spread \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-sspread-val\u0026quot;\u0026gt;0px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-sspread\u0026quot; min=\u0026quot;-10\u0026quot; max=\u0026quot;20\u0026quot; value=\u0026quot;0\u0026quot;\u0026gt;\n\u0026lt;/div\u0026gt;\n\u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Shadow Color\u0026lt;/label\u0026gt;\n  \u0026lt;div class=\u0026quot;bgen-color-row\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;bgen-scolor\u0026quot; value=\u0026quot;#4f46e5\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bgen-scolor-hex\u0026quot; value=\u0026quot;#4f46e5\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Shadow Opacity \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-sopacity-val\u0026quot;\u0026gt;30%\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-sopacity\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;100\u0026quot; value=\u0026quot;30\u0026quot;\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;hr class=\u0026quot;bgen-divider\u0026quot;\u0026gt;\n\n\u0026lt;!-- Hover Effects --\u0026gt;\n\u0026lt;div class=\u0026quot;bgen-section-title\u0026quot;\u0026gt;Hover Effects\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Hover Background\u0026lt;/label\u0026gt;\n  \u0026lt;div class=\u0026quot;bgen-color-row\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;bgen-hbg\u0026quot; value=\u0026quot;#4338ca\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;bgen-hbg-hex\u0026quot; value=\u0026quot;#4338ca\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;bgen-toggle-row\u0026quot;\u0026gt;\n  \u0026lt;label style=\u0026quot;font-size:13px;font-weight:500;color:#333;\u0026quot;\u0026gt;Scale on Hover\u0026lt;/label\u0026gt;\n  \u0026lt;label class=\u0026quot;bgen-toggle\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;bgen-hover-scale\u0026quot;\u0026gt;\n    \u0026lt;span class=\u0026quot;bgen-toggle-slider\u0026quot;\u0026gt;\u0026lt;/span\u0026gt;\n  \u0026lt;/label\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Hover Scale \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-hscale-val\u0026quot;\u0026gt;1.05\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-hscale\u0026quot; min=\u0026quot;100\u0026quot; max=\u0026quot;120\u0026quot; value=\u0026quot;105\u0026quot;\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;bgen-toggle-row\u0026quot;\u0026gt;\n  \u0026lt;label style=\u0026quot;font-size:13px;font-weight:500;color:#333;\u0026quot;\u0026gt;Hover Shadow\u0026lt;/label\u0026gt;\n  \u0026lt;label class=\u0026quot;bgen-toggle\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;bgen-hover-shadow\u0026quot; checked\u0026gt;\n    \u0026lt;span class=\u0026quot;bgen-toggle-slider\u0026quot;\u0026gt;\u0026lt;/span\u0026gt;\n  \u0026lt;/label\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;bgen-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Transition Duration \u0026lt;span class=\u0026quot;bgen-val\u0026quot; id=\u0026quot;bgen-trans-val\u0026quot;\u0026gt;300ms\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;bgen-trans\u0026quot; min=\u0026quot;50\u0026quot; max=\u0026quot;1000\u0026quot; step=\u0026quot;50\u0026quot; value=\u0026quot;300\u0026quot;\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n  \u003c!-- RIGHT: Preview + Code --\u003e\n  \u003cdiv class=\"bgen-right\"\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;div class=\u0026quot;bgen-preview-box light\u0026quot; id=\u0026quot;bgen-preview-box\u0026quot;\u0026gt;\n  \u0026lt;span class=\u0026quot;bgen-preview-label\u0026quot;\u0026gt;Live Preview\u0026lt;/span\u0026gt;\n  \u0026lt;div class=\u0026quot;bgen-theme-toggle\u0026quot;\u0026gt;\n    \u0026lt;button class=\u0026quot;bgen-theme-btn active\u0026quot; data-theme=\u0026quot;light\u0026quot;\u0026gt;Light\u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;bgen-theme-btn\u0026quot; data-theme=\u0026quot;dark\u0026quot;\u0026gt;Dark\u0026lt;/button\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;button id=\u0026quot;bgen-live-btn\u0026quot;\u0026gt;Click Me\u0026lt;/button\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;bgen-code-block\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;bgen-code-tabs\u0026quot;\u0026gt;\n    \u0026lt;button class=\u0026quot;bgen-code-tab active\u0026quot; data-tab=\u0026quot;css\u0026quot;\u0026gt;CSS\u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;bgen-code-tab\u0026quot; data-tab=\u0026quot;html\u0026quot;\u0026gt;HTML\u0026lt;/button\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;button class=\u0026quot;bgen-copy-btn\u0026quot; id=\u0026quot;bgen-copy-btn\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt;\n  \u0026lt;div class=\u0026quot;bgen-code-output\u0026quot; id=\u0026quot;bgen-code-output\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  // State\n  var S = {\n    text: \"Click Me\",\n    bg: \"#4f46e5\",\n    color: \"#ffffff\",\n    borderColor: \"#4f46e5\",\n    fontSize: 16,\n    padH: 20,\n    padV: 12,\n    radius: 8,\n    bwidth: 0,\n    bstyle: \"solid\",\n    sx: 0, sy: 4, sblur: 14, sspread: 0,\n    scolor: \"#4f46e5\",\n    sopacity: 30,\n    hbg: \"#4338ca\",\n    hoverScale: false,\n    hscale: 1.05,\n    hoverShadow: true,\n    trans: 300,\n    activeTab: \"css\"\n  };\n\n  var presets = {\n    primary: { bg:\"#4f46e5\", color:\"#ffffff\", borderColor:\"#4f46e5\", bwidth:0, radius:8, sx:0, sy:4, sblur:14, sspread:0, scolor:\"#4f46e5\", sopacity:30, hbg:\"#4338ca\", hoverScale:false, hoverShadow:true, padH:20, padV:12 },\n    secondary: { bg:\"#6b7280\", color:\"#ffffff\", borderColor:\"#6b7280\", bwidth:0, radius:8, sx:0, sy:2, sblur:8, sspread:0, scolor:\"#000000\", sopacity:20, hbg:\"#4b5563\", hoverScale:false, hoverShadow:false, padH:20, padV:12 },\n    outline: { bg:\"#ffffff\", color:\"#4f46e5\", borderColor:\"#4f46e5\", bwidth:2, radius:8, sx:0, sy:0, sblur:0, sspread:0, scolor:\"#4f46e5\", sopacity:0, hbg:\"#eef2ff\", hoverScale:false, hoverShadow:false, padH:20, padV:12 },\n    ghost: { bg:\"transparent\", color:\"#4f46e5\", borderColor:\"transparent\", bwidth:0, radius:8, sx:0, sy:0, sblur:0, sspread:0, scolor:\"#4f46e5\", sopacity:0, hbg:\"#eef2ff\", hoverScale:false, hoverShadow:false, padH:20, padV:12 },\n    gradient: { bg:\"#6366f1\", color:\"#ffffff\", borderColor:\"transparent\", bwidth:0, radius:10, sx:0, sy:4, sblur:20, sspread:0, scolor:\"#6366f1\", sopacity:40, hbg:\"#4f46e5\", hoverScale:true, hoverShadow:true, padH:24, padV:14 },\n    threed: { bg:\"#4f46e5\", color:\"#ffffff\", borderColor:\"#3730a3\", bwidth:0, radius:8, sx:0, sy:5, sblur:0, sspread:0, scolor:\"#3730a3\", sopacity:100, hbg:\"#4338ca\", hoverScale:false, hoverShadow:false, padH:20, padV:12 },\n    pill: { bg:\"#4f46e5\", color:\"#ffffff\", borderColor:\"#4f46e5\", bwidth:0, radius:60, sx:0, sy:4, sblur:14, sspread:0, scolor:\"#4f46e5\", sopacity:30, hbg:\"#4338ca\", hoverScale:true, hoverShadow:true, padH:28, padV:12 },\n    icon: { bg:\"#4f46e5\", color:\"#ffffff\", borderColor:\"#4f46e5\", bwidth:0, radius:50, sx:0, sy:4, sblur:14, sspread:0, scolor:\"#4f46e5\", sopacity:30, hbg:\"#4338ca\", hoverScale:true, hoverShadow:true, padH:14, padV:14 }\n  };\n\n  function hexToRgb(hex) {\n    var r = 0, g = 0, b = 0;\n    hex = hex.replace('#','');\n    if(hex.length === 3) hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];\n    if(hex.length === 6) {\n      r = parseInt(hex.substring(0,2),16);\n      g = parseInt(hex.substring(2,4),16);\n      b = parseInt(hex.substring(4,6),16);\n    }\n    return {r:r,g:g,b:b};\n  }\n\n  function shadowCSS() {\n    var rgb = hexToRgb(S.scolor);\n    var a = S.sopacity / 100;\n    return S.sx+'px '+S.sy+'px '+S.sblur+'px '+S.sspread+'px rgba('+rgb.r+','+rgb.g+','+rgb.b+','+a.toFixed(2)+')';\n  }\n\n  function hoverShadowCSS() {\n    var rgb = hexToRgb(S.scolor);\n    return S.sx+'px '+(S.sy+4)+'px '+(S.sblur+10)+'px '+S.sspread+'px rgba('+rgb.r+','+rgb.g+','+rgb.b+',0.45)';\n  }\n\n  function buildBaseCSS() {\n    var bg = S.bg === 'transparent' ? 'transparent' : S.bg;\n    var shadow = (S.sopacity \u003e 0 || S.sx !== 0 || S.sy !== 0 || S.sblur !== 0 || S.sspread !== 0) ? shadowCSS() : 'none';\n    return [\n      'display: inline-block;',\n      'background-color: '+bg+';',\n      'color: '+S.color+';',\n      'font-size: '+S.fontSize+'px;',\n      'padding: '+S.padV+'px '+S.padH+'px;',\n      'border-radius: '+S.radius+'px;',\n      'border: '+S.bwidth+'px '+S.bstyle+' '+S.borderColor+';',\n      'box-shadow: '+shadow+';',\n      'cursor: pointer;',\n      'transition: all '+S.trans+'ms ease;',\n      'text-decoration: none;',\n    ].join('\\n  ');\n  }\n\n  function buildHoverCSS() {\n    var parts = ['background-color: '+S.hbg+';'];\n    if(S.hoverScale) parts.push('transform: scale('+S.hscale+');');\n    if(S.hoverShadow) parts.push('box-shadow: '+hoverShadowCSS()+';');\n    return parts.join('\\n  ');\n  }\n\n  function generateCSS() {\n    return '.my-button {\\n  '+buildBaseCSS()+'\\n}\\n\\n.my-button:hover {\\n  '+buildHoverCSS()+'\\n}';\n  }\n\n  function generateHTML() {\n    return '\u003cbutton class=\"my-button\"\u003e'+S.text+'\u003c/button\u003e';\n  }\n\n  var liveBtn = document.getElementById('bgen-live-btn');\n  var codeOut = document.getElementById('bgen-code-output');\n\n  function applyLiveStyle() {\n    var bg = S.bg === 'transparent' ? 'transparent' : S.bg;\n    var shadow = (S.sopacity \u003e 0 || S.sx !== 0 || S.sy !== 0 || S.sblur !== 0 || S.sspread !== 0) ? shadowCSS() : 'none';\n    liveBtn.textContent = S.text;\n    liveBtn.style.backgroundColor = bg;\n    liveBtn.style.color = S.color;\n    liveBtn.style.fontSize = S.fontSize+'px';\n    liveBtn.style.padding = S.padV+'px '+S.padH+'px';\n    liveBtn.style.borderRadius = S.radius+'px';\n    liveBtn.style.border = S.bwidth+'px '+S.bstyle+' '+S.borderColor;\n    liveBtn.style.boxShadow = shadow;\n    liveBtn.style.transition = 'all '+S.trans+'ms ease';\n    liveBtn.style.cursor = 'pointer';\n    liveBtn.style.background = S.bg === 'gradient'\n      ? 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)'\n      : bg;\n\n    // Hover via mouseover/mouseout\n    liveBtn.onmouseover = function() {\n      this.style.backgroundColor = S.hbg;\n      if(S.hoverScale) this.style.transform = 'scale('+S.hscale+')';\n      if(S.hoverShadow) this.style.boxShadow = hoverShadowCSS();\n    };\n    liveBtn.onmouseout = function() {\n      this.style.backgroundColor = bg;\n      this.style.transform = '';\n      this.style.boxShadow = shadow;\n    };\n  }\n\n  function updateCode() {\n    if(S.activeTab === 'css') {\n      codeOut.textContent = generateCSS();\n    } else {\n      codeOut.textContent = generateHTML();\n    }\n  }\n\n  function update() {\n    applyLiveStyle();\n    updateCode();\n  }\n\n  // Helpers: sync color picker + hex text\n  function syncColor(pickerId, hexId, stateKey) {\n    var picker = document.getElementById(pickerId);\n    var hex = document.getElementById(hexId);\n    picker.addEventListener('input', function() {\n      S[stateKey] = this.value;\n      hex.value = this.value;\n      update();\n    });\n    hex.addEventListener('input', function() {\n      if(/^#[0-9a-fA-F]{6}$/.test(this.value)) {\n        S[stateKey] = this.value;\n        picker.value = this.value;\n        update();\n      }\n    });\n  }\n\n  function syncRange(id, stateKey, valId, suffix, divisor) {\n    var el = document.getElementById(id);\n    var valEl = document.getElementById(valId);\n    el.addEventListener('input', function() {\n      var v = parseFloat(this.value);\n      S[stateKey] = divisor ? v/divisor : v;\n      valEl.textContent = divisor ? (v/divisor).toFixed(2) : v+suffix;\n      update();\n    });\n  }\n\n  // Wire up controls\n  document.getElementById('bgen-text').addEventListener('input', function() {\n    S.text = this.value || \"Button\";\n    update();\n  });\n\n  syncColor('bgen-bg','bgen-bg-hex','bg');\n  syncColor('bgen-color','bgen-color-hex','color');\n  syncColor('bgen-border-color','bgen-border-color-hex','borderColor');\n  syncColor('bgen-scolor','bgen-scolor-hex','scolor');\n  syncColor('bgen-hbg','bgen-hbg-hex','hbg');\n\n  syncRange('bgen-fontsize','fontSize','bgen-fontsize-val','px',null);\n  syncRange('bgen-padh','padH','bgen-padh-val','px',null);\n  syncRange('bgen-padv','padV','bgen-padv-val','px',null);\n  syncRange('bgen-radius','radius','bgen-radius-val','px',null);\n  syncRange('bgen-bwidth','bwidth','bgen-bwidth-val','px',null);\n  syncRange('bgen-sx','sx','bgen-sx-val','px',null);\n  syncRange('bgen-sy','sy','bgen-sy-val','px',null);\n  syncRange('bgen-sblur','sblur','bgen-sblur-val','px',null);\n  syncRange('bgen-sspread','sspread','bgen-sspread-val','px',null);\n  syncRange('bgen-sopacity','sopacity','bgen-sopacity-val','%',null);\n  syncRange('bgen-hscale','hscale','bgen-hscale-val','',100);\n  syncRange('bgen-trans','trans','bgen-trans-val','ms',null);\n\n  document.getElementById('bgen-bstyle').addEventListener('change', function() {\n    S.bstyle = this.value; update();\n  });\n  document.getElementById('bgen-hover-scale').addEventListener('change', function() {\n    S.hoverScale = this.checked; update();\n  });\n  document.getElementById('bgen-hover-shadow').addEventListener('change', function() {\n    S.hoverShadow = this.checked; update();\n  });\n\n  // Presets\n  document.querySelectorAll('#btn-app .bgen-preset-btn').forEach(function(btn) {\n    btn.addEventListener('click', function() {\n      var p = presets[this.dataset.preset];\n      if(!p) return;\n      Object.assign(S, p);\n      // Sync all inputs\n      document.getElementById('bgen-bg').value = S.bg !== 'transparent' ? S.bg : '#ffffff';\n      document.getElementById('bgen-bg-hex').value = S.bg;\n      document.getElementById('bgen-color').value = S.color;\n      document.getElementById('bgen-color-hex').value = S.color;\n      document.getElementById('bgen-border-color').value = S.borderColor !== 'transparent' ? S.borderColor : '#ffffff';\n      document.getElementById('bgen-border-color-hex').value = S.borderColor;\n      document.getElementById('bgen-scolor').value = S.scolor;\n      document.getElementById('bgen-scolor-hex').value = S.scolor;\n      document.getElementById('bgen-hbg').value = S.hbg;\n      document.getElementById('bgen-hbg-hex').value = S.hbg;\n      document.getElementById('bgen-fontsize').value = S.fontSize;\n      document.getElementById('bgen-fontsize-val').textContent = S.fontSize+'px';\n      document.getElementById('bgen-padh').value = S.padH;\n      document.getElementById('bgen-padh-val').textContent = S.padH+'px';\n      document.getElementById('bgen-padv').value = S.padV;\n      document.getElementById('bgen-padv-val').textContent = S.padV+'px';\n      document.getElementById('bgen-radius').value = S.radius;\n      document.getElementById('bgen-radius-val').textContent = S.radius+'px';\n      document.getElementById('bgen-bwidth').value = S.bwidth;\n      document.getElementById('bgen-bwidth-val').textContent = S.bwidth+'px';\n      document.getElementById('bgen-sx').value = S.sx;\n      document.getElementById('bgen-sx-val').textContent = S.sx+'px';\n      document.getElementById('bgen-sy').value = S.sy;\n      document.getElementById('bgen-sy-val').textContent = S.sy+'px';\n      document.getElementById('bgen-sblur').value = S.sblur;\n      document.getElementById('bgen-sblur-val').textContent = S.sblur+'px';\n      document.getElementById('bgen-sspread').value = S.sspread;\n      document.getElementById('bgen-sspread-val').textContent = S.sspread+'px';\n      document.getElementById('bgen-sopacity').value = S.sopacity;\n      document.getElementById('bgen-sopacity-val').textContent = S.sopacity+'%';\n      document.getElementById('bgen-hscale').value = Math.round(S.hscale*100);\n      document.getElementById('bgen-hscale-val').textContent = S.hscale.toFixed(2);\n      document.getElementById('bgen-hover-scale').checked = S.hoverScale;\n      document.getElementById('bgen-hover-shadow').checked = S.hoverShadow;\n      document.getElementById('bgen-trans').value = S.trans;\n      document.getElementById('bgen-trans-val').textContent = S.trans+'ms';\n      update();\n    });\n  });\n\n  // Theme toggle\n  document.querySelectorAll('#btn-app .bgen-theme-btn').forEach(function(btn) {\n    btn.addEventListener('click', function() {\n      document.querySelectorAll('#btn-app .bgen-theme-btn').forEach(function(b){ b.classList.remove('active'); });\n      this.classList.add('active');\n      var box = document.getElementById('bgen-preview-box');\n      box.classList.remove('light','dark');\n      box.classList.add(this.dataset.theme);\n    });\n  });\n\n  // Code tabs\n  document.querySelectorAll('#btn-app .bgen-code-tab').forEach(function(tab) {\n    tab.addEventListener('click', function() {\n      document.querySelectorAll('#btn-app .bgen-code-tab').forEach(function(t){ t.classList.remove('active'); });\n      this.classList.add('active');\n      S.activeTab = this.dataset.tab;\n      updateCode();\n    });\n  });\n\n  // Copy button\n  document.getElementById('bgen-copy-btn').addEventListener('click', function() {\n    var text = codeOut.textContent;\n    var btn = this;\n    if(navigator.clipboard) {\n      navigator.clipboard.writeText(text).then(function() {\n        btn.textContent = 'Copied!';\n        btn.classList.add('copied');\n        setTimeout(function(){ btn.textContent='Copy'; btn.classList.remove('copied'); }, 1800);\n      });\n    } else {\n      var ta = document.createElement('textarea');\n      ta.value = text;\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      btn.textContent = 'Copied!';\n      btn.classList.add('copied');\n      setTimeout(function(){ btn.textContent='Copy'; btn.classList.remove('copied'); }, 1800);\n    }\n  });\n\n  // Init\n  update();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-to-use-the-css-button-generator\"\u003eHow to Use the CSS Button Generator\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eChoose a preset\u003c/strong\u003e — start with Primary, Outline, Gradient, or any of the 8 presets.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAdjust controls\u003c/strong\u003e — tweak colors, size, border, shadow, and hover effects live.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eToggle preview theme\u003c/strong\u003e — check how the button looks on light and dark backgrounds.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eCopy CSS or HTML\u003c/strong\u003e — switch tabs and click Copy to grab the finished code.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ePaste into your project\u003c/strong\u003e — rename \u003ccode\u003e.my-button\u003c/code\u003e to match your class name.\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch2 id=\"css-properties-explained\"\u003eCSS Properties Explained\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eProperty\u003c/th\u003e\n          \u003cth\u003eWhat it does\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003ebackground-color\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eFill color of the button\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003ecolor\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eText color\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003efont-size\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eText size in pixels\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003epadding\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eInner spacing (vertical / horizontal)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eborder-radius\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eCorner roundness; \u003ccode\u003e9999px\u003c/code\u003e = pill shape\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eborder\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eWidth, style, and color of the border\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003ebox-shadow\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eDrop or glow shadow with full offset control\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003etransition\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eAnimation speed for hover effects\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003etransform: scale()\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eScale-up effect on hover\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003ch2 id=\"preset-reference\"\u003ePreset Reference\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003ePrimary\u003c/strong\u003e — solid indigo fill, subtle shadow, standard border-radius\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSecondary\u003c/strong\u003e — muted gray, minimal shadow\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eOutline\u003c/strong\u003e — transparent background, colored border and text\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eGhost\u003c/strong\u003e — no border, no background; background appears on hover only\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eGradient\u003c/strong\u003e — \u003ccode\u003elinear-gradient\u003c/code\u003e background with glow shadow and scale hover\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e3D\u003c/strong\u003e — solid bottom shadow creates a raised, pressable look\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ePill\u003c/strong\u003e — \u003ccode\u003eborder-radius: 9999px\u003c/code\u003e for fully rounded ends\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eIcon Button\u003c/strong\u003e — compact square with equal padding, suitable for icon-only actions\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch2 id=\"tips-for-accessible-buttons\"\u003eTips for Accessible Buttons\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eMaintain at least \u003cstrong\u003e4.5:1 contrast ratio\u003c/strong\u003e between text and background.\u003c/li\u003e\n\u003cli\u003eNever rely on color alone — add a visible focus ring with \u003ccode\u003e:focus-visible\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eKeep tap targets at least \u003cstrong\u003e44×44 px\u003c/strong\u003e for mobile users.\u003c/li\u003e\n\u003cli\u003eUse \u003ccode\u003e\u0026lt;button\u0026gt;\u003c/code\u003e for actions and \u003ccode\u003e\u0026lt;a\u0026gt;\u003c/code\u003e for navigation links.\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-free-tools\"\u003eRelated Free Tools\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://productivity-works.com/tools/box-shadow-generator/\"\u003eCSS Box Shadow Generator\u003c/a\u003e\n — build and preview multi-layer shadows\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://productivity-works.com/tools/css-gradient-generator/\"\u003eCSS Gradient Generator\u003c/a\u003e\n — linear, radial, and conic gradients with live preview\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://productivity-works.com/tools/css-animation-generator/\"\u003eCSS Animation Generator\u003c/a\u003e\n — keyframe animations with timing and easing controls\u003c/li\u003e\n\u003c/ul\u003e","title":"CSS Button Generator — Free Visual Builder"},{"content":" \u0026lt;!-- Shape Type --\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div class=\u0026quot;cp-section-label\u0026quot;\u0026gt;Shape Type\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-shape-tabs\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;cp-tab active\u0026quot; data-type=\u0026quot;polygon\u0026quot; onclick=\u0026quot;cpSetType('polygon')\u0026quot;\u0026gt;Polygon\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cp-tab\u0026quot; data-type=\u0026quot;circle\u0026quot; onclick=\u0026quot;cpSetType('circle')\u0026quot;\u0026gt;Circle\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cp-tab\u0026quot; data-type=\u0026quot;ellipse\u0026quot; onclick=\u0026quot;cpSetType('ellipse')\u0026quot;\u0026gt;Ellipse\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cp-tab\u0026quot; data-type=\u0026quot;inset\u0026quot; onclick=\u0026quot;cpSetType('inset')\u0026quot;\u0026gt;Inset\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cp-tab\u0026quot; data-type=\u0026quot;custom\u0026quot; onclick=\u0026quot;cpSetType('custom')\u0026quot;\u0026gt;Custom\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Polygon Presets (shown when type=polygon) --\u0026gt; \u0026lt;div id=\u0026quot;cp-polygon-panel\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cp-section-label\u0026quot;\u0026gt;Preset Shapes\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-presets-grid\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;cp-preset-btn active\u0026quot; data-preset=\u0026quot;triangle\u0026quot; onclick=\u0026quot;cpLoadPreset('triangle')\u0026quot;\u0026gt; \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;polygon points=\u0026quot;16,4 30,28 2,28\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt; Triangle \u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cp-preset-btn\u0026quot; data-preset=\u0026quot;diamond\u0026quot; onclick=\u0026quot;cpLoadPreset('diamond')\u0026quot;\u0026gt; \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;polygon points=\u0026quot;16,2 30,16 16,30 2,16\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt; Diamond \u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cp-preset-btn\u0026quot; data-preset=\u0026quot;pentagon\u0026quot; onclick=\u0026quot;cpLoadPreset('pentagon')\u0026quot;\u0026gt; \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;polygon points=\u0026quot;16,2 29,11 24,27 8,27 3,11\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt; Pentagon \u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cp-preset-btn\u0026quot; data-preset=\u0026quot;hexagon\u0026quot; onclick=\u0026quot;cpLoadPreset('hexagon')\u0026quot;\u0026gt; \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;polygon points=\u0026quot;16,2 28,9 28,23 16,30 4,23 4,9\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt; Hexagon \u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cp-preset-btn\u0026quot; data-preset=\u0026quot;octagon\u0026quot; onclick=\u0026quot;cpLoadPreset('octagon')\u0026quot;\u0026gt; \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;polygon points=\u0026quot;10,2 22,2 30,10 30,22 22,30 10,30 2,22 2,10\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt; Octagon \u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cp-preset-btn\u0026quot; data-preset=\u0026quot;star\u0026quot; onclick=\u0026quot;cpLoadPreset('star')\u0026quot;\u0026gt; \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;polygon points=\u0026quot;16,2 19.5,12 30,12 21.5,18.5 24.5,29 16,22.5 7.5,29 10.5,18.5 2,12 12.5,12\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt; Star \u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cp-preset-btn\u0026quot; data-preset=\u0026quot;arrow\u0026quot; onclick=\u0026quot;cpLoadPreset('arrow')\u0026quot;\u0026gt; \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;polygon points=\u0026quot;2,10 18,10 18,4 30,16 18,28 18,22 2,22\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt; Arrow \u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cp-preset-btn\u0026quot; data-preset=\u0026quot;cross\u0026quot; onclick=\u0026quot;cpLoadPreset('cross')\u0026quot;\u0026gt; \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;polygon points=\u0026quot;10,2 22,2 22,10 30,10 30,22 22,22 22,30 10,30 10,22 2,22 2,10 10,10\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt; Cross \u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cp-preset-btn\u0026quot; data-preset=\u0026quot;heart\u0026quot; onclick=\u0026quot;cpLoadPreset('heart')\u0026quot;\u0026gt; \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;path d=\u0026quot;M16,28 C16,28 3,18 3,10 C3,6 6,3 10,3 C12.5,3 15,5 16,7 C17,5 19.5,3 22,3 C26,3 29,6 29,10 C29,18 16,28 16,28Z\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt; Heart \u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cp-preset-btn\u0026quot; data-preset=\u0026quot;chevron\u0026quot; onclick=\u0026quot;cpLoadPreset('chevron')\u0026quot;\u0026gt; \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;polygon points=\u0026quot;2,6 18,6 30,16 18,26 2,26 14,16\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt; Chevron \u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Circle controls --\u0026gt; \u0026lt;div id=\u0026quot;cp-circle-panel\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cp-section-label\u0026quot;\u0026gt;Circle Settings\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Radius\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-circle-r\u0026quot; min=\u0026quot;10\u0026quot; max=\u0026quot;50\u0026quot; value=\u0026quot;50\u0026quot; oninput=\u0026quot;cpUpdateCircle()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-circle-r-val\u0026quot;\u0026gt;50%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Center X\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-circle-cx\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;100\u0026quot; value=\u0026quot;50\u0026quot; oninput=\u0026quot;cpUpdateCircle()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-circle-cx-val\u0026quot;\u0026gt;50%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Center Y\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-circle-cy\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;100\u0026quot; value=\u0026quot;50\u0026quot; oninput=\u0026quot;cpUpdateCircle()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-circle-cy-val\u0026quot;\u0026gt;50%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Ellipse controls --\u0026gt; \u0026lt;div id=\u0026quot;cp-ellipse-panel\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cp-section-label\u0026quot;\u0026gt;Ellipse Settings\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Radius X\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-ellipse-rx\u0026quot; min=\u0026quot;5\u0026quot; max=\u0026quot;50\u0026quot; value=\u0026quot;50\u0026quot; oninput=\u0026quot;cpUpdateEllipse()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-ellipse-rx-val\u0026quot;\u0026gt;50%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Radius Y\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-ellipse-ry\u0026quot; min=\u0026quot;5\u0026quot; max=\u0026quot;50\u0026quot; value=\u0026quot;30\u0026quot; oninput=\u0026quot;cpUpdateEllipse()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-ellipse-ry-val\u0026quot;\u0026gt;30%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Center X\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-ellipse-cx\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;100\u0026quot; value=\u0026quot;50\u0026quot; oninput=\u0026quot;cpUpdateEllipse()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-ellipse-cx-val\u0026quot;\u0026gt;50%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Center Y\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-ellipse-cy\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;100\u0026quot; value=\u0026quot;50\u0026quot; oninput=\u0026quot;cpUpdateEllipse()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-ellipse-cy-val\u0026quot;\u0026gt;50%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Inset controls --\u0026gt; \u0026lt;div id=\u0026quot;cp-inset-panel\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cp-section-label\u0026quot;\u0026gt;Inset Settings\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-inset-grid\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot; style=\u0026quot;flex-direction:column;align-items:flex-start;gap:0.2rem\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Top\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-inset-t\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;49\u0026quot; value=\u0026quot;10\u0026quot; oninput=\u0026quot;cpUpdateInset()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-inset-t-val\u0026quot; style=\u0026quot;min-width:auto\u0026quot;\u0026gt;10%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot; style=\u0026quot;flex-direction:column;align-items:flex-start;gap:0.2rem\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Right\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-inset-r\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;49\u0026quot; value=\u0026quot;10\u0026quot; oninput=\u0026quot;cpUpdateInset()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-inset-r-val\u0026quot; style=\u0026quot;min-width:auto\u0026quot;\u0026gt;10%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot; style=\u0026quot;flex-direction:column;align-items:flex-start;gap:0.2rem\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Bottom\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-inset-b\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;49\u0026quot; value=\u0026quot;10\u0026quot; oninput=\u0026quot;cpUpdateInset()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-inset-b-val\u0026quot; style=\u0026quot;min-width:auto\u0026quot;\u0026gt;10%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot; style=\u0026quot;flex-direction:column;align-items:flex-start;gap:0.2rem\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Left\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-inset-l\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;49\u0026quot; value=\u0026quot;10\u0026quot; oninput=\u0026quot;cpUpdateInset()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-inset-l-val\u0026quot; style=\u0026quot;min-width:auto\u0026quot;\u0026gt;10%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot; style=\u0026quot;margin-top:0.5rem\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Border Radius\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-inset-br\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;50\u0026quot; value=\u0026quot;0\u0026quot; oninput=\u0026quot;cpUpdateInset()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-inset-br-val\u0026quot;\u0026gt;0%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Custom polygon panel --\u0026gt; \u0026lt;div id=\u0026quot;cp-custom-panel\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cp-section-label\u0026quot;\u0026gt;Custom Polygon\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-custom-hint\u0026quot;\u0026gt; Click on the preview area to add points. Drag points to move them. Right-click a point to delete it. \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-custom-actions\u0026quot; style=\u0026quot;margin-top:0.6rem\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;cp-btn cp-btn-outline\u0026quot; onclick=\u0026quot;cpUndoPoint()\u0026quot;\u0026gt;Undo\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;cp-btn cp-btn-danger\u0026quot; onclick=\u0026quot;cpClearPoints()\u0026quot;\u0026gt;Clear All\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-points-list\u0026quot; id=\u0026quot;cp-points-list\u0026quot;\u0026gt;No points yet — click preview to start.\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Background color --\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div class=\u0026quot;cp-section-label\u0026quot;\u0026gt;Preview Options\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-color-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Shape Color\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;cp-shape-color\u0026quot; value=\u0026quot;#667eea\u0026quot; oninput=\u0026quot;cpUpdateColor()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-preview-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cp-preview-label\u0026quot;\u0026gt;Live Preview — \u0026lt;span id=\u0026quot;cp-preview-size-label\u0026quot;\u0026gt;\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-canvas-wrap\u0026quot; id=\u0026quot;cp-canvas\u0026quot; onclick=\u0026quot;cpCanvasClick(event)\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cp-shape-el\u0026quot; id=\u0026quot;cp-shape\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-output-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cp-output-header\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cp-output-title\u0026quot;\u0026gt;CSS Output\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026quot;cp-copy-btn\u0026quot; id=\u0026quot;cp-copy-btn\u0026quot; onclick=\u0026quot;cpCopyCSS()\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-output-code\u0026quot; id=\u0026quot;cp-output-code\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Related Tools Generate box shadows → CSS Box Shadow Generator Build border radius → CSS Border Radius Generator Create SVG paths → SVG Path Editor ","permalink":"https://productivity-works.com/tools/clip-path-generator/","summary":"\u003cdiv id=\"cp-app\"\u003e\n\u003cstyle\u003e\n#cp-app *,\n#cp-app *::before,\n#cp-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#cp-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  color: #1a1a2e;\n  max-width: 960px;\n  margin: 0 auto;\n  padding: 0 0 2rem 0;\n}\n#cp-app h2 {\n  font-size: 1.1rem;\n  font-weight: 700;\n  margin-bottom: 0.75rem;\n  color: #16213e;\n}\n#cp-app .cp-layout {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1.5rem;\n  align-items: start;\n}\n@media (max-width: 700px) {\n  #cp-app .cp-layout {\n    grid-template-columns: 1fr;\n  }\n}\n/* Controls panel */\n#cp-app .cp-controls {\n  background: #f8f9fc;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 1.25rem;\n  display: flex;\n  flex-direction: column;\n  gap: 1.1rem;\n}\n#cp-app .cp-section-label {\n  font-size: 0.72rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #718096;\n  margin-bottom: 0.45rem;\n}\n/* Shape type tabs */\n#cp-app .cp-shape-tabs {\n  display: flex;\n  gap: 0.4rem;\n  flex-wrap: wrap;\n}\n#cp-app .cp-tab {\n  padding: 0.35rem 0.75rem;\n  border-radius: 20px;\n  border: 1.5px solid #cbd5e0;\n  background: #fff;\n  font-size: 0.82rem;\n  cursor: pointer;\n  transition: all 0.15s;\n  color: #4a5568;\n  font-weight: 500;\n}\n#cp-app .cp-tab:hover {\n  border-color: #667eea;\n  color: #667eea;\n}\n#cp-app .cp-tab.active {\n  background: #667eea;\n  border-color: #667eea;\n  color: #fff;\n}\n/* Preset grid */\n#cp-app .cp-presets-grid {\n  display: grid;\n  grid-template-columns: repeat(5, 1fr);\n  gap: 0.5rem;\n}\n@media (max-width: 400px) {\n  #cp-app .cp-presets-grid {\n    grid-template-columns: repeat(3, 1fr);\n  }\n}\n#cp-app .cp-preset-btn {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 0.3rem;\n  padding: 0.5rem 0.25rem;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 8px;\n  background: #fff;\n  cursor: pointer;\n  transition: all 0.15s;\n  font-size: 0.7rem;\n  color: #4a5568;\n  font-weight: 500;\n}\n#cp-app .cp-preset-btn:hover {\n  border-color: #667eea;\n  color: #667eea;\n  background: #f0f4ff;\n}\n#cp-app .cp-preset-btn.active {\n  border-color: #667eea;\n  background: #eef1ff;\n  color: #667eea;\n}\n#cp-app .cp-preset-icon {\n  width: 32px;\n  height: 32px;\n  display: block;\n}\n/* Sliders */\n#cp-app .cp-slider-row {\n  display: flex;\n  align-items: center;\n  gap: 0.6rem;\n  margin-bottom: 0.4rem;\n}\n#cp-app .cp-slider-row label {\n  font-size: 0.8rem;\n  color: #4a5568;\n  min-width: 80px;\n  flex-shrink: 0;\n}\n#cp-app .cp-slider-row input[type=range] {\n  flex: 1;\n  accent-color: #667eea;\n  height: 4px;\n  cursor: pointer;\n}\n#cp-app .cp-slider-val {\n  font-size: 0.78rem;\n  color: #667eea;\n  font-weight: 700;\n  min-width: 38px;\n  text-align: right;\n}\n/* Color picker */\n#cp-app .cp-color-row {\n  display: flex;\n  align-items: center;\n  gap: 0.7rem;\n}\n#cp-app .cp-color-row label {\n  font-size: 0.8rem;\n  color: #4a5568;\n}\n#cp-app .cp-color-row input[type=color] {\n  width: 38px;\n  height: 32px;\n  border: none;\n  border-radius: 6px;\n  cursor: pointer;\n  padding: 2px;\n  background: #fff;\n  border: 1px solid #e2e8f0;\n}\n/* Custom polygon controls */\n#cp-app .cp-custom-hint {\n  font-size: 0.78rem;\n  color: #718096;\n  background: #fff9db;\n  border: 1px solid #f6e05e;\n  border-radius: 8px;\n  padding: 0.55rem 0.75rem;\n  line-height: 1.5;\n}\n#cp-app .cp-custom-actions {\n  display: flex;\n  gap: 0.5rem;\n  flex-wrap: wrap;\n}\n#cp-app .cp-btn {\n  padding: 0.42rem 0.9rem;\n  border-radius: 8px;\n  border: 1.5px solid #667eea;\n  background: #667eea;\n  color: #fff;\n  font-size: 0.8rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n#cp-app .cp-btn:hover {\n  background: #5a67d8;\n  border-color: #5a67d8;\n}\n#cp-app .cp-btn-outline {\n  background: #fff;\n  color: #667eea;\n}\n#cp-app .cp-btn-outline:hover {\n  background: #eef1ff;\n}\n#cp-app .cp-btn-danger {\n  background: #fff;\n  border-color: #fc8181;\n  color: #e53e3e;\n}\n#cp-app .cp-btn-danger:hover {\n  background: #fff5f5;\n}\n/* Inset controls */\n#cp-app .cp-inset-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 0.4rem 0.8rem;\n}\n/* Right panel: preview + output */\n#cp-app .cp-right {\n  display: flex;\n  flex-direction: column;\n  gap: 1.25rem;\n}\n/* Preview box */\n#cp-app .cp-preview-wrap {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 1.25rem;\n}\n#cp-app .cp-preview-label {\n  font-size: 0.72rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #718096;\n  margin-bottom: 0.75rem;\n}\n#cp-app .cp-canvas-wrap {\n  position: relative;\n  width: 100%;\n  padding-bottom: 70%;\n  background: repeating-conic-gradient(#f0f0f0 0% 25%, #fff 0% 50%) 0 0 / 18px 18px;\n  border-radius: 8px;\n  overflow: hidden;\n  cursor: crosshair;\n}\n#cp-app .cp-shape-el {\n  position: absolute;\n  inset: 0;\n  background: #667eea;\n  transition: clip-path 0.1s;\n}\n#cp-app .cp-point-dot {\n  position: absolute;\n  width: 14px;\n  height: 14px;\n  border-radius: 50%;\n  background: #fff;\n  border: 2.5px solid #e53e3e;\n  transform: translate(-50%, -50%);\n  cursor: grab;\n  z-index: 10;\n  touch-action: none;\n}\n#cp-app .cp-point-dot:hover {\n  background: #fff5f5;\n  border-color: #c53030;\n}\n/* Output box */\n#cp-app .cp-output-wrap {\n  background: #1a1a2e;\n  border-radius: 12px;\n  padding: 1.1rem 1.25rem;\n}\n#cp-app .cp-output-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 0.6rem;\n}\n#cp-app .cp-output-title {\n  font-size: 0.72rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #a0aec0;\n}\n#cp-app .cp-copy-btn {\n  padding: 0.3rem 0.8rem;\n  border-radius: 20px;\n  border: 1.5px solid #4a5568;\n  background: transparent;\n  color: #a0aec0;\n  font-size: 0.75rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#cp-app .cp-copy-btn:hover {\n  border-color: #667eea;\n  color: #667eea;\n}\n#cp-app .cp-copy-btn.copied {\n  border-color: #48bb78;\n  color: #48bb78;\n}\n#cp-app .cp-output-code {\n  font-family: \"Fira Mono\", \"Cascadia Code\", \"Consolas\", monospace;\n  font-size: 0.82rem;\n  color: #90cdf4;\n  line-height: 1.7;\n  word-break: break-all;\n  white-space: pre-wrap;\n}\n#cp-app .cp-output-code .cp-prop {\n  color: #f6ad55;\n}\n#cp-app .cp-output-code .cp-val {\n  color: #68d391;\n}\n/* Points list */\n#cp-app .cp-points-list {\n  font-size: 0.75rem;\n  color: #718096;\n  background: #f8f9fc;\n  border: 1px solid #e2e8f0;\n  border-radius: 8px;\n  padding: 0.6rem 0.75rem;\n  max-height: 90px;\n  overflow-y: auto;\n  line-height: 1.7;\n  font-family: monospace;\n}\n\u003c/style\u003e\n\u003cdiv class=\"cp-layout\"\u003e\n  \u003c!-- LEFT: Controls --\u003e\n  \u003cdiv class=\"cp-controls\"\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;!-- Shape Type --\u0026gt;\n\u0026lt;div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-section-label\u0026quot;\u0026gt;Shape Type\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-shape-tabs\u0026quot;\u0026gt;\n    \u0026lt;button class=\u0026quot;cp-tab active\u0026quot; data-type=\u0026quot;polygon\u0026quot; onclick=\u0026quot;cpSetType('polygon')\u0026quot;\u0026gt;Polygon\u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;cp-tab\u0026quot; data-type=\u0026quot;circle\u0026quot; onclick=\u0026quot;cpSetType('circle')\u0026quot;\u0026gt;Circle\u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;cp-tab\u0026quot; data-type=\u0026quot;ellipse\u0026quot; onclick=\u0026quot;cpSetType('ellipse')\u0026quot;\u0026gt;Ellipse\u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;cp-tab\u0026quot; data-type=\u0026quot;inset\u0026quot; onclick=\u0026quot;cpSetType('inset')\u0026quot;\u0026gt;Inset\u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;cp-tab\u0026quot; data-type=\u0026quot;custom\u0026quot; onclick=\u0026quot;cpSetType('custom')\u0026quot;\u0026gt;Custom\u0026lt;/button\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Polygon Presets (shown when type=polygon) --\u0026gt;\n\u0026lt;div id=\u0026quot;cp-polygon-panel\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-section-label\u0026quot;\u0026gt;Preset Shapes\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-presets-grid\u0026quot;\u0026gt;\n    \u0026lt;button class=\u0026quot;cp-preset-btn active\u0026quot; data-preset=\u0026quot;triangle\u0026quot; onclick=\u0026quot;cpLoadPreset('triangle')\u0026quot;\u0026gt;\n      \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;polygon points=\u0026quot;16,4 30,28 2,28\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt;\n      Triangle\n    \u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;cp-preset-btn\u0026quot; data-preset=\u0026quot;diamond\u0026quot; onclick=\u0026quot;cpLoadPreset('diamond')\u0026quot;\u0026gt;\n      \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;polygon points=\u0026quot;16,2 30,16 16,30 2,16\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt;\n      Diamond\n    \u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;cp-preset-btn\u0026quot; data-preset=\u0026quot;pentagon\u0026quot; onclick=\u0026quot;cpLoadPreset('pentagon')\u0026quot;\u0026gt;\n      \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;polygon points=\u0026quot;16,2 29,11 24,27 8,27 3,11\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt;\n      Pentagon\n    \u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;cp-preset-btn\u0026quot; data-preset=\u0026quot;hexagon\u0026quot; onclick=\u0026quot;cpLoadPreset('hexagon')\u0026quot;\u0026gt;\n      \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;polygon points=\u0026quot;16,2 28,9 28,23 16,30 4,23 4,9\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt;\n      Hexagon\n    \u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;cp-preset-btn\u0026quot; data-preset=\u0026quot;octagon\u0026quot; onclick=\u0026quot;cpLoadPreset('octagon')\u0026quot;\u0026gt;\n      \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;polygon points=\u0026quot;10,2 22,2 30,10 30,22 22,30 10,30 2,22 2,10\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt;\n      Octagon\n    \u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;cp-preset-btn\u0026quot; data-preset=\u0026quot;star\u0026quot; onclick=\u0026quot;cpLoadPreset('star')\u0026quot;\u0026gt;\n      \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;polygon points=\u0026quot;16,2 19.5,12 30,12 21.5,18.5 24.5,29 16,22.5 7.5,29 10.5,18.5 2,12 12.5,12\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt;\n      Star\n    \u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;cp-preset-btn\u0026quot; data-preset=\u0026quot;arrow\u0026quot; onclick=\u0026quot;cpLoadPreset('arrow')\u0026quot;\u0026gt;\n      \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;polygon points=\u0026quot;2,10 18,10 18,4 30,16 18,28 18,22 2,22\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt;\n      Arrow\n    \u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;cp-preset-btn\u0026quot; data-preset=\u0026quot;cross\u0026quot; onclick=\u0026quot;cpLoadPreset('cross')\u0026quot;\u0026gt;\n      \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;polygon points=\u0026quot;10,2 22,2 22,10 30,10 30,22 22,22 22,30 10,30 10,22 2,22 2,10 10,10\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt;\n      Cross\n    \u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;cp-preset-btn\u0026quot; data-preset=\u0026quot;heart\u0026quot; onclick=\u0026quot;cpLoadPreset('heart')\u0026quot;\u0026gt;\n      \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;path d=\u0026quot;M16,28 C16,28 3,18 3,10 C3,6 6,3 10,3 C12.5,3 15,5 16,7 C17,5 19.5,3 22,3 C26,3 29,6 29,10 C29,18 16,28 16,28Z\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt;\n      Heart\n    \u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;cp-preset-btn\u0026quot; data-preset=\u0026quot;chevron\u0026quot; onclick=\u0026quot;cpLoadPreset('chevron')\u0026quot;\u0026gt;\n      \u0026lt;svg class=\u0026quot;cp-preset-icon\u0026quot; viewBox=\u0026quot;0 0 32 32\u0026quot;\u0026gt;\u0026lt;polygon points=\u0026quot;2,6 18,6 30,16 18,26 2,26 14,16\u0026quot; fill=\u0026quot;#667eea\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt;\n      Chevron\n    \u0026lt;/button\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Circle controls --\u0026gt;\n\u0026lt;div id=\u0026quot;cp-circle-panel\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-section-label\u0026quot;\u0026gt;Circle Settings\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Radius\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-circle-r\u0026quot; min=\u0026quot;10\u0026quot; max=\u0026quot;50\u0026quot; value=\u0026quot;50\u0026quot; oninput=\u0026quot;cpUpdateCircle()\u0026quot;\u0026gt;\n    \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-circle-r-val\u0026quot;\u0026gt;50%\u0026lt;/span\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Center X\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-circle-cx\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;100\u0026quot; value=\u0026quot;50\u0026quot; oninput=\u0026quot;cpUpdateCircle()\u0026quot;\u0026gt;\n    \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-circle-cx-val\u0026quot;\u0026gt;50%\u0026lt;/span\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Center Y\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-circle-cy\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;100\u0026quot; value=\u0026quot;50\u0026quot; oninput=\u0026quot;cpUpdateCircle()\u0026quot;\u0026gt;\n    \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-circle-cy-val\u0026quot;\u0026gt;50%\u0026lt;/span\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Ellipse controls --\u0026gt;\n\u0026lt;div id=\u0026quot;cp-ellipse-panel\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-section-label\u0026quot;\u0026gt;Ellipse Settings\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Radius X\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-ellipse-rx\u0026quot; min=\u0026quot;5\u0026quot; max=\u0026quot;50\u0026quot; value=\u0026quot;50\u0026quot; oninput=\u0026quot;cpUpdateEllipse()\u0026quot;\u0026gt;\n    \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-ellipse-rx-val\u0026quot;\u0026gt;50%\u0026lt;/span\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Radius Y\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-ellipse-ry\u0026quot; min=\u0026quot;5\u0026quot; max=\u0026quot;50\u0026quot; value=\u0026quot;30\u0026quot; oninput=\u0026quot;cpUpdateEllipse()\u0026quot;\u0026gt;\n    \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-ellipse-ry-val\u0026quot;\u0026gt;30%\u0026lt;/span\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Center X\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-ellipse-cx\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;100\u0026quot; value=\u0026quot;50\u0026quot; oninput=\u0026quot;cpUpdateEllipse()\u0026quot;\u0026gt;\n    \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-ellipse-cx-val\u0026quot;\u0026gt;50%\u0026lt;/span\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Center Y\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-ellipse-cy\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;100\u0026quot; value=\u0026quot;50\u0026quot; oninput=\u0026quot;cpUpdateEllipse()\u0026quot;\u0026gt;\n    \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-ellipse-cy-val\u0026quot;\u0026gt;50%\u0026lt;/span\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Inset controls --\u0026gt;\n\u0026lt;div id=\u0026quot;cp-inset-panel\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-section-label\u0026quot;\u0026gt;Inset Settings\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-inset-grid\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot; style=\u0026quot;flex-direction:column;align-items:flex-start;gap:0.2rem\u0026quot;\u0026gt;\n      \u0026lt;label\u0026gt;Top\u0026lt;/label\u0026gt;\n      \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-inset-t\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;49\u0026quot; value=\u0026quot;10\u0026quot; oninput=\u0026quot;cpUpdateInset()\u0026quot;\u0026gt;\n      \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-inset-t-val\u0026quot; style=\u0026quot;min-width:auto\u0026quot;\u0026gt;10%\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot; style=\u0026quot;flex-direction:column;align-items:flex-start;gap:0.2rem\u0026quot;\u0026gt;\n      \u0026lt;label\u0026gt;Right\u0026lt;/label\u0026gt;\n      \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-inset-r\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;49\u0026quot; value=\u0026quot;10\u0026quot; oninput=\u0026quot;cpUpdateInset()\u0026quot;\u0026gt;\n      \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-inset-r-val\u0026quot; style=\u0026quot;min-width:auto\u0026quot;\u0026gt;10%\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot; style=\u0026quot;flex-direction:column;align-items:flex-start;gap:0.2rem\u0026quot;\u0026gt;\n      \u0026lt;label\u0026gt;Bottom\u0026lt;/label\u0026gt;\n      \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-inset-b\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;49\u0026quot; value=\u0026quot;10\u0026quot; oninput=\u0026quot;cpUpdateInset()\u0026quot;\u0026gt;\n      \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-inset-b-val\u0026quot; style=\u0026quot;min-width:auto\u0026quot;\u0026gt;10%\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot; style=\u0026quot;flex-direction:column;align-items:flex-start;gap:0.2rem\u0026quot;\u0026gt;\n      \u0026lt;label\u0026gt;Left\u0026lt;/label\u0026gt;\n      \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-inset-l\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;49\u0026quot; value=\u0026quot;10\u0026quot; oninput=\u0026quot;cpUpdateInset()\u0026quot;\u0026gt;\n      \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-inset-l-val\u0026quot; style=\u0026quot;min-width:auto\u0026quot;\u0026gt;10%\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-slider-row\u0026quot; style=\u0026quot;margin-top:0.5rem\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Border Radius\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;cp-inset-br\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;50\u0026quot; value=\u0026quot;0\u0026quot; oninput=\u0026quot;cpUpdateInset()\u0026quot;\u0026gt;\n    \u0026lt;span class=\u0026quot;cp-slider-val\u0026quot; id=\u0026quot;cp-inset-br-val\u0026quot;\u0026gt;0%\u0026lt;/span\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Custom polygon panel --\u0026gt;\n\u0026lt;div id=\u0026quot;cp-custom-panel\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-section-label\u0026quot;\u0026gt;Custom Polygon\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-custom-hint\u0026quot;\u0026gt;\n    Click on the preview area to add points. Drag points to move them. Right-click a point to delete it.\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-custom-actions\u0026quot; style=\u0026quot;margin-top:0.6rem\u0026quot;\u0026gt;\n    \u0026lt;button class=\u0026quot;cp-btn cp-btn-outline\u0026quot; onclick=\u0026quot;cpUndoPoint()\u0026quot;\u0026gt;Undo\u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;cp-btn cp-btn-danger\u0026quot; onclick=\u0026quot;cpClearPoints()\u0026quot;\u0026gt;Clear All\u0026lt;/button\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-points-list\u0026quot; id=\u0026quot;cp-points-list\u0026quot;\u0026gt;No points yet — click preview to start.\u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Background color --\u0026gt;\n\u0026lt;div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-section-label\u0026quot;\u0026gt;Preview Options\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-color-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Shape Color\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;cp-shape-color\u0026quot; value=\u0026quot;#667eea\u0026quot; oninput=\u0026quot;cpUpdateColor()\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\u003c!-- /cp-controls --\u003e\n  \u003c!-- RIGHT: Preview + Output --\u003e\n  \u003cdiv class=\"cp-right\"\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;div class=\u0026quot;cp-preview-wrap\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-preview-label\u0026quot;\u0026gt;Live Preview — \u0026lt;span id=\u0026quot;cp-preview-size-label\u0026quot;\u0026gt;\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-canvas-wrap\u0026quot; id=\u0026quot;cp-canvas\u0026quot; onclick=\u0026quot;cpCanvasClick(event)\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;cp-shape-el\u0026quot; id=\u0026quot;cp-shape\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;cp-output-wrap\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-output-header\u0026quot;\u0026gt;\n    \u0026lt;span class=\u0026quot;cp-output-title\u0026quot;\u0026gt;CSS Output\u0026lt;/span\u0026gt;\n    \u0026lt;button class=\u0026quot;cp-copy-btn\u0026quot; id=\u0026quot;cp-copy-btn\u0026quot; onclick=\u0026quot;cpCopyCSS()\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-output-code\u0026quot; id=\u0026quot;cp-output-code\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\u003c!-- /cp-right --\u003e\n\u003c/div\u003e\u003c!-- /cp-layout --\u003e\n\u003c/div\u003e\u003c!-- /cp-app --\u003e\n\u003cscript\u003e\n(function() {\n  // ── State ──────────────────────────────────────────────────────────────────\n  var cpState = {\n    type: 'polygon',\n    preset: 'triangle',\n    clipPath: '',\n    customPoints: [],\n    draggingIdx: -1,\n    dragOffX: 0,\n    dragOffY: 0,\n  };\n\n  // ── Preset definitions (% values) ─────────────────────────────────────────\n  var PRESETS = {\n    triangle:  [[50,0],[100,100],[0,100]],\n    diamond:   [[50,0],[100,50],[50,100],[0,50]],\n    pentagon:  [[50,0],[100,38],[82,100],[18,100],[0,38]],\n    hexagon:   [[50,0],[100,25],[100,75],[50,100],[0,75],[0,25]],\n    octagon:   [[30,0],[70,0],[100,30],[100,70],[70,100],[30,100],[0,70],[0,30]],\n    star:      [[50,0],[61,35],[98,35],[68,57],[79,91],[50,70],[21,91],[32,57],[2,35],[39,35]],\n    arrow:     [[0,35],[60,35],[60,10],[100,50],[60,90],[60,65],[0,65]],\n    cross:     [[35,0],[65,0],[65,35],[100,35],[100,65],[65,65],[65,100],[35,100],[35,65],[0,65],[0,35],[35,35]],\n    heart:     [[50,75],[15,45],[5,30],[5,20],[15,10],[25,10],[35,15],[50,30],[65,15],[75,10],[85,10],[95,20],[95,30],[85,45]],\n    chevron:   [[0,20],[65,20],[100,50],[65,80],[0,80],[35,50]],\n  };\n\n  // ── Init ───────────────────────────────────────────────────────────────────\n  function init() {\n    updateSizeLabel();\n    cpLoadPreset('triangle');\n    window.addEventListener('resize', updateSizeLabel);\n\n    // Drag support\n    var canvas = document.getElementById('cp-canvas');\n    canvas.addEventListener('mousemove', onMouseMove);\n    canvas.addEventListener('mouseup', onMouseUp);\n    canvas.addEventListener('mouseleave', onMouseUp);\n    canvas.addEventListener('touchmove', onTouchMove, {passive: false});\n    canvas.addEventListener('touchend', onTouchEnd);\n  }\n\n  function updateSizeLabel() {\n    var canvas = document.getElementById('cp-canvas');\n    var rect = canvas.getBoundingClientRect();\n    document.getElementById('cp-preview-size-label').textContent =\n      Math.round(rect.width) + ' × ' + Math.round(rect.height) + 'px';\n  }\n\n  // ── Type switching ─────────────────────────────────────────────────────────\n  window.cpSetType = function(type) {\n    cpState.type = type;\n    document.querySelectorAll('#cp-app .cp-tab').forEach(function(t) {\n      t.classList.toggle('active', t.dataset.type === type);\n    });\n    document.getElementById('cp-polygon-panel').style.display  = type === 'polygon'  ? '' : 'none';\n    document.getElementById('cp-circle-panel').style.display   = type === 'circle'   ? '' : 'none';\n    document.getElementById('cp-ellipse-panel').style.display  = type === 'ellipse'  ? '' : 'none';\n    document.getElementById('cp-inset-panel').style.display    = type === 'inset'    ? '' : 'none';\n    document.getElementById('cp-custom-panel').style.display   = type === 'custom'   ? '' : 'none';\n    clearDots();\n    if (type === 'polygon') { cpLoadPreset(cpState.preset); }\n    else if (type === 'circle')  { cpUpdateCircle(); }\n    else if (type === 'ellipse') { cpUpdateEllipse(); }\n    else if (type === 'inset')   { cpUpdateInset(); }\n    else if (type === 'custom')  { renderCustomPoints(); applyClipPath(buildPolygonCSS(cpState.customPoints)); }\n  };\n\n  // ── Presets ────────────────────────────────────────────────────────────────\n  window.cpLoadPreset = function(name) {\n    cpState.preset = name;\n    document.querySelectorAll('#cp-app .cp-preset-btn').forEach(function(b) {\n      b.classList.toggle('active', b.dataset.preset === name);\n    });\n    clearDots();\n    var pts = PRESETS[name];\n    applyClipPath(buildPolygonCSS(pts));\n  };\n\n  function buildPolygonCSS(pts) {\n    if (!pts || pts.length \u003c 3) return 'none';\n    var pairs = pts.map(function(p) { return p[0] + '% ' + p[1] + '%'; });\n    return 'polygon(' + pairs.join(', ') + ')';\n  }\n\n  // ── Circle ─────────────────────────────────────────────────────────────────\n  window.cpUpdateCircle = function() {\n    var r  = document.getElementById('cp-circle-r').value;\n    var cx = document.getElementById('cp-circle-cx').value;\n    var cy = document.getElementById('cp-circle-cy').value;\n    document.getElementById('cp-circle-r-val').textContent  = r + '%';\n    document.getElementById('cp-circle-cx-val').textContent = cx + '%';\n    document.getElementById('cp-circle-cy-val').textContent = cy + '%';\n    applyClipPath('circle(' + r + '% at ' + cx + '% ' + cy + '%)');\n  };\n\n  // ── Ellipse ────────────────────────────────────────────────────────────────\n  window.cpUpdateEllipse = function() {\n    var rx = document.getElementById('cp-ellipse-rx').value;\n    var ry = document.getElementById('cp-ellipse-ry').value;\n    var cx = document.getElementById('cp-ellipse-cx').value;\n    var cy = document.getElementById('cp-ellipse-cy').value;\n    document.getElementById('cp-ellipse-rx-val').textContent = rx + '%';\n    document.getElementById('cp-ellipse-ry-val').textContent = ry + '%';\n    document.getElementById('cp-ellipse-cx-val').textContent = cx + '%';\n    document.getElementById('cp-ellipse-cy-val').textContent = cy + '%';\n    applyClipPath('ellipse(' + rx + '% ' + ry + '% at ' + cx + '% ' + cy + '%)');\n  };\n\n  // ── Inset ──────────────────────────────────────────────────────────────────\n  window.cpUpdateInset = function() {\n    var t  = document.getElementById('cp-inset-t').value;\n    var r  = document.getElementById('cp-inset-r').value;\n    var b  = document.getElementById('cp-inset-b').value;\n    var l  = document.getElementById('cp-inset-l').value;\n    var br = document.getElementById('cp-inset-br').value;\n    document.getElementById('cp-inset-t-val').textContent  = t + '%';\n    document.getElementById('cp-inset-r-val').textContent  = r + '%';\n    document.getElementById('cp-inset-b-val').textContent  = b + '%';\n    document.getElementById('cp-inset-l-val').textContent  = l + '%';\n    document.getElementById('cp-inset-br-val').textContent = br + '%';\n    var val = 'inset(' + t + '% ' + r + '% ' + b + '% ' + l + '%';\n    if (parseInt(br) \u003e 0) val += ' round ' + br + '%';\n    val += ')';\n    applyClipPath(val);\n  };\n\n  // ── Custom polygon ─────────────────────────────────────────────────────────\n  window.cpCanvasClick = function(e) {\n    if (cpState.type !== 'custom') return;\n    if (cpState.draggingIdx \u003e= 0) return;\n    var pos = getCanvasPos(e);\n    cpState.customPoints.push([Math.round(pos.x), Math.round(pos.y)]);\n    renderCustomPoints();\n    applyClipPath(buildPolygonCSS(cpState.customPoints));\n  };\n\n  window.cpUndoPoint = function() {\n    if (cpState.customPoints.length \u003e 0) {\n      cpState.customPoints.pop();\n      renderCustomPoints();\n      applyClipPath(buildPolygonCSS(cpState.customPoints));\n    }\n  };\n\n  window.cpClearPoints = function() {\n    cpState.customPoints = [];\n    renderCustomPoints();\n    applyClipPath('none');\n  };\n\n  function getCanvasPos(e) {\n    var canvas = document.getElementById('cp-canvas');\n    var rect = canvas.getBoundingClientRect();\n    var clientX = e.clientX !== undefined ? e.clientX : e.touches[0].clientX;\n    var clientY = e.clientY !== undefined ? e.clientY : e.touches[0].clientY;\n    return {\n      x: Math.min(100, Math.max(0, ((clientX - rect.left) / rect.width) * 100)),\n      y: Math.min(100, Math.max(0, ((clientY - rect.top)  / rect.height) * 100))\n    };\n  }\n\n  function renderCustomPoints() {\n    clearDots();\n    var canvas = document.getElementById('cp-canvas');\n    cpState.customPoints.forEach(function(pt, idx) {\n      var dot = document.createElement('div');\n      dot.className = 'cp-point-dot';\n      dot.style.left = pt[0] + '%';\n      dot.style.top  = pt[1] + '%';\n      dot.dataset.idx = idx;\n      dot.addEventListener('mousedown', function(e) { e.stopPropagation(); startDrag(idx, e); });\n      dot.addEventListener('touchstart', function(e) { e.stopPropagation(); startDragTouch(idx, e); }, {passive: false});\n      dot.addEventListener('contextmenu', function(e) {\n        e.preventDefault();\n        cpState.customPoints.splice(idx, 1);\n        renderCustomPoints();\n        applyClipPath(buildPolygonCSS(cpState.customPoints));\n      });\n      canvas.appendChild(dot);\n    });\n    // Points list\n    var list = document.getElementById('cp-points-list');\n    if (cpState.customPoints.length === 0) {\n      list.textContent = 'No points yet — click preview to start.';\n    } else {\n      list.textContent = cpState.customPoints.map(function(p, i) {\n        return 'P' + (i+1) + ': ' + p[0] + '% ' + p[1] + '%';\n      }).join('   ');\n    }\n  }\n\n  function clearDots() {\n    document.querySelectorAll('#cp-canvas .cp-point-dot').forEach(function(d) { d.remove(); });\n  }\n\n  function startDrag(idx, e) {\n    cpState.draggingIdx = idx;\n    e.preventDefault();\n  }\n  function startDragTouch(idx, e) {\n    cpState.draggingIdx = idx;\n    e.preventDefault();\n  }\n  function onMouseMove(e) {\n    if (cpState.draggingIdx \u003c 0) return;\n    var pos = getCanvasPos(e);\n    cpState.customPoints[cpState.draggingIdx] = [Math.round(pos.x), Math.round(pos.y)];\n    renderCustomPoints();\n    applyClipPath(buildPolygonCSS(cpState.customPoints));\n  }\n  function onMouseUp() { cpState.draggingIdx = -1; }\n  function onTouchMove(e) {\n    if (cpState.draggingIdx \u003c 0) return;\n    e.preventDefault();\n    var pos = getCanvasPos(e);\n    cpState.customPoints[cpState.draggingIdx] = [Math.round(pos.x), Math.round(pos.y)];\n    renderCustomPoints();\n    applyClipPath(buildPolygonCSS(cpState.customPoints));\n  }\n  function onTouchEnd() { cpState.draggingIdx = -1; }\n\n  // ── Color ──────────────────────────────────────────────────────────────────\n  window.cpUpdateColor = function() {\n    var color = document.getElementById('cp-shape-color').value;\n    document.getElementById('cp-shape').style.background = color;\n  };\n\n  // ── Apply clip-path ────────────────────────────────────────────────────────\n  function applyClipPath(val) {\n    cpState.clipPath = val;\n    var shape = document.getElementById('cp-shape');\n    shape.style.clipPath = val;\n    renderOutput(val);\n  }\n\n  function renderOutput(val) {\n    var code = document.getElementById('cp-output-code');\n    code.innerHTML =\n      '\u003cspan class=\"cp-prop\"\u003e.your-element\u003c/span\u003e {\\n' +\n      '  \u003cspan class=\"cp-prop\"\u003eclip-path\u003c/span\u003e: \u003cspan class=\"cp-val\"\u003e' + escHtml(val) + '\u003c/span\u003e;\\n' +\n      '  \u003cspan class=\"cp-prop\"\u003e-webkit-clip-path\u003c/span\u003e: \u003cspan class=\"cp-val\"\u003e' + escHtml(val) + '\u003c/span\u003e;\\n' +\n      '}';\n  }\n\n  function escHtml(s) {\n    return s.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n  }\n\n  // ── Copy ───────────────────────────────────────────────────────────────────\n  window.cpCopyCSS = function() {\n    var text = '.your-element {\\n  clip-path: ' + cpState.clipPath + ';\\n  -webkit-clip-path: ' + cpState.clipPath + ';\\n}';\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(text).then(flashCopied);\n    } else {\n      var ta = document.createElement('textarea');\n      ta.value = text;\n      ta.style.position = 'fixed';\n      ta.style.opacity = '0';\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      ta.remove();\n      flashCopied();\n    }\n  };\n\n  function flashCopied() {\n    var btn = document.getElementById('cp-copy-btn');\n    btn.textContent = 'Copied!';\n    btn.classList.add('copied');\n    setTimeout(function() {\n      btn.textContent = 'Copy';\n      btn.classList.remove('copied');\n    }, 1800);\n  }\n\n  // ── Boot ───────────────────────────────────────────────────────────────────\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', init);\n  } else {\n    init();\n  }\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch3 id=\"related-tools\"\u003eRelated Tools\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003eGenerate box shadows → \u003ca href=\"https://productivity-works.com/tools/box-shadow-generator/\"\u003eCSS Box Shadow Generator\u003c/a\u003e\n\nBuild border radius → \u003ca href=\"https://productivity-works.com/tools/border-radius-generator/\"\u003eCSS Border Radius Generator\u003c/a\u003e\n\nCreate SVG paths → \u003ca href=\"https://productivity-works.com/tools/svg-path-editor/\"\u003eSVG Path Editor\u003c/a\u003e\n\u003c/p\u003e","title":"CSS Clip-Path Generator — Shape Builder"},{"content":" Drag points to reshape Click on the shape edge to add a point. Right-click a point to delete it. Presets \u0026lt;!-- Background --\u0026gt; \u0026lt;div class=\u0026quot;cp-card\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Preview\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;cp-bg-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Shape color\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; class=\u0026quot;cp-color-input\u0026quot; id=\u0026quot;cp-shape-color\u0026quot; value=\u0026quot;#6366f1\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-bg-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;BG color\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; class=\u0026quot;cp-color-input\u0026quot; id=\u0026quot;cp-bg-color\u0026quot; value=\u0026quot;#e2e8f0\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cp-bg-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Image\u0026lt;/label\u0026gt; \u0026lt;button class=\u0026quot;cp-img-btn\u0026quot; id=\u0026quot;cp-upload-btn\u0026quot;\u0026gt;Upload Image\u0026lt;/button\u0026gt; \u0026lt;input type=\u0026quot;file\u0026quot; id=\u0026quot;cp-file-input\u0026quot; accept=\u0026quot;image/*\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;cp-reset-btn\u0026quot; id=\u0026quot;cp-clear-img\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt;Remove Image\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Output --\u0026gt; \u0026lt;div class=\u0026quot;cp-card\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;CSS Output\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;cp-output-box\u0026quot; id=\u0026quot;cp-output\u0026quot;\u0026gt;clip-path: polygon(...);\u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;cp-copy-btn\u0026quot; id=\u0026quot;cp-copy-btn\u0026quot;\u0026gt;Copy CSS\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Shape info --\u0026gt; \u0026lt;div class=\u0026quot;cp-shape-info\u0026quot; id=\u0026quot;cp-shape-info\u0026quot;\u0026gt;polygon · 0 points\u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;cp-reset-btn\u0026quot; id=\u0026quot;cp-reset-btn\u0026quot;\u0026gt;Reset to Preset\u0026lt;/button\u0026gt; How to Use the CSS Clip-Path Maker Choose a preset from the shape gallery — triangle, star, hexagon, and more. Drag the white control points to reshape the polygon to your liking. Double-click on the canvas to add new points along the nearest edge. Right-click a point to remove it (minimum 3 points). Customize the shape color and background color in the Preview panel. Upload an image to see how your clip-path looks on a real photo. Copy the CSS and paste it directly into your stylesheet. Understanding clip-path The clip-path CSS property lets you define a visible region for an element — everything outside is hidden. The most flexible value is polygon(), which takes a list of percentage coordinates:\n/* Triangle */ clip-path: polygon(50% 0%, 0% 100%, 100% 100%); /* Hexagon */ clip-path: polygon(25% 6%, 75% 6%, 100% 50%, 75% 94%, 25% 94%, 0% 50%); /* Star */ clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%); Other clip-path functions include circle(), ellipse(), and inset().\nBrowser Support clip-path with polygon() is supported in all modern browsers (Chrome, Firefox, Safari, Edge). No vendor prefix needed as of 2025.\nRelated Tools\nGenerate gradients → Gradient Gallery CSS specificity → CSS Specificity Calculator ","permalink":"https://productivity-works.com/tools/css-clip-path/","summary":"\u003cdiv id=\"cp-app\"\u003e\n\u003cstyle\u003e\n#cp-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 0 0 48px 0;\n  color: #374151;\n}\n#cp-app * { box-sizing: border-box; }\n\n.cp-layout {\n  display: grid;\n  grid-template-columns: 1fr 320px;\n  gap: 20px;\n  align-items: start;\n}\n@media (max-width: 720px) {\n  .cp-layout { grid-template-columns: 1fr; }\n}\n\n/* Canvas area */\n.cp-canvas-wrap {\n  position: relative;\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 14px;\n  overflow: hidden;\n}\n.cp-canvas-inner {\n  position: relative;\n  width: 100%;\n  aspect-ratio: 1 / 1;\n  max-height: 480px;\n  overflow: hidden;\n}\n#cp-canvas {\n  display: block;\n  width: 100%;\n  height: 100%;\n  touch-action: none;\n  cursor: crosshair;\n}\n.cp-canvas-label {\n  position: absolute;\n  bottom: 10px;\n  left: 50%;\n  transform: translateX(-50%);\n  background: rgba(0,0,0,0.5);\n  color: #fff;\n  font-size: 11px;\n  padding: 3px 10px;\n  border-radius: 20px;\n  pointer-events: none;\n  white-space: nowrap;\n}\n\n/* Controls panel */\n.cp-panel {\n  display: flex;\n  flex-direction: column;\n  gap: 14px;\n}\n\n.cp-card {\n  background: #fff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 16px;\n}\n.cp-card h3 {\n  margin: 0 0 10px 0;\n  font-size: 13px;\n  font-weight: 700;\n  color: #1e293b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n\n/* Presets */\n.cp-presets {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 7px;\n}\n.cp-preset-btn {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 5px;\n  padding: 8px 4px;\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 8px;\n  cursor: pointer;\n  font-size: 11px;\n  color: #475569;\n  font-weight: 600;\n  transition: all 0.15s;\n}\n.cp-preset-btn:hover {\n  border-color: #6366f1;\n  color: #4f46e5;\n  background: #eef2ff;\n}\n.cp-preset-btn.active {\n  border-color: #6366f1;\n  background: #eef2ff;\n  color: #4f46e5;\n}\n.cp-preset-icon {\n  width: 36px;\n  height: 36px;\n  background: #6366f1;\n}\n\n/* Background controls */\n.cp-bg-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-bottom: 8px;\n}\n.cp-bg-row label {\n  font-size: 13px;\n  color: #374151;\n  font-weight: 500;\n  min-width: 80px;\n}\n.cp-color-input {\n  width: 40px;\n  height: 32px;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 6px;\n  padding: 2px;\n  cursor: pointer;\n  background: none;\n}\n.cp-img-btn {\n  flex: 1;\n  padding: 6px 10px;\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 7px;\n  font-size: 12px;\n  cursor: pointer;\n  color: #475569;\n  font-weight: 600;\n  transition: all 0.15s;\n}\n.cp-img-btn:hover {\n  border-color: #6366f1;\n  color: #4f46e5;\n}\n\n/* CSS Output */\n.cp-output-box {\n  background: #1e293b;\n  border-radius: 10px;\n  padding: 14px 16px;\n  font-family: 'SFMono-Regular', Consolas, monospace;\n  font-size: 12.5px;\n  color: #a5f3fc;\n  line-height: 1.6;\n  word-break: break-all;\n  min-height: 56px;\n}\n.cp-copy-btn {\n  width: 100%;\n  margin-top: 10px;\n  padding: 11px;\n  background: linear-gradient(135deg, #6366f1, #8b5cf6);\n  color: #fff;\n  border: none;\n  border-radius: 9px;\n  font-size: 14px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: opacity 0.15s;\n}\n.cp-copy-btn:hover { opacity: 0.9; }\n.cp-copy-btn.copied {\n  background: linear-gradient(135deg, #10b981, #059669);\n}\n\n/* Point info */\n.cp-point-info {\n  font-size: 12px;\n  color: #64748b;\n  margin-top: 6px;\n  text-align: center;\n}\n\n/* Shape info */\n.cp-shape-info {\n  font-size: 12px;\n  color: #6366f1;\n  font-weight: 600;\n  background: #eef2ff;\n  border-radius: 6px;\n  padding: 6px 10px;\n  text-align: center;\n}\n\n/* Reset btn */\n.cp-reset-btn {\n  width: 100%;\n  padding: 9px;\n  background: #f1f5f9;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 8px;\n  font-size: 13px;\n  font-weight: 600;\n  color: #475569;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n.cp-reset-btn:hover {\n  border-color: #cbd5e1;\n  background: #e2e8f0;\n}\n\u003c/style\u003e\n\u003cdiv class=\"cp-layout\"\u003e\n  \u003c!-- Left: Canvas --\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"cp-canvas-wrap\"\u003e\n      \u003cdiv class=\"cp-canvas-inner\"\u003e\n        \u003ccanvas id=\"cp-canvas\"\u003e\u003c/canvas\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"cp-canvas-label\"\u003eDrag points to reshape\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cp-point-info\" id=\"cp-point-info\"\u003eClick on the shape edge to add a point. Right-click a point to delete it.\u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Right: Panel --\u003e\n  \u003cdiv class=\"cp-panel\"\u003e\n    \u003c!-- Presets --\u003e\n    \u003cdiv class=\"cp-card\"\u003e\n      \u003ch3\u003ePresets\u003c/h3\u003e\n      \u003cdiv class=\"cp-presets\" id=\"cp-presets\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;!-- Background --\u0026gt;\n\u0026lt;div class=\u0026quot;cp-card\u0026quot;\u0026gt;\n  \u0026lt;h3\u0026gt;Preview\u0026lt;/h3\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-bg-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Shape color\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;color\u0026quot; class=\u0026quot;cp-color-input\u0026quot; id=\u0026quot;cp-shape-color\u0026quot; value=\u0026quot;#6366f1\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-bg-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;BG color\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;color\u0026quot; class=\u0026quot;cp-color-input\u0026quot; id=\u0026quot;cp-bg-color\u0026quot; value=\u0026quot;#e2e8f0\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-bg-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;Image\u0026lt;/label\u0026gt;\n    \u0026lt;button class=\u0026quot;cp-img-btn\u0026quot; id=\u0026quot;cp-upload-btn\u0026quot;\u0026gt;Upload Image\u0026lt;/button\u0026gt;\n    \u0026lt;input type=\u0026quot;file\u0026quot; id=\u0026quot;cp-file-input\u0026quot; accept=\u0026quot;image/*\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;button class=\u0026quot;cp-reset-btn\u0026quot; id=\u0026quot;cp-clear-img\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt;Remove Image\u0026lt;/button\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Output --\u0026gt;\n\u0026lt;div class=\u0026quot;cp-card\u0026quot;\u0026gt;\n  \u0026lt;h3\u0026gt;CSS Output\u0026lt;/h3\u0026gt;\n  \u0026lt;div class=\u0026quot;cp-output-box\u0026quot; id=\u0026quot;cp-output\u0026quot;\u0026gt;clip-path: polygon(...);\u0026lt;/div\u0026gt;\n  \u0026lt;button class=\u0026quot;cp-copy-btn\u0026quot; id=\u0026quot;cp-copy-btn\u0026quot;\u0026gt;Copy CSS\u0026lt;/button\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Shape info --\u0026gt;\n\u0026lt;div class=\u0026quot;cp-shape-info\u0026quot; id=\u0026quot;cp-shape-info\u0026quot;\u0026gt;polygon · 0 points\u0026lt;/div\u0026gt;\n\u0026lt;button class=\u0026quot;cp-reset-btn\u0026quot; id=\u0026quot;cp-reset-btn\u0026quot;\u0026gt;Reset to Preset\u0026lt;/button\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  const PRESETS = {\n    circle: {\n      label: 'Circle',\n      icon: (ctx, w, h) =\u003e { ctx.arc(w/2, h/2, Math.min(w,h)*0.42, 0, Math.PI*2); },\n      type: 'circle',\n      css: () =\u003e 'clip-path: circle(50% at 50% 50%);'\n    },\n    ellipse: {\n      label: 'Ellipse',\n      icon: (ctx, w, h) =\u003e { ctx.ellipse(w/2, h/2, w*0.42, h*0.28, 0, 0, Math.PI*2); },\n      type: 'ellipse',\n      css: () =\u003e 'clip-path: ellipse(55% 35% at 50% 50%);'\n    },\n    triangle: {\n      label: 'Triangle',\n      points: [[50,5],[95,95],[5,95]]\n    },\n    diamond: {\n      label: 'Diamond',\n      points: [[50,5],[95,50],[50,95],[5,50]]\n    },\n    pentagon: {\n      label: 'Pentagon',\n      points: (function() {\n        var pts = [];\n        for (var i = 0; i \u003c 5; i++) {\n          var a = (i * 72 - 90) * Math.PI / 180;\n          pts.push([50 + 45 * Math.cos(a), 50 + 45 * Math.sin(a)]);\n        }\n        return pts;\n      })()\n    },\n    hexagon: {\n      label: 'Hexagon',\n      points: (function() {\n        var pts = [];\n        for (var i = 0; i \u003c 6; i++) {\n          var a = (i * 60 - 30) * Math.PI / 180;\n          pts.push([50 + 47 * Math.cos(a), 50 + 47 * Math.sin(a)]);\n        }\n        return pts;\n      })()\n    },\n    star: {\n      label: 'Star',\n      points: (function() {\n        var pts = [];\n        for (var i = 0; i \u003c 10; i++) {\n          var a = (i * 36 - 90) * Math.PI / 180;\n          var r = i % 2 === 0 ? 47 : 20;\n          pts.push([50 + r * Math.cos(a), 50 + r * Math.sin(a)]);\n        }\n        return pts;\n      })()\n    },\n    arrow: {\n      label: 'Arrow',\n      points: [[0,35],[60,35],[60,15],[100,50],[60,85],[60,65],[0,65]]\n    },\n    cross: {\n      label: 'Cross',\n      points: [[33,0],[67,0],[67,33],[100,33],[100,67],[67,67],[67,100],[33,100],[33,67],[0,67],[0,33],[33,33]]\n    }\n  };\n\n  const canvas = document.getElementById('cp-canvas');\n  const ctx = canvas.getContext('2d');\n  const outputEl = document.getElementById('cp-output');\n  const copyBtn = document.getElementById('cp-copy-btn');\n  const resetBtn = document.getElementById('cp-reset-btn');\n  const shapeInfoEl = document.getElementById('cp-shape-info');\n  const pointInfoEl = document.getElementById('cp-point-info');\n  const shapeColorInput = document.getElementById('cp-shape-color');\n  const bgColorInput = document.getElementById('cp-bg-color');\n  const uploadBtn = document.getElementById('cp-upload-btn');\n  const fileInput = document.getElementById('cp-file-input');\n  const clearImgBtn = document.getElementById('cp-clear-img');\n\n  let points = []; // percentage [x, y]\n  let activePreset = 'triangle';\n  let dragIndex = -1;\n  let shapeColor = '#6366f1';\n  let bgColor = '#e2e8f0';\n  let uploadedImage = null;\n  let currentType = 'polygon'; // 'polygon', 'circle', 'ellipse'\n\n  // Resize canvas to match display size\n  function resizeCanvas() {\n    const rect = canvas.parentElement.getBoundingClientRect();\n    const size = Math.min(rect.width, 480);\n    canvas.width = size;\n    canvas.height = size;\n    draw();\n  }\n\n  // Convert percentage to canvas px\n  function toCanvasPx(px, py) {\n    return [px / 100 * canvas.width, py / 100 * canvas.height];\n  }\n\n  function toPercent(cx, cy) {\n    return [cx / canvas.width * 100, cy / canvas.height * 100];\n  }\n\n  function fmt(n) { return Math.round(n * 10) / 10; }\n\n  function buildCSS() {\n    if (currentType === 'circle') {\n      return 'clip-path: circle(50% at 50% 50%);';\n    } else if (currentType === 'ellipse') {\n      return 'clip-path: ellipse(55% 35% at 50% 50%);';\n    }\n    if (points.length \u003c 3) return 'clip-path: polygon(/* add points */);';\n    const pts = points.map(p =\u003e fmt(p[0]) + '% ' + fmt(p[1]) + '%').join(', ');\n    return 'clip-path: polygon(' + pts + ');';\n  }\n\n  function updateOutput() {\n    const css = buildCSS();\n    outputEl.textContent = css;\n    const count = currentType === 'polygon' ? points.length + ' points' : '';\n    shapeInfoEl.textContent = currentType + (count ? ' · ' + count : '');\n  }\n\n  function draw() {\n    const w = canvas.width, h = canvas.height;\n    ctx.clearRect(0, 0, w, h);\n\n    // Background\n    ctx.fillStyle = bgColor;\n    ctx.fillRect(0, 0, w, h);\n\n    // Draw checkerboard pattern hint\n    const cs = 20;\n    for (let r = 0; r \u003c h / cs; r++) {\n      for (let c = 0; c \u003c w / cs; c++) {\n        if ((r + c) % 2 === 0) {\n          ctx.fillStyle = 'rgba(0,0,0,0.04)';\n          ctx.fillRect(c * cs, r * cs, cs, cs);\n        }\n      }\n    }\n\n    // Shape with clip\n    ctx.save();\n    if (currentType === 'circle') {\n      ctx.beginPath();\n      ctx.arc(w / 2, h / 2, Math.min(w, h) * 0.47, 0, Math.PI * 2);\n      ctx.clip();\n    } else if (currentType === 'ellipse') {\n      ctx.beginPath();\n      ctx.ellipse(w / 2, h / 2, w * 0.52, h * 0.34, 0, 0, Math.PI * 2);\n      ctx.clip();\n    } else if (points.length \u003e= 3) {\n      ctx.beginPath();\n      const [x0, y0] = toCanvasPx(points[0][0], points[0][1]);\n      ctx.moveTo(x0, y0);\n      for (let i = 1; i \u003c points.length; i++) {\n        const [xi, yi] = toCanvasPx(points[i][0], points[i][1]);\n        ctx.lineTo(xi, yi);\n      }\n      ctx.closePath();\n      ctx.clip();\n    }\n\n    if (uploadedImage) {\n      // Draw image fitted\n      const ar = uploadedImage.width / uploadedImage.height;\n      let dw = w, dh = h;\n      if (ar \u003e 1) { dh = w / ar; } else { dw = h * ar; }\n      const dx = (w - dw) / 2, dy = (h - dh) / 2;\n      ctx.drawImage(uploadedImage, dx, dy, dw, dh);\n    } else {\n      ctx.fillStyle = shapeColor;\n      ctx.fillRect(0, 0, w, h);\n    }\n    ctx.restore();\n\n    // Draw outline and control points for polygon only\n    if (currentType === 'polygon' \u0026\u0026 points.length \u003e= 2) {\n      ctx.beginPath();\n      const [x0, y0] = toCanvasPx(points[0][0], points[0][1]);\n      ctx.moveTo(x0, y0);\n      for (let i = 1; i \u003c points.length; i++) {\n        const [xi, yi] = toCanvasPx(points[i][0], points[i][1]);\n        ctx.lineTo(xi, yi);\n      }\n      ctx.closePath();\n      ctx.strokeStyle = 'rgba(255,255,255,0.9)';\n      ctx.lineWidth = 1.5;\n      ctx.setLineDash([5, 4]);\n      ctx.stroke();\n      ctx.setLineDash([]);\n\n      // Draw points\n      points.forEach(([px, py], i) =\u003e {\n        const [cx, cy] = toCanvasPx(px, py);\n        ctx.beginPath();\n        ctx.arc(cx, cy, dragIndex === i ? 10 : 7, 0, Math.PI * 2);\n        ctx.fillStyle = dragIndex === i ? '#f59e0b' : '#fff';\n        ctx.fill();\n        ctx.strokeStyle = dragIndex === i ? '#d97706' : '#6366f1';\n        ctx.lineWidth = 2;\n        ctx.stroke();\n      });\n    }\n\n    updateOutput();\n  }\n\n  function loadPreset(name) {\n    const preset = PRESETS[name];\n    if (!preset) return;\n    activePreset = name;\n    if (preset.type === 'circle' || preset.type === 'ellipse') {\n      currentType = preset.type;\n      points = [];\n    } else {\n      currentType = 'polygon';\n      points = preset.points.map(p =\u003e [...p]);\n    }\n    document.querySelectorAll('.cp-preset-btn').forEach(b =\u003e {\n      b.classList.toggle('active', b.dataset.preset === name);\n    });\n    pointInfoEl.textContent = currentType === 'polygon'\n      ? 'Drag points to reshape. Right-click a point to delete it.'\n      : 'This shape uses a fixed CSS function. Switch to a polygon preset to edit points.';\n    draw();\n  }\n\n  // Build preset buttons\n  const presetsEl = document.getElementById('cp-presets');\n  Object.keys(PRESETS).forEach(name =\u003e {\n    const btn = document.createElement('button');\n    btn.className = 'cp-preset-btn';\n    btn.dataset.preset = name;\n    // Mini SVG icon for each shape\n    const svg = buildPresetSVG(name);\n    btn.innerHTML = svg + '\u003cspan\u003e' + PRESETS[name].label + '\u003c/span\u003e';\n    btn.addEventListener('click', () =\u003e loadPreset(name));\n    presetsEl.appendChild(btn);\n  });\n\n  function buildPresetSVG(name) {\n    const size = 36;\n    const preset = PRESETS[name];\n    let path = '';\n\n    if (name === 'circle') {\n      return `\u003csvg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 100 100\"\u003e\u003ccircle cx=\"50\" cy=\"50\" r=\"45\" fill=\"#6366f1\"/\u003e\u003c/svg\u003e`;\n    }\n    if (name === 'ellipse') {\n      return `\u003csvg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 100 100\"\u003e\u003cellipse cx=\"50\" cy=\"50\" rx=\"48\" ry=\"30\" fill=\"#6366f1\"/\u003e\u003c/svg\u003e`;\n    }\n    const pts = preset.points;\n    if (pts) {\n      path = pts.map((p, i) =\u003e (i === 0 ? 'M' : 'L') + p[0] + ',' + p[1]).join(' ') + 'Z';\n      return `\u003csvg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 100 100\"\u003e\u003cpath d=\"${path}\" fill=\"#6366f1\"/\u003e\u003c/svg\u003e`;\n    }\n    return `\u003csvg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 100 100\"\u003e\u003crect width=\"100\" height=\"100\" fill=\"#6366f1\"/\u003e\u003c/svg\u003e`;\n  }\n\n  // Pointer events\n  function getCanvasPos(e) {\n    const rect = canvas.getBoundingClientRect();\n    const scaleX = canvas.width / rect.width;\n    const scaleY = canvas.height / rect.height;\n    const clientX = e.touches ? e.touches[0].clientX : e.clientX;\n    const clientY = e.touches ? e.touches[0].clientY : e.clientY;\n    return [(clientX - rect.left) * scaleX, (clientY - rect.top) * scaleY];\n  }\n\n  function hitTest(cx, cy) {\n    const radius = 14;\n    for (let i = points.length - 1; i \u003e= 0; i--) {\n      const [px, py] = toCanvasPx(points[i][0], points[i][1]);\n      if (Math.hypot(cx - px, cy - py) \u003c radius) return i;\n    }\n    return -1;\n  }\n\n  canvas.addEventListener('mousedown', onPointerDown);\n  canvas.addEventListener('touchstart', onPointerDown, {passive: false});\n\n  function onPointerDown(e) {\n    if (currentType !== 'polygon') return;\n    e.preventDefault();\n    const [cx, cy] = getCanvasPos(e);\n    const hit = hitTest(cx, cy);\n    if (hit \u003e= 0) {\n      dragIndex = hit;\n    }\n    draw();\n  }\n\n  canvas.addEventListener('mousemove', onPointerMove);\n  canvas.addEventListener('touchmove', onPointerMove, {passive: false});\n\n  function onPointerMove(e) {\n    if (currentType !== 'polygon' || dragIndex \u003c 0) return;\n    e.preventDefault();\n    const [cx, cy] = getCanvasPos(e);\n    const px = Math.max(0, Math.min(100, cx / canvas.width * 100));\n    const py = Math.max(0, Math.min(100, cy / canvas.height * 100));\n    points[dragIndex] = [px, py];\n    draw();\n  }\n\n  canvas.addEventListener('mouseup', onPointerUp);\n  canvas.addEventListener('touchend', onPointerUp);\n\n  function onPointerUp(e) {\n    dragIndex = -1;\n    draw();\n  }\n\n  canvas.addEventListener('contextmenu', (e) =\u003e {\n    if (currentType !== 'polygon') return;\n    e.preventDefault();\n    const [cx, cy] = getCanvasPos(e);\n    const hit = hitTest(cx, cy);\n    if (hit \u003e= 0 \u0026\u0026 points.length \u003e 3) {\n      points.splice(hit, 1);\n      draw();\n    }\n  });\n\n  // Double-click to add point\n  canvas.addEventListener('dblclick', (e) =\u003e {\n    if (currentType !== 'polygon') return;\n    const [cx, cy] = getCanvasPos(e);\n    const hit = hitTest(cx, cy);\n    if (hit \u003e= 0) return; // don't add on existing point\n\n    // Find nearest edge to insert\n    const px = cx / canvas.width * 100;\n    const py = cy / canvas.height * 100;\n    let bestEdge = 0, bestDist = Infinity;\n    for (let i = 0; i \u003c points.length; i++) {\n      const j = (i + 1) % points.length;\n      const [ax, ay] = points[i];\n      const [bx, by] = points[j];\n      const mx = (ax + bx) / 2, my = (ay + by) / 2;\n      const d = Math.hypot(px - mx, py - my);\n      if (d \u003c bestDist) { bestDist = d; bestEdge = j; }\n    }\n    points.splice(bestEdge, 0, [px, py]);\n    draw();\n  });\n\n  // Colors\n  shapeColorInput.addEventListener('input', (e) =\u003e { shapeColor = e.target.value; draw(); });\n  bgColorInput.addEventListener('input', (e) =\u003e { bgColor = e.target.value; draw(); });\n\n  // Image upload\n  uploadBtn.addEventListener('click', () =\u003e fileInput.click());\n  fileInput.addEventListener('change', (e) =\u003e {\n    const file = e.target.files[0];\n    if (!file) return;\n    const reader = new FileReader();\n    reader.onload = (ev) =\u003e {\n      const img = new Image();\n      img.onload = () =\u003e {\n        uploadedImage = img;\n        clearImgBtn.style.display = 'block';\n        draw();\n      };\n      img.src = ev.target.result;\n    };\n    reader.readAsDataURL(file);\n  });\n  clearImgBtn.addEventListener('click', () =\u003e {\n    uploadedImage = null;\n    fileInput.value = '';\n    clearImgBtn.style.display = 'none';\n    draw();\n  });\n\n  // Copy CSS\n  copyBtn.addEventListener('click', () =\u003e {\n    const css = buildCSS();\n    navigator.clipboard.writeText(css).then(() =\u003e {\n      copyBtn.textContent = 'Copied!';\n      copyBtn.classList.add('copied');\n      setTimeout(() =\u003e {\n        copyBtn.textContent = 'Copy CSS';\n        copyBtn.classList.remove('copied');\n      }, 2000);\n    });\n  });\n\n  // Reset\n  resetBtn.addEventListener('click', () =\u003e loadPreset(activePreset));\n\n  // Init\n  window.addEventListener('resize', resizeCanvas);\n  resizeCanvas();\n  loadPreset('triangle');\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-to-use-the-css-clip-path-maker\"\u003eHow to Use the CSS Clip-Path Maker\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eChoose a preset\u003c/strong\u003e from the shape gallery — triangle, star, hexagon, and more.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eDrag the white control points\u003c/strong\u003e to reshape the polygon to your liking.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eDouble-click\u003c/strong\u003e on the canvas to add new points along the nearest edge.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eRight-click\u003c/strong\u003e a point to remove it (minimum 3 points).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eCustomize\u003c/strong\u003e the shape color and background color in the Preview panel.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eUpload an image\u003c/strong\u003e to see how your clip-path looks on a real photo.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eCopy the CSS\u003c/strong\u003e and paste it directly into your stylesheet.\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"understanding-clip-path\"\u003eUnderstanding clip-path\u003c/h2\u003e\n\u003cp\u003eThe \u003ccode\u003eclip-path\u003c/code\u003e CSS property lets you define a visible region for an element — everything outside is hidden. The most flexible value is \u003ccode\u003epolygon()\u003c/code\u003e, which takes a list of percentage coordinates:\u003c/p\u003e","title":"CSS Clip-Path Maker"},{"content":"Browse all CSS cursor values interactively. Hover any card to preview the cursor, then click to copy the CSS snippet. Use the search box or category tabs to filter.\nAll General Links \u0026amp; Status Selection Drag \u0026amp; Drop Resize Zoom \u0026#9881; Custom Cursor URL Builder Upload cursor image (PNG, ICO, SVG) \u0026#128247; Choose image Hotspot X (px) Hotspot Y (px) Fallback cursor auto default pointer crosshair move grab none Preview (hover here) Hover here to preview your cursor Generated CSS /* Upload an image to generate CSS */ Copy CSS CSS Animations CSS Animation Generator CSS Buttons CSS Button Generator How to Use CSS Cursors Set the cursor on any element using the cursor CSS property:\n/* keyword cursor */ .btn { cursor: pointer; } /* custom image cursor */ .custom { cursor: url(\u0026#39;/cursors/my-cursor.png\u0026#39;) 0 0, auto; } The cursor property accepts a comma-separated list — the browser uses the first value it supports, falling back to the last. Always end custom cursor lists with a CSS keyword such as auto or default.\nBrowser Support All keyword cursor values listed above are supported in every modern browser (Chrome, Firefox, Safari, Edge). Custom URL cursors support PNG, SVG, and ICO formats; recommended maximum size is 128×128 px.\nRelated tools: CSS Animation Generator | CSS Button Generator ","permalink":"https://productivity-works.com/tools/css-cursor-gallery/","summary":"\u003cp\u003eBrowse all CSS \u003ccode\u003ecursor\u003c/code\u003e values interactively. Hover any card to preview the cursor, then click to copy the CSS snippet. Use the search box or category tabs to filter.\u003c/p\u003e\n\u003cdiv id=\"cu-app\"\u003e\n\u003cstyle\u003e\n#cu-app *,#cu-app *::before,#cu-app *::after{box-sizing:border-box;margin:0;padding:0}\n#cu-app{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:#1e293b;max-width:960px;margin:0 auto;padding:0 0 48px}\n#cu-app h2{display:none}\n\u003cp\u003e/* ── controls ── */\n#cu-controls{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:20px;align-items:center}\n#cu-search{flex:1;min-width:180px;padding:9px 14px;border:1.5px solid #cbd5e1;border-radius:8px;font-size:14px;outline:none;transition:border-color .2s}\n#cu-search:focus{border-color:#6366f1}\n#cu-cats{display:flex;flex-wrap:wrap;gap:6px}\n.cu-cat{padding:6px 14px;border:1.5px solid #cbd5e1;border-radius:20px;background:#fff;font-size:13px;cursor:pointer;transition:all .15s}\n.cu-cat:hover{border-color:#6366f1;color:#6366f1}\n.cu-cat.active{background:#6366f1;border-color:#6366f1;color:#fff}\u003c/p\u003e\n\u003cp\u003e/* ── grid ── */\n#cu-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(148px,1fr));gap:12px}\n.cu-card{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;height:110px;border:1.5px solid #e2e8f0;border-radius:10px;background:#f8fafc;transition:border-color .15s,box-shadow .15s;user-select:none}\n.cu-card:hover{border-color:#6366f1;box-shadow:0 2px 12px rgba(99,102,241,.15)}\n.cu-icon{font-size:22px;pointer-events:none}\n.cu-name{font-size:12px;font-weight:600;color:#475569;pointer-events:none}\n.cu-copy-btn{padding:3px 10px;font-size:11px;border:1px solid #cbd5e1;border-radius:5px;background:#fff;color:#6366f1;cursor:pointer;transition:all .15s;pointer-events:auto}\n.cu-copy-btn:hover{background:#6366f1;color:#fff;border-color:#6366f1}\n.cu-copied{position:absolute;top:6px;right:8px;font-size:10px;color:#22c55e;font-weight:700;opacity:0;transition:opacity .3s}\n.cu-copied.show{opacity:1}\n.cu-no-results{grid-column:1/-1;text-align:center;padding:40px;color:#94a3b8;font-size:15px}\u003c/p\u003e","title":"CSS Cursor Gallery"},{"content":" CSS Filter Generator\nAdjust sliders to create filter effects and copy the CSS instantly. Drag \u0026 drop an image or paste a URL to use your own photo.\nLive Preview \u0026#9654; Before / After Copy CSS \u0026lt;div class=\u0026quot;cf-image-wrapper\u0026quot; id=\u0026quot;cf-image-wrapper\u0026quot;\u0026gt; \u0026lt;canvas id=\u0026quot;cf-default-canvas\u0026quot; style=\u0026quot;display:none;\u0026quot;\u0026gt;\u0026lt;/canvas\u0026gt; \u0026lt;img id=\u0026quot;cf-preview-img\u0026quot; class=\u0026quot;cf-preview-img\u0026quot; alt=\u0026quot;Preview\u0026quot; /\u0026gt; \u0026lt;img id=\u0026quot;cf-before-img\u0026quot; class=\u0026quot;cf-before-img\u0026quot; alt=\u0026quot;Before\u0026quot; /\u0026gt; \u0026lt;div class=\u0026quot;cf-compare-label cf-label-before\u0026quot;\u0026gt;Before\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cf-compare-label cf-label-after\u0026quot;\u0026gt;After\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cf-divider-line\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cf-drop-overlay\u0026quot;\u0026gt;Drop image here\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cf-upload-bar\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cf-url-input\u0026quot; id=\u0026quot;cf-url-input\u0026quot; placeholder=\u0026quot;Paste image URL and press Enter…\u0026quot; /\u0026gt; \u0026lt;button class=\u0026quot;cf-btn cf-btn-sm\u0026quot; id=\u0026quot;cf-load-url-btn\u0026quot;\u0026gt;Load\u0026lt;/button\u0026gt; \u0026lt;label class=\u0026quot;cf-btn cf-btn-sm\u0026quot; style=\u0026quot;cursor:pointer;\u0026quot;\u0026gt; Upload \u0026lt;input type=\u0026quot;file\u0026quot; id=\u0026quot;cf-file-input\u0026quot; accept=\u0026quot;image/*\u0026quot; style=\u0026quot;display:none;\u0026quot; /\u0026gt; \u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cf-output-panel\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cf-css-code\u0026quot; id=\u0026quot;cf-css-output\u0026quot;\u0026gt;\u0026lt;span class=\u0026quot;cf-css-prop\u0026quot;\u0026gt;filter\u0026lt;/span\u0026gt;: \u0026lt;span class=\u0026quot;cf-css-val\u0026quot; id=\u0026quot;cf-css-val-span\u0026quot;\u0026gt;none\u0026lt;/span\u0026gt;;\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026quot;cf-copy-btn\u0026quot; id=\u0026quot;cf-copy-output-btn\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; Filter Controls Reset All \u0026lt;div class=\u0026quot;cf-filter-list\u0026quot; id=\u0026quot;cf-filter-list\u0026quot;\u0026gt; \u0026lt;!-- Filters rendered by JS --\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cf-presets-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cf-presets-label\u0026quot;\u0026gt;Presets\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cf-presets-grid\u0026quot; id=\u0026quot;cf-presets-grid\u0026quot;\u0026gt; \u0026lt;!-- Presets rendered by JS --\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Related Tools Generate gradients → CSS Gradient Generator Check color contrast → Color Contrast Checker Resize images → Image Resizer ","permalink":"https://productivity-works.com/tools/css-filter-generator/","summary":"\u003cdiv id=\"cf-app\"\u003e\n\u003cstyle\u003e\n#cf-app *,\n#cf-app *::before,\n#cf-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\n#cf-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 15px;\n  color: #1a1a2e;\n  line-height: 1.5;\n}\n\n/* Layout */\n#cf-app .cf-layout {\n  display: grid;\n  grid-template-columns: 1fr 340px;\n  gap: 24px;\n  align-items: start;\n}\n\n@media (max-width: 900px) {\n  #cf-app .cf-layout {\n    grid-template-columns: 1fr;\n  }\n}\n\n/* Preview Panel */\n#cf-app .cf-preview-panel {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  overflow: hidden;\n}\n\n#cf-app .cf-preview-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  padding: 12px 16px;\n  background: #f8fafc;\n  border-bottom: 1px solid #e2e8f0;\n  flex-wrap: wrap;\n  gap: 8px;\n}\n\n#cf-app .cf-preview-header h3 {\n  font-size: 14px;\n  font-weight: 600;\n  color: #475569;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n\n#cf-app .cf-preview-actions {\n  display: flex;\n  gap: 8px;\n  flex-wrap: wrap;\n}\n\n#cf-app .cf-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  padding: 6px 12px;\n  border: 1px solid #cbd5e1;\n  border-radius: 6px;\n  background: #fff;\n  color: #374151;\n  font-size: 13px;\n  font-weight: 500;\n  cursor: pointer;\n  transition: all 0.15s ease;\n  white-space: nowrap;\n}\n\n#cf-app .cf-btn:hover {\n  background: #f1f5f9;\n  border-color: #94a3b8;\n}\n\n#cf-app .cf-btn-primary {\n  background: #6366f1;\n  border-color: #6366f1;\n  color: #fff;\n}\n\n#cf-app .cf-btn-primary:hover {\n  background: #4f46e5;\n  border-color: #4f46e5;\n}\n\n#cf-app .cf-btn-success {\n  background: #10b981;\n  border-color: #10b981;\n  color: #fff;\n}\n\n#cf-app .cf-btn-danger {\n  background: #ef4444;\n  border-color: #ef4444;\n  color: #fff;\n  font-size: 12px;\n  padding: 4px 8px;\n}\n\n#cf-app .cf-btn-sm {\n  padding: 4px 8px;\n  font-size: 12px;\n}\n\n/* Image Wrapper */\n#cf-app .cf-image-wrapper {\n  position: relative;\n  overflow: hidden;\n  background: #0f172a;\n  min-height: 260px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n#cf-app .cf-preview-img {\n  display: block;\n  max-width: 100%;\n  max-height: 400px;\n  width: auto;\n  height: auto;\n  object-fit: contain;\n  transition: filter 0.2s ease;\n  user-select: none;\n}\n\n#cf-app .cf-before-img {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 50%;\n  height: 100%;\n  object-fit: cover;\n  clip-path: inset(0 50% 0 0);\n  filter: none !important;\n  display: none;\n  max-width: none;\n  max-height: none;\n}\n\n#cf-app.cf-compare-mode .cf-before-img {\n  display: block;\n}\n\n#cf-app.cf-compare-mode .cf-preview-img {\n  width: 100%;\n  max-height: 400px;\n  object-fit: cover;\n}\n\n#cf-app .cf-compare-label {\n  position: absolute;\n  top: 8px;\n  font-size: 11px;\n  font-weight: 700;\n  letter-spacing: 0.08em;\n  text-transform: uppercase;\n  padding: 3px 8px;\n  border-radius: 4px;\n  display: none;\n}\n\n#cf-app.cf-compare-mode .cf-compare-label {\n  display: block;\n}\n\n#cf-app .cf-label-before {\n  left: 8px;\n  background: rgba(0,0,0,0.6);\n  color: #fff;\n}\n\n#cf-app .cf-label-after {\n  right: 8px;\n  background: rgba(99,102,241,0.85);\n  color: #fff;\n}\n\n#cf-app .cf-divider-line {\n  position: absolute;\n  top: 0;\n  left: 50%;\n  transform: translateX(-50%);\n  width: 2px;\n  height: 100%;\n  background: #6366f1;\n  display: none;\n}\n\n#cf-app.cf-compare-mode .cf-divider-line {\n  display: block;\n}\n\n/* Drop zone overlay */\n#cf-app .cf-drop-overlay {\n  position: absolute;\n  inset: 0;\n  background: rgba(99,102,241,0.85);\n  color: #fff;\n  font-size: 18px;\n  font-weight: 600;\n  display: none;\n  align-items: center;\n  justify-content: center;\n  border-radius: 0;\n  pointer-events: none;\n}\n\n#cf-app .cf-image-wrapper.cf-drag-over .cf-drop-overlay {\n  display: flex;\n}\n\n/* Image upload bar */\n#cf-app .cf-upload-bar {\n  display: flex;\n  gap: 8px;\n  padding: 10px 14px;\n  background: #f8fafc;\n  border-top: 1px solid #e2e8f0;\n  align-items: center;\n  flex-wrap: wrap;\n}\n\n#cf-app .cf-url-input {\n  flex: 1;\n  min-width: 120px;\n  padding: 6px 10px;\n  border: 1px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 13px;\n  outline: none;\n  transition: border-color 0.15s;\n}\n\n#cf-app .cf-url-input:focus {\n  border-color: #6366f1;\n  box-shadow: 0 0 0 3px rgba(99,102,241,0.12);\n}\n\n/* CSS Output */\n#cf-app .cf-output-panel {\n  background: #0f172a;\n  color: #e2e8f0;\n  padding: 14px 16px;\n  border-top: 1px solid #e2e8f0;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 13px;\n  line-height: 1.6;\n  display: flex;\n  align-items: flex-start;\n  gap: 10px;\n  justify-content: space-between;\n}\n\n#cf-app .cf-css-code {\n  flex: 1;\n  white-space: pre-wrap;\n  word-break: break-all;\n  color: #a5b4fc;\n}\n\n#cf-app .cf-css-prop {\n  color: #67e8f9;\n}\n\n#cf-app .cf-css-val {\n  color: #fde68a;\n}\n\n#cf-app .cf-copy-btn {\n  flex-shrink: 0;\n  padding: 5px 10px;\n  border: 1px solid #334155;\n  border-radius: 6px;\n  background: #1e293b;\n  color: #94a3b8;\n  font-size: 12px;\n  cursor: pointer;\n  transition: all 0.15s;\n  font-family: inherit;\n}\n\n#cf-app .cf-copy-btn:hover {\n  background: #334155;\n  color: #e2e8f0;\n}\n\n#cf-app .cf-copy-btn.cf-copied {\n  background: #10b981;\n  border-color: #10b981;\n  color: #fff;\n}\n\n/* Controls Panel */\n#cf-app .cf-controls-panel {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  overflow: hidden;\n}\n\n#cf-app .cf-controls-header {\n  padding: 12px 16px;\n  background: #f8fafc;\n  border-bottom: 1px solid #e2e8f0;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n\n#cf-app .cf-controls-header h3 {\n  font-size: 14px;\n  font-weight: 600;\n  color: #475569;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n\n#cf-app .cf-filter-list {\n  padding: 12px;\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n  max-height: 520px;\n  overflow-y: auto;\n}\n\n#cf-app .cf-filter-item {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 8px;\n  padding: 10px 12px;\n}\n\n#cf-app .cf-filter-top {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 6px;\n}\n\n#cf-app .cf-filter-name {\n  font-size: 13px;\n  font-weight: 600;\n  color: #374151;\n}\n\n#cf-app .cf-filter-val {\n  font-size: 12px;\n  font-weight: 600;\n  color: #6366f1;\n  min-width: 48px;\n  text-align: right;\n}\n\n#cf-app .cf-filter-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n\n#cf-app .cf-slider {\n  -webkit-appearance: none;\n  flex: 1;\n  height: 4px;\n  border-radius: 2px;\n  background: #e2e8f0;\n  outline: none;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n\n#cf-app .cf-slider::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  width: 16px;\n  height: 16px;\n  border-radius: 50%;\n  background: #6366f1;\n  cursor: pointer;\n  border: 2px solid #fff;\n  box-shadow: 0 0 0 1px #6366f1;\n  transition: transform 0.1s;\n}\n\n#cf-app .cf-slider::-webkit-slider-thumb:hover {\n  transform: scale(1.2);\n}\n\n#cf-app .cf-slider::-moz-range-thumb {\n  width: 16px;\n  height: 16px;\n  border-radius: 50%;\n  background: #6366f1;\n  cursor: pointer;\n  border: 2px solid #fff;\n  box-shadow: 0 0 0 1px #6366f1;\n}\n\n#cf-app .cf-reset-single {\n  background: none;\n  border: none;\n  color: #94a3b8;\n  font-size: 15px;\n  cursor: pointer;\n  padding: 0 2px;\n  line-height: 1;\n  transition: color 0.15s;\n  flex-shrink: 0;\n}\n\n#cf-app .cf-reset-single:hover {\n  color: #ef4444;\n}\n\n/* Drop shadow sub-controls */\n#cf-app .cf-shadow-sub {\n  margin-top: 8px;\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 6px;\n}\n\n#cf-app .cf-shadow-sub label {\n  font-size: 11px;\n  color: #64748b;\n  display: block;\n  margin-bottom: 2px;\n}\n\n#cf-app .cf-shadow-color-row {\n  grid-column: span 2;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n\n#cf-app .cf-color-input {\n  width: 36px;\n  height: 28px;\n  border: 1px solid #cbd5e1;\n  border-radius: 4px;\n  cursor: pointer;\n  padding: 1px;\n  background: none;\n}\n\n/* Presets */\n#cf-app .cf-presets-section {\n  padding: 12px;\n  border-top: 1px solid #e2e8f0;\n}\n\n#cf-app .cf-presets-label {\n  font-size: 12px;\n  font-weight: 600;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 8px;\n}\n\n#cf-app .cf-presets-grid {\n  display: grid;\n  grid-template-columns: repeat(2, 1fr);\n  gap: 6px;\n}\n\n#cf-app .cf-preset-btn {\n  padding: 7px 6px;\n  border: 1px solid #e2e8f0;\n  border-radius: 6px;\n  background: #f8fafc;\n  color: #374151;\n  font-size: 12px;\n  font-weight: 500;\n  cursor: pointer;\n  transition: all 0.15s;\n  text-align: center;\n}\n\n#cf-app .cf-preset-btn:hover {\n  background: #6366f1;\n  border-color: #6366f1;\n  color: #fff;\n}\n\n#cf-app .cf-preset-btn.cf-active {\n  background: #6366f1;\n  border-color: #6366f1;\n  color: #fff;\n}\n\n/* Notification */\n#cf-app .cf-toast {\n  position: fixed;\n  bottom: 24px;\n  right: 24px;\n  background: #1e293b;\n  color: #fff;\n  padding: 10px 18px;\n  border-radius: 8px;\n  font-size: 14px;\n  font-weight: 500;\n  opacity: 0;\n  transform: translateY(8px);\n  transition: opacity 0.2s, transform 0.2s;\n  pointer-events: none;\n  z-index: 9999;\n  box-shadow: 0 4px 16px rgba(0,0,0,0.25);\n}\n\n#cf-app .cf-toast.cf-show {\n  opacity: 1;\n  transform: translateY(0);\n}\n\n/* Section label */\n#cf-app .cf-section-title {\n  font-size: 22px;\n  font-weight: 700;\n  color: #1e293b;\n  margin-bottom: 6px;\n}\n\n#cf-app .cf-section-sub {\n  font-size: 14px;\n  color: #64748b;\n  margin-bottom: 20px;\n}\n\u003c/style\u003e\n\u003cp class=\"cf-section-title\"\u003eCSS Filter Generator\u003c/p\u003e","title":"CSS Filter Generator — Image Effects Tool"},{"content":" Presets: Navigation Bar Card Grid Holy Grail Sidebar Layout Centered Content \u0026lt;!-- Container --\u0026gt; \u0026lt;div class=\u0026quot;fa-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fa-section-title\u0026quot;\u0026gt;Container\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;display\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;fa-display\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;flex\u0026quot; selected\u0026gt;flex\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;inline-flex\u0026quot;\u0026gt;inline-flex\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;flex-direction\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;fa-direction\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;row\u0026quot; selected\u0026gt;row\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;row-reverse\u0026quot;\u0026gt;row-reverse\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;column\u0026quot;\u0026gt;column\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;column-reverse\u0026quot;\u0026gt;column-reverse\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;justify-content\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;fa-justify\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;flex-start\u0026quot; selected\u0026gt;flex-start\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;flex-end\u0026quot;\u0026gt;flex-end\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;center\u0026quot;\u0026gt;center\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;space-between\u0026quot;\u0026gt;space-between\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;space-around\u0026quot;\u0026gt;space-around\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;space-evenly\u0026quot;\u0026gt;space-evenly\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;align-items\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;fa-align-items\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;stretch\u0026quot; selected\u0026gt;stretch\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;flex-start\u0026quot;\u0026gt;flex-start\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;flex-end\u0026quot;\u0026gt;flex-end\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;center\u0026quot;\u0026gt;center\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;baseline\u0026quot;\u0026gt;baseline\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;align-content\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;fa-align-content\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;normal\u0026quot; selected\u0026gt;normal\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;flex-start\u0026quot;\u0026gt;flex-start\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;flex-end\u0026quot;\u0026gt;flex-end\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;center\u0026quot;\u0026gt;center\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;space-between\u0026quot;\u0026gt;space-between\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;space-around\u0026quot;\u0026gt;space-around\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;stretch\u0026quot;\u0026gt;stretch\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;flex-wrap\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;fa-wrap\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;nowrap\u0026quot; selected\u0026gt;nowrap\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;wrap\u0026quot;\u0026gt;wrap\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;wrap-reverse\u0026quot;\u0026gt;wrap-reverse\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;gap\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;fa-gap\u0026quot; value=\u0026quot;0px\u0026quot; style=\u0026quot;max-width:90px;\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;height\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;fa-height\u0026quot; value=\u0026quot;260px\u0026quot; style=\u0026quot;max-width:90px;\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Items --\u0026gt; \u0026lt;div class=\u0026quot;fa-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fa-section-title\u0026quot;\u0026gt;Items\u0026lt;/div\u0026gt; \u0026lt;div style=\u0026quot;display:flex;gap:5px;margin-bottom:8px;\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;fa-add-btn\u0026quot; id=\u0026quot;fa-add-item\u0026quot;\u0026gt;+ Add Item\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;fa-remove-btn\u0026quot; id=\u0026quot;fa-remove-item\u0026quot;\u0026gt;- Remove\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fa-item-tabs\u0026quot; id=\u0026quot;fa-item-tabs\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Per-item --\u0026gt; \u0026lt;div class=\u0026quot;fa-section\u0026quot; id=\u0026quot;fa-item-controls\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fa-section-title\u0026quot;\u0026gt;Selected Item\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;flex-grow\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;fa-grow\u0026quot; value=\u0026quot;0\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;10\u0026quot; step=\u0026quot;1\u0026quot; style=\u0026quot;max-width:80px;\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;flex-shrink\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;fa-shrink\u0026quot; value=\u0026quot;1\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;10\u0026quot; step=\u0026quot;1\u0026quot; style=\u0026quot;max-width:80px;\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;flex-basis\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;fa-basis\u0026quot; value=\u0026quot;auto\u0026quot; style=\u0026quot;max-width:80px;\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;order\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;fa-order\u0026quot; value=\u0026quot;0\u0026quot; min=\u0026quot;-10\u0026quot; max=\u0026quot;10\u0026quot; step=\u0026quot;1\u0026quot; style=\u0026quot;max-width:80px;\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;align-self\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;fa-align-self\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;auto\u0026quot; selected\u0026gt;auto\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;flex-start\u0026quot;\u0026gt;flex-start\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;flex-end\u0026quot;\u0026gt;flex-end\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;center\u0026quot;\u0026gt;center\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;baseline\u0026quot;\u0026gt;baseline\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;stretch\u0026quot;\u0026gt;stretch\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;width\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;fa-item-width\u0026quot; value=\u0026quot;auto\u0026quot; style=\u0026quot;max-width:80px;\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;height\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;fa-item-height\u0026quot; value=\u0026quot;auto\u0026quot; style=\u0026quot;max-width:80px;\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt; \u0026lt;label\u0026gt;label\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;fa-item-label\u0026quot; value=\u0026quot;\u0026quot; style=\u0026quot;max-width:80px;\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Live Preview Click an item to select it \u0026lt;div class=\u0026quot;fa-output-header\u0026quot;\u0026gt; \u0026lt;span\u0026gt;Generated CSS\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026quot;fa-copy-btn\u0026quot; id=\u0026quot;fa-copy-btn\u0026quot;\u0026gt;Copy CSS\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fa-output\u0026quot; id=\u0026quot;fa-output\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; Related Tools Generate box shadows → CSS Box Shadow Generator Build gradients → CSS Gradient Generator Create color palettes → Color Palette Generator ","permalink":"https://productivity-works.com/tools/flexbox-generator/","summary":"\u003cdiv id=\"flex-app\"\u003e\n\u003cstyle\u003e\n#flex-app *,\n#flex-app *::before,\n#flex-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\n#flex-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 14px;\n  color: #1a1a2e;\n  background: #f0f4ff;\n  border-radius: 12px;\n  overflow: hidden;\n  margin: 1.5rem 0;\n}\n\n/* ── Toolbar ── */\n#flex-app .fa-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  align-items: center;\n  background: #1a1a2e;\n  padding: 10px 14px;\n}\n\n#flex-app .fa-toolbar span.fa-label {\n  color: #a0aec0;\n  font-size: 12px;\n  font-weight: 600;\n  letter-spacing: .05em;\n  text-transform: uppercase;\n  margin-right: 2px;\n}\n\n#flex-app .fa-preset-btn {\n  background: #2d3748;\n  color: #e2e8f0;\n  border: 1px solid #4a5568;\n  border-radius: 6px;\n  padding: 5px 11px;\n  cursor: pointer;\n  font-size: 12px;\n  transition: background .15s;\n}\n#flex-app .fa-preset-btn:hover {\n  background: #4a5568;\n}\n\n/* ── Main layout ── */\n#flex-app .fa-main {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0;\n  min-height: 520px;\n}\n\n/* ── Control panel ── */\n#flex-app .fa-controls {\n  width: 260px;\n  flex-shrink: 0;\n  background: #ffffff;\n  border-right: 1px solid #e2e8f0;\n  overflow-y: auto;\n  max-height: 680px;\n}\n\n@media (max-width: 700px) {\n  #flex-app .fa-controls {\n    width: 100%;\n    max-height: none;\n    border-right: none;\n    border-bottom: 1px solid #e2e8f0;\n  }\n  #flex-app .fa-main {\n    flex-direction: column;\n  }\n}\n\n#flex-app .fa-section {\n  border-bottom: 1px solid #e8ecf4;\n  padding: 12px 14px;\n}\n\n#flex-app .fa-section-title {\n  font-size: 11px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: .07em;\n  color: #718096;\n  margin-bottom: 10px;\n}\n\n#flex-app .fa-row {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 8px;\n  gap: 6px;\n}\n\n#flex-app .fa-row label {\n  font-size: 12px;\n  color: #4a5568;\n  white-space: nowrap;\n  min-width: 90px;\n}\n\n#flex-app .fa-row select,\n#flex-app .fa-row input[type=\"text\"],\n#flex-app .fa-row input[type=\"number\"] {\n  flex: 1;\n  padding: 4px 7px;\n  border: 1px solid #cbd5e0;\n  border-radius: 5px;\n  font-size: 12px;\n  background: #f7fafc;\n  color: #2d3748;\n  outline: none;\n  transition: border-color .15s;\n}\n#flex-app .fa-row select:focus,\n#flex-app .fa-row input:focus {\n  border-color: #667eea;\n}\n\n#flex-app .fa-row input[type=\"range\"] {\n  flex: 1;\n  accent-color: #667eea;\n}\n\n/* ── Item selector ── */\n#flex-app .fa-item-tabs {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 4px;\n  margin-bottom: 10px;\n}\n\n#flex-app .fa-item-tab {\n  padding: 3px 9px;\n  border-radius: 4px;\n  border: 1px solid #cbd5e0;\n  font-size: 11px;\n  cursor: pointer;\n  background: #f7fafc;\n  color: #4a5568;\n  transition: all .15s;\n}\n#flex-app .fa-item-tab.active {\n  background: #667eea;\n  color: #fff;\n  border-color: #667eea;\n}\n\n#flex-app .fa-add-btn,\n#flex-app .fa-remove-btn {\n  font-size: 11px;\n  padding: 3px 9px;\n  border-radius: 4px;\n  border: none;\n  cursor: pointer;\n  transition: background .15s;\n}\n#flex-app .fa-add-btn {\n  background: #48bb78;\n  color: #fff;\n}\n#flex-app .fa-add-btn:hover { background: #38a169; }\n#flex-app .fa-remove-btn {\n  background: #fc8181;\n  color: #fff;\n}\n#flex-app .fa-remove-btn:hover { background: #e53e3e; }\n\n/* ── Preview area ── */\n#flex-app .fa-preview-col {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  min-width: 0;\n}\n\n#flex-app .fa-preview-header {\n  padding: 10px 14px;\n  background: #edf2ff;\n  border-bottom: 1px solid #c3d0f5;\n  font-size: 12px;\n  font-weight: 600;\n  color: #4a5568;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n\n#flex-app .fa-preview-wrap {\n  flex: 1;\n  padding: 16px;\n  overflow: auto;\n}\n\n#flex-app #fa-preview-container {\n  display: flex;\n  width: 100%;\n  min-height: 260px;\n  background: #dde6ff;\n  border-radius: 8px;\n  border: 2px dashed #a3b4f5;\n  padding: 10px;\n  transition: all .2s;\n  position: relative;\n}\n\n#flex-app .fa-flex-item {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  border-radius: 6px;\n  font-size: 13px;\n  font-weight: 700;\n  color: #fff;\n  min-width: 40px;\n  min-height: 40px;\n  cursor: pointer;\n  transition: outline .15s, transform .1s;\n  position: relative;\n  text-shadow: 0 1px 2px rgba(0,0,0,.25);\n}\n\n#flex-app .fa-flex-item.selected {\n  outline: 3px solid #1a1a2e;\n  outline-offset: 2px;\n  transform: scale(1.04);\n}\n\n/* ── CSS Output ── */\n#flex-app .fa-output {\n  background: #1a1a2e;\n  color: #a8d8ea;\n  padding: 14px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 12px;\n  line-height: 1.7;\n  white-space: pre-wrap;\n  word-break: break-all;\n  border-top: 1px solid #2d3748;\n  min-height: 120px;\n}\n\n#flex-app .fa-output-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  background: #2d3748;\n  padding: 8px 14px;\n  border-top: 1px solid #4a5568;\n}\n\n#flex-app .fa-output-header span {\n  color: #a0aec0;\n  font-size: 11px;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: .06em;\n}\n\n#flex-app .fa-copy-btn {\n  background: #667eea;\n  color: #fff;\n  border: none;\n  border-radius: 5px;\n  padding: 4px 12px;\n  font-size: 12px;\n  cursor: pointer;\n  transition: background .15s;\n}\n#flex-app .fa-copy-btn:hover { background: #5a67d8; }\n#flex-app .fa-copy-btn.copied { background: #48bb78; }\n\n/* ── Syntax colors ── */\n#flex-app .fa-output .tok-sel  { color: #fbd38d; }\n#flex-app .fa-output .tok-prop { color: #90cdf4; }\n#flex-app .fa-output .tok-val  { color: #9ae6b4; }\n#flex-app .fa-output .tok-punc { color: #e2e8f0; }\n\u003c/style\u003e\n\u003c!-- Toolbar: Presets --\u003e\n\u003cdiv class=\"fa-toolbar\"\u003e\n  \u003cspan class=\"fa-label\"\u003ePresets:\u003c/span\u003e\n  \u003cbutton class=\"fa-preset-btn\" data-preset=\"navbar\"\u003eNavigation Bar\u003c/button\u003e\n  \u003cbutton class=\"fa-preset-btn\" data-preset=\"cardgrid\"\u003eCard Grid\u003c/button\u003e\n  \u003cbutton class=\"fa-preset-btn\" data-preset=\"holygrail\"\u003eHoly Grail\u003c/button\u003e\n  \u003cbutton class=\"fa-preset-btn\" data-preset=\"sidebar\"\u003eSidebar Layout\u003c/button\u003e\n  \u003cbutton class=\"fa-preset-btn\" data-preset=\"centered\"\u003eCentered Content\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"fa-main\"\u003e\n  \u003c!-- Controls --\u003e\n  \u003cdiv class=\"fa-controls\"\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;!-- Container --\u0026gt;\n\u0026lt;div class=\u0026quot;fa-section\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;fa-section-title\u0026quot;\u0026gt;Container\u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;display\u0026lt;/label\u0026gt;\n    \u0026lt;select id=\u0026quot;fa-display\u0026quot;\u0026gt;\n      \u0026lt;option value=\u0026quot;flex\u0026quot; selected\u0026gt;flex\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;inline-flex\u0026quot;\u0026gt;inline-flex\u0026lt;/option\u0026gt;\n    \u0026lt;/select\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;flex-direction\u0026lt;/label\u0026gt;\n    \u0026lt;select id=\u0026quot;fa-direction\u0026quot;\u0026gt;\n      \u0026lt;option value=\u0026quot;row\u0026quot; selected\u0026gt;row\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;row-reverse\u0026quot;\u0026gt;row-reverse\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;column\u0026quot;\u0026gt;column\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;column-reverse\u0026quot;\u0026gt;column-reverse\u0026lt;/option\u0026gt;\n    \u0026lt;/select\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;justify-content\u0026lt;/label\u0026gt;\n    \u0026lt;select id=\u0026quot;fa-justify\u0026quot;\u0026gt;\n      \u0026lt;option value=\u0026quot;flex-start\u0026quot; selected\u0026gt;flex-start\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;flex-end\u0026quot;\u0026gt;flex-end\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;center\u0026quot;\u0026gt;center\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;space-between\u0026quot;\u0026gt;space-between\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;space-around\u0026quot;\u0026gt;space-around\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;space-evenly\u0026quot;\u0026gt;space-evenly\u0026lt;/option\u0026gt;\n    \u0026lt;/select\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;align-items\u0026lt;/label\u0026gt;\n    \u0026lt;select id=\u0026quot;fa-align-items\u0026quot;\u0026gt;\n      \u0026lt;option value=\u0026quot;stretch\u0026quot; selected\u0026gt;stretch\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;flex-start\u0026quot;\u0026gt;flex-start\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;flex-end\u0026quot;\u0026gt;flex-end\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;center\u0026quot;\u0026gt;center\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;baseline\u0026quot;\u0026gt;baseline\u0026lt;/option\u0026gt;\n    \u0026lt;/select\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;align-content\u0026lt;/label\u0026gt;\n    \u0026lt;select id=\u0026quot;fa-align-content\u0026quot;\u0026gt;\n      \u0026lt;option value=\u0026quot;normal\u0026quot; selected\u0026gt;normal\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;flex-start\u0026quot;\u0026gt;flex-start\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;flex-end\u0026quot;\u0026gt;flex-end\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;center\u0026quot;\u0026gt;center\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;space-between\u0026quot;\u0026gt;space-between\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;space-around\u0026quot;\u0026gt;space-around\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;stretch\u0026quot;\u0026gt;stretch\u0026lt;/option\u0026gt;\n    \u0026lt;/select\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;flex-wrap\u0026lt;/label\u0026gt;\n    \u0026lt;select id=\u0026quot;fa-wrap\u0026quot;\u0026gt;\n      \u0026lt;option value=\u0026quot;nowrap\u0026quot; selected\u0026gt;nowrap\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;wrap\u0026quot;\u0026gt;wrap\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;wrap-reverse\u0026quot;\u0026gt;wrap-reverse\u0026lt;/option\u0026gt;\n    \u0026lt;/select\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;gap\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;fa-gap\u0026quot; value=\u0026quot;0px\u0026quot; style=\u0026quot;max-width:90px;\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;height\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;fa-height\u0026quot; value=\u0026quot;260px\u0026quot; style=\u0026quot;max-width:90px;\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Items --\u0026gt;\n\u0026lt;div class=\u0026quot;fa-section\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;fa-section-title\u0026quot;\u0026gt;Items\u0026lt;/div\u0026gt;\n  \u0026lt;div style=\u0026quot;display:flex;gap:5px;margin-bottom:8px;\u0026quot;\u0026gt;\n    \u0026lt;button class=\u0026quot;fa-add-btn\u0026quot; id=\u0026quot;fa-add-item\u0026quot;\u0026gt;+ Add Item\u0026lt;/button\u0026gt;\n    \u0026lt;button class=\u0026quot;fa-remove-btn\u0026quot; id=\u0026quot;fa-remove-item\u0026quot;\u0026gt;- Remove\u0026lt;/button\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;fa-item-tabs\u0026quot; id=\u0026quot;fa-item-tabs\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Per-item --\u0026gt;\n\u0026lt;div class=\u0026quot;fa-section\u0026quot; id=\u0026quot;fa-item-controls\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;fa-section-title\u0026quot;\u0026gt;Selected Item\u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;flex-grow\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;fa-grow\u0026quot; value=\u0026quot;0\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;10\u0026quot; step=\u0026quot;1\u0026quot; style=\u0026quot;max-width:80px;\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;flex-shrink\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;fa-shrink\u0026quot; value=\u0026quot;1\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;10\u0026quot; step=\u0026quot;1\u0026quot; style=\u0026quot;max-width:80px;\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;flex-basis\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;fa-basis\u0026quot; value=\u0026quot;auto\u0026quot; style=\u0026quot;max-width:80px;\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;order\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;fa-order\u0026quot; value=\u0026quot;0\u0026quot; min=\u0026quot;-10\u0026quot; max=\u0026quot;10\u0026quot; step=\u0026quot;1\u0026quot; style=\u0026quot;max-width:80px;\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;align-self\u0026lt;/label\u0026gt;\n    \u0026lt;select id=\u0026quot;fa-align-self\u0026quot;\u0026gt;\n      \u0026lt;option value=\u0026quot;auto\u0026quot; selected\u0026gt;auto\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;flex-start\u0026quot;\u0026gt;flex-start\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;flex-end\u0026quot;\u0026gt;flex-end\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;center\u0026quot;\u0026gt;center\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;baseline\u0026quot;\u0026gt;baseline\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;stretch\u0026quot;\u0026gt;stretch\u0026lt;/option\u0026gt;\n    \u0026lt;/select\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;width\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;fa-item-width\u0026quot; value=\u0026quot;auto\u0026quot; style=\u0026quot;max-width:80px;\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;height\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;fa-item-height\u0026quot; value=\u0026quot;auto\u0026quot; style=\u0026quot;max-width:80px;\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;fa-row\u0026quot;\u0026gt;\n    \u0026lt;label\u0026gt;label\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;fa-item-label\u0026quot; value=\u0026quot;\u0026quot; style=\u0026quot;max-width:80px;\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\u003c!-- /fa-controls --\u003e\n  \u003c!-- Preview + Output --\u003e\n  \u003cdiv class=\"fa-preview-col\"\u003e\n    \u003cdiv class=\"fa-preview-header\"\u003e\n      \u003cspan\u003eLive Preview\u003c/span\u003e\n      \u003cspan style=\"font-weight:400;color:#718096;font-size:11px;\"\u003eClick an item to select it\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"fa-preview-wrap\"\u003e\n      \u003cdiv id=\"fa-preview-container\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;div class=\u0026quot;fa-output-header\u0026quot;\u0026gt;\n  \u0026lt;span\u0026gt;Generated CSS\u0026lt;/span\u0026gt;\n  \u0026lt;button class=\u0026quot;fa-copy-btn\u0026quot; id=\u0026quot;fa-copy-btn\u0026quot;\u0026gt;Copy CSS\u0026lt;/button\u0026gt;\n\u0026lt;/div\u0026gt;\n\u0026lt;div class=\u0026quot;fa-output\u0026quot; id=\u0026quot;fa-output\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\u003c!-- /fa-main --\u003e\n\u003c/div\u003e\u003c!-- #flex-app --\u003e\n\u003cscript\u003e\n(function() {\n  /* ── State ── */\n  var COLORS = [\n    '#667eea','#f6ad55','#68d391','#fc8181','#63b3ed',\n    '#b794f4','#f687b3','#4fd1c5','#fbd38d','#90cdf4'\n  ];\n\n  var container = {\n    display: 'flex',\n    flexDirection: 'row',\n    justifyContent: 'flex-start',\n    alignItems: 'stretch',\n    alignContent: 'normal',\n    flexWrap: 'nowrap',\n    gap: '0px',\n    height: '260px'\n  };\n\n  var defaultItem = function(i) {\n    return {\n      grow: 0, shrink: 1, basis: 'auto',\n      order: 0, alignSelf: 'auto',\n      width: 'auto', height: 'auto',\n      label: 'Item ' + (i + 1),\n      color: COLORS[i % COLORS.length]\n    };\n  };\n\n  var items = [defaultItem(0), defaultItem(1), defaultItem(2)];\n  var selectedItem = 0;\n\n  /* ── DOM refs ── */\n  var preview   = document.getElementById('fa-preview-container');\n  var outputEl  = document.getElementById('fa-output');\n  var tabsEl    = document.getElementById('fa-item-tabs');\n  var copyBtn   = document.getElementById('fa-copy-btn');\n\n  /* ── Bind container controls ── */\n  function bindSel(id, key, obj) {\n    var el = document.getElementById(id);\n    el.addEventListener('change', function() { obj[key] = el.value; render(); });\n  }\n  function bindInput(id, key, obj) {\n    var el = document.getElementById(id);\n    el.addEventListener('input', function() { obj[key] = el.value; render(); });\n  }\n\n  bindSel('fa-display',        'display',        container);\n  bindSel('fa-direction',      'flexDirection',  container);\n  bindSel('fa-justify',        'justifyContent', container);\n  bindSel('fa-align-items',    'alignItems',     container);\n  bindSel('fa-align-content',  'alignContent',   container);\n  bindSel('fa-wrap',           'flexWrap',       container);\n  bindInput('fa-gap',          'gap',            container);\n  bindInput('fa-height',       'height',         container);\n\n  /* ── Bind item controls ── */\n  function getItem() { return items[selectedItem]; }\n\n  function bindItemInput(id, key, parser) {\n    var el = document.getElementById(id);\n    el.addEventListener('input', function() {\n      getItem()[key] = parser ? parser(el.value) : el.value;\n      render();\n    });\n  }\n  function bindItemSel(id, key) {\n    var el = document.getElementById(id);\n    el.addEventListener('change', function() {\n      getItem()[key] = el.value;\n      render();\n    });\n  }\n\n  bindItemInput('fa-grow',        'grow',    Number);\n  bindItemInput('fa-shrink',      'shrink',  Number);\n  bindItemInput('fa-basis',       'basis',   null);\n  bindItemInput('fa-order',       'order',   Number);\n  bindItemSel  ('fa-align-self',  'alignSelf');\n  bindItemInput('fa-item-width',  'width',   null);\n  bindItemInput('fa-item-height', 'height',  null);\n  bindItemInput('fa-item-label',  'label',   null);\n\n  /* ── Add / Remove ── */\n  document.getElementById('fa-add-item').addEventListener('click', function() {\n    if (items.length \u003e= 10) return;\n    items.push(defaultItem(items.length));\n    selectedItem = items.length - 1;\n    render();\n  });\n\n  document.getElementById('fa-remove-item').addEventListener('click', function() {\n    if (items.length \u003c= 1) return;\n    items.splice(selectedItem, 1);\n    if (selectedItem \u003e= items.length) selectedItem = items.length - 1;\n    render();\n  });\n\n  /* ── Copy button ── */\n  copyBtn.addEventListener('click', function() {\n    var text = buildCSSText();\n    if (navigator.clipboard) {\n      navigator.clipboard.writeText(text).then(function() { flashCopy(); });\n    } else {\n      var ta = document.createElement('textarea');\n      ta.value = text;\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      flashCopy();\n    }\n  });\n  function flashCopy() {\n    copyBtn.textContent = 'Copied!';\n    copyBtn.classList.add('copied');\n    setTimeout(function() {\n      copyBtn.textContent = 'Copy CSS';\n      copyBtn.classList.remove('copied');\n    }, 1800);\n  }\n\n  /* ── Presets ── */\n  var PRESETS = {\n    navbar: {\n      container: { display:'flex', flexDirection:'row', justifyContent:'space-between',\n                   alignItems:'center', alignContent:'normal', flexWrap:'nowrap', gap:'0px', height:'56px' },\n      items: [\n        { grow:0, shrink:0, basis:'auto', order:0, alignSelf:'auto', width:'auto', height:'auto', label:'Logo', color:'#667eea' },\n        { grow:1, shrink:1, basis:'auto', order:0, alignSelf:'auto', width:'auto', height:'auto', label:'Nav Links', color:'#4a5568' },\n        { grow:0, shrink:0, basis:'auto', order:0, alignSelf:'auto', width:'auto', height:'auto', label:'CTA', color:'#48bb78' }\n      ]\n    },\n    cardgrid: {\n      container: { display:'flex', flexDirection:'row', justifyContent:'flex-start',\n                   alignItems:'stretch', alignContent:'normal', flexWrap:'wrap', gap:'16px', height:'auto' },\n      items: [0,1,2,3,4,5].map(function(i) {\n        return { grow:0, shrink:0, basis:'calc(33% - 12px)', order:0, alignSelf:'auto', width:'auto', height:'120px', label:'Card '+(i+1), color:COLORS[i] };\n      })\n    },\n    holygrail: {\n      container: { display:'flex', flexDirection:'row', justifyContent:'flex-start',\n                   alignItems:'stretch', alignContent:'normal', flexWrap:'nowrap', gap:'0px', height:'320px' },\n      items: [\n        { grow:0, shrink:0, basis:'180px', order:0, alignSelf:'auto', width:'auto', height:'auto', label:'Left Sidebar', color:'#667eea' },\n        { grow:1, shrink:1, basis:'auto', order:0, alignSelf:'auto', width:'auto', height:'auto', label:'Main Content', color:'#63b3ed' },\n        { grow:0, shrink:0, basis:'160px', order:0, alignSelf:'auto', width:'auto', height:'auto', label:'Right Sidebar', color:'#b794f4' }\n      ]\n    },\n    sidebar: {\n      container: { display:'flex', flexDirection:'row', justifyContent:'flex-start',\n                   alignItems:'stretch', alignContent:'normal', flexWrap:'nowrap', gap:'0px', height:'340px' },\n      items: [\n        { grow:0, shrink:0, basis:'220px', order:0, alignSelf:'auto', width:'auto', height:'auto', label:'Sidebar', color:'#4a5568' },\n        { grow:1, shrink:1, basis:'auto', order:0, alignSelf:'auto', width:'auto', height:'auto', label:'Content', color:'#68d391' }\n      ]\n    },\n    centered: {\n      container: { display:'flex', flexDirection:'column', justifyContent:'center',\n                   alignItems:'center', alignContent:'normal', flexWrap:'nowrap', gap:'12px', height:'300px' },\n      items: [\n        { grow:0, shrink:0, basis:'auto', order:0, alignSelf:'auto', width:'auto', height:'auto', label:'Centered', color:'#f6ad55' }\n      ]\n    }\n  };\n\n  document.querySelectorAll('#flex-app .fa-preset-btn').forEach(function(btn) {\n    btn.addEventListener('click', function() {\n      var p = PRESETS[btn.dataset.preset];\n      if (!p) return;\n      Object.assign(container, p.container);\n      items = p.items.map(function(it) { return Object.assign({}, it); });\n      selectedItem = 0;\n      syncContainerUI();\n      render();\n    });\n  });\n\n  function syncContainerUI() {\n    document.getElementById('fa-display').value        = container.display;\n    document.getElementById('fa-direction').value      = container.flexDirection;\n    document.getElementById('fa-justify').value        = container.justifyContent;\n    document.getElementById('fa-align-items').value    = container.alignItems;\n    document.getElementById('fa-align-content').value  = container.alignContent;\n    document.getElementById('fa-wrap').value           = container.flexWrap;\n    document.getElementById('fa-gap').value            = container.gap;\n    document.getElementById('fa-height').value         = container.height;\n  }\n\n  function syncItemUI() {\n    var it = getItem();\n    document.getElementById('fa-grow').value        = it.grow;\n    document.getElementById('fa-shrink').value      = it.shrink;\n    document.getElementById('fa-basis').value       = it.basis;\n    document.getElementById('fa-order').value       = it.order;\n    document.getElementById('fa-align-self').value  = it.alignSelf;\n    document.getElementById('fa-item-width').value  = it.width;\n    document.getElementById('fa-item-height').value = it.height;\n    document.getElementById('fa-item-label').value  = it.label;\n  }\n\n  /* ── Build CSS text ── */\n  function buildCSSText() {\n    var lines = [\n      '.container {',\n      '  display: ' + container.display + ';',\n      '  flex-direction: ' + container.flexDirection + ';',\n      '  justify-content: ' + container.justifyContent + ';',\n      '  align-items: ' + container.alignItems + ';'\n    ];\n    if (container.alignContent !== 'normal') {\n      lines.push('  align-content: ' + container.alignContent + ';');\n    }\n    if (container.flexWrap !== 'nowrap') {\n      lines.push('  flex-wrap: ' + container.flexWrap + ';');\n    }\n    if (container.gap !== '0px' \u0026\u0026 container.gap !== '0') {\n      lines.push('  gap: ' + container.gap + ';');\n    }\n    if (container.height \u0026\u0026 container.height !== 'auto') {\n      lines.push('  height: ' + container.height + ';');\n    }\n    lines.push('}');\n    lines.push('');\n\n    items.forEach(function(it, i) {\n      var sel = '.item-' + (i + 1);\n      var props = [];\n      if (it.grow !== 0)           props.push('  flex-grow: ' + it.grow + ';');\n      if (it.shrink !== 1)         props.push('  flex-shrink: ' + it.shrink + ';');\n      if (it.basis !== 'auto')     props.push('  flex-basis: ' + it.basis + ';');\n      if (it.order !== 0)          props.push('  order: ' + it.order + ';');\n      if (it.alignSelf !== 'auto') props.push('  align-self: ' + it.alignSelf + ';');\n      if (it.width !== 'auto')     props.push('  width: ' + it.width + ';');\n      if (it.height !== 'auto')    props.push('  height: ' + it.height + ';');\n      if (props.length \u003e 0) {\n        lines.push(sel + ' {');\n        props.forEach(function(p) { lines.push(p); });\n        lines.push('}');\n        lines.push('');\n      }\n    });\n\n    return lines.join('\\n');\n  }\n\n  /* ── Render highlighted CSS ── */\n  function renderCSS() {\n    var raw = buildCSSText();\n    var esc = raw\n      .replace(/\u0026/g,'\u0026amp;')\n      .replace(/\u003c/g,'\u0026lt;')\n      .replace(/\u003e/g,'\u0026gt;');\n\n    // Simple syntax highlight\n    esc = esc.replace(/(\\.[\\w-]+(?:\\s*,\\s*\\.[\\w-]+)*)\\s*\\{/g, '\u003cspan class=\"tok-sel\"\u003e$1\u003c/span\u003e \u003cspan class=\"tok-punc\"\u003e{\u003c/span\u003e');\n    esc = esc.replace(/\\}/g, '\u003cspan class=\"tok-punc\"\u003e}\u003c/span\u003e');\n    esc = esc.replace(/([\\w-]+)(\\s*:\\s*)([^;\u003c\\n]+)(;)/g,\n      '\u003cspan class=\"tok-prop\"\u003e$1\u003c/span\u003e\u003cspan class=\"tok-punc\"\u003e$2\u003c/span\u003e\u003cspan class=\"tok-val\"\u003e$3\u003c/span\u003e\u003cspan class=\"tok-punc\"\u003e$4\u003c/span\u003e');\n\n    outputEl.innerHTML = esc;\n  }\n\n  /* ── Render tabs ── */\n  function renderTabs() {\n    tabsEl.innerHTML = '';\n    items.forEach(function(it, i) {\n      var btn = document.createElement('button');\n      btn.className = 'fa-item-tab' + (i === selectedItem ? ' active' : '');\n      btn.textContent = it.label || ('Item ' + (i + 1));\n      btn.addEventListener('click', function() {\n        selectedItem = i;\n        render();\n      });\n      tabsEl.appendChild(btn);\n    });\n  }\n\n  /* ── Render preview ── */\n  function renderPreview() {\n    // Apply container styles\n    preview.style.display        = container.display;\n    preview.style.flexDirection  = container.flexDirection;\n    preview.style.justifyContent = container.justifyContent;\n    preview.style.alignItems     = container.alignItems;\n    preview.style.alignContent   = container.alignContent;\n    preview.style.flexWrap       = container.flexWrap;\n    preview.style.gap            = container.gap;\n    preview.style.height         = container.height;\n\n    // Clear and rebuild items\n    preview.innerHTML = '';\n    items.forEach(function(it, i) {\n      var el = document.createElement('div');\n      el.className = 'fa-flex-item' + (i === selectedItem ? ' selected' : '');\n      el.textContent = it.label || ('Item ' + (i + 1));\n      el.style.background  = it.color;\n      el.style.flexGrow    = it.grow;\n      el.style.flexShrink  = it.shrink;\n      el.style.flexBasis   = it.basis;\n      el.style.order       = it.order;\n      el.style.alignSelf   = it.alignSelf;\n      if (it.width  !== 'auto') el.style.width  = it.width;\n      if (it.height !== 'auto') el.style.height = it.height;\n      el.style.padding = '8px';\n      el.addEventListener('click', function() {\n        selectedItem = i;\n        render();\n      });\n      preview.appendChild(el);\n    });\n  }\n\n  /* ── Master render ── */\n  function render() {\n    renderPreview();\n    renderTabs();\n    syncItemUI();\n    renderCSS();\n  }\n\n  /* ── Init ── */\n  render();\n\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch3 id=\"related-tools\"\u003eRelated Tools\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003eGenerate box shadows → \u003ca href=\"https://productivity-works.com/tools/box-shadow-generator/\"\u003eCSS Box Shadow Generator\u003c/a\u003e\n\u003c/p\u003e","title":"CSS Flexbox Generator — Visual Playground"},{"content":" CSS Flexbox Playground Experiment with all Flexbox properties visually. Click an item to adjust its individual properties. Copy the generated CSS anytime.\nItems \u0026#8722; 3 items \u0026#43; \u0026lt;!-- Container --\u0026gt; \u0026lt;div class=\u0026quot;fb-panel\u0026quot; style=\u0026quot;margin-bottom:16px;\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fb-panel-header\u0026quot;\u0026gt;Container Properties\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fb-panel-body\u0026quot;\u0026gt; \u0026lt;p class=\u0026quot;fb-section-title\u0026quot;\u0026gt;flex-direction\u0026lt;/p\u0026gt; \u0026lt;div class=\u0026quot;fb-control-group\u0026quot;\u0026gt; \u0026lt;select class=\u0026quot;fb-select\u0026quot; id=\u0026quot;fb-flex-direction\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;row\u0026quot;\u0026gt;row\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;row-reverse\u0026quot;\u0026gt;row-reverse\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;column\u0026quot;\u0026gt;column\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;column-reverse\u0026quot;\u0026gt;column-reverse\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;p class=\u0026quot;fb-section-title\u0026quot;\u0026gt;flex-wrap\u0026lt;/p\u0026gt; \u0026lt;div class=\u0026quot;fb-control-group\u0026quot;\u0026gt; \u0026lt;select class=\u0026quot;fb-select\u0026quot; id=\u0026quot;fb-flex-wrap\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;nowrap\u0026quot;\u0026gt;nowrap\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;wrap\u0026quot;\u0026gt;wrap\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;wrap-reverse\u0026quot;\u0026gt;wrap-reverse\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;p class=\u0026quot;fb-section-title\u0026quot;\u0026gt;justify-content\u0026lt;/p\u0026gt; \u0026lt;div class=\u0026quot;fb-control-group\u0026quot;\u0026gt; \u0026lt;select class=\u0026quot;fb-select\u0026quot; id=\u0026quot;fb-justify-content\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;flex-start\u0026quot;\u0026gt;flex-start\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;flex-end\u0026quot;\u0026gt;flex-end\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;center\u0026quot;\u0026gt;center\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;space-between\u0026quot;\u0026gt;space-between\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;space-around\u0026quot;\u0026gt;space-around\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;space-evenly\u0026quot;\u0026gt;space-evenly\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;p class=\u0026quot;fb-section-title\u0026quot;\u0026gt;align-items\u0026lt;/p\u0026gt; \u0026lt;div class=\u0026quot;fb-control-group\u0026quot;\u0026gt; \u0026lt;select class=\u0026quot;fb-select\u0026quot; id=\u0026quot;fb-align-items\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;stretch\u0026quot;\u0026gt;stretch\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;flex-start\u0026quot;\u0026gt;flex-start\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;flex-end\u0026quot;\u0026gt;flex-end\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;center\u0026quot;\u0026gt;center\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;baseline\u0026quot;\u0026gt;baseline\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;p class=\u0026quot;fb-section-title\u0026quot;\u0026gt;align-content\u0026lt;/p\u0026gt; \u0026lt;div class=\u0026quot;fb-control-group\u0026quot;\u0026gt; \u0026lt;select class=\u0026quot;fb-select\u0026quot; id=\u0026quot;fb-align-content\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;normal\u0026quot;\u0026gt;normal\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;flex-start\u0026quot;\u0026gt;flex-start\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;flex-end\u0026quot;\u0026gt;flex-end\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;center\u0026quot;\u0026gt;center\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;space-between\u0026quot;\u0026gt;space-between\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;space-around\u0026quot;\u0026gt;space-around\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;space-evenly\u0026quot;\u0026gt;space-evenly\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;stretch\u0026quot;\u0026gt;stretch\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;p class=\u0026quot;fb-section-title\u0026quot;\u0026gt;gap\u0026lt;/p\u0026gt; \u0026lt;div class=\u0026quot;fb-control-group\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fb-range-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; class=\u0026quot;fb-range\u0026quot; id=\u0026quot;fb-gap\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;40\u0026quot; value=\u0026quot;8\u0026quot; step=\u0026quot;2\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;fb-range-val\u0026quot; id=\u0026quot;fb-gap-val\u0026quot;\u0026gt;8px\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Item --\u0026gt; \u0026lt;div class=\u0026quot;fb-panel\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fb-panel-header\u0026quot;\u0026gt;Item Properties \u0026lt;span id=\u0026quot;fb-item-label\u0026quot; style=\u0026quot;opacity:0.75;font-weight:400;\u0026quot;\u0026gt;(Item 1)\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fb-panel-body\u0026quot;\u0026gt; \u0026lt;p class=\u0026quot;fb-section-title\u0026quot;\u0026gt;order\u0026lt;/p\u0026gt; \u0026lt;div class=\u0026quot;fb-control-group\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fb-range-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; class=\u0026quot;fb-range\u0026quot; id=\u0026quot;fb-order\u0026quot; min=\u0026quot;-5\u0026quot; max=\u0026quot;10\u0026quot; value=\u0026quot;0\u0026quot; step=\u0026quot;1\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;fb-range-val\u0026quot; id=\u0026quot;fb-order-val\u0026quot;\u0026gt;0\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;p class=\u0026quot;fb-section-title\u0026quot;\u0026gt;flex-grow\u0026lt;/p\u0026gt; \u0026lt;div class=\u0026quot;fb-control-group\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fb-range-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; class=\u0026quot;fb-range\u0026quot; id=\u0026quot;fb-flex-grow\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;10\u0026quot; value=\u0026quot;0\u0026quot; step=\u0026quot;1\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;fb-range-val\u0026quot; id=\u0026quot;fb-flex-grow-val\u0026quot;\u0026gt;0\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;p class=\u0026quot;fb-section-title\u0026quot;\u0026gt;flex-shrink\u0026lt;/p\u0026gt; \u0026lt;div class=\u0026quot;fb-control-group\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fb-range-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; class=\u0026quot;fb-range\u0026quot; id=\u0026quot;fb-flex-shrink\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;10\u0026quot; value=\u0026quot;1\u0026quot; step=\u0026quot;1\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;fb-range-val\u0026quot; id=\u0026quot;fb-flex-shrink-val\u0026quot;\u0026gt;1\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;p class=\u0026quot;fb-section-title\u0026quot;\u0026gt;flex-basis\u0026lt;/p\u0026gt; \u0026lt;div class=\u0026quot;fb-control-group\u0026quot;\u0026gt; \u0026lt;select class=\u0026quot;fb-select\u0026quot; id=\u0026quot;fb-flex-basis\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;auto\u0026quot;\u0026gt;auto\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;0\u0026quot;\u0026gt;0\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;50px\u0026quot;\u0026gt;50px\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;80px\u0026quot;\u0026gt;80px\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;100px\u0026quot;\u0026gt;100px\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;120px\u0026quot;\u0026gt;120px\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;150px\u0026quot;\u0026gt;150px\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;200px\u0026quot;\u0026gt;200px\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;25%\u0026quot;\u0026gt;25%\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;33%\u0026quot;\u0026gt;33%\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;50%\u0026quot;\u0026gt;50%\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;p class=\u0026quot;fb-section-title\u0026quot;\u0026gt;align-self\u0026lt;/p\u0026gt; \u0026lt;div class=\u0026quot;fb-control-group\u0026quot;\u0026gt; \u0026lt;select class=\u0026quot;fb-select\u0026quot; id=\u0026quot;fb-align-self\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;auto\u0026quot;\u0026gt;auto\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;flex-start\u0026quot;\u0026gt;flex-start\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;flex-end\u0026quot;\u0026gt;flex-end\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;center\u0026quot;\u0026gt;center\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;baseline\u0026quot;\u0026gt;baseline\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;stretch\u0026quot;\u0026gt;stretch\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Live Preview \u0026lt;div class=\u0026quot;fb-code-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fb-code-header\u0026quot;\u0026gt; \u0026lt;span\u0026gt;Generated CSS\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026quot;fb-copy-btn\u0026quot; id=\u0026quot;fb-copy-btn\u0026quot;\u0026gt;Copy CSS\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;pre class=\u0026quot;fb-code\u0026quot; id=\u0026quot;fb-code-output\u0026quot;\u0026gt;\u0026lt;/pre\u0026gt; \u0026lt;/div\u0026gt; CSS Clip-Path → CSS Clip-Path Maker CSS Specificity → CSS Specificity Calculator ","permalink":"https://productivity-works.com/tools/flexbox-playground/","summary":"\u003cdiv id=\"fb-app\"\u003e\n\u003cstyle\u003e\n  #fb-app *, #fb-app *::before, #fb-app *::after {\n    box-sizing: border-box;\n    margin: 0;\n    padding: 0;\n  }\n  #fb-app {\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n    font-size: 14px;\n    color: #1a1a2e;\n    background: #f8f9ff;\n    border-radius: 12px;\n    padding: 24px;\n    max-width: 100%;\n    overflow: hidden;\n  }\n  #fb-app h2.fb-title {\n    font-size: 1.4rem;\n    font-weight: 700;\n    margin-bottom: 4px;\n    color: #1a1a2e;\n  }\n  #fb-app p.fb-subtitle {\n    color: #666;\n    margin-bottom: 20px;\n    font-size: 0.9rem;\n  }\n  #fb-app .fb-layout {\n    display: grid;\n    grid-template-columns: 300px 1fr;\n    gap: 20px;\n    align-items: start;\n  }\n  @media (max-width: 768px) {\n    #fb-app .fb-layout {\n      grid-template-columns: 1fr;\n    }\n  }\n  /* ---- Panels ---- */\n  #fb-app .fb-panel {\n    background: #fff;\n    border: 1.5px solid #dde1ff;\n    border-radius: 10px;\n    overflow: hidden;\n  }\n  #fb-app .fb-panel-header {\n    background: linear-gradient(90deg, #4f46e5 0%, #7c3aed 100%);\n    color: #fff;\n    font-weight: 700;\n    font-size: 0.85rem;\n    padding: 9px 14px;\n    letter-spacing: 0.03em;\n    text-transform: uppercase;\n  }\n  #fb-app .fb-panel-body {\n    padding: 14px;\n  }\n  /* ---- Controls ---- */\n  #fb-app .fb-control-group {\n    margin-bottom: 12px;\n  }\n  #fb-app .fb-control-group:last-child {\n    margin-bottom: 0;\n  }\n  #fb-app .fb-label {\n    display: block;\n    font-size: 0.78rem;\n    font-weight: 600;\n    color: #4f46e5;\n    margin-bottom: 4px;\n    font-family: \"Courier New\", monospace;\n  }\n  #fb-app select.fb-select,\n  #fb-app input.fb-input {\n    width: 100%;\n    padding: 6px 10px;\n    border: 1.5px solid #c7d2fe;\n    border-radius: 7px;\n    font-size: 0.85rem;\n    color: #1a1a2e;\n    background: #f5f7ff;\n    appearance: none;\n    -webkit-appearance: none;\n    cursor: pointer;\n    outline: none;\n    transition: border-color 0.15s;\n  }\n  #fb-app select.fb-select:focus,\n  #fb-app input.fb-input:focus {\n    border-color: #4f46e5;\n    background: #fff;\n  }\n  #fb-app input[type=\"range\"].fb-range {\n    width: 100%;\n    accent-color: #4f46e5;\n    cursor: pointer;\n  }\n  #fb-app .fb-range-row {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n  }\n  #fb-app .fb-range-val {\n    min-width: 38px;\n    font-size: 0.82rem;\n    font-weight: 700;\n    color: #4f46e5;\n    text-align: right;\n  }\n  /* ---- Section divider ---- */\n  #fb-app .fb-section-title {\n    font-size: 0.72rem;\n    font-weight: 800;\n    text-transform: uppercase;\n    letter-spacing: 0.08em;\n    color: #7c3aed;\n    border-bottom: 1.5px solid #ede9fe;\n    padding-bottom: 4px;\n    margin-bottom: 10px;\n    margin-top: 14px;\n  }\n  #fb-app .fb-section-title:first-child {\n    margin-top: 0;\n  }\n  /* ---- Item count ---- */\n  #fb-app .fb-item-count-row {\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    margin-bottom: 14px;\n  }\n  #fb-app .fb-count-btn {\n    width: 28px;\n    height: 28px;\n    border-radius: 50%;\n    border: 2px solid #4f46e5;\n    background: #fff;\n    color: #4f46e5;\n    font-size: 1.1rem;\n    font-weight: 700;\n    cursor: pointer;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    line-height: 1;\n    transition: all 0.15s;\n  }\n  #fb-app .fb-count-btn:hover {\n    background: #4f46e5;\n    color: #fff;\n  }\n  #fb-app .fb-count-display {\n    font-size: 0.9rem;\n    font-weight: 700;\n    min-width: 60px;\n    color: #1a1a2e;\n  }\n  /* ---- Item selector tabs ---- */\n  #fb-app .fb-item-tabs {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 5px;\n    margin-bottom: 12px;\n  }\n  #fb-app .fb-item-tab {\n    padding: 4px 10px;\n    border-radius: 5px;\n    border: 1.5px solid #c7d2fe;\n    background: #f5f7ff;\n    font-size: 0.78rem;\n    font-weight: 600;\n    cursor: pointer;\n    color: #4f46e5;\n    transition: all 0.15s;\n  }\n  #fb-app .fb-item-tab.active {\n    background: #4f46e5;\n    color: #fff;\n    border-color: #4f46e5;\n  }\n  #fb-app .fb-item-tab:hover:not(.active) {\n    background: #ede9fe;\n  }\n  /* ---- Right column ---- */\n  #fb-app .fb-right {\n    display: flex;\n    flex-direction: column;\n    gap: 20px;\n  }\n  /* ---- Preview ---- */\n  #fb-app .fb-preview-wrap {\n    background: #fff;\n    border: 1.5px solid #dde1ff;\n    border-radius: 10px;\n    overflow: hidden;\n  }\n  #fb-app .fb-preview-header {\n    background: linear-gradient(90deg, #4f46e5 0%, #7c3aed 100%);\n    color: #fff;\n    font-weight: 700;\n    font-size: 0.85rem;\n    padding: 9px 14px;\n    letter-spacing: 0.03em;\n    text-transform: uppercase;\n  }\n  #fb-app .fb-preview-container {\n    padding: 20px;\n    background: #f0f4ff;\n    min-height: 220px;\n    position: relative;\n  }\n  #fb-app .fb-preview-inner {\n    background: #e8eeff;\n    border: 2px dashed #a5b4fc;\n    border-radius: 8px;\n    min-height: 180px;\n    padding: 8px;\n    width: 100%;\n    overflow: auto;\n  }\n  #fb-app .fb-flex-container {\n    width: 100%;\n    min-height: 160px;\n  }\n  #fb-app .fb-flex-item {\n    min-width: 40px;\n    min-height: 50px;\n    border-radius: 7px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    font-weight: 800;\n    font-size: 1rem;\n    color: #fff;\n    text-shadow: 0 1px 3px rgba(0,0,0,0.25);\n    cursor: pointer;\n    transition: box-shadow 0.15s, outline 0.15s;\n    position: relative;\n  }\n  #fb-app .fb-flex-item.selected-item {\n    outline: 3px solid #1a1a2e;\n    outline-offset: 2px;\n  }\n  /* ---- Code output ---- */\n  #fb-app .fb-code-wrap {\n    background: #fff;\n    border: 1.5px solid #dde1ff;\n    border-radius: 10px;\n    overflow: hidden;\n  }\n  #fb-app .fb-code-header {\n    background: linear-gradient(90deg, #4f46e5 0%, #7c3aed 100%);\n    color: #fff;\n    font-weight: 700;\n    font-size: 0.85rem;\n    padding: 9px 14px;\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n  }\n  #fb-app .fb-copy-btn {\n    background: rgba(255,255,255,0.2);\n    border: 1.5px solid rgba(255,255,255,0.5);\n    color: #fff;\n    border-radius: 6px;\n    padding: 3px 12px;\n    font-size: 0.78rem;\n    font-weight: 700;\n    cursor: pointer;\n    transition: background 0.15s;\n  }\n  #fb-app .fb-copy-btn:hover {\n    background: rgba(255,255,255,0.35);\n  }\n  #fb-app .fb-copy-btn.copied {\n    background: #10b981;\n    border-color: #10b981;\n  }\n  #fb-app pre.fb-code {\n    margin: 0;\n    padding: 14px 16px;\n    background: #1e1b4b;\n    color: #c4b5fd;\n    font-family: \"Courier New\", Courier, monospace;\n    font-size: 0.82rem;\n    line-height: 1.6;\n    overflow-x: auto;\n    white-space: pre;\n  }\n  #fb-app pre.fb-code .fb-prop { color: #a5f3fc; }\n  #fb-app pre.fb-code .fb-val  { color: #fde68a; }\n  #fb-app pre.fb-code .fb-sel  { color: #86efac; }\n  #fb-app pre.fb-code .fb-brace{ color: #f0abfc; }\n  #fb-app pre.fb-code .fb-comment{ color: #6b7280; font-style: italic; }\n\u003c/style\u003e\n\u003ch2 class=\"fb-title\"\u003eCSS Flexbox Playground\u003c/h2\u003e\n\u003cp class=\"fb-subtitle\"\u003eExperiment with all Flexbox properties visually. Click an item to adjust its individual properties. Copy the generated CSS anytime.\u003c/p\u003e","title":"CSS Flexbox Playground"},{"content":"Craft beautiful gradient borders in seconds — choose colors, adjust direction, width, and radius, then copy the clean CSS code instantly. No libraries, no login required.\nPreview Color Stops + Add Color Stop Gradient Direction ↑ 0° ↗ 45° → 90° ↘ 135° ↓ 180° ↙ 225° ← 270° ↖ 315° Custom Angle ° Border Settings Border Width 3px Border Radius 12px Padding 20px CSS Technique background-clip (recommended) border-image Uses background-clip: padding-box with a gradient background behind — supports border-radius and all browsers.\nPresets Generated CSS Copy CSS Related Tools CSS Gradient Generator Border Radius Generator CSS Animation Generator How to Use the CSS Gradient Border Generator Pick your colors — Click any color swatch to open the color picker, or type a hex code directly. You can add up to 5 color stops using the \u0026ldquo;+ Add Color Stop\u0026rdquo; button, and remove any stop with the × button (minimum 2 required).\nAdjust position — Use the position slider next to each color stop to control where each color begins in the gradient (0–100%).\nChoose direction — Click any of the 8 preset direction buttons (0°, 45°, 90°, etc.) or drag the custom angle slider for precise control from 0° to 360°.\nSet border properties — Use the sliders to control border width (1–10px), border radius (0–50px for rounded corners), and padding inside the border.\nSelect CSS technique — Choose between two approaches:\nbackground-clip (recommended): Wraps content in a container with a gradient background. Works with border-radius and all modern browsers. border-image: A simpler single-element approach, but does not support border-radius. Apply a preset — Click any preset tile in the gallery to instantly apply a curated color combination.\nCopy the CSS — Click \u0026ldquo;Copy CSS\u0026rdquo; to copy the generated code to your clipboard. Paste it directly into your stylesheet.\nCSS Gradient Border Techniques Explained background-clip Method The most versatile approach. The outer element has a gradient background and border-radius, while the inner element has a white (or any color) background with a slightly smaller radius:\n.gradient-border { padding: 20px; background: linear-gradient(90deg, #6366f1 0%, #ec4899 100%); border-radius: 12px; } .gradient-border-inner { background: #fff; border-radius: 9px; padding: 3px; } border-image Method Simpler HTML — a single element — but border-radius is not supported:\n.gradient-border { border: 3px solid transparent; border-image: linear-gradient(90deg, #6366f1 0%, #ec4899 100%) 1; } For transparent backgrounds or dark-mode designs, the background-clip: padding-box technique combined with a pseudo-element (::before) is another popular approach that gives full radius support.\nRelated CSS Tools\nCSS Gradient Generator — Linear, radial, and conic gradients Border Radius Generator — Visual corner radius editor CSS Animation Generator — Keyframe animations without the syntax hassle ","permalink":"https://productivity-works.com/tools/gradient-border-generator/","summary":"\u003cp\u003eCraft beautiful gradient borders in seconds — choose colors, adjust direction, width, and radius, then copy the clean CSS code instantly. No libraries, no login required.\u003c/p\u003e\n\u003cdiv id=\"gb-app\"\u003e\n\u003cstyle\u003e\n#gb-app *,\n#gb-app *::before,\n#gb-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#gb-app {\n  font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 880px;\n  margin: 0 auto;\n  padding: 0 0 56px;\n  color: #1f2937;\n}\n\n/* ── Preview ────────────────────────────────────────── */\n.gb-preview-wrap {\n  background: repeating-conic-gradient(#e5e7eb 0% 25%, #fff 0% 50%) 0 0 / 20px 20px;\n  border-radius: 16px;\n  padding: 48px 32px;\n  margin-bottom: 20px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  min-height: 220px;\n  box-shadow: 0 4px 24px rgba(0,0,0,0.10);\n}\n.gb-preview-box {\n  background: #fff;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 15px;\n  color: #6b7280;\n  font-weight: 500;\n  min-width: 200px;\n  min-height: 100px;\n  transition: all 0.2s;\n  word-break: break-all;\n}\n\n/* ── Cards ──────────────────────────────────────────── */\n.gb-card {\n  background: #fff;\n  border-radius: 14px;\n  box-shadow: 0 2px 16px rgba(0,0,0,0.07);\n  padding: 22px 24px;\n  margin-bottom: 16px;\n}\n.gb-card-title {\n  font-size: 12px;\n  font-weight: 700;\n  color: #374151;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  border-bottom: 2px solid #f3f4f6;\n  padding-bottom: 10px;\n  margin-bottom: 18px;\n}\n\n/* ── Color Stops ─────────────────────────────────────── */\n.gb-stops-list {\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n  margin-bottom: 14px;\n}\n.gb-stop-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n.gb-color-swatch {\n  width: 38px;\n  height: 38px;\n  border-radius: 8px;\n  border: 2px solid #e5e7eb;\n  cursor: pointer;\n  position: relative;\n  overflow: hidden;\n  flex-shrink: 0;\n}\n.gb-color-swatch input[type=\"color\"] {\n  position: absolute;\n  inset: -4px;\n  width: calc(100% + 8px);\n  height: calc(100% + 8px);\n  opacity: 0;\n  cursor: pointer;\n}\n.gb-color-hex {\n  width: 88px;\n  padding: 7px 10px;\n  border: 1.5px solid #e5e7eb;\n  border-radius: 8px;\n  font-size: 13px;\n  font-family: 'Menlo', 'Consolas', monospace;\n  color: #374151;\n  outline: none;\n  transition: border-color 0.15s;\n}\n.gb-color-hex:focus { border-color: #6366f1; }\n.gb-stop-label {\n  font-size: 12px;\n  color: #9ca3af;\n  width: 32px;\n  text-align: center;\n  flex-shrink: 0;\n}\n.gb-stop-percent {\n  flex: 1;\n  accent-color: #6366f1;\n}\n.gb-stop-pct-val {\n  font-size: 12px;\n  color: #6b7280;\n  width: 34px;\n  text-align: right;\n  flex-shrink: 0;\n}\n.gb-stop-remove {\n  width: 28px;\n  height: 28px;\n  border-radius: 6px;\n  border: none;\n  background: #fee2e2;\n  color: #ef4444;\n  font-size: 16px;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  flex-shrink: 0;\n  transition: background 0.15s;\n  line-height: 1;\n}\n.gb-stop-remove:hover { background: #fecaca; }\n.gb-stop-remove:disabled { opacity: 0.3; cursor: default; }\n.gb-add-stop-btn {\n  padding: 8px 18px;\n  border-radius: 8px;\n  border: 1.5px dashed #c7d2fe;\n  background: #eef2ff;\n  color: #6366f1;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n.gb-add-stop-btn:hover { background: #e0e7ff; }\n.gb-add-stop-btn:disabled { opacity: 0.4; cursor: default; }\n\n/* ── Direction ───────────────────────────────────────── */\n.gb-dir-row {\n  display: flex;\n  align-items: center;\n  gap: 14px;\n  flex-wrap: wrap;\n}\n.gb-dir-presets {\n  display: flex;\n  gap: 6px;\n  flex-wrap: wrap;\n}\n.gb-dir-btn {\n  padding: 6px 12px;\n  border-radius: 7px;\n  border: 1.5px solid #e5e7eb;\n  background: #f9fafb;\n  color: #374151;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n.gb-dir-btn:hover { border-color: #a5b4fc; background: #eef2ff; }\n.gb-dir-btn.gb-active { border-color: #6366f1; background: #eef2ff; color: #4f46e5; }\n.gb-angle-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-top: 14px;\n}\n.gb-angle-label { font-size: 13px; color: #6b7280; flex-shrink: 0; }\n.gb-angle-slider { flex: 1; accent-color: #6366f1; }\n.gb-angle-num {\n  width: 60px;\n  padding: 6px 8px;\n  border: 1.5px solid #e5e7eb;\n  border-radius: 7px;\n  font-size: 13px;\n  text-align: center;\n  outline: none;\n}\n.gb-angle-num:focus { border-color: #6366f1; }\n.gb-angle-deg { font-size: 13px; color: #9ca3af; }\n\n/* ── Sliders ─────────────────────────────────────────── */\n.gb-slider-row {\n  display: flex;\n  align-items: center;\n  gap: 14px;\n  margin-bottom: 14px;\n}\n.gb-slider-row:last-child { margin-bottom: 0; }\n.gb-slider-label {\n  font-size: 13px;\n  color: #374151;\n  width: 110px;\n  flex-shrink: 0;\n}\n.gb-slider {\n  flex: 1;\n  accent-color: #6366f1;\n}\n.gb-slider-val {\n  font-size: 13px;\n  color: #6b7280;\n  width: 44px;\n  text-align: right;\n  flex-shrink: 0;\n  font-variant-numeric: tabular-nums;\n}\n\n/* ── Technique Toggle ────────────────────────────────── */\n.gb-tech-row {\n  display: flex;\n  gap: 8px;\n  flex-wrap: wrap;\n}\n.gb-tech-btn {\n  padding: 7px 16px;\n  border-radius: 8px;\n  border: 1.5px solid #e5e7eb;\n  background: #f9fafb;\n  color: #374151;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n.gb-tech-btn:hover { border-color: #a5b4fc; }\n.gb-tech-btn.gb-active { border-color: #6366f1; background: #eef2ff; color: #4f46e5; }\n.gb-tech-note {\n  font-size: 12px;\n  color: #9ca3af;\n  margin-top: 10px;\n  line-height: 1.5;\n}\n\n/* ── Presets ─────────────────────────────────────────── */\n.gb-presets-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));\n  gap: 10px;\n}\n.gb-preset-item {\n  border-radius: 10px;\n  height: 52px;\n  cursor: pointer;\n  border: 3px solid transparent;\n  transition: all 0.15s;\n  position: relative;\n  overflow: hidden;\n}\n.gb-preset-item:hover { transform: scale(1.06); box-shadow: 0 4px 12px rgba(0,0,0,0.15); }\n.gb-preset-item.gb-active { border-color: #6366f1; }\n.gb-preset-label {\n  position: absolute;\n  bottom: 0; left: 0; right: 0;\n  background: rgba(0,0,0,0.45);\n  color: #fff;\n  font-size: 9px;\n  text-align: center;\n  padding: 2px 4px;\n}\n\n/* ── CSS Output ──────────────────────────────────────── */\n.gb-output-wrap {\n  position: relative;\n}\n.gb-output-code {\n  display: block;\n  background: #1e1e2e;\n  color: #cdd6f4;\n  border-radius: 10px;\n  padding: 18px 20px;\n  font-family: 'Menlo', 'Consolas', monospace;\n  font-size: 13px;\n  line-height: 1.7;\n  white-space: pre-wrap;\n  word-break: break-all;\n  min-height: 80px;\n  user-select: all;\n}\n.gb-copy-btn {\n  margin-top: 12px;\n  padding: 11px 28px;\n  background: linear-gradient(135deg, #6366f1, #8b5cf6);\n  color: #fff;\n  border: none;\n  border-radius: 9px;\n  font-size: 14px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: opacity 0.15s, transform 0.1s;\n  width: 100%;\n}\n.gb-copy-btn:hover { opacity: 0.9; transform: translateY(-1px); }\n.gb-copy-btn.gb-copied { background: linear-gradient(135deg, #10b981, #059669); }\n\n/* ── Related Links ───────────────────────────────────── */\n.gb-related {\n  margin-top: 28px;\n  padding-top: 22px;\n  border-top: 2px solid #f3f4f6;\n}\n.gb-related-title {\n  font-size: 13px;\n  font-weight: 700;\n  color: #374151;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  margin-bottom: 12px;\n}\n.gb-related-links {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n}\n.gb-related-links a {\n  padding: 7px 14px;\n  background: #f3f4f6;\n  border-radius: 7px;\n  font-size: 13px;\n  color: #4f46e5;\n  text-decoration: none;\n  font-weight: 500;\n  transition: background 0.15s;\n}\n.gb-related-links a:hover { background: #e0e7ff; }\n\u003c/style\u003e\n\u003c!-- Preview --\u003e\n\u003cdiv class=\"gb-preview-wrap\"\u003e\n  \u003cdiv class=\"gb-preview-box\" id=\"gb-preview\"\u003ePreview\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Color Stops --\u003e\n\u003cdiv class=\"gb-card\"\u003e\n  \u003cdiv class=\"gb-card-title\"\u003eColor Stops\u003c/div\u003e\n  \u003cdiv class=\"gb-stops-list\" id=\"gb-stops-list\"\u003e\u003c/div\u003e\n  \u003cbutton class=\"gb-add-stop-btn\" id=\"gb-add-stop\"\u003e+ Add Color Stop\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Direction --\u003e\n\u003cdiv class=\"gb-card\"\u003e\n  \u003cdiv class=\"gb-card-title\"\u003eGradient Direction\u003c/div\u003e\n  \u003cdiv class=\"gb-dir-row\"\u003e\n    \u003cdiv class=\"gb-dir-presets\" id=\"gb-dir-presets\"\u003e\n      \u003cbutton class=\"gb-dir-btn\" data-angle=\"0\"\u003e↑ 0°\u003c/button\u003e\n      \u003cbutton class=\"gb-dir-btn\" data-angle=\"45\"\u003e↗ 45°\u003c/button\u003e\n      \u003cbutton class=\"gb-dir-btn gb-active\" data-angle=\"90\"\u003e→ 90°\u003c/button\u003e\n      \u003cbutton class=\"gb-dir-btn\" data-angle=\"135\"\u003e↘ 135°\u003c/button\u003e\n      \u003cbutton class=\"gb-dir-btn\" data-angle=\"180\"\u003e↓ 180°\u003c/button\u003e\n      \u003cbutton class=\"gb-dir-btn\" data-angle=\"225\"\u003e↙ 225°\u003c/button\u003e\n      \u003cbutton class=\"gb-dir-btn\" data-angle=\"270\"\u003e← 270°\u003c/button\u003e\n      \u003cbutton class=\"gb-dir-btn\" data-angle=\"315\"\u003e↖ 315°\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"gb-angle-row\"\u003e\n    \u003cspan class=\"gb-angle-label\"\u003eCustom Angle\u003c/span\u003e\n    \u003cinput type=\"range\" class=\"gb-angle-slider\" id=\"gb-angle-slider\" min=\"0\" max=\"360\" value=\"90\"\u003e\n    \u003cinput type=\"number\" class=\"gb-angle-num\" id=\"gb-angle-num\" min=\"0\" max=\"360\" value=\"90\"\u003e\n    \u003cspan class=\"gb-angle-deg\"\u003e°\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Border Controls --\u003e\n\u003cdiv class=\"gb-card\"\u003e\n  \u003cdiv class=\"gb-card-title\"\u003eBorder Settings\u003c/div\u003e\n  \u003cdiv class=\"gb-slider-row\"\u003e\n    \u003cspan class=\"gb-slider-label\"\u003eBorder Width\u003c/span\u003e\n    \u003cinput type=\"range\" class=\"gb-slider\" id=\"gb-border-width\" min=\"1\" max=\"10\" value=\"3\"\u003e\n    \u003cspan class=\"gb-slider-val\" id=\"gb-border-width-val\"\u003e3px\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"gb-slider-row\"\u003e\n    \u003cspan class=\"gb-slider-label\"\u003eBorder Radius\u003c/span\u003e\n    \u003cinput type=\"range\" class=\"gb-slider\" id=\"gb-border-radius\" min=\"0\" max=\"50\" value=\"12\"\u003e\n    \u003cspan class=\"gb-slider-val\" id=\"gb-border-radius-val\"\u003e12px\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"gb-slider-row\"\u003e\n    \u003cspan class=\"gb-slider-label\"\u003ePadding\u003c/span\u003e\n    \u003cinput type=\"range\" class=\"gb-slider\" id=\"gb-padding\" min=\"8\" max=\"48\" value=\"20\"\u003e\n    \u003cspan class=\"gb-slider-val\" id=\"gb-padding-val\"\u003e20px\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Technique --\u003e\n\u003cdiv class=\"gb-card\"\u003e\n  \u003cdiv class=\"gb-card-title\"\u003eCSS Technique\u003c/div\u003e\n  \u003cdiv class=\"gb-tech-row\" id=\"gb-tech-row\"\u003e\n    \u003cbutton class=\"gb-tech-btn gb-active\" data-tech=\"background-clip\"\u003ebackground-clip (recommended)\u003c/button\u003e\n    \u003cbutton class=\"gb-tech-btn\" data-tech=\"border-image\"\u003eborder-image\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cp class=\"gb-tech-note\" id=\"gb-tech-note\"\u003eUses \u003ccode\u003ebackground-clip: padding-box\u003c/code\u003e with a gradient background behind — supports border-radius and all browsers.\u003c/p\u003e","title":"CSS Gradient Border Generator"},{"content":" linear-gradient(135deg, ...) Random Gradient Copy CSS Gradient Type Linear Radial Conic Angle ° Shape Circle Ellipse Position ↖ ↑ ↗ ← · → ↙ ↓ ↘ From Angle ° Color Stops + Add Color Stop Presets CSS Code Copy How to Use the CSS Gradient Generator Choose a gradient type at the top: Linear gradients flow along a straight line at your chosen angle. Radial gradients expand outward from a center point in a circle or ellipse shape. Conic gradients sweep around a center point like a color wheel.\nControl the angle with the slider (0–360°) or type a value directly. 0° is top-to-bottom, 90° is left-to-right, 135° is diagonal.\nAdd color stops using the color swatches (click to open the native color picker) or type a HEX code directly. Drag the position slider or type a percentage to set where each color starts. You can add up to 5 stops and remove any stop with the × button.\nApply a preset from the grid to get started instantly. Presets include Sunset, Ocean, Forest, Neon, Lavender, and more.\nHit Random for an instant surprise gradient — great for inspiration.\nCopy the CSS with the Copy button. The output includes both the standard background property and the -webkit- prefixed version for maximum browser compatibility.\nCSS Gradient Syntax Reference A linear gradient uses linear-gradient(angle, color1 pos1, color2 pos2, ...). The angle is in degrees: 0deg goes bottom to top, 90deg goes left to right, 180deg goes top to bottom.\nA radial gradient uses radial-gradient(shape size at position, colors...). Shape is circle or ellipse. Position can be keywords like center, top left, or percentage values.\nA conic gradient uses conic-gradient(from angle, colors...). Colors sweep around a point like a pie chart rather than radiating outward. Browser support is now excellent across modern browsers.\nFor smooth gradients, avoid placing two color stops at the same position unless you want a hard color edge. Spreading stops evenly (0%, 50%, 100%) creates the smoothest transitions.\nRelated: Pick perfect colors with our Color Picker ","permalink":"https://productivity-works.com/tools/css-gradient-generator/","summary":"\u003cdiv id=\"grad-app\"\u003e\n\u003cstyle\u003e\n#grad-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  padding: 0 0 48px 0;\n  color: #374151;\n}\n\n#grad-app * {\n  box-sizing: border-box;\n}\n\n/* Preview */\n.ga-preview-wrap {\n  border-radius: 16px;\n  overflow: hidden;\n  margin-bottom: 20px;\n  box-shadow: 0 4px 24px rgba(0,0,0,0.15);\n  position: relative;\n}\n\n.ga-preview {\n  width: 100%;\n  height: 320px;\n  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);\n  transition: background 0.2s;\n}\n\n.ga-preview-label {\n  position: absolute;\n  bottom: 14px;\n  left: 50%;\n  transform: translateX(-50%);\n  background: rgba(0,0,0,0.45);\n  color: #fff;\n  font-size: 12px;\n  padding: 4px 12px;\n  border-radius: 20px;\n  white-space: nowrap;\n  pointer-events: none;\n}\n\n/* Cards */\n.ga-card {\n  background: #fff;\n  border-radius: 14px;\n  box-shadow: 0 2px 16px rgba(0,0,0,0.07);\n  padding: 22px 24px;\n  margin-bottom: 16px;\n}\n\n.ga-card-title {\n  margin: 0 0 16px 0;\n  font-size: 13px;\n  font-weight: 700;\n  color: #374151;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  border-bottom: 2px solid #f3f4f6;\n  padding-bottom: 10px;\n}\n\n/* Type toggle */\n.ga-type-row {\n  display: flex;\n  gap: 8px;\n  flex-wrap: wrap;\n}\n\n.ga-type-btn {\n  flex: 1;\n  min-width: 90px;\n  padding: 9px 14px;\n  border: 2px solid #e5e7eb;\n  border-radius: 10px;\n  background: #f9fafb;\n  font-size: 13px;\n  font-weight: 600;\n  color: #374151;\n  cursor: pointer;\n  transition: all 0.18s;\n  text-align: center;\n}\n\n.ga-type-btn:hover {\n  border-color: #9ca3af;\n  background: #f3f4f6;\n}\n\n.ga-type-btn.ga-active {\n  border-color: #374151;\n  background: #374151;\n  color: #fff;\n}\n\n/* Linear angle */\n.ga-angle-row {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  margin-top: 14px;\n}\n\n.ga-angle-label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #6b7280;\n  white-space: nowrap;\n  width: 110px;\n  flex-shrink: 0;\n}\n\n.ga-angle-slider {\n  flex: 1;\n  height: 6px;\n  border-radius: 3px;\n  -webkit-appearance: none;\n  appearance: none;\n  background: #e5e7eb;\n  outline: none;\n  cursor: pointer;\n  min-width: 0;\n}\n\n.ga-angle-slider::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 20px;\n  height: 20px;\n  border-radius: 50%;\n  background: #374151;\n  border: 3px solid #fff;\n  box-shadow: 0 1px 6px rgba(0,0,0,0.25);\n  cursor: pointer;\n}\n\n.ga-angle-slider::-moz-range-thumb {\n  width: 20px;\n  height: 20px;\n  border-radius: 50%;\n  background: #374151;\n  border: 3px solid #fff;\n  box-shadow: 0 1px 6px rgba(0,0,0,0.25);\n  cursor: pointer;\n}\n\n.ga-angle-num {\n  width: 64px;\n  padding: 7px 10px;\n  border: 2px solid #e5e7eb;\n  border-radius: 8px;\n  font-size: 13px;\n  font-weight: 600;\n  text-align: center;\n  color: #374151;\n  font-family: 'Courier New', monospace;\n  flex-shrink: 0;\n}\n\n.ga-angle-num:focus {\n  outline: none;\n  border-color: #374151;\n}\n\n/* Radial options */\n.ga-radial-row {\n  display: flex;\n  gap: 16px;\n  margin-top: 14px;\n  flex-wrap: wrap;\n  align-items: flex-start;\n}\n\n.ga-radial-field {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n}\n\n.ga-field-label {\n  font-size: 12px;\n  font-weight: 700;\n  color: #6b7280;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n\n.ga-select {\n  padding: 8px 12px;\n  border: 2px solid #e5e7eb;\n  border-radius: 8px;\n  font-size: 13px;\n  color: #374151;\n  background: #fff;\n  cursor: pointer;\n  font-weight: 600;\n}\n\n.ga-select:focus {\n  outline: none;\n  border-color: #374151;\n}\n\n/* Position grid */\n.ga-pos-grid {\n  display: grid;\n  grid-template-columns: repeat(3, 32px);\n  gap: 4px;\n}\n\n.ga-pos-dot {\n  width: 32px;\n  height: 32px;\n  border-radius: 8px;\n  border: 2px solid #e5e7eb;\n  background: #f9fafb;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 10px;\n  color: #9ca3af;\n  transition: all 0.15s;\n}\n\n.ga-pos-dot:hover {\n  border-color: #9ca3af;\n  background: #f3f4f6;\n}\n\n.ga-pos-dot.ga-pos-active {\n  border-color: #374151;\n  background: #374151;\n  color: #fff;\n}\n\n/* Color stops */\n.ga-stops-list {\n  display: flex;\n  flex-direction: column;\n  gap: 12px;\n  margin-bottom: 14px;\n}\n\n.ga-stop-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n\n.ga-stop-swatch {\n  width: 38px;\n  height: 38px;\n  border-radius: 10px;\n  border: 2px solid #e5e7eb;\n  cursor: pointer;\n  padding: 2px;\n  flex-shrink: 0;\n}\n\n.ga-stop-hex {\n  flex: 1;\n  min-width: 0;\n  padding: 8px 10px;\n  border: 2px solid #e5e7eb;\n  border-radius: 8px;\n  font-size: 13px;\n  font-family: 'Courier New', monospace;\n  color: #374151;\n  max-width: 100px;\n}\n\n.ga-stop-hex:focus {\n  outline: none;\n  border-color: #374151;\n}\n\n.ga-stop-pos-wrap {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  flex: 1;\n  min-width: 0;\n}\n\n.ga-stop-pos-label {\n  font-size: 12px;\n  color: #6b7280;\n  font-weight: 600;\n  white-space: nowrap;\n  width: 26px;\n  flex-shrink: 0;\n}\n\n.ga-stop-pos-slider {\n  flex: 1;\n  height: 5px;\n  border-radius: 3px;\n  -webkit-appearance: none;\n  appearance: none;\n  background: #e5e7eb;\n  outline: none;\n  cursor: pointer;\n  min-width: 0;\n}\n\n.ga-stop-pos-slider::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 16px;\n  height: 16px;\n  border-radius: 50%;\n  background: #374151;\n  border: 2px solid #fff;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.2);\n  cursor: pointer;\n}\n\n.ga-stop-pos-slider::-moz-range-thumb {\n  width: 16px;\n  height: 16px;\n  border-radius: 50%;\n  background: #374151;\n  border: 2px solid #fff;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.2);\n  cursor: pointer;\n}\n\n.ga-stop-pos-num {\n  width: 46px;\n  padding: 5px 6px;\n  border: 2px solid #e5e7eb;\n  border-radius: 6px;\n  font-size: 12px;\n  text-align: center;\n  color: #374151;\n  font-family: 'Courier New', monospace;\n  flex-shrink: 0;\n}\n\n.ga-stop-pos-num:focus {\n  outline: none;\n  border-color: #374151;\n}\n\n.ga-stop-remove {\n  width: 28px;\n  height: 28px;\n  border-radius: 7px;\n  border: 2px solid #e5e7eb;\n  background: #fff;\n  color: #9ca3af;\n  font-size: 16px;\n  line-height: 1;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  transition: all 0.15s;\n  flex-shrink: 0;\n}\n\n.ga-stop-remove:hover {\n  border-color: #ef4444;\n  color: #ef4444;\n  background: #fef2f2;\n}\n\n/* Gradient bar */\n.ga-gradient-bar {\n  height: 18px;\n  border-radius: 9px;\n  margin-bottom: 14px;\n  box-shadow: inset 0 1px 4px rgba(0,0,0,0.12);\n}\n\n/* Add stop button */\n.ga-add-stop-btn {\n  padding: 8px 18px;\n  border: 2px dashed #d1d5db;\n  border-radius: 10px;\n  background: #f9fafb;\n  font-size: 13px;\n  font-weight: 600;\n  color: #6b7280;\n  cursor: pointer;\n  transition: all 0.18s;\n}\n\n.ga-add-stop-btn:hover {\n  border-color: #374151;\n  color: #374151;\n  background: #f3f4f6;\n}\n\n/* Presets */\n.ga-presets-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));\n  gap: 10px;\n}\n\n.ga-preset-item {\n  border-radius: 12px;\n  overflow: hidden;\n  cursor: pointer;\n  border: 2px solid transparent;\n  transition: all 0.18s;\n  box-shadow: 0 1px 6px rgba(0,0,0,0.1);\n}\n\n.ga-preset-item:hover {\n  transform: translateY(-2px);\n  box-shadow: 0 4px 16px rgba(0,0,0,0.18);\n}\n\n.ga-preset-item.ga-preset-active {\n  border-color: #374151;\n}\n\n.ga-preset-swatch {\n  height: 52px;\n  width: 100%;\n}\n\n.ga-preset-name {\n  background: #fff;\n  padding: 5px 8px;\n  font-size: 11px;\n  font-weight: 600;\n  color: #374151;\n  text-align: center;\n  border-top: 1px solid #f3f4f6;\n}\n\n/* Action buttons */\n.ga-actions {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n  margin-bottom: 16px;\n}\n\n.ga-btn {\n  padding: 10px 20px;\n  border: none;\n  border-radius: 10px;\n  font-size: 13px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: all 0.18s;\n  letter-spacing: 0.02em;\n}\n\n.ga-btn-primary {\n  background: #374151;\n  color: #fff;\n}\n\n.ga-btn-primary:hover {\n  background: #1f2937;\n}\n\n.ga-btn-secondary {\n  background: #f3f4f6;\n  color: #374151;\n  border: 2px solid #e5e7eb;\n}\n\n.ga-btn-secondary:hover {\n  background: #e5e7eb;\n}\n\n/* CSS output */\n.ga-css-output {\n  background: #1a1b2e;\n  border-radius: 12px;\n  padding: 20px 22px;\n  position: relative;\n  font-family: 'Courier New', monospace;\n  font-size: 13px;\n  line-height: 1.9;\n  color: #a5b4fc;\n}\n\n.ga-css-prop { color: #7dd3fc; }\n.ga-css-val  { color: #fde68a; }\n.ga-css-semi { color: #94a3b8; }\n\n.ga-css-copy-btn {\n  position: absolute;\n  top: 12px;\n  right: 14px;\n  padding: 5px 14px;\n  background: rgba(255,255,255,0.08);\n  color: #a5b4fc;\n  border: 1px solid rgba(255,255,255,0.12);\n  border-radius: 6px;\n  font-size: 11px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n\n.ga-css-copy-btn:hover {\n  background: rgba(255,255,255,0.16);\n}\n\n.ga-css-copy-btn.ga-copied {\n  background: rgba(52,211,153,0.2);\n  color: #6ee7b7;\n  border-color: rgba(52,211,153,0.3);\n}\n\n/* Responsive */\n@media (max-width: 600px) {\n  .ga-preview { height: 220px; }\n  .ga-card { padding: 16px 14px; }\n  .ga-presets-grid { grid-template-columns: repeat(auto-fill, minmax(90px, 1fr)); }\n  .ga-stop-row { flex-wrap: wrap; }\n  .ga-stop-hex { max-width: 90px; }\n  .ga-angle-label { width: 80px; font-size: 12px; }\n  .ga-actions { flex-direction: column; }\n  .ga-btn { text-align: center; }\n}\n\u003c/style\u003e\n\u003c!-- Preview --\u003e\n\u003cdiv class=\"ga-preview-wrap\"\u003e\n  \u003cdiv class=\"ga-preview\" id=\"ga-preview\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"ga-preview-label\" id=\"ga-preview-label\"\u003elinear-gradient(135deg, ...)\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Actions --\u003e\n\u003cdiv class=\"ga-actions\"\u003e\n  \u003cbutton class=\"ga-btn ga-btn-primary\" id=\"ga-random-btn\"\u003eRandom Gradient\u003c/button\u003e\n  \u003cbutton class=\"ga-btn ga-btn-secondary\" id=\"ga-copy-css-main-btn\"\u003eCopy CSS\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Gradient Type --\u003e\n\u003cdiv class=\"ga-card\"\u003e\n  \u003cdiv class=\"ga-card-title\"\u003eGradient Type\u003c/div\u003e\n  \u003cdiv class=\"ga-type-row\"\u003e\n    \u003cbutton class=\"ga-type-btn ga-active\" data-type=\"linear\"\u003eLinear\u003c/button\u003e\n    \u003cbutton class=\"ga-type-btn\" data-type=\"radial\"\u003eRadial\u003c/button\u003e\n    \u003cbutton class=\"ga-type-btn\" data-type=\"conic\"\u003eConic\u003c/button\u003e\n  \u003c/div\u003e\n  \u003c!-- Linear controls --\u003e\n  \u003cdiv id=\"ga-linear-controls\"\u003e\n    \u003cdiv class=\"ga-angle-row\"\u003e\n      \u003cspan class=\"ga-angle-label\"\u003eAngle\u003c/span\u003e\n      \u003cinput type=\"range\" class=\"ga-angle-slider\" id=\"ga-angle-slider\" min=\"0\" max=\"360\" value=\"135\"\u003e\n      \u003cinput type=\"number\" class=\"ga-angle-num\" id=\"ga-angle-num\" min=\"0\" max=\"360\" value=\"135\"\u003e °\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Radial controls --\u003e\n  \u003cdiv id=\"ga-radial-controls\" style=\"display:none;\"\u003e\n    \u003cdiv class=\"ga-radial-row\"\u003e\n      \u003cdiv class=\"ga-radial-field\"\u003e\n        \u003cspan class=\"ga-field-label\"\u003eShape\u003c/span\u003e\n        \u003cselect class=\"ga-select\" id=\"ga-radial-shape\"\u003e\n          \u003coption value=\"circle\"\u003eCircle\u003c/option\u003e\n          \u003coption value=\"ellipse\" selected\u003eEllipse\u003c/option\u003e\n        \u003c/select\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"ga-radial-field\"\u003e\n        \u003cspan class=\"ga-field-label\"\u003ePosition\u003c/span\u003e\n        \u003cdiv class=\"ga-pos-grid\" id=\"ga-pos-grid\"\u003e\n          \u003cdiv class=\"ga-pos-dot\" data-pos=\"left top\" title=\"top left\"\u003e↖\u003c/div\u003e\n          \u003cdiv class=\"ga-pos-dot\" data-pos=\"center top\" title=\"top\"\u003e↑\u003c/div\u003e\n          \u003cdiv class=\"ga-pos-dot\" data-pos=\"right top\" title=\"top right\"\u003e↗\u003c/div\u003e\n          \u003cdiv class=\"ga-pos-dot\" data-pos=\"left center\" title=\"left\"\u003e←\u003c/div\u003e\n          \u003cdiv class=\"ga-pos-dot ga-pos-active\" data-pos=\"center center\" title=\"center\"\u003e·\u003c/div\u003e\n          \u003cdiv class=\"ga-pos-dot\" data-pos=\"right center\" title=\"right\"\u003e→\u003c/div\u003e\n          \u003cdiv class=\"ga-pos-dot\" data-pos=\"left bottom\" title=\"bottom left\"\u003e↙\u003c/div\u003e\n          \u003cdiv class=\"ga-pos-dot\" data-pos=\"center bottom\" title=\"bottom\"\u003e↓\u003c/div\u003e\n          \u003cdiv class=\"ga-pos-dot\" data-pos=\"right bottom\" title=\"bottom right\"\u003e↘\u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Conic controls --\u003e\n  \u003cdiv id=\"ga-conic-controls\" style=\"display:none;\"\u003e\n    \u003cdiv class=\"ga-angle-row\"\u003e\n      \u003cspan class=\"ga-angle-label\"\u003eFrom Angle\u003c/span\u003e\n      \u003cinput type=\"range\" class=\"ga-angle-slider\" id=\"ga-conic-angle-slider\" min=\"0\" max=\"360\" value=\"0\"\u003e\n      \u003cinput type=\"number\" class=\"ga-angle-num\" id=\"ga-conic-angle-num\" min=\"0\" max=\"360\" value=\"0\"\u003e °\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Color Stops --\u003e\n\u003cdiv class=\"ga-card\"\u003e\n  \u003cdiv class=\"ga-card-title\"\u003eColor Stops\u003c/div\u003e\n  \u003cdiv class=\"ga-gradient-bar\" id=\"ga-gradient-bar\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"ga-stops-list\" id=\"ga-stops-list\"\u003e\u003c/div\u003e\n  \u003cbutton class=\"ga-add-stop-btn\" id=\"ga-add-stop-btn\"\u003e+ Add Color Stop\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Presets --\u003e\n\u003cdiv class=\"ga-card\"\u003e\n  \u003cdiv class=\"ga-card-title\"\u003ePresets\u003c/div\u003e\n  \u003cdiv class=\"ga-presets-grid\" id=\"ga-presets-grid\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- CSS Output --\u003e\n\u003cdiv class=\"ga-card\"\u003e\n  \u003cdiv class=\"ga-card-title\"\u003eCSS Code\u003c/div\u003e\n  \u003cdiv class=\"ga-css-output\" id=\"ga-css-output\"\u003e\n    \u003cbutton class=\"ga-css-copy-btn\" id=\"ga-css-copy-btn\"\u003eCopy\u003c/button\u003e\n    \u003cdiv id=\"ga-css-code\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  'use strict';\n\n  // ---- State ----\n  var state = {\n    type: 'linear',\n    angle: 135,\n    conicAngle: 0,\n    radialShape: 'ellipse',\n    radialPos: 'center center',\n    stops: [\n      { color: '#f093fb', pos: 0 },\n      { color: '#f5576c', pos: 100 }\n    ]\n  };\n\n  var activePreset = null;\n\n  // ---- Presets ----\n  var PRESETS = [\n    { name: 'Sunset',       type: 'linear', angle: 135, stops: [{ color: '#f093fb', pos: 0 }, { color: '#f5576c', pos: 100 }] },\n    { name: 'Ocean',        type: 'linear', angle: 160, stops: [{ color: '#2980b9', pos: 0 }, { color: '#6dd5fa', pos: 50 }, { color: '#ffffff', pos: 100 }] },\n    { name: 'Forest',       type: 'linear', angle: 120, stops: [{ color: '#11998e', pos: 0 }, { color: '#38ef7d', pos: 100 }] },\n    { name: 'Neon',         type: 'linear', angle: 90,  stops: [{ color: '#f7971e', pos: 0 }, { color: '#ffd200', pos: 50 }, { color: '#21d4fd', pos: 100 }] },\n    { name: 'Aurora',       type: 'linear', angle: 135, stops: [{ color: '#a8edea', pos: 0 }, { color: '#fed6e3', pos: 100 }] },\n    { name: 'Midnight',     type: 'linear', angle: 180, stops: [{ color: '#0f0c29', pos: 0 }, { color: '#302b63', pos: 50 }, { color: '#24243e', pos: 100 }] },\n    { name: 'Peach',        type: 'linear', angle: 45,  stops: [{ color: '#ffecd2', pos: 0 }, { color: '#fcb69f', pos: 100 }] },\n    { name: 'Lavender',     type: 'linear', angle: 135, stops: [{ color: '#e0c3fc', pos: 0 }, { color: '#8ec5fc', pos: 100 }] },\n    { name: 'Fire',         type: 'linear', angle: 90,  stops: [{ color: '#f83600', pos: 0 }, { color: '#f9d423', pos: 100 }] },\n    { name: 'Cotton Candy', type: 'radial', shape: 'ellipse', pos: 'center center', stops: [{ color: '#fddb92', pos: 0 }, { color: '#d1fdff', pos: 100 }] },\n    { name: 'Cosmic',       type: 'conic',  conicAngle: 0, stops: [{ color: '#6a3093', pos: 0 }, { color: '#a044ff', pos: 33 }, { color: '#6a3093', pos: 66 }, { color: '#a044ff', pos: 100 }] },\n    { name: 'Mango',        type: 'linear', angle: 135, stops: [{ color: '#ffe259', pos: 0 }, { color: '#ffa751', pos: 100 }] }\n  ];\n\n  // ---- DOM refs ----\n  var previewEl       = document.getElementById('ga-preview');\n  var previewLabel    = document.getElementById('ga-preview-label');\n  var gradientBar     = document.getElementById('ga-gradient-bar');\n  var stopsList       = document.getElementById('ga-stops-list');\n  var addStopBtn      = document.getElementById('ga-add-stop-btn');\n  var presetsGrid     = document.getElementById('ga-presets-grid');\n  var cssCode         = document.getElementById('ga-css-code');\n  var cssCopyBtn      = document.getElementById('ga-css-copy-btn');\n  var copyCssMainBtn  = document.getElementById('ga-copy-css-main-btn');\n  var randomBtn       = document.getElementById('ga-random-btn');\n  var angleSlider     = document.getElementById('ga-angle-slider');\n  var angleNum        = document.getElementById('ga-angle-num');\n  var conicSlider     = document.getElementById('ga-conic-angle-slider');\n  var conicNum        = document.getElementById('ga-conic-angle-num');\n  var radialShape     = document.getElementById('ga-radial-shape');\n  var linearControls  = document.getElementById('ga-linear-controls');\n  var radialControls  = document.getElementById('ga-radial-controls');\n  var conicControls   = document.getElementById('ga-conic-controls');\n\n  // ---- CSS Builder ----\n  function buildGradientCSS() {\n    var stops = state.stops.map(function(s) {\n      return s.color + ' ' + s.pos + '%';\n    }).join(', ');\n\n    if (state.type === 'linear') {\n      return 'linear-gradient(' + state.angle + 'deg, ' + stops + ')';\n    } else if (state.type === 'radial') {\n      return 'radial-gradient(' + state.radialShape + ' at ' + state.radialPos + ', ' + stops + ')';\n    } else {\n      return 'conic-gradient(from ' + state.conicAngle + 'deg, ' + stops + ')';\n    }\n  }\n\n  function buildFullCSS() {\n    var grad = buildGradientCSS();\n    return 'background: ' + grad + ';\\nbackground: -webkit-' + grad + ';';\n  }\n\n  // ---- Render ----\n  function render() {\n    var grad = buildGradientCSS();\n    previewEl.style.background = grad;\n    gradientBar.style.background = 'linear-gradient(to right, ' +\n      state.stops.map(function(s) { return s.color + ' ' + s.pos + '%'; }).join(', ') + ')';\n    previewLabel.textContent = grad.length \u003e 80 ? grad.slice(0, 77) + '...' : grad;\n    renderCSS(grad);\n    renderStops();\n  }\n\n  function renderCSS(grad) {\n    cssCode.innerHTML =\n      '\u003cspan class=\"ga-css-prop\"\u003ebackground\u003c/span\u003e\u003cspan class=\"ga-css-semi\"\u003e: \u003c/span\u003e' +\n      '\u003cspan class=\"ga-css-val\"\u003e-webkit-' + escHtml(grad) + '\u003c/span\u003e\u003cspan class=\"ga-css-semi\"\u003e;\u003c/span\u003e\\n' +\n      '\u003cspan class=\"ga-css-prop\"\u003ebackground\u003c/span\u003e\u003cspan class=\"ga-css-semi\"\u003e: \u003c/span\u003e' +\n      '\u003cspan class=\"ga-css-val\"\u003e' + escHtml(grad) + '\u003c/span\u003e\u003cspan class=\"ga-css-semi\"\u003e;\u003c/span\u003e';\n  }\n\n  function escHtml(s) {\n    return s.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n  }\n\n  function renderStops() {\n    stopsList.innerHTML = '';\n    state.stops.forEach(function(stop, idx) {\n      var row = document.createElement('div');\n      row.className = 'ga-stop-row';\n\n      // Color swatch (native color input)\n      var swatchWrap = document.createElement('label');\n      swatchWrap.style.cssText = 'position:relative;cursor:pointer;flex-shrink:0;';\n      var swatch = document.createElement('div');\n      swatch.style.cssText = 'width:38px;height:38px;border-radius:10px;border:2px solid #e5e7eb;background:' + stop.color + ';cursor:pointer;';\n      var colorInput = document.createElement('input');\n      colorInput.type = 'color';\n      colorInput.value = stop.color;\n      colorInput.style.cssText = 'position:absolute;opacity:0;width:0;height:0;';\n      colorInput.addEventListener('input', (function(i) {\n        return function() {\n          state.stops[i].color = colorInput.value;\n          swatch.style.background = colorInput.value;\n          hexField.value = colorInput.value;\n          render();\n        };\n      })(idx));\n      swatchWrap.appendChild(swatch);\n      swatchWrap.appendChild(colorInput);\n      swatchWrap.addEventListener('click', function() { colorInput.click(); });\n\n      // HEX field\n      var hexField = document.createElement('input');\n      hexField.type = 'text';\n      hexField.className = 'ga-stop-hex';\n      hexField.value = stop.color;\n      hexField.maxLength = 7;\n      hexField.spellcheck = false;\n      hexField.addEventListener('input', (function(i, sw, ci) {\n        return function() {\n          var v = hexField.value.trim();\n          if (!v.startsWith('#')) v = '#' + v;\n          if (/^#[0-9a-fA-F]{6}$/.test(v)) {\n            state.stops[i].color = v;\n            sw.style.background = v;\n            ci.value = v;\n            render();\n          }\n        };\n      })(idx, swatch, colorInput));\n\n      // Position slider + num\n      var posWrap = document.createElement('div');\n      posWrap.className = 'ga-stop-pos-wrap';\n\n      var posLabel = document.createElement('span');\n      posLabel.className = 'ga-stop-pos-label';\n      posLabel.textContent = 'Pos';\n\n      var posSlider = document.createElement('input');\n      posSlider.type = 'range';\n      posSlider.className = 'ga-stop-pos-slider';\n      posSlider.min = 0;\n      posSlider.max = 100;\n      posSlider.value = stop.pos;\n\n      var posNum = document.createElement('input');\n      posNum.type = 'number';\n      posNum.className = 'ga-stop-pos-num';\n      posNum.min = 0;\n      posNum.max = 100;\n      posNum.value = stop.pos;\n\n      posSlider.addEventListener('input', (function(i, pn) {\n        return function() {\n          state.stops[i].pos = parseInt(posSlider.value);\n          pn.value = posSlider.value;\n          render();\n        };\n      })(idx, posNum));\n\n      posNum.addEventListener('input', (function(i, ps) {\n        return function() {\n          var v = Math.max(0, Math.min(100, parseInt(posNum.value) || 0));\n          state.stops[i].pos = v;\n          ps.value = v;\n          render();\n        };\n      })(idx, posSlider));\n\n      posWrap.appendChild(posLabel);\n      posWrap.appendChild(posSlider);\n      posWrap.appendChild(posNum);\n\n      // Remove button\n      var removeBtn = document.createElement('button');\n      removeBtn.className = 'ga-stop-remove';\n      removeBtn.textContent = '×';\n      removeBtn.title = 'Remove stop';\n      removeBtn.addEventListener('click', (function(i) {\n        return function() {\n          if (state.stops.length \u003c= 2) return;\n          state.stops.splice(i, 1);\n          render();\n        };\n      })(idx));\n\n      row.appendChild(swatchWrap);\n      row.appendChild(hexField);\n      row.appendChild(posWrap);\n      row.appendChild(removeBtn);\n      stopsList.appendChild(row);\n    });\n  }\n\n  // ---- Presets ----\n  function renderPresets() {\n    PRESETS.forEach(function(preset, idx) {\n      var item = document.createElement('div');\n      item.className = 'ga-preset-item';\n      item.dataset.idx = idx;\n\n      var swatch = document.createElement('div');\n      swatch.className = 'ga-preset-swatch';\n\n      // Build gradient for swatch\n      var stops = preset.stops.map(function(s) { return s.color + ' ' + s.pos + '%'; }).join(', ');\n      var grad;\n      if (preset.type === 'radial') {\n        grad = 'radial-gradient(ellipse at center center, ' + stops + ')';\n      } else if (preset.type === 'conic') {\n        grad = 'linear-gradient(135deg, ' + stops + ')'; // approximate for thumbnail\n      } else {\n        grad = 'linear-gradient(' + preset.angle + 'deg, ' + stops + ')';\n      }\n      swatch.style.background = grad;\n\n      var nameEl = document.createElement('div');\n      nameEl.className = 'ga-preset-name';\n      nameEl.textContent = preset.name;\n\n      item.appendChild(swatch);\n      item.appendChild(nameEl);\n\n      item.addEventListener('click', function() {\n        applyPreset(idx);\n      });\n\n      presetsGrid.appendChild(item);\n    });\n  }\n\n  function applyPreset(idx) {\n    var preset = PRESETS[idx];\n    state.type = preset.type;\n    state.stops = preset.stops.map(function(s) { return { color: s.color, pos: s.pos }; });\n\n    if (preset.type === 'linear') {\n      state.angle = preset.angle;\n      angleSlider.value = state.angle;\n      angleNum.value = state.angle;\n    } else if (preset.type === 'radial') {\n      state.radialShape = preset.shape || 'ellipse';\n      state.radialPos = preset.pos || 'center center';\n      radialShape.value = state.radialShape;\n      syncPosGrid(state.radialPos);\n    } else if (preset.type === 'conic') {\n      state.conicAngle = preset.conicAngle || 0;\n      conicSlider.value = state.conicAngle;\n      conicNum.value = state.conicAngle;\n    }\n\n    // Type buttons\n    document.querySelectorAll('#grad-app .ga-type-btn').forEach(function(btn) {\n      btn.classList.toggle('ga-active', btn.dataset.type === state.type);\n    });\n    showTypeControls(state.type);\n\n    // Preset active highlight\n    document.querySelectorAll('#grad-app .ga-preset-item').forEach(function(el, i) {\n      el.classList.toggle('ga-preset-active', i === idx);\n    });\n\n    activePreset = idx;\n    render();\n  }\n\n  function showTypeControls(type) {\n    linearControls.style.display = type === 'linear' ? '' : 'none';\n    radialControls.style.display = type === 'radial' ? '' : 'none';\n    conicControls.style.display  = type === 'conic'  ? '' : 'none';\n  }\n\n  function syncPosGrid(pos) {\n    document.querySelectorAll('#ga-pos-grid .ga-pos-dot').forEach(function(dot) {\n      dot.classList.toggle('ga-pos-active', dot.dataset.pos === pos);\n    });\n  }\n\n  // ---- Random gradient ----\n  var RANDOM_COLORS = [\n    '#f093fb','#f5576c','#4facfe','#00f2fe','#43e97b','#38f9d7',\n    '#fa709a','#fee140','#a18cd1','#fbc2eb','#ffecd2','#fcb69f',\n    '#a1c4fd','#c2e9fb','#d4fc79','#96e6a1','#f7971e','#ffd200',\n    '#e0c3fc','#8ec5fc','#fddb92','#d1fdff','#0f0c29','#24243e',\n    '#11998e','#38ef7d','#6a3093','#a044ff'\n  ];\n\n  function randomColor() {\n    return RANDOM_COLORS[Math.floor(Math.random() * RANDOM_COLORS.length)];\n  }\n\n  function randomInt(min, max) {\n    return Math.floor(Math.random() * (max - min + 1)) + min;\n  }\n\n  randomBtn.addEventListener('click', function() {\n    var count = randomInt(2, 4);\n    var positions = [];\n    positions.push(0);\n    for (var i = 1; i \u003c count - 1; i++) {\n      positions.push(randomInt(20, 80));\n    }\n    positions.push(100);\n    positions.sort(function(a,b){return a-b;});\n\n    state.stops = positions.map(function(p) {\n      return { color: randomColor(), pos: p };\n    });\n\n    var types = ['linear', 'radial', 'conic'];\n    state.type = types[randomInt(0, 2)];\n    state.angle = randomInt(0, 360);\n    state.conicAngle = randomInt(0, 360);\n    state.radialShape = Math.random() \u003e 0.5 ? 'circle' : 'ellipse';\n\n    angleSlider.value = state.angle;\n    angleNum.value = state.angle;\n    conicSlider.value = state.conicAngle;\n    conicNum.value = state.conicAngle;\n    radialShape.value = state.radialShape;\n\n    document.querySelectorAll('#grad-app .ga-type-btn').forEach(function(btn) {\n      btn.classList.toggle('ga-active', btn.dataset.type === state.type);\n    });\n    showTypeControls(state.type);\n    document.querySelectorAll('#grad-app .ga-preset-item').forEach(function(el) {\n      el.classList.remove('ga-preset-active');\n    });\n    activePreset = null;\n    render();\n  });\n\n  // ---- Type buttons ----\n  document.querySelectorAll('#grad-app .ga-type-btn').forEach(function(btn) {\n    btn.addEventListener('click', function() {\n      state.type = btn.dataset.type;\n      document.querySelectorAll('#grad-app .ga-type-btn').forEach(function(b) {\n        b.classList.remove('ga-active');\n      });\n      btn.classList.add('ga-active');\n      showTypeControls(state.type);\n      render();\n    });\n  });\n\n  // ---- Angle sliders ----\n  angleSlider.addEventListener('input', function() {\n    state.angle = parseInt(angleSlider.value);\n    angleNum.value = state.angle;\n    render();\n  });\n\n  angleNum.addEventListener('input', function() {\n    state.angle = Math.max(0, Math.min(360, parseInt(angleNum.value) || 0));\n    angleSlider.value = state.angle;\n    render();\n  });\n\n  conicSlider.addEventListener('input', function() {\n    state.conicAngle = parseInt(conicSlider.value);\n    conicNum.value = state.conicAngle;\n    render();\n  });\n\n  conicNum.addEventListener('input', function() {\n    state.conicAngle = Math.max(0, Math.min(360, parseInt(conicNum.value) || 0));\n    conicSlider.value = state.conicAngle;\n    render();\n  });\n\n  // ---- Radial controls ----\n  radialShape.addEventListener('change', function() {\n    state.radialShape = radialShape.value;\n    render();\n  });\n\n  document.querySelectorAll('#ga-pos-grid .ga-pos-dot').forEach(function(dot) {\n    dot.addEventListener('click', function() {\n      state.radialPos = dot.dataset.pos;\n      syncPosGrid(state.radialPos);\n      render();\n    });\n  });\n\n  // ---- Add stop ----\n  addStopBtn.addEventListener('click', function() {\n    if (state.stops.length \u003e= 5) return;\n    var lastPos = state.stops[state.stops.length - 1].pos;\n    var newPos = Math.min(100, lastPos + Math.round((100 - lastPos) / 2));\n    state.stops.push({ color: randomColor(), pos: newPos });\n    render();\n  });\n\n  // ---- Copy CSS ----\n  function doCopy(text, btn) {\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(text).then(function() { flashCopied(btn); });\n    } else {\n      var ta = document.createElement('textarea');\n      ta.value = text;\n      ta.style.cssText = 'position:fixed;opacity:0;';\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      flashCopied(btn);\n    }\n  }\n\n  function flashCopied(btn) {\n    var orig = btn.textContent;\n    btn.classList.add('ga-copied');\n    btn.textContent = 'Copied!';\n    setTimeout(function() {\n      btn.classList.remove('ga-copied');\n      btn.textContent = orig;\n    }, 1600);\n  }\n\n  cssCopyBtn.addEventListener('click', function() {\n    doCopy(buildFullCSS(), cssCopyBtn);\n  });\n\n  copyCssMainBtn.addEventListener('click', function() {\n    doCopy(buildFullCSS(), copyCssMainBtn);\n  });\n\n  // ---- Init ----\n  renderPresets();\n  render();\n\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003ch2 id=\"how-to-use-the-css-gradient-generator\"\u003eHow to Use the CSS Gradient Generator\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eChoose a gradient type\u003c/strong\u003e at the top: Linear gradients flow along a straight line at your chosen angle. Radial gradients expand outward from a center point in a circle or ellipse shape. Conic gradients sweep around a center point like a color wheel.\u003c/p\u003e","title":"CSS Gradient Generator - Free Online Tool for Linear, Radial \u0026 Conic Gradients"},{"content":" CSS Grid Generator Build CSS Grid layouts visually. Adjust tracks, merge cells, add named areas, then copy your CSS.\nDashboard Photo Gallery Magazine Layout Holy Grail Responsive Cards Reset Columns Rows Column Gap Row Gap Justify Items stretch start end center Align Items stretch start end center Column Track Sizes Row Track Sizes Named Grid Areas (optional) Enter one row per line. Space-separated area names. Use . for empty cells.\nLive Preview — click cells to select Grid Items + Add Item Generated CSS Copy CSS Shorthand Values Copy Related Tools Build Flexbox layouts → CSS Flexbox Generator Generate box shadows → CSS Box Shadow Generator Create gradients → CSS Gradient Generator ","permalink":"https://productivity-works.com/tools/css-grid-generator/","summary":"\u003cdiv id=\"grid-app\"\u003e\n\u003cstyle\u003e\n  #grid-app *,\n  #grid-app *::before,\n  #grid-app *::after {\n    box-sizing: border-box;\n    margin: 0;\n    padding: 0;\n  }\n\u003cp\u003e#grid-app {\nfont-family: -apple-system, BlinkMacSystemFont, \u0026ldquo;Segoe UI\u0026rdquo;, Roboto, sans-serif;\nfont-size: 14px;\ncolor: #1a1a2e;\nbackground: #f8f9ff;\nborder-radius: 12px;\npadding: 24px;\nmax-width: 100%;\noverflow: hidden;\n}\u003c/p\u003e\n\u003cp\u003e#grid-app h2.ga-title {\nfont-size: 1.4rem;\nfont-weight: 700;\nmargin-bottom: 4px;\ncolor: #1a1a2e;\n}\u003c/p\u003e\n\u003cp\u003e#grid-app p.ga-subtitle {\ncolor: #666;\nmargin-bottom: 20px;\nfont-size: 0.9rem;\n}\u003c/p\u003e\n\u003cp\u003e/* Presets */\n#grid-app .ga-presets {\ndisplay: flex;\nflex-wrap: wrap;\ngap: 8px;\nmargin-bottom: 20px;\n}\u003c/p\u003e","title":"CSS Grid Generator — Visual Layout Builder"},{"content":"Build CSS media queries visually — pick device presets, set breakpoints, toggle orientation and accessibility features, then copy the generated @media code straight into your stylesheet.\nApproach Mobile-First\nmin-width queries Desktop-First\nmax-width queries Device Presets 📱 iPhone SE 320–480px 📱 iPhone 12/13 375–812px 📱 iPhone 14 Pro 390–844px 📟 iPad 768–1024px 📟 iPad Pro 1024–1366px 🖥️ Desktop HD 1280–1920px 🖥️ 4K / QHD 1920–2560px ✏️ Custom enter values Width \u0026amp; Height Min Width px Max Width px Min Height px Max Height px Media Features \u0026lt;label class=\u0026quot;mq-feature-chip\u0026quot; id=\u0026quot;mqChipScreen\u0026quot; onclick=\u0026quot;mqToggleChip('mqChipScreen','mqFeatScreen')\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;mq-feature-dot\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;mqFeatScreen\u0026quot;\u0026gt; screen \u0026lt;/label\u0026gt; \u0026lt;label class=\u0026quot;mq-feature-chip\u0026quot; id=\u0026quot;mqChipPrint\u0026quot; onclick=\u0026quot;mqToggleChip('mqChipPrint','mqFeatPrint')\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;mq-feature-dot\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;mqFeatPrint\u0026quot;\u0026gt; print \u0026lt;/label\u0026gt; \u0026lt;label class=\u0026quot;mq-feature-chip\u0026quot; id=\u0026quot;mqChipOrient\u0026quot; onclick=\u0026quot;mqToggleChip('mqChipOrient','mqFeatOrient')\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;mq-feature-dot\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;mqFeatOrient\u0026quot;\u0026gt; orientation: \u0026lt;select class=\u0026quot;mq-chip-select\u0026quot; id=\u0026quot;mqOrientVal\u0026quot; onclick=\u0026quot;event.stopPropagation()\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;portrait\u0026quot;\u0026gt;portrait\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;landscape\u0026quot;\u0026gt;landscape\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/label\u0026gt; \u0026lt;label class=\u0026quot;mq-feature-chip\u0026quot; id=\u0026quot;mqChipColor\u0026quot; onclick=\u0026quot;mqToggleChip('mqChipColor','mqFeatColor')\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;mq-feature-dot\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;mqFeatColor\u0026quot;\u0026gt; prefers-color-scheme: \u0026lt;select class=\u0026quot;mq-chip-select\u0026quot; id=\u0026quot;mqColorVal\u0026quot; onclick=\u0026quot;event.stopPropagation()\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;dark\u0026quot;\u0026gt;dark\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;light\u0026quot;\u0026gt;light\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/label\u0026gt; \u0026lt;label class=\u0026quot;mq-feature-chip\u0026quot; id=\u0026quot;mqChipMotion\u0026quot; onclick=\u0026quot;mqToggleChip('mqChipMotion','mqFeatMotion')\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;mq-feature-dot\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;mqFeatMotion\u0026quot;\u0026gt; prefers-reduced-motion: \u0026lt;select class=\u0026quot;mq-chip-select\u0026quot; id=\u0026quot;mqMotionVal\u0026quot; onclick=\u0026quot;event.stopPropagation()\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;reduce\u0026quot;\u0026gt;reduce\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;no-preference\u0026quot;\u0026gt;no-preference\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/label\u0026gt; Generate @media Query\nLive Viewport Match Viewport: — × — No query generated yet Generated CSS Copy CSS /* Your @media query will appear here */ Quick Reference — Common Breakpoints Name Framework Breakpoint Typical Device xs Bootstrap \u0026lt; 576px Small phones sm Bootstrap ≥ 576px Phones landscape md Bootstrap ≥ 768px Tablets portrait lg Bootstrap ≥ 992px Tablets landscape / small laptop xl Bootstrap ≥ 1200px Desktop HD xxl Bootstrap ≥ 1400px Wide desktop sm Tailwind ≥ 640px Phones landscape md Tailwind ≥ 768px Tablets lg Tailwind ≥ 1024px Laptop / large tablet xl Tailwind ≥ 1280px Desktop 2xl Tailwind ≥ 1536px Wide / 4K mobile Custom \u0026lt; 768px Phones (portrait + landscape) tablet Custom 768px–1199px Tablets all orientations desktop Custom ≥ 1200px Laptop / desktop monitor 4k Custom ≥ 2560px Ultra-wide / 4K displays Related Tools \u0026#9633; CSS Flexbox Generator \u0026#9635; CSS Grid Generator \u0026#128250; Screen Resolution Checker How to Use the Generator Choose your approach — Mobile-first outputs min-width queries that scale up; desktop-first uses max-width to scale down. Pick a device preset or enter custom min/max width and height values. Toggle media features — add orientation, prefers-color-scheme, prefers-reduced-motion, or print. Click Generate @media Query to see the code and the live viewport match status. Click Copy CSS and paste into your stylesheet. When to Use Mobile-First vs. Desktop-First Mobile-First Desktop-First Base styles target Smallest screens Largest screens Query type min-width max-width Progressive enhancement Yes No Framework alignment Tailwind, Bootstrap 4+ Older frameworks Best practice: mobile-first is the modern default. It reduces specificity conflicts and keeps base styles lean.\nHow prefers-color-scheme Works /* Apply dark styles only when the OS is in dark mode */ @media screen and (prefers-color-scheme: dark) { body { background: #0f172a; color: #e2e8f0; } } Pair this with a CSS custom-property theme to avoid duplicating declarations.\nprefers-reduced-motion — Accessibility Users who have vestibular disorders or motion sensitivity can request reduced animations at the OS level. Always respect it:\n@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } } Related Free Tools CSS Flexbox Generator — build flex containers visually CSS Grid Generator — define grid tracks and areas with a point-and-click interface Screen Resolution Checker — see your exact screen and device pixel ratio ","permalink":"https://productivity-works.com/tools/media-query-generator/","summary":"\u003cp\u003eBuild CSS media queries visually — pick device presets, set breakpoints, toggle orientation and accessibility features, then copy the generated \u003ccode\u003e@media\u003c/code\u003e code straight into your stylesheet.\u003c/p\u003e\n\u003cdiv id=\"mq-app\"\u003e\n\u003cstyle\u003e\n  #mq-app *,\n  #mq-app *::before,\n  #mq-app *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\u003cp\u003e#mq-app {\nfont-family: -apple-system, BlinkMacSystemFont, \u0026ldquo;Segoe UI\u0026rdquo;, Roboto, sans-serif;\nfont-size: 15px;\ncolor: #1a1a2e;\nbackground: #f4f6fb;\nborder-radius: 12px;\npadding: 24px;\nmax-width: 900px;\nmargin: 0 auto;\n}\u003c/p\u003e\n\u003cp\u003e#mq-app h2.mq-section-title {\nfont-size: 13px;\nfont-weight: 700;\ntext-transform: uppercase;\nletter-spacing: .08em;\ncolor: #6b7280;\nmargin-bottom: 10px;\n}\u003c/p\u003e","title":"CSS Media Query Generator — Responsive Builder"},{"content":"Minify CSS to reduce file size or beautify/format compressed CSS for readability. Shows compression ratio instantly. No server upload — all processing happens in your browser.\nFormat HTML → HTML Beautifier Minify Beautify Indent: 2 spaces 4 spaces Tab Minify CSS Clear Upload File Input CSS Output Copy Output ","permalink":"https://productivity-works.com/tools/css-minifier/","summary":"\u003cp\u003eMinify CSS to reduce file size or beautify/format compressed CSS for readability. Shows compression ratio instantly. No server upload — all processing happens in your browser.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eFormat HTML → \u003ca href=\"https://productivity-works.com/tools/html-beautifier/\"\u003eHTML Beautifier\u003c/a\u003e\n\u003c/p\u003e\u003c/blockquote\u003e\n\u003cdiv id=\"cm2-app\"\u003e\n\u003cstyle\u003e\n#cm2-app *,\n#cm2-app *::before,\n#cm2-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#cm2-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  color: #1e293b;\n  max-width: 820px;\n}\n#cm2-app h2 {\n  font-size: 1rem;\n  font-weight: 700;\n  color: #0f172a;\n  margin-bottom: 10px;\n}\n#cm2-app .cm2-mode-tabs {\n  display: flex;\n  gap: 6px;\n  margin-bottom: 16px;\n}\n#cm2-app .cm2-tab {\n  padding: 8px 20px;\n  border-radius: 7px;\n  border: 1.5px solid #e2e8f0;\n  background: #f8fafc;\n  color: #475569;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#cm2-app .cm2-tab.active {\n  background: #6366f1;\n  color: #fff;\n  border-color: #6366f1;\n}\n#cm2-app .cm2-tab:hover:not(.active) {\n  background: #f1f5f9;\n  border-color: #cbd5e1;\n}\n#cm2-app .cm2-panels {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 14px;\n  margin-bottom: 14px;\n}\n@media (max-width: 600px) {\n  #cm2-app .cm2-panels { grid-template-columns: 1fr; }\n}\n#cm2-app .cm2-panel-label {\n  font-size: 12px;\n  font-weight: 600;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 6px;\n}\n#cm2-app textarea {\n  width: 100%;\n  height: 240px;\n  padding: 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 13px;\n  line-height: 1.6;\n  resize: vertical;\n  font-family: \"SFMono-Regular\", \"Consolas\", \"Menlo\", monospace;\n  color: #1e293b;\n  background: #fff;\n  transition: border-color 0.2s;\n}\n#cm2-app textarea:focus {\n  outline: none;\n  border-color: #6366f1;\n}\n#cm2-app textarea[readonly] {\n  background: #f8fafc;\n  color: #334155;\n}\n#cm2-app .cm2-options {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 12px;\n  margin-bottom: 14px;\n  align-items: center;\n}\n#cm2-app .cm2-option-group {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 13px;\n  color: #475569;\n}\n#cm2-app .cm2-option-group label { cursor: pointer; user-select: none; }\n#cm2-app .cm2-option-group select {\n  padding: 4px 8px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 13px;\n  color: #1e293b;\n  background: #fff;\n  cursor: pointer;\n}\n#cm2-app .cm2-btn-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 16px;\n}\n#cm2-app button {\n  padding: 9px 20px;\n  border: none;\n  border-radius: 7px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: opacity 0.15s;\n}\n#cm2-app button:hover { opacity: 0.88; }\n#cm2-app .cm2-btn-go {\n  background: #6366f1;\n  color: #fff;\n}\n#cm2-app .cm2-btn-clear {\n  background: #f1f5f9;\n  color: #475569;\n}\n#cm2-app .cm2-btn-copy {\n  background: #f1f5f9;\n  color: #475569;\n}\n#cm2-app .cm2-btn-upload {\n  background: #f0fdf4;\n  color: #166534;\n  border: 1px solid #bbf7d0;\n}\n#cm2-app input[type=\"file\"] { display: none; }\n#cm2-app .cm2-stats {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 18px;\n  padding: 12px 16px;\n  background: #f8fafc;\n  border-radius: 8px;\n  font-size: 13px;\n  color: #475569;\n  margin-bottom: 8px;\n}\n#cm2-app .cm2-stats span strong {\n  color: #1e293b;\n  font-size: 15px;\n}\n#cm2-app .cm2-ratio-pill {\n  display: inline-block;\n  padding: 2px 10px;\n  border-radius: 999px;\n  font-size: 12px;\n  font-weight: 700;\n  background: #ecfdf5;\n  color: #065f46;\n}\n#cm2-app .cm2-ratio-pill.good { background: #ecfdf5; color: #065f46; }\n#cm2-app .cm2-ratio-pill.mid { background: #fef9c3; color: #854d0e; }\n#cm2-app .cm2-ratio-pill.bad { background: #fef2f2; color: #991b1b; }\n\u003c/style\u003e\n\u003cdiv class=\"cm2-mode-tabs\"\u003e\n  \u003cbutton class=\"cm2-tab active\" id=\"cm2-tab-minify\"\u003eMinify\u003c/button\u003e\n  \u003cbutton class=\"cm2-tab\" id=\"cm2-tab-beautify\"\u003eBeautify\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv id=\"cm2-beautify-opts\" class=\"cm2-options\" style=\"display:none\"\u003e\n  \u003cdiv class=\"cm2-option-group\"\u003e\n    \u003clabel for=\"cm2-indent\"\u003eIndent:\u003c/label\u003e\n    \u003cselect id=\"cm2-indent\"\u003e\n      \u003coption value=\"2\"\u003e2 spaces\u003c/option\u003e\n      \u003coption value=\"4\"\u003e4 spaces\u003c/option\u003e\n      \u003coption value=\"tab\"\u003eTab\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"cm2-btn-row\"\u003e\n  \u003cbutton class=\"cm2-btn-go\" id=\"cm2-go-btn\"\u003eMinify CSS\u003c/button\u003e\n  \u003cbutton class=\"cm2-btn-clear\" id=\"cm2-clear-btn\"\u003eClear\u003c/button\u003e\n  \u003cbutton class=\"cm2-btn-upload\" id=\"cm2-upload-btn\"\u003eUpload File\u003c/button\u003e\n  \u003cinput type=\"file\" id=\"cm2-file-input\" accept=\".css,text/css\"\u003e\n\u003c/div\u003e\n\u003cdiv class=\"cm2-panels\"\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"cm2-panel-label\"\u003eInput CSS\u003c/div\u003e\n    \u003ctextarea id=\"cm2-input\" placeholder=\"Paste your CSS here...\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"cm2-panel-label\"\u003eOutput\u003c/div\u003e\n    \u003ctextarea id=\"cm2-output\" readonly placeholder=\"Output will appear here...\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"cm2-stats\" class=\"cm2-stats\" style=\"display:none\"\u003e\u003c/div\u003e\n\u003cdiv class=\"cm2-btn-row\" style=\"margin-top:8px\"\u003e\n  \u003cbutton class=\"cm2-btn-copy\" id=\"cm2-copy-btn\"\u003eCopy Output\u003c/button\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n  let currentMode = \"minify\";\n\n  function minifyCSS(css) {\n    // Remove comments\n    css = css.replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\");\n    // Remove whitespace around selectors and braces\n    css = css.replace(/\\s*([{}:;,\u003e~+])\\s*/g, \"$1\");\n    // Remove whitespace at line starts/ends\n    css = css.replace(/^\\s+|\\s+$/gm, \"\");\n    // Collapse multiple spaces\n    css = css.replace(/\\s{2,}/g, \" \");\n    // Remove last semicolon before closing brace\n    css = css.replace(/;}/g, \"}\");\n    // Collapse newlines\n    css = css.replace(/\\n+/g, \"\");\n    // Remove spaces inside parentheses for rgb/rgba etc (optional cleanup)\n    css = css.replace(/:\\s+/g, \":\");\n    return css.trim();\n  }\n\n  function beautifyCSS(css, indent) {\n    const indentStr = indent === \"tab\" ? \"\\t\" : \" \".repeat(parseInt(indent));\n    // First minify to normalize\n    css = minifyCSS(css);\n    let out = \"\";\n    let depth = 0;\n    let i = 0;\n    while (i \u003c css.length) {\n      const ch = css[i];\n      if (ch === \"{\") {\n        out += \" {\\n\";\n        depth++;\n        out += indentStr.repeat(depth);\n      } else if (ch === \"}\") {\n        depth = Math.max(0, depth - 1);\n        out = out.replace(/\\s+$/, \"\");\n        out += \"\\n}\\n\";\n        if (depth \u003e 0) out += indentStr.repeat(depth);\n      } else if (ch === \";\") {\n        out += \";\\n\" + indentStr.repeat(depth);\n      } else if (ch === \",\") {\n        // Check if inside a rule (depth \u003e 0) or between selectors\n        if (depth === 0) {\n          out += \",\\n\";\n        } else {\n          out += \", \";\n        }\n      } else {\n        out += ch;\n      }\n      i++;\n    }\n    // Cleanup extra blank lines\n    out = out.replace(/\\n{3,}/g, \"\\n\\n\").trim();\n    return out;\n  }\n\n  function fmtBytes(n) {\n    if (n \u003c 1024) return n + \" B\";\n    return (n / 1024).toFixed(2) + \" KB\";\n  }\n\n  function process() {\n    const input = document.getElementById(\"cm2-input\").value;\n    if (!input.trim()) return;\n    let output;\n    if (currentMode === \"minify\") {\n      output = minifyCSS(input);\n    } else {\n      const indent = document.getElementById(\"cm2-indent\").value;\n      output = beautifyCSS(input, indent);\n    }\n    document.getElementById(\"cm2-output\").value = output;\n    const origSize = new Blob([input]).size;\n    const newSize = new Blob([output]).size;\n    const saved = origSize - newSize;\n    const ratio = origSize \u003e 0 ? ((saved / origSize) * 100).toFixed(1) : 0;\n    const pillClass = ratio \u003e= 30 ? \"good\" : ratio \u003e= 10 ? \"mid\" : \"bad\";\n    const statsEl = document.getElementById(\"cm2-stats\");\n    statsEl.style.display = \"flex\";\n    if (currentMode === \"minify\") {\n      statsEl.innerHTML = `\n        \u003cspan\u003eOriginal: \u003cstrong\u003e${fmtBytes(origSize)}\u003c/strong\u003e\u003c/span\u003e\n        \u003cspan\u003eMinified: \u003cstrong\u003e${fmtBytes(newSize)}\u003c/strong\u003e\u003c/span\u003e\n        \u003cspan\u003eSaved: \u003cstrong\u003e${fmtBytes(Math.max(0,saved))}\u003c/strong\u003e\u003c/span\u003e\n        \u003cspan\u003eRatio: \u003cspan class=\"cm2-ratio-pill ${pillClass}\"\u003e${ratio}% smaller\u003c/span\u003e\u003c/span\u003e\n      `;\n    } else {\n      statsEl.innerHTML = `\n        \u003cspan\u003eOriginal: \u003cstrong\u003e${fmtBytes(origSize)}\u003c/strong\u003e\u003c/span\u003e\n        \u003cspan\u003eBeautified: \u003cstrong\u003e${fmtBytes(newSize)}\u003c/strong\u003e\u003c/span\u003e\n        \u003cspan\u003eLines: \u003cstrong\u003e${output.split(\"\\n\").length}\u003c/strong\u003e\u003c/span\u003e\n      `;\n    }\n  }\n\n  document.getElementById(\"cm2-tab-minify\").addEventListener(\"click\", function(){\n    currentMode = \"minify\";\n    this.classList.add(\"active\");\n    document.getElementById(\"cm2-tab-beautify\").classList.remove(\"active\");\n    document.getElementById(\"cm2-beautify-opts\").style.display = \"none\";\n    document.getElementById(\"cm2-go-btn\").textContent = \"Minify CSS\";\n    document.getElementById(\"cm2-output\").value = \"\";\n    document.getElementById(\"cm2-stats\").style.display = \"none\";\n  });\n\n  document.getElementById(\"cm2-tab-beautify\").addEventListener(\"click\", function(){\n    currentMode = \"beautify\";\n    this.classList.add(\"active\");\n    document.getElementById(\"cm2-tab-minify\").classList.remove(\"active\");\n    document.getElementById(\"cm2-beautify-opts\").style.display = \"flex\";\n    document.getElementById(\"cm2-go-btn\").textContent = \"Beautify CSS\";\n    document.getElementById(\"cm2-output\").value = \"\";\n    document.getElementById(\"cm2-stats\").style.display = \"none\";\n  });\n\n  document.getElementById(\"cm2-go-btn\").addEventListener(\"click\", process);\n\n  document.getElementById(\"cm2-clear-btn\").addEventListener(\"click\", function(){\n    document.getElementById(\"cm2-input\").value = \"\";\n    document.getElementById(\"cm2-output\").value = \"\";\n    document.getElementById(\"cm2-stats\").style.display = \"none\";\n  });\n\n  document.getElementById(\"cm2-copy-btn\").addEventListener(\"click\", function(){\n    const out = document.getElementById(\"cm2-output\").value;\n    if (!out) return;\n    navigator.clipboard.writeText(out).then(() =\u003e {\n      const btn = document.getElementById(\"cm2-copy-btn\");\n      const orig = btn.textContent;\n      btn.textContent = \"Copied!\";\n      setTimeout(() =\u003e btn.textContent = orig, 1500);\n    });\n  });\n\n  document.getElementById(\"cm2-upload-btn\").addEventListener(\"click\", function(){\n    document.getElementById(\"cm2-file-input\").click();\n  });\n\n  document.getElementById(\"cm2-file-input\").addEventListener(\"change\", function(e){\n    const file = e.target.files[0];\n    if (!file) return;\n    const reader = new FileReader();\n    reader.onload = function(ev) {\n      document.getElementById(\"cm2-input\").value = ev.target.result;\n      process();\n    };\n    reader.readAsText(file);\n    e.target.value = \"\";\n  });\n\n  document.getElementById(\"cm2-input\").addEventListener(\"keydown\", function(e){\n    if (e.ctrlKey \u0026\u0026 e.key === \"Enter\") process();\n  });\n})();\n\u003c/script\u003e\n\u003c/div\u003e","title":"CSS Minifier \u0026 Beautifier"},{"content":"Generate CSS outline code instantly. Adjust style, width, color, and offset — see the live preview update in real time, then copy the ready-to-use CSS.\nPresets\nControls\n\u0026lt;!-- outline-style --\u0026gt; \u0026lt;div class=\u0026quot;ot-control-group\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;ot-style\u0026quot;\u0026gt;outline-style\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;ot-style\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;solid\u0026quot; selected\u0026gt;solid\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;dashed\u0026quot;\u0026gt;dashed\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;dotted\u0026quot;\u0026gt;dotted\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;double\u0026quot;\u0026gt;double\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;groove\u0026quot;\u0026gt;groove\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;ridge\u0026quot;\u0026gt;ridge\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;inset\u0026quot;\u0026gt;inset\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;outset\u0026quot;\u0026gt;outset\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- outline-width --\u0026gt; \u0026lt;div class=\u0026quot;ot-control-group\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;ot-width\u0026quot;\u0026gt;outline-width \u0026lt;span class=\u0026quot;ot-val\u0026quot; id=\u0026quot;ot-width-val\u0026quot;\u0026gt;3px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;ot-width\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;20\u0026quot; value=\u0026quot;3\u0026quot; step=\u0026quot;1\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- outline-color --\u0026gt; \u0026lt;div class=\u0026quot;ot-control-group\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;ot-color\u0026quot;\u0026gt;outline-color\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;ot-color-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;ot-color\u0026quot; value=\u0026quot;#4f46e5\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;ot-hex-input\u0026quot; id=\u0026quot;ot-hex\u0026quot; value=\u0026quot;#4f46e5\u0026quot; maxlength=\u0026quot;9\u0026quot; spellcheck=\u0026quot;false\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- outline-offset --\u0026gt; \u0026lt;div class=\u0026quot;ot-control-group\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;ot-offset\u0026quot;\u0026gt;outline-offset \u0026lt;span class=\u0026quot;ot-val\u0026quot; id=\u0026quot;ot-offset-val\u0026quot;\u0026gt;3px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;ot-offset\u0026quot; min=\u0026quot;-20\u0026quot; max=\u0026quot;20\u0026quot; value=\u0026quot;3\u0026quot; step=\u0026quot;1\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- compare with border toggle --\u0026gt; \u0026lt;label class=\u0026quot;ot-toggle-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ot-toggle\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ot-compare\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ot-toggle-track\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;ot-toggle-thumb\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/span\u0026gt; Compare with border \u0026lt;/label\u0026gt; \u0026lt;!-- focus state toggle --\u0026gt; \u0026lt;label class=\u0026quot;ot-toggle-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ot-toggle\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ot-focus-mode\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ot-toggle-track\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;ot-toggle-thumb\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/span\u0026gt; Focus state preview \u0026lt;/label\u0026gt; \u0026lt;!-- Preview --\u0026gt; \u0026lt;div class=\u0026quot;ot-preview-wrap\u0026quot;\u0026gt; \u0026lt;p class=\u0026quot;ot-section-title\u0026quot;\u0026gt;Live Preview\u0026lt;/p\u0026gt; \u0026lt;div class=\u0026quot;ot-preview-stage\u0026quot; id=\u0026quot;ot-stage\u0026quot;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div class=\u0026quot;ot-preview-box\u0026quot; id=\u0026quot;ot-box-outline\u0026quot;\u0026gt; Outline \u0026lt;span class=\u0026quot;ot-label\u0026quot;\u0026gt;outline\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;ot-border-col\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ot-preview-box\u0026quot; id=\u0026quot;ot-box-border\u0026quot;\u0026gt; Border \u0026lt;span class=\u0026quot;ot-label\u0026quot;\u0026gt;border\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;ot-focus-col\u0026quot; style=\u0026quot;display:none\u0026quot; class=\u0026quot;ot-focus-demo\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;ot-focus-btn\u0026quot; id=\u0026quot;ot-focus-btn\u0026quot;\u0026gt;Focus me\u0026lt;/button\u0026gt; \u0026lt;span class=\u0026quot;ot-focus-label\u0026quot;\u0026gt;Tab to focus\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- CSS Output --\u0026gt; \u0026lt;div class=\u0026quot;ot-output-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ot-output-header\u0026quot;\u0026gt; \u0026lt;p class=\u0026quot;ot-section-title\u0026quot; style=\u0026quot;margin:0\u0026quot;\u0026gt;CSS Output\u0026lt;/p\u0026gt; \u0026lt;button class=\u0026quot;ot-copy-btn\u0026quot; id=\u0026quot;ot-copy-btn\u0026quot;\u0026gt; \u0026lt;svg width=\u0026quot;14\u0026quot; height=\u0026quot;14\u0026quot; viewBox=\u0026quot;0 0 16 16\u0026quot; fill=\u0026quot;none\u0026quot; aria-hidden=\u0026quot;true\u0026quot;\u0026gt; \u0026lt;rect x=\u0026quot;5\u0026quot; y=\u0026quot;5\u0026quot; width=\u0026quot;9\u0026quot; height=\u0026quot;10\u0026quot; rx=\u0026quot;1.5\u0026quot; stroke=\u0026quot;currentColor\u0026quot; stroke-width=\u0026quot;1.5\u0026quot;/\u0026gt; \u0026lt;path d=\u0026quot;M3 11H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1v1\u0026quot; stroke=\u0026quot;currentColor\u0026quot; stroke-width=\u0026quot;1.5\u0026quot; stroke-linecap=\u0026quot;round\u0026quot;/\u0026gt; \u0026lt;/svg\u0026gt; Copy CSS \u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;pre class=\u0026quot;ot-code\u0026quot; id=\u0026quot;ot-code-out\u0026quot;\u0026gt;\u0026lt;/pre\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Accessibility / Focus State Output --\u0026gt; \u0026lt;div class=\u0026quot;ot-a11y-wrap\u0026quot; id=\u0026quot;ot-a11y-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ot-output-header\u0026quot;\u0026gt; \u0026lt;p class=\u0026quot;ot-section-title\u0026quot; style=\u0026quot;margin:0\u0026quot;\u0026gt;Focus State CSS (Accessibility)\u0026lt;/p\u0026gt; \u0026lt;button class=\u0026quot;ot-copy-btn\u0026quot; id=\u0026quot;ot-copy-a11y-btn\u0026quot;\u0026gt; \u0026lt;svg width=\u0026quot;14\u0026quot; height=\u0026quot;14\u0026quot; viewBox=\u0026quot;0 0 16 16\u0026quot; fill=\u0026quot;none\u0026quot; aria-hidden=\u0026quot;true\u0026quot;\u0026gt; \u0026lt;rect x=\u0026quot;5\u0026quot; y=\u0026quot;5\u0026quot; width=\u0026quot;9\u0026quot; height=\u0026quot;10\u0026quot; rx=\u0026quot;1.5\u0026quot; stroke=\u0026quot;currentColor\u0026quot; stroke-width=\u0026quot;1.5\u0026quot;/\u0026gt; \u0026lt;path d=\u0026quot;M3 11H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1v1\u0026quot; stroke=\u0026quot;currentColor\u0026quot; stroke-width=\u0026quot;1.5\u0026quot; stroke-linecap=\u0026quot;round\u0026quot;/\u0026gt; \u0026lt;/svg\u0026gt; Copy \u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;p class=\u0026quot;ot-a11y-note\u0026quot;\u0026gt;Use \u0026lt;code\u0026gt;:focus-visible\u0026lt;/code\u0026gt; for keyboard-only focus rings (WCAG 2.4.11 recommendation). Minimum 3:1 contrast ratio required.\u0026lt;/p\u0026gt; \u0026lt;pre class=\u0026quot;ot-code-a11y\u0026quot; id=\u0026quot;ot-a11y-out\u0026quot;\u0026gt;\u0026lt;/pre\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ot-info\u0026quot;\u0026gt; \u0026lt;strong\u0026gt;Tip:\u0026lt;/strong\u0026gt; Unlike \u0026lt;code\u0026gt;border\u0026lt;/code\u0026gt;, \u0026lt;code\u0026gt;outline\u0026lt;/code\u0026gt; does not affect layout — it draws outside the element without shifting surrounding content. Use \u0026lt;code\u0026gt;outline-offset\u0026lt;/code\u0026gt; to create a gap between element and outline. \u0026lt;/div\u0026gt; How to Use Choose an outline-style from the dropdown — solid, dashed, dotted, double, and more. Drag outline-width to set thickness (1–20 px). Pick an outline-color from the color picker or type a hex value. Adjust outline-offset to push the outline away from (or inside) the element boundary. Enable Compare with border to see the layout difference side by side. Enable Focus state preview to see how the outline looks as a focus ring. Click Copy CSS to copy the generated code. Outline vs. Border — Key Differences Property Affects layout Follows border-radius Offset support outline No Partially Yes (outline-offset) border Yes Yes No Because outline does not affect layout, it is ideal for focus indicators — adding a visible ring around focused elements without causing content reflow.\nAccessibility Tips WCAG 2.4.11 (AA, WCAG 2.2) requires focus indicators to have a contrast ratio of at least 3:1 against adjacent colors. Use :focus-visible instead of :focus to show rings only for keyboard navigation, not mouse clicks. outline-offset of 2–4 px improves legibility on rounded elements. Never use outline: none without providing an alternative focus style. Related Tools CSS Border Radius Generator CSS Box Shadow Generator CSS Button Generator ","permalink":"https://productivity-works.com/tools/css-outline-generator/","summary":"\u003cp\u003eGenerate CSS outline code instantly. Adjust style, width, color, and offset — see the live preview update in real time, then copy the ready-to-use CSS.\u003c/p\u003e\n\u003cdiv id=\"ot-app\"\u003e\n\u003cstyle\u003e\n/* ── Reset \u0026 layout ─────────────────────────────────────────────── */\n#ot-app *,\n#ot-app *::before,\n#ot-app *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n#ot-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 15px;\n  color: #1a1a2e;\n  background: #f8f9fc;\n  border-radius: 12px;\n  padding: 0 0 32px;\n  max-width: 900px;\n}\n\n/* ── Section headings ────────────────────────────────────────────── */\n#ot-app .ot-section-title {\n  font-size: 13px;\n  font-weight: 700;\n  letter-spacing: .06em;\n  text-transform: uppercase;\n  color: #6b7280;\n  margin-bottom: 12px;\n}\n\n/* ── Two-column layout ───────────────────────────────────────────── */\n#ot-app .ot-layout {\n  display: grid;\n  grid-template-columns: 300px 1fr;\n  gap: 20px;\n  align-items: start;\n}\n@media (max-width: 680px) {\n  #ot-app .ot-layout { grid-template-columns: 1fr; }\n}\n\n/* ── Controls panel ──────────────────────────────────────────────── */\n#ot-app .ot-panel {\n  background: #fff;\n  border: 1px solid #e5e7eb;\n  border-radius: 10px;\n  padding: 20px;\n  display: flex;\n  flex-direction: column;\n  gap: 18px;\n}\n\n#ot-app .ot-control-group { display: flex; flex-direction: column; gap: 6px; }\n\n#ot-app label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #374151;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n#ot-app label span.ot-val {\n  font-weight: 700;\n  color: #4f46e5;\n  font-size: 12px;\n  background: #eef2ff;\n  border-radius: 4px;\n  padding: 1px 6px;\n  min-width: 44px;\n  text-align: center;\n}\n\n#ot-app select,\n#ot-app input[type=\"range\"] {\n  width: 100%;\n}\n\n#ot-app select {\n  appearance: none;\n  background: #f3f4f6 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%236b7280' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E\") no-repeat right 10px center;\n  border: 1px solid #d1d5db;\n  border-radius: 6px;\n  padding: 7px 30px 7px 10px;\n  font-size: 13px;\n  color: #1a1a2e;\n  cursor: pointer;\n}\n#ot-app select:focus { outline: 2px solid #4f46e5; outline-offset: 1px; }\n\n#ot-app input[type=\"range\"] {\n  accent-color: #4f46e5;\n  height: 4px;\n  cursor: pointer;\n}\n\n/* Color row */\n#ot-app .ot-color-row {\n  display: grid;\n  grid-template-columns: 44px 1fr;\n  gap: 8px;\n  align-items: center;\n}\n#ot-app input[type=\"color\"] {\n  width: 44px;\n  height: 36px;\n  border: 1px solid #d1d5db;\n  border-radius: 6px;\n  padding: 2px;\n  cursor: pointer;\n  background: #fff;\n}\n#ot-app .ot-hex-input {\n  width: 100%;\n  border: 1px solid #d1d5db;\n  border-radius: 6px;\n  padding: 7px 10px;\n  font-size: 13px;\n  font-family: \"Fira Mono\", \"Courier New\", monospace;\n  color: #1a1a2e;\n}\n#ot-app .ot-hex-input:focus { outline: 2px solid #4f46e5; outline-offset: 1px; }\n\n/* Toggle */\n#ot-app .ot-toggle-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  font-size: 13px;\n  font-weight: 600;\n  color: #374151;\n  cursor: pointer;\n  user-select: none;\n}\n#ot-app .ot-toggle {\n  position: relative;\n  width: 40px;\n  height: 22px;\n  flex-shrink: 0;\n}\n#ot-app .ot-toggle input { opacity: 0; width: 0; height: 0; position: absolute; }\n#ot-app .ot-toggle-track {\n  position: absolute;\n  inset: 0;\n  background: #d1d5db;\n  border-radius: 11px;\n  transition: background .2s;\n}\n#ot-app .ot-toggle input:checked ~ .ot-toggle-track { background: #4f46e5; }\n#ot-app .ot-toggle-thumb {\n  position: absolute;\n  top: 3px;\n  left: 3px;\n  width: 16px;\n  height: 16px;\n  background: #fff;\n  border-radius: 50%;\n  transition: transform .2s;\n  box-shadow: 0 1px 3px rgba(0,0,0,.2);\n}\n#ot-app .ot-toggle input:checked ~ .ot-toggle-thumb { transform: translateX(18px); }\n\n/* ── Right column ────────────────────────────────────────────────── */\n#ot-app .ot-right { display: flex; flex-direction: column; gap: 20px; }\n\n/* ── Preview area ────────────────────────────────────────────────── */\n#ot-app .ot-preview-wrap {\n  background: #fff;\n  border: 1px solid #e5e7eb;\n  border-radius: 10px;\n  padding: 24px;\n}\n#ot-app .ot-preview-stage {\n  display: flex;\n  gap: 32px;\n  flex-wrap: wrap;\n  justify-content: center;\n  align-items: center;\n  min-height: 140px;\n  background: repeating-conic-gradient(#f0f0f0 0% 25%, #fff 0% 50%) 0 0 / 16px 16px;\n  border-radius: 8px;\n  padding: 32px 24px;\n}\n#ot-app .ot-preview-box {\n  width: 120px;\n  height: 80px;\n  background: #fff;\n  border-radius: 6px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 12px;\n  font-weight: 600;\n  color: #6b7280;\n  flex-direction: column;\n  gap: 4px;\n  transition: outline .15s, border .15s, outline-offset .15s;\n}\n#ot-app .ot-preview-box .ot-label {\n  font-size: 10px;\n  font-weight: 700;\n  letter-spacing: .05em;\n  text-transform: uppercase;\n  color: #9ca3af;\n  margin-top: 4px;\n}\n\n/* Focus demo */\n#ot-app .ot-focus-demo {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 8px;\n}\n#ot-app .ot-focus-btn {\n  padding: 8px 18px;\n  border: none;\n  border-radius: 6px;\n  background: #4f46e5;\n  color: #fff;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: outline .15s, outline-offset .15s;\n}\n#ot-app .ot-focus-btn:focus { outline-style: solid; outline-width: 2px; outline-color: #4f46e5; outline-offset: 3px; }\n#ot-app .ot-focus-label {\n  font-size: 10px;\n  font-weight: 700;\n  letter-spacing: .05em;\n  text-transform: uppercase;\n  color: #9ca3af;\n}\n\n/* ── Presets ─────────────────────────────────────────────────────── */\n#ot-app .ot-presets-wrap {\n  background: #fff;\n  border: 1px solid #e5e7eb;\n  border-radius: 10px;\n  padding: 20px;\n}\n#ot-app .ot-presets-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));\n  gap: 10px;\n}\n#ot-app .ot-preset-btn {\n  border: 1.5px solid #e5e7eb;\n  border-radius: 8px;\n  padding: 10px 12px;\n  background: #f9fafb;\n  font-size: 12px;\n  font-weight: 600;\n  color: #374151;\n  cursor: pointer;\n  text-align: left;\n  transition: border-color .15s, background .15s;\n  display: flex;\n  flex-direction: column;\n  gap: 4px;\n}\n#ot-app .ot-preset-btn:hover { border-color: #4f46e5; background: #eef2ff; color: #4f46e5; }\n#ot-app .ot-preset-swatch {\n  width: 100%;\n  height: 28px;\n  border-radius: 4px;\n  background: #f3f4f6;\n  margin-bottom: 4px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n#ot-app .ot-preset-swatch span {\n  width: 60px;\n  height: 16px;\n  background: #fff;\n  border-radius: 3px;\n}\n\n/* ── Output ──────────────────────────────────────────────────────── */\n#ot-app .ot-output-wrap {\n  background: #fff;\n  border: 1px solid #e5e7eb;\n  border-radius: 10px;\n  padding: 20px;\n}\n#ot-app .ot-output-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin-bottom: 12px;\n}\n#ot-app .ot-copy-btn {\n  background: #4f46e5;\n  color: #fff;\n  border: none;\n  border-radius: 6px;\n  padding: 7px 16px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background .15s;\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n#ot-app .ot-copy-btn:hover { background: #4338ca; }\n#ot-app .ot-copy-btn.copied { background: #059669; }\n#ot-app pre.ot-code {\n  background: #0f172a;\n  border-radius: 8px;\n  padding: 16px 18px;\n  overflow-x: auto;\n  font-family: \"Fira Mono\", \"Courier New\", monospace;\n  font-size: 13px;\n  line-height: 1.7;\n  white-space: pre;\n  color: #e2e8f0;\n}\n#ot-app .ot-kw  { color: #818cf8; }\n#ot-app .ot-prop{ color: #7dd3fc; }\n#ot-app .ot-val-c{ color: #86efac; }\n#ot-app .ot-punc{ color: #94a3b8; }\n#ot-app .ot-comment { color: #64748b; font-style: italic; }\n\n/* ── Accessibility output ────────────────────────────────────────── */\n#ot-app .ot-a11y-wrap {\n  background: #fff;\n  border: 1px solid #e5e7eb;\n  border-radius: 10px;\n  padding: 20px;\n}\n#ot-app .ot-a11y-note {\n  font-size: 12px;\n  color: #6b7280;\n  margin-bottom: 12px;\n  line-height: 1.5;\n}\n#ot-app pre.ot-code-a11y {\n  background: #0f172a;\n  border-radius: 8px;\n  padding: 16px 18px;\n  overflow-x: auto;\n  font-family: \"Fira Mono\", \"Courier New\", monospace;\n  font-size: 13px;\n  line-height: 1.7;\n  white-space: pre;\n  color: #e2e8f0;\n}\n\n/* ── Info strip ──────────────────────────────────────────────────── */\n#ot-app .ot-info {\n  background: #eef2ff;\n  border-left: 3px solid #4f46e5;\n  border-radius: 0 6px 6px 0;\n  padding: 12px 16px;\n  font-size: 12.5px;\n  color: #3730a3;\n  line-height: 1.6;\n}\n\u003c/style\u003e\n\u003c!-- ── Presets ──────────────────────────────────────────────── --\u003e\n\u003cdiv class=\"ot-presets-wrap\"\u003e\n  \u003cp class=\"ot-section-title\"\u003ePresets\u003c/p\u003e","title":"CSS Outline Generator"},{"content":" CSS Scrollbar Styler Customize your scrollbar and copy ready-to-use CSS for WebKit and Firefox.\nMinimal Dark Mode Colorful Rounded Hidden Controls \u0026lt;div class=\u0026quot;sb-control\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Scrollbar Width Mode\u0026lt;/label\u0026gt; \u0026lt;select class=\u0026quot;sb-select\u0026quot; id=\u0026quot;sb-width-mode\u0026quot; onchange=\u0026quot;sbWidthModeChange()\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;auto\u0026quot;\u0026gt;auto (default)\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;thin\u0026quot;\u0026gt;thin\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;custom\u0026quot; selected\u0026gt;custom (px)\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sb-control\u0026quot; id=\u0026quot;sb-custom-width-wrap\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Custom Width: \u0026lt;span class=\u0026quot;sb-val\u0026quot; id=\u0026quot;sb-width-val\u0026quot;\u0026gt;10px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;sb-width\u0026quot; min=\u0026quot;2\u0026quot; max=\u0026quot;32\u0026quot; value=\u0026quot;10\u0026quot; oninput=\u0026quot;sbUpdate()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sb-control\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Track Color\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;sb-color-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;sb-track-color\u0026quot; value=\u0026quot;#f1f5f9\u0026quot; oninput=\u0026quot;sbSyncColor('sb-track-color','sb-track-color-hex')\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;sb-track-color-hex\u0026quot; value=\u0026quot;#f1f5f9\u0026quot; oninput=\u0026quot;sbSyncHex('sb-track-color','sb-track-color-hex')\u0026quot; maxlength=\u0026quot;9\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sb-control\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Thumb Color\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;sb-color-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;sb-thumb-color\u0026quot; value=\u0026quot;#94a3b8\u0026quot; oninput=\u0026quot;sbSyncColor('sb-thumb-color','sb-thumb-color-hex')\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;sb-thumb-color-hex\u0026quot; value=\u0026quot;#94a3b8\u0026quot; oninput=\u0026quot;sbSyncHex('sb-thumb-color','sb-thumb-color-hex')\u0026quot; maxlength=\u0026quot;9\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sb-control\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Thumb Hover Color\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;sb-color-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;sb-thumb-hover-color\u0026quot; value=\u0026quot;#6366f1\u0026quot; oninput=\u0026quot;sbSyncColor('sb-thumb-hover-color','sb-thumb-hover-hex')\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;sb-thumb-hover-hex\u0026quot; value=\u0026quot;#6366f1\u0026quot; oninput=\u0026quot;sbSyncHex('sb-thumb-hover-color','sb-thumb-hover-hex')\u0026quot; maxlength=\u0026quot;9\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sb-control\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Thumb Border-Radius: \u0026lt;span class=\u0026quot;sb-val\u0026quot; id=\u0026quot;sb-thumb-radius-val\u0026quot;\u0026gt;6px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;sb-thumb-radius\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;50\u0026quot; value=\u0026quot;6\u0026quot; oninput=\u0026quot;sbUpdate()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sb-control\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Track Border-Radius: \u0026lt;span class=\u0026quot;sb-val\u0026quot; id=\u0026quot;sb-track-radius-val\u0026quot;\u0026gt;0px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;sb-track-radius\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;50\u0026quot; value=\u0026quot;0\u0026quot; oninput=\u0026quot;sbUpdate()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; Live Preview Scroll me to see your custom scrollbar in action. This preview box contains enough content to make scrolling necessary so you can see exactly how your scrollbar looks.\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation.\nCustomize width, colors, and border-radius with the controls on the left. Your changes update the preview in real time.\nCopy the generated CSS for WebKit (Chrome, Safari, Edge) or Firefox using the tabs below.\nUse presets for quick starting points — then fine-tune to match your design system.\nTry the \"Hidden\" preset to remove scrollbars while keeping scroll functionality.\nPreview reflects your current settings.\nApply to element (CSS selector): Leave body for global styles. WebKit (Chrome/Safari/Edge) Firefox Combined Copy CSS Browser Compatibility Property Chrome Firefox Safari Edge ::-webkit-scrollbar Yes No Yes Yes scrollbar-width 121+ Yes 16+ 121+ scrollbar-color 121+ Yes 16+ 121+ Hover effects Yes (WebKit) Not supported Yes (WebKit) Yes (WebKit) Tip: Use the Combined output to support all modern browsers. Firefox does not support per-element scrollbar hover colors.\nStyle buttons \u0026rarr; CSS Button Generator\nCustomize animations \u0026rarr; CSS Animation Generator\nCSS variables \u0026rarr; CSS Variables Generator\n","permalink":"https://productivity-works.com/tools/scrollbar-styler/","summary":"\u003cdiv id=\"sb-app\" style=\"font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;max-width:900px;margin:0 auto;\"\u003e\n\u003cstyle\u003e\n#sb-app *,#sb-app *::before,#sb-app *::after{box-sizing:border-box;}\n#sb-app h2{font-size:1.1rem;font-weight:700;margin:0 0 12px;color:#1e293b;}\n#sb-app .sb-grid{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-bottom:20px;}\n@media(max-width:640px){#sb-app .sb-grid{grid-template-columns:1fr;}}\n#sb-app .sb-panel{background:#f8fafc;border:1px solid #e2e8f0;border-radius:10px;padding:18px;}\n#sb-app .sb-control{margin-bottom:14px;}\n#sb-app .sb-control label{display:block;font-size:13px;font-weight:600;color:#475569;margin-bottom:5px;}\n#sb-app .sb-control input[type=range]{width:100%;accent-color:#6366f1;}\n#sb-app .sb-control input[type=color]{width:48px;height:32px;border:1px solid #cbd5e1;border-radius:6px;cursor:pointer;padding:2px;}\n#sb-app .sb-color-row{display:flex;align-items:center;gap:10px;}\n#sb-app .sb-color-row input[type=text]{flex:1;padding:6px 10px;border:1px solid #cbd5e1;border-radius:6px;font-size:13px;font-family:monospace;}\n#sb-app .sb-select{width:100%;padding:7px 10px;border:1px solid #cbd5e1;border-radius:6px;font-size:13px;background:#fff;}\n#sb-app .sb-val{font-size:12px;color:#6366f1;font-weight:700;margin-left:6px;}\n#sb-app .sb-presets{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:20px;}\n#sb-app .sb-preset-btn{padding:7px 15px;border:1.5px solid #cbd5e1;border-radius:20px;font-size:13px;font-weight:600;cursor:pointer;background:#fff;color:#334155;transition:all .18s;}\n#sb-app .sb-preset-btn:hover{border-color:#6366f1;color:#6366f1;}\n#sb-app .sb-preset-btn.active{background:#6366f1;color:#fff;border-color:#6366f1;}\n#sb-app .sb-preview-wrap{margin-bottom:20px;}\n#sb-app .sb-preview-box{height:180px;overflow-y:scroll;border:1px solid #e2e8f0;border-radius:10px;padding:16px;background:#fff;font-size:14px;color:#475569;line-height:1.7;}\n#sb-app .sb-output-wrap{margin-bottom:20px;}\n#sb-app .sb-tabs{display:flex;gap:0;margin-bottom:0;}\n#sb-app .sb-tab{padding:8px 18px;font-size:13px;font-weight:600;cursor:pointer;border:1.5px solid #e2e8f0;border-bottom:none;border-radius:8px 8px 0 0;background:#f1f5f9;color:#64748b;transition:all .15s;}\n#sb-app .sb-tab.active{background:#1e293b;color:#fff;border-color:#1e293b;}\n#sb-app .sb-code-block{position:relative;background:#1e293b;border-radius:0 10px 10px 10px;padding:18px 16px;min-height:120px;overflow-x:auto;}\n#sb-app .sb-code-block pre{margin:0;font-family:'Fira Code','Cascadia Code',monospace;font-size:13px;line-height:1.7;white-space:pre-wrap;color:#e2e8f0;}\n#sb-app .sb-kw{color:#c084fc;}\n#sb-app .sb-prop{color:#67e8f9;}\n#sb-app .sb-val-c{color:#86efac;}\n#sb-app .sb-copy-btn{position:absolute;top:10px;right:10px;padding:6px 14px;background:#6366f1;color:#fff;border:none;border-radius:6px;font-size:12px;font-weight:700;cursor:pointer;transition:background .15s;}\n#sb-app .sb-copy-btn:hover{background:#4f46e5;}\n#sb-app .sb-copy-btn.copied{background:#10b981;}\n#sb-app .sb-scope-row{display:flex;align-items:center;gap:10px;margin-bottom:20px;padding:12px 16px;background:#fefce8;border:1px solid #fde68a;border-radius:8px;}\n#sb-app .sb-scope-row label{font-size:13px;font-weight:600;color:#92400e;margin:0;}\n#sb-app .sb-scope-row input[type=text]{flex:1;padding:6px 10px;border:1px solid #fcd34d;border-radius:6px;font-size:13px;font-family:monospace;background:#fff;}\n#sb-app .sb-compat{margin-top:20px;padding:14px 18px;background:#f0fdf4;border:1px solid #bbf7d0;border-radius:10px;}\n#sb-app .sb-compat h3{margin:0 0 8px;font-size:14px;font-weight:700;color:#166534;}\n#sb-app .sb-compat table{width:100%;border-collapse:collapse;font-size:13px;}\n#sb-app .sb-compat th{text-align:left;padding:4px 8px;color:#166534;font-weight:600;border-bottom:1px solid #bbf7d0;}\n#sb-app .sb-compat td{padding:4px 8px;color:#374151;}\n#sb-app .sb-compat .ok{color:#16a34a;font-weight:700;}\n#sb-app .sb-compat .partial{color:#d97706;font-weight:700;}\n#sb-app .sb-compat .no{color:#dc2626;font-weight:700;}\n#sb-app .sb-crosslinks{margin-top:24px;padding:14px 18px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:10px;font-size:14px;}\n#sb-app .sb-crosslinks p{margin:4px 0;}\n#sb-app .sb-crosslinks a{color:#6366f1;text-decoration:none;font-weight:600;}\n#sb-app .sb-crosslinks a:hover{text-decoration:underline;}\n\u003c/style\u003e\n\u003c!-- Presets --\u003e\n\u003cdiv style=\"margin-bottom:8px;\"\u003e\n  \u003ch2 style=\"font-size:1.25rem;margin-bottom:4px;\"\u003eCSS Scrollbar Styler\u003c/h2\u003e\n  \u003cp style=\"font-size:14px;color:#64748b;margin:0 0 14px;\"\u003eCustomize your scrollbar and copy ready-to-use CSS for WebKit and Firefox.\u003c/p\u003e","title":"CSS Scrollbar Styler"},{"content":" Presets Comfortable Card Tight List Section Spacing Button Padding Input Field Unit: px rem em % Link all sides Margin Padding Box Model Live Preview Content CSS Output Shorthand Longhand Copy CSS How to Use the CSS Spacing Generator Choose a preset or set your own values. Select a unit — px, rem, em, or %. Adjust margin and padding per side using sliders or number inputs. Enable \u0026ldquo;Link all sides\u0026rdquo; to set all four sides at once. Watch the box model diagram and live preview update instantly. Pick Shorthand or Longhand output, then click Copy CSS. Box Model Quick Reference Layer Purpose Margin Space outside the element border Border The element\u0026rsquo;s border (set separately) Padding Space inside the border, around the content Content The actual content area (text, images, etc.) CSS Shorthand Rules CSS uses a clockwise order: top → right → bottom → left.\n/* 1 value: all sides equal */ margin: 16px; /* 2 values: top/bottom left/right */ padding: 12px 24px; /* 3 values: top left/right bottom */ margin: 8px 16px 24px; /* 4 values: top right bottom left */ padding: 8px 16px 12px 16px; Related Tools CSS Flexbox Generator — Build flexbox layouts visually with live output. CSS Box Shadow Generator — Design layered shadows with real-time preview. CSS Variables Generator — Define and export custom property systems. ","permalink":"https://productivity-works.com/tools/css-spacing-generator/","summary":"\u003cdiv id=\"sp-app\"\u003e\n\u003cstyle\u003e\n#sp-app *,\n#sp-app *::before,\n#sp-app *::after {\n  box-sizing: border-box;\n}\n\n#sp-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 15px;\n  color: #1a1a2e;\n  line-height: 1.5;\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 0 0 40px;\n}\n\n#sp-app h2 {\n  font-size: 1.1rem;\n  font-weight: 700;\n  margin: 0 0 12px;\n  color: #1a1a2e;\n}\n\n/* Layout */\n#sp-layout {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 20px;\n  margin-bottom: 20px;\n}\n\n@media (max-width: 640px) {\n  #sp-layout {\n    grid-template-columns: 1fr;\n  }\n}\n\n/* Panel */\n.sp-panel {\n  background: #f8f9fc;\n  border: 1px solid #dde1ec;\n  border-radius: 10px;\n  padding: 18px;\n}\n\n/* Unit selector */\n#sp-unit-bar {\n  display: flex;\n  gap: 6px;\n  margin-bottom: 18px;\n  flex-wrap: wrap;\n}\n\n#sp-unit-bar label {\n  font-size: 0.78rem;\n  font-weight: 600;\n  color: #555;\n  margin-right: 4px;\n  align-self: center;\n}\n\n.sp-unit-btn {\n  padding: 4px 12px;\n  border: 1px solid #c0c7d8;\n  border-radius: 5px;\n  background: #fff;\n  font-size: 0.82rem;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s, border-color 0.15s;\n  color: #444;\n}\n\n.sp-unit-btn:hover {\n  border-color: #6c63ff;\n  color: #6c63ff;\n}\n\n.sp-unit-btn.active {\n  background: #6c63ff;\n  color: #fff;\n  border-color: #6c63ff;\n}\n\n/* Side controls grid */\n.sp-sides-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 10px;\n}\n\n.sp-side-group {\n  display: flex;\n  flex-direction: column;\n  gap: 4px;\n}\n\n.sp-side-group label {\n  font-size: 0.78rem;\n  font-weight: 600;\n  color: #666;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n\n.sp-side-row {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n\n.sp-side-row input[type=\"range\"] {\n  flex: 1;\n  accent-color: #6c63ff;\n  height: 4px;\n  cursor: pointer;\n}\n\n.sp-side-row input[type=\"number\"] {\n  width: 56px;\n  padding: 4px 6px;\n  border: 1px solid #c0c7d8;\n  border-radius: 5px;\n  font-size: 0.82rem;\n  text-align: right;\n  background: #fff;\n  color: #1a1a2e;\n}\n\n.sp-side-row input[type=\"number\"]:focus {\n  outline: 2px solid #6c63ff;\n  border-color: #6c63ff;\n}\n\n/* Link toggle */\n#sp-link-bar {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin: 12px 0;\n}\n\n#sp-link-bar label {\n  font-size: 0.82rem;\n  color: #555;\n  cursor: pointer;\n}\n\n.sp-toggle {\n  position: relative;\n  width: 36px;\n  height: 20px;\n  flex-shrink: 0;\n}\n\n.sp-toggle input {\n  opacity: 0;\n  width: 0;\n  height: 0;\n  position: absolute;\n}\n\n.sp-toggle-track {\n  position: absolute;\n  inset: 0;\n  background: #c0c7d8;\n  border-radius: 20px;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n\n.sp-toggle input:checked + .sp-toggle-track {\n  background: #6c63ff;\n}\n\n.sp-toggle-track::after {\n  content: \"\";\n  position: absolute;\n  top: 2px;\n  left: 2px;\n  width: 16px;\n  height: 16px;\n  background: #fff;\n  border-radius: 50%;\n  transition: transform 0.2s;\n}\n\n.sp-toggle input:checked + .sp-toggle-track::after {\n  transform: translateX(16px);\n}\n\n/* Section divider */\n.sp-divider {\n  border: none;\n  border-top: 1px solid #dde1ec;\n  margin: 14px 0;\n}\n\n/* Box model diagram */\n#sp-box-diagram {\n  position: relative;\n  width: 100%;\n  max-width: 340px;\n  margin: 0 auto 16px;\n  aspect-ratio: 1;\n  user-select: none;\n}\n\n.sp-layer {\n  position: absolute;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  border-radius: 6px;\n  font-size: 0.72rem;\n  font-weight: 700;\n  letter-spacing: 0.06em;\n  text-transform: uppercase;\n}\n\n#sp-margin-layer {\n  inset: 0;\n  background: #fde8cc;\n  border: 2px dashed #e8a45a;\n  color: #c47a20;\n}\n\n#sp-padding-layer {\n  background: #cce8fd;\n  border: 2px solid #5aade8;\n  color: #1a6fad;\n}\n\n#sp-content-layer {\n  background: #d4f5e0;\n  border: 2px solid #4caf78;\n  color: #2a7a50;\n  font-size: 0.8rem;\n}\n\n.sp-layer-label {\n  position: absolute;\n  pointer-events: none;\n}\n\n#sp-margin-layer \u003e .sp-layer-label { top: 4px; left: 8px; }\n#sp-padding-layer \u003e .sp-layer-label { top: 4px; left: 8px; }\n\n/* Edge handles */\n.sp-edge {\n  position: absolute;\n  background: transparent;\n  cursor: ns-resize;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 0.65rem;\n  font-weight: 700;\n  color: rgba(0,0,0,0.45);\n  z-index: 10;\n  border-radius: 4px;\n  transition: background 0.12s;\n}\n\n.sp-edge:hover { background: rgba(108,99,255,0.12); }\n.sp-edge.horiz { cursor: ew-resize; }\n\n/* Presets */\n#sp-presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 7px;\n  margin-bottom: 18px;\n}\n\n.sp-preset-btn {\n  padding: 5px 12px;\n  border: 1px solid #c0c7d8;\n  border-radius: 6px;\n  background: #fff;\n  font-size: 0.8rem;\n  cursor: pointer;\n  color: #444;\n  transition: background 0.15s, border-color 0.15s, color 0.15s;\n}\n\n.sp-preset-btn:hover {\n  border-color: #6c63ff;\n  color: #6c63ff;\n}\n\n/* Preview */\n#sp-preview-wrap {\n  background: #e8eaf2;\n  border-radius: 10px;\n  padding: 20px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  min-height: 140px;\n  overflow: auto;\n}\n\n#sp-preview-box {\n  background: #fff;\n  border: 2px dashed #aaa;\n  border-radius: 6px;\n  max-width: 100%;\n}\n\n#sp-preview-inner {\n  background: #6c63ff;\n  color: #fff;\n  border-radius: 4px;\n  font-size: 0.82rem;\n  font-weight: 600;\n  text-align: center;\n  white-space: nowrap;\n}\n\n/* CSS Output */\n#sp-output-wrap {\n  margin-top: 20px;\n}\n\n#sp-output-tabs {\n  display: flex;\n  gap: 0;\n  border-bottom: 2px solid #dde1ec;\n  margin-bottom: 0;\n}\n\n.sp-tab-btn {\n  padding: 7px 18px;\n  border: none;\n  background: none;\n  font-size: 0.85rem;\n  font-weight: 600;\n  color: #888;\n  cursor: pointer;\n  border-bottom: 3px solid transparent;\n  margin-bottom: -2px;\n  transition: color 0.15s;\n}\n\n.sp-tab-btn.active {\n  color: #6c63ff;\n  border-bottom-color: #6c63ff;\n}\n\n#sp-output-code {\n  background: #1a1a2e;\n  color: #e0e0f0;\n  border-radius: 0 0 10px 10px;\n  padding: 16px 18px;\n  font-family: \"Fira Code\", \"Courier New\", monospace;\n  font-size: 0.82rem;\n  line-height: 1.7;\n  white-space: pre;\n  overflow-x: auto;\n  min-height: 80px;\n}\n\n#sp-copy-btn {\n  margin-top: 10px;\n  padding: 8px 22px;\n  background: #6c63ff;\n  color: #fff;\n  border: none;\n  border-radius: 7px;\n  font-size: 0.9rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s;\n}\n\n#sp-copy-btn:hover { background: #574fd6; }\n#sp-copy-btn:active { transform: scale(0.97); }\n#sp-copy-btn.copied { background: #2ea86a; }\n\u003c/style\u003e\n\u003c!-- Presets --\u003e\n\u003cdiv style=\"margin-bottom:18px;\"\u003e\n  \u003ch2\u003ePresets\u003c/h2\u003e\n  \u003cdiv id=\"sp-presets\"\u003e\n    \u003cbutton class=\"sp-preset-btn\" data-preset=\"card\"\u003eComfortable Card\u003c/button\u003e\n    \u003cbutton class=\"sp-preset-btn\" data-preset=\"list\"\u003eTight List\u003c/button\u003e\n    \u003cbutton class=\"sp-preset-btn\" data-preset=\"section\"\u003eSection Spacing\u003c/button\u003e\n    \u003cbutton class=\"sp-preset-btn\" data-preset=\"button\"\u003eButton Padding\u003c/button\u003e\n    \u003cbutton class=\"sp-preset-btn\" data-preset=\"input\"\u003eInput Field\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Unit bar --\u003e\n\u003cdiv id=\"sp-unit-bar\"\u003e\n  \u003clabel\u003eUnit:\u003c/label\u003e\n  \u003cbutton class=\"sp-unit-btn active\" data-unit=\"px\"\u003epx\u003c/button\u003e\n  \u003cbutton class=\"sp-unit-btn\" data-unit=\"rem\"\u003erem\u003c/button\u003e\n  \u003cbutton class=\"sp-unit-btn\" data-unit=\"em\"\u003eem\u003c/button\u003e\n  \u003cbutton class=\"sp-unit-btn\" data-unit=\"%\"\u003e%\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Link toggle --\u003e\n\u003cdiv id=\"sp-link-bar\"\u003e\n  \u003clabel class=\"sp-toggle\" for=\"sp-link-toggle\"\u003e\n    \u003cinput type=\"checkbox\" id=\"sp-link-toggle\"\u003e\n    \u003cspan class=\"sp-toggle-track\"\u003e\u003c/span\u003e\n  \u003c/label\u003e\n  \u003clabel for=\"sp-link-toggle\"\u003eLink all sides\u003c/label\u003e\n\u003c/div\u003e\n\u003c!-- Controls + Diagram --\u003e\n\u003cdiv id=\"sp-layout\"\u003e\n  \u003c!-- Left: Margin + Padding controls --\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"sp-panel\"\u003e\n      \u003ch2\u003eMargin\u003c/h2\u003e\n      \u003cdiv class=\"sp-sides-grid\" id=\"sp-margin-controls\"\u003e\u003c/div\u003e\n      \u003chr class=\"sp-divider\"\u003e\n      \u003ch2\u003ePadding\u003c/h2\u003e\n      \u003cdiv class=\"sp-sides-grid\" id=\"sp-padding-controls\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Right: Box model diagram + preview --\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"sp-panel\"\u003e\n      \u003ch2\u003eBox Model\u003c/h2\u003e\n      \u003cdiv id=\"sp-box-diagram\"\u003e\n        \u003c!-- Layers rendered by JS --\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"sp-panel\" style=\"margin-top:14px;\"\u003e\n      \u003ch2\u003eLive Preview\u003c/h2\u003e\n      \u003cdiv id=\"sp-preview-wrap\"\u003e\n        \u003cdiv id=\"sp-preview-box\"\u003e\n          \u003cdiv id=\"sp-preview-inner\"\u003eContent\u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- CSS Output --\u003e\n\u003cdiv id=\"sp-output-wrap\" class=\"sp-panel\"\u003e\n  \u003ch2\u003eCSS Output\u003c/h2\u003e\n  \u003cdiv id=\"sp-output-tabs\"\u003e\n    \u003cbutton class=\"sp-tab-btn active\" data-tab=\"shorthand\"\u003eShorthand\u003c/button\u003e\n    \u003cbutton class=\"sp-tab-btn\" data-tab=\"longhand\"\u003eLonghand\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cpre id=\"sp-output-code\"\u003e\u003c/pre\u003e\n  \u003cbutton id=\"sp-copy-btn\"\u003eCopy CSS\u003c/button\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  // State\n  var state = {\n    unit: \"px\",\n    linked: false,\n    margin: { top: 16, right: 16, bottom: 16, left: 16 },\n    padding: { top: 12, right: 16, bottom: 12, left: 16 },\n    tab: \"shorthand\"\n  };\n\n  var PRESETS = {\n    card:    { margin: {top:0,right:0,bottom:0,left:0},    padding: {top:24,right:24,bottom:24,left:24} },\n    list:    { margin: {top:4,right:0,bottom:4,left:0},     padding: {top:6,right:12,bottom:6,left:12} },\n    section: { margin: {top:48,right:0,bottom:48,left:0},   padding: {top:32,right:24,bottom:32,left:24} },\n    button:  { margin: {top:0,right:8,bottom:0,left:8},     padding: {top:10,right:20,bottom:10,left:20} },\n    input:   { margin: {top:4,right:0,bottom:4,left:0},     padding: {top:8,right:12,bottom:8,left:12} }\n  };\n\n  var MAX_VAL = { px: 120, rem: 8, em: 8, \"%\": 20 };\n  var STEP    = { px: 1,   rem: 0.25, em: 0.25, \"%\": 0.5 };\n\n  // Helpers\n  function fmt(v) {\n    if (state.unit === \"px\" || state.unit === \"%\") return Math.round(v);\n    return parseFloat(v.toFixed(2));\n  }\n\n  function clamp(v) { return Math.max(0, Math.min(v, MAX_VAL[state.unit])); }\n\n  function val2px(v) {\n    if (state.unit === \"px\") return v;\n    if (state.unit === \"rem\" || state.unit === \"em\") return v * 16;\n    return v * 3; // rough % estimate for diagram\n  }\n\n  // Build side controls\n  function buildControls(prop, containerId) {\n    var container = document.getElementById(containerId);\n    container.innerHTML = \"\";\n    [\"top\",\"right\",\"bottom\",\"left\"].forEach(function(side) {\n      var group = document.createElement(\"div\");\n      group.className = \"sp-side-group\";\n\n      var lbl = document.createElement(\"label\");\n      lbl.textContent = side.charAt(0).toUpperCase() + side.slice(1);\n      group.appendChild(lbl);\n\n      var row = document.createElement(\"div\");\n      row.className = \"sp-side-row\";\n\n      var range = document.createElement(\"input\");\n      range.type = \"range\";\n      range.min = 0;\n      range.max = MAX_VAL[state.unit];\n      range.step = STEP[state.unit];\n      range.value = state[prop][side];\n      range.id = \"sp-\" + prop + \"-\" + side + \"-range\";\n\n      var num = document.createElement(\"input\");\n      num.type = \"number\";\n      num.min = 0;\n      num.max = MAX_VAL[state.unit];\n      num.step = STEP[state.unit];\n      num.value = fmt(state[prop][side]);\n      num.id = \"sp-\" + prop + \"-\" + side + \"-num\";\n\n      function onRangeChange() {\n        var v = clamp(parseFloat(range.value) || 0);\n        setSide(prop, side, v);\n      }\n      function onNumChange() {\n        var v = clamp(parseFloat(num.value) || 0);\n        setSide(prop, side, v);\n      }\n\n      range.addEventListener(\"input\", onRangeChange);\n      num.addEventListener(\"input\", onNumChange);\n\n      row.appendChild(range);\n      row.appendChild(num);\n      group.appendChild(row);\n      container.appendChild(group);\n    });\n  }\n\n  function setSide(prop, side, v) {\n    if (state.linked) {\n      [\"top\",\"right\",\"bottom\",\"left\"].forEach(function(s) {\n        state[prop][s] = v;\n      });\n      // also mirror to the other prop if linked\n    } else {\n      state[prop][side] = v;\n    }\n    syncInputs(prop);\n    render();\n  }\n\n  function syncInputs(prop) {\n    [\"top\",\"right\",\"bottom\",\"left\"].forEach(function(side) {\n      var r = document.getElementById(\"sp-\" + prop + \"-\" + side + \"-range\");\n      var n = document.getElementById(\"sp-\" + prop + \"-\" + side + \"-num\");\n      if (r) { r.max = MAX_VAL[state.unit]; r.step = STEP[state.unit]; r.value = state[prop][side]; }\n      if (n) { n.max = MAX_VAL[state.unit]; n.step = STEP[state.unit]; n.value = fmt(state[prop][side]); }\n    });\n  }\n\n  // Box model diagram\n  function renderDiagram() {\n    var diagram = document.getElementById(\"sp-box-diagram\");\n    var W = diagram.offsetWidth || 300;\n    var H = W; // square\n\n    // px values for diagram\n    var m = {}, p = {};\n    [\"top\",\"right\",\"bottom\",\"left\"].forEach(function(s) {\n      m[s] = Math.min(val2px(state.margin[s]), W * 0.22);\n      p[s] = Math.min(val2px(state.padding[s]), W * 0.18);\n    });\n\n    diagram.innerHTML = \"\";\n\n    // Margin layer\n    var mLayer = document.createElement(\"div\");\n    mLayer.className = \"sp-layer\";\n    mLayer.id = \"sp-margin-layer\";\n    Object.assign(mLayer.style, {\n      top: \"0\", left: \"0\", right: \"0\", bottom: \"0\"\n    });\n    var mLbl = document.createElement(\"span\");\n    mLbl.className = \"sp-layer-label\";\n    mLbl.textContent = \"Margin\";\n    mLayer.appendChild(mLbl);\n    diagram.appendChild(mLayer);\n\n    // Padding layer (inside margin)\n    var pLayer = document.createElement(\"div\");\n    pLayer.className = \"sp-layer\";\n    pLayer.id = \"sp-padding-layer\";\n    Object.assign(pLayer.style, {\n      top: m.top + \"px\",\n      right: m.right + \"px\",\n      bottom: m.bottom + \"px\",\n      left: m.left + \"px\"\n    });\n    var pLbl = document.createElement(\"span\");\n    pLbl.className = \"sp-layer-label\";\n    pLbl.textContent = \"Padding\";\n    pLayer.appendChild(pLbl);\n    diagram.appendChild(pLayer);\n\n    // Content layer (inside padding)\n    var cLayer = document.createElement(\"div\");\n    cLayer.className = \"sp-layer\";\n    cLayer.id = \"sp-content-layer\";\n    Object.assign(cLayer.style, {\n      top: (m.top + p.top) + \"px\",\n      right: (m.right + p.right) + \"px\",\n      bottom: (m.bottom + p.bottom) + \"px\",\n      left: (m.left + p.left) + \"px\"\n    });\n    cLayer.textContent = \"Content\";\n    diagram.appendChild(cLayer);\n\n    // Edge labels\n    var edges = [\n      { prop:\"margin\", side:\"top\",    style:{ top: \"2px\", left:\"50%\", transform:\"translateX(-50%)\" } },\n      { prop:\"margin\", side:\"bottom\", style:{ bottom:\"2px\", left:\"50%\", transform:\"translateX(-50%)\" } },\n      { prop:\"margin\", side:\"left\",   style:{ left:\"2px\", top:\"50%\", transform:\"translateY(-50%)\" } },\n      { prop:\"margin\", side:\"right\",  style:{ right:\"2px\", top:\"50%\", transform:\"translateY(-50%)\" } },\n      { prop:\"padding\", side:\"top\",    layer: pLayer, style:{ top: \"2px\", left:\"50%\", transform:\"translateX(-50%)\" } },\n      { prop:\"padding\", side:\"bottom\", layer: pLayer, style:{ bottom:\"2px\", left:\"50%\", transform:\"translateX(-50%)\" } },\n      { prop:\"padding\", side:\"left\",   layer: pLayer, style:{ left:\"2px\", top:\"50%\", transform:\"translateY(-50%)\" } },\n      { prop:\"padding\", side:\"right\",  layer: pLayer, style:{ right:\"2px\", top:\"50%\", transform:\"translateY(-50%)\" } },\n    ];\n\n    edges.forEach(function(e) {\n      var el = document.createElement(\"span\");\n      el.style.position = \"absolute\";\n      el.style.fontSize = \"0.62rem\";\n      el.style.fontWeight = \"700\";\n      el.style.color = \"rgba(0,0,0,0.5)\";\n      el.style.pointerEvents = \"none\";\n      el.style.whiteSpace = \"nowrap\";\n      Object.assign(el.style, e.style);\n      el.textContent = fmt(state[e.prop][e.side]) + state.unit;\n      (e.layer || mLayer).appendChild(el);\n    });\n  }\n\n  // Preview\n  function renderPreview() {\n    var box = document.getElementById(\"sp-preview-box\");\n    var inner = document.getElementById(\"sp-preview-inner\");\n    function u(v) { return fmt(v) + state.unit; }\n    box.style.margin = u(state.margin.top) + \" \" + u(state.margin.right) + \" \" + u(state.margin.bottom) + \" \" + u(state.margin.left);\n    inner.style.padding = u(state.padding.top) + \" \" + u(state.padding.right) + \" \" + u(state.padding.bottom) + \" \" + u(state.padding.left);\n  }\n\n  // CSS output\n  function shorthand(prop, obj) {\n    function u(v) { return fmt(v) + state.unit; }\n    var t = u(obj.top), r = u(obj.right), b = u(obj.bottom), l = u(obj.left);\n    if (t===r \u0026\u0026 r===b \u0026\u0026 b===l) return prop + \": \" + t + \";\";\n    if (t===b \u0026\u0026 r===l) return prop + \": \" + t + \" \" + r + \";\";\n    if (r===l) return prop + \": \" + t + \" \" + r + \" \" + b + \";\";\n    return prop + \": \" + t + \" \" + r + \" \" + b + \" \" + l + \";\";\n  }\n\n  function longhand(prop, obj) {\n    function u(v) { return fmt(v) + state.unit; }\n    return [\n      prop + \"-top: \" + u(obj.top) + \";\",\n      prop + \"-right: \" + u(obj.right) + \";\",\n      prop + \"-bottom: \" + u(obj.bottom) + \";\",\n      prop + \"-left: \" + u(obj.left) + \";\"\n    ].join(\"\\n\");\n  }\n\n  function renderOutput() {\n    var code = document.getElementById(\"sp-output-code\");\n    if (state.tab === \"shorthand\") {\n      code.textContent = shorthand(\"margin\", state.margin) + \"\\n\" + shorthand(\"padding\", state.padding);\n    } else {\n      code.textContent = longhand(\"margin\", state.margin) + \"\\n\" + longhand(\"padding\", state.padding);\n    }\n  }\n\n  function render() {\n    renderDiagram();\n    renderPreview();\n    renderOutput();\n  }\n\n  // Init controls\n  function initControls() {\n    buildControls(\"margin\", \"sp-margin-controls\");\n    buildControls(\"padding\", \"sp-padding-controls\");\n  }\n\n  // Unit buttons\n  document.querySelectorAll(\".sp-unit-btn\").forEach(function(btn) {\n    btn.addEventListener(\"click\", function() {\n      document.querySelectorAll(\".sp-unit-btn\").forEach(function(b){ b.classList.remove(\"active\"); });\n      btn.classList.add(\"active\");\n      state.unit = btn.dataset.unit;\n      // Convert values to new unit (approximate: keep logical ratios via px base)\n      initControls();\n      syncInputs(\"margin\");\n      syncInputs(\"padding\");\n      render();\n    });\n  });\n\n  // Link toggle\n  document.getElementById(\"sp-link-toggle\").addEventListener(\"change\", function() {\n    state.linked = this.checked;\n  });\n\n  // Presets\n  document.querySelectorAll(\".sp-preset-btn\").forEach(function(btn) {\n    btn.addEventListener(\"click\", function() {\n      var p = PRESETS[btn.dataset.preset];\n      // Copy values; if unit is not px, scale down\n      var scale = state.unit === \"px\" ? 1 : state.unit === \"rem\" || state.unit === \"em\" ? 1/16 : 1/5;\n      [\"top\",\"right\",\"bottom\",\"left\"].forEach(function(s) {\n        state.margin[s] = clamp(p.margin[s] * (state.unit === \"px\" ? 1 : scale));\n        state.padding[s] = clamp(p.padding[s] * (state.unit === \"px\" ? 1 : scale));\n      });\n      syncInputs(\"margin\");\n      syncInputs(\"padding\");\n      render();\n    });\n  });\n\n  // Tab buttons\n  document.querySelectorAll(\".sp-tab-btn\").forEach(function(btn) {\n    btn.addEventListener(\"click\", function() {\n      document.querySelectorAll(\".sp-tab-btn\").forEach(function(b){ b.classList.remove(\"active\"); });\n      btn.classList.add(\"active\");\n      state.tab = btn.dataset.tab;\n      renderOutput();\n    });\n  });\n\n  // Copy button\n  document.getElementById(\"sp-copy-btn\").addEventListener(\"click\", function() {\n    var text = document.getElementById(\"sp-output-code\").textContent;\n    var btn = this;\n    if (navigator.clipboard) {\n      navigator.clipboard.writeText(text).then(function() {\n        btn.textContent = \"Copied!\";\n        btn.classList.add(\"copied\");\n        setTimeout(function(){ btn.textContent = \"Copy CSS\"; btn.classList.remove(\"copied\"); }, 1600);\n      });\n    } else {\n      var ta = document.createElement(\"textarea\");\n      ta.value = text;\n      ta.style.position = \"fixed\"; ta.style.opacity = \"0\";\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand(\"copy\");\n      document.body.removeChild(ta);\n      btn.textContent = \"Copied!\";\n      btn.classList.add(\"copied\");\n      setTimeout(function(){ btn.textContent = \"Copy CSS\"; btn.classList.remove(\"copied\"); }, 1600);\n    }\n  });\n\n  // Init\n  initControls();\n  render();\n\n  // Redraw diagram on resize\n  var resizeTimer;\n  window.addEventListener(\"resize\", function() {\n    clearTimeout(resizeTimer);\n    resizeTimer = setTimeout(renderDiagram, 120);\n  });\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-to-use-the-css-spacing-generator\"\u003eHow to Use the CSS Spacing Generator\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eChoose a preset\u003c/strong\u003e or set your own values.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSelect a unit\u003c/strong\u003e — px, rem, em, or %.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAdjust margin and padding\u003c/strong\u003e per side using sliders or number inputs.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eEnable \u0026ldquo;Link all sides\u0026rdquo;\u003c/strong\u003e to set all four sides at once.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eWatch the box model diagram and live preview\u003c/strong\u003e update instantly.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ePick Shorthand or Longhand\u003c/strong\u003e output, then click \u003cstrong\u003eCopy CSS\u003c/strong\u003e.\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch2 id=\"box-model-quick-reference\"\u003eBox Model Quick Reference\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eLayer\u003c/th\u003e\n          \u003cth\u003ePurpose\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eMargin\u003c/td\u003e\n          \u003ctd\u003eSpace \u003cstrong\u003eoutside\u003c/strong\u003e the element border\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eBorder\u003c/td\u003e\n          \u003ctd\u003eThe element\u0026rsquo;s border (set separately)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ePadding\u003c/td\u003e\n          \u003ctd\u003eSpace \u003cstrong\u003einside\u003c/strong\u003e the border, around the content\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eContent\u003c/td\u003e\n          \u003ctd\u003eThe actual content area (text, images, etc.)\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003ch2 id=\"css-shorthand-rules\"\u003eCSS Shorthand Rules\u003c/h2\u003e\n\u003cp\u003eCSS uses a clockwise order: \u003cstrong\u003etop → right → bottom → left\u003c/strong\u003e.\u003c/p\u003e","title":"CSS Spacing Generator — Margin \u0026 Padding Tool"},{"content":" CSS Specificity Calculator Enter a CSS selector to calculate its specificity score instantly. Add multiple selectors to compare them side by side.\nCalculate + Compare Specificity Score (a, b, c, d) 0 Inline\nStyles (a) , 0 IDs (b) , 0 Classes,\nAttrs,\nPseudo-classes (c) , 0 Elements,\nPseudo-elements (d) (0, 0, 0, 0) Inline (a) 0 IDs (b) 0 Classes (c) 0 Elements (d) 0 Selector Breakdown Inline style ID selector Class / Attribute / Pseudo-class Element / Pseudo-element Combinator (not counted) Selector Comparison — sorted by specificity No selectors added yet. Enter a selector and click \"+ Compare\". Sort by Specificity Clear All Common Examples — click to calculate How CSS Specificity Works a — Inline Styles Style attributes on elements (style=\"...\"). Highest specificity. Overrides all selectors in stylesheets. b — ID Selectors Selectors using #id. Very high specificity. One ID beats any number of classes or elements. c — Classes, Attributes \u0026amp; Pseudo-classes Class selectors (.class), attribute selectors ([attr]), and pseudo-classes (:hover, :nth-child()). d — Elements \u0026amp; Pseudo-elements Type selectors (div, p) and pseudo-elements (::before, ::after). Lowest specificity. Related Tools Build grid layouts visually → CSS Grid Generator Generate flexbox layouts → CSS Flexbox Generator ","permalink":"https://productivity-works.com/tools/css-specificity-calculator/","summary":"\u003cdiv id=\"cs-app\"\u003e\n\u003cstyle\u003e\n  #cs-app *,\n  #cs-app *::before,\n  #cs-app *::after {\n    box-sizing: border-box;\n    margin: 0;\n    padding: 0;\n  }\n\u003cp\u003e#cs-app {\nfont-family: -apple-system, BlinkMacSystemFont, \u0026ldquo;Segoe UI\u0026rdquo;, Roboto, sans-serif;\nfont-size: 14px;\ncolor: #1a1a2e;\nbackground: #f8f9ff;\nborder-radius: 12px;\npadding: 24px;\nmax-width: 100%;\noverflow: hidden;\n}\u003c/p\u003e\n\u003cp\u003e#cs-app h2.cs-title {\nfont-size: 1.4rem;\nfont-weight: 700;\nmargin-bottom: 4px;\ncolor: #1a1a2e;\n}\u003c/p\u003e\n\u003cp\u003e#cs-app p.cs-subtitle {\ncolor: #666;\nmargin-bottom: 20px;\nfont-size: 0.9rem;\n}\u003c/p\u003e\n\u003cp\u003e/* Input area */\n#cs-app .cs-input-row {\ndisplay: flex;\ngap: 8px;\nmargin-bottom: 16px;\nalign-items: stretch;\n}\u003c/p\u003e","title":"CSS Specificity Calculator"},{"content":" Gradient Text Presets \u0026lt;!-- Text input --\u0026gt; \u0026lt;div class=\u0026quot;tg-field tg-full\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;tg-label\u0026quot;\u0026gt;Preview Text\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;tg-text-input\u0026quot; value=\u0026quot;Gradient Text\u0026quot; placeholder=\u0026quot;Type your text...\u0026quot; maxlength=\u0026quot;120\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Gradient type --\u0026gt; \u0026lt;div class=\u0026quot;tg-field\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;tg-label\u0026quot;\u0026gt;Gradient Type\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;tg-type-group\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;tg-type-btn active\u0026quot; data-type=\u0026quot;linear\u0026quot;\u0026gt;Linear\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;tg-type-btn\u0026quot; data-type=\u0026quot;radial\u0026quot;\u0026gt;Radial\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Angle (linear) --\u0026gt; \u0026lt;div class=\u0026quot;tg-field\u0026quot; id=\u0026quot;tg-angle-field\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;tg-label\u0026quot;\u0026gt;Angle\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;tg-range-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;tg-angle\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;360\u0026quot; value=\u0026quot;135\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;tg-range-val\u0026quot; id=\u0026quot;tg-angle-val\u0026quot;\u0026gt;135deg\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Color stops --\u0026gt; \u0026lt;div class=\u0026quot;tg-field tg-full\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;tg-label\u0026quot;\u0026gt;Color Stops (2–4)\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;tg-stops-wrap\u0026quot; id=\u0026quot;tg-stops-wrap\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;tg-add-stop-btn\u0026quot; id=\u0026quot;tg-add-stop\u0026quot;\u0026gt;+ Add Color Stop\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Font size --\u0026gt; \u0026lt;div class=\u0026quot;tg-field\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;tg-label\u0026quot;\u0026gt;Font Size\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;tg-range-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;tg-font-size\u0026quot; min=\u0026quot;24\u0026quot; max=\u0026quot;120\u0026quot; value=\u0026quot;64\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;tg-range-val\u0026quot; id=\u0026quot;tg-font-size-val\u0026quot;\u0026gt;64px\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Font weight --\u0026gt; \u0026lt;div class=\u0026quot;tg-field\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;tg-label\u0026quot;\u0026gt;Font Weight\u0026lt;/span\u0026gt; \u0026lt;select id=\u0026quot;tg-font-weight\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;300\u0026quot;\u0026gt;300 — Light\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;400\u0026quot;\u0026gt;400 — Regular\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;600\u0026quot;\u0026gt;600 — Semibold\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;700\u0026quot;\u0026gt;700 — Bold\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;800\u0026quot; selected\u0026gt;800 — Extrabold\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;900\u0026quot;\u0026gt;900 — Black\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Text align --\u0026gt; \u0026lt;div class=\u0026quot;tg-field tg-full\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;tg-label\u0026quot;\u0026gt;Text Align\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;tg-align-group\u0026quot; id=\u0026quot;tg-align-group\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;tg-align-btn active\u0026quot; data-align=\u0026quot;left\u0026quot; title=\u0026quot;Left\u0026quot;\u0026gt;\u0026amp;#9664;\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;tg-align-btn\u0026quot; data-align=\u0026quot;center\u0026quot; title=\u0026quot;Center\u0026quot;\u0026gt;\u0026amp;#9644;\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;tg-align-btn\u0026quot; data-align=\u0026quot;right\u0026quot; title=\u0026quot;Right\u0026quot;\u0026gt;\u0026amp;#9654;\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Generated CSS Copy CSS How It Works CSS text gradients use the background-clip: text technique:\nApply a gradient to the element\u0026rsquo;s background property. Clip the background to the text shape with -webkit-background-clip: text and background-clip: text. Make the text itself transparent with -webkit-text-fill-color: transparent. The result is gradient-painted characters with no SVG or images required — pure CSS.\nBrowser Compatibility Browser Support Chrome / Edge Full Firefox Full (Firefox 122+) Safari Full (-webkit- prefix required) iOS Safari Full Tips for Best Results High contrast presets (Sunset, Fire, Neon) work best on dark backgrounds. Pastel gradients suit light-mode body text at large sizes. Always include both -webkit-background-clip: text and background-clip: text for cross-browser safety. Add display: inline-block so the gradient clips correctly; block elements expand to full width and may show extra gradient area. Related Tools Build background gradients → CSS Gradient Generator Generate color palettes → Color Palette Generator Create box shadows → CSS Box Shadow Generator ","permalink":"https://productivity-works.com/tools/text-gradient-generator/","summary":"\u003cdiv id=\"tg-app\"\u003e\n\u003cstyle\u003e\n/* ── Scoped styles: all selectors prefixed with #tg-app ── */\n#tg-app *,\n#tg-app *::before,\n#tg-app *::after {\n  box-sizing: border-box;\n}\n#tg-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n\u003cp\u003e/* Preview */\n#tg-app .tg-preview-wrap {\nbackground: #f0f2f8;\nborder-radius: 14px;\npadding: 40px 24px;\nmargin-bottom: 28px;\nmin-height: 180px;\ndisplay: flex;\nalign-items: center;\njustify-content: center;\noverflow: hidden;\n}\n#tg-app #tg-preview-text {\ndisplay: inline-block;\nbackground: linear-gradient(135deg, #f5af19, #f12711);\n-webkit-background-clip: text;\nbackground-clip: text;\n-webkit-text-fill-color: transparent;\ncolor: transparent;\nfont-size: 64px;\nfont-weight: 800;\nline-height: 1.2;\nword-break: break-word;\ntransition: all 0.2s ease;\n}\u003c/p\u003e","title":"CSS Text Gradient Generator — Free Tool"},{"content":"Generate text-shadow CSS instantly — add multiple shadow layers, pick from presets, preview live, and copy the CSS with one click.\nLive Preview Text Shadow Text: Color: Size: 52px BG: Presets Shadow Layers + Add Layer Generated CSS Copy How to use Add layers — click \u0026ldquo;Add Layer\u0026rdquo; or choose a preset to load shadow layers instantly. Adjust each layer — slide Horizontal, Vertical, Blur, Color, and Opacity controls. Customize preview — change the preview text, its color, font size, and background. Copy CSS — click \u0026ldquo;Copy\u0026rdquo; and paste the text-shadow rule into your stylesheet. Presets explained Preset Effect Neon Glow Multi-layered glow using identical hue with expanding blur Emboss Light + dark shadows to create a pressed/raised look Retro Hard stacked shadows giving a vintage print feel Fire Upward blur layers from yellow to deep red 3D Stepped solid shadows building depth and dimension Outline Four single-pixel shadows forming a clean text stroke CSS text-shadow syntax text-shadow: h-offset v-offset blur color, /* layer 1 */ h-offset v-offset blur color; /* layer 2 */ h-offset — horizontal shift (negative = left, positive = right) v-offset — vertical shift (negative = up, positive = down) blur — blur radius in px (0 = sharp) color — any valid CSS color including rgba() for transparency Multiple shadows are comma-separated and stack front-to-back.\nRelated tools Box Shadow Generator — Generate box-shadow CSS for elements and cards Text Gradient Generator — Create background-clip text gradients with live preview ","permalink":"https://productivity-works.com/tools/text-shadow-generator/","summary":"\u003cp\u003eGenerate \u003ccode\u003etext-shadow\u003c/code\u003e CSS instantly — add multiple shadow layers, pick from presets, preview live, and copy the CSS with one click.\u003c/p\u003e\n\u003chr\u003e\n\u003cdiv id=\"ts-app\"\u003e\n\u003cstyle\u003e\n#ts-app *,#ts-app *::before,#ts-app *::after{box-sizing:border-box;margin:0;padding:0}\n#ts-app{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;font-size:14px;color:#1e293b;max-width:860px}\n#ts-app h2{font-size:15px;font-weight:700;margin-bottom:10px;color:#0f172a}\n#ts-app .ts-grid{display:grid;grid-template-columns:1fr 1fr;gap:20px}\n@media(max-width:640px){#ts-app .ts-grid{grid-template-columns:1fr}}\n#ts-app .ts-panel{background:#f8fafc;border:1.5px solid #e2e8f0;border-radius:10px;padding:16px}\n#ts-app .ts-preview-wrap{background:#1e293b;border-radius:10px;padding:30px 20px;text-align:center;min-height:140px;display:flex;align-items:center;justify-content:center;margin-bottom:16px;overflow:hidden;position:relative}\n#ts-app #ts-preview-text{font-weight:700;white-space:nowrap;transition:text-shadow .15s,color .15s,font-size .15s}\n#ts-app .ts-ctrl-row{display:flex;align-items:center;gap:8px;margin-bottom:10px;flex-wrap:wrap}\n#ts-app .ts-ctrl-row label{min-width:90px;font-size:13px;color:#475569}\n#ts-app input[type=range]{flex:1;min-width:80px;accent-color:#6366f1}\n#ts-app input[type=color]{width:36px;height:28px;border:1.5px solid #cbd5e1;border-radius:5px;cursor:pointer;padding:1px}\n#ts-app input[type=text]{border:1.5px solid #cbd5e1;border-radius:6px;padding:5px 8px;font-size:13px;width:100%;background:#fff}\n#ts-app select{border:1.5px solid #cbd5e1;border-radius:6px;padding:5px 8px;font-size:13px;background:#fff;cursor:pointer}\n#ts-app .ts-val{min-width:36px;text-align:right;font-size:12px;color:#64748b}\n#ts-app .ts-btn{display:inline-flex;align-items:center;gap:6px;padding:8px 16px;border-radius:7px;font-size:13px;font-weight:600;border:none;cursor:pointer;transition:opacity .15s}\n#ts-app .ts-btn-primary{background:#6366f1;color:#fff}\n#ts-app .ts-btn-primary:hover{opacity:.88}\n#ts-app .ts-btn-sm{padding:5px 11px;font-size:12px;background:#fff;border:1.5px solid #cbd5e1;color:#374151;border-radius:6px}\n#ts-app .ts-btn-sm:hover{background:#f1f5f9}\n#ts-app .ts-btn-danger{background:#fff;border:1.5px solid #fca5a5;color:#dc2626;padding:5px 10px;font-size:12px;border-radius:6px}\n#ts-app .ts-btn-danger:hover{background:#fef2f2}\n#ts-app .ts-layers{display:flex;flex-direction:column;gap:8px;margin-bottom:12px}\n#ts-app .ts-layer-card{background:#fff;border:1.5px solid #e2e8f0;border-radius:8px;padding:12px}\n#ts-app .ts-layer-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}\n#ts-app .ts-layer-title{font-size:13px;font-weight:600;color:#334155}\n#ts-app .ts-presets{display:flex;flex-wrap:wrap;gap:7px;margin-bottom:14px}\n#ts-app .ts-preset-btn{padding:6px 13px;border-radius:20px;border:1.5px solid #cbd5e1;background:#fff;font-size:12px;font-weight:600;cursor:pointer;transition:border-color .15s,background .15s}\n#ts-app .ts-preset-btn:hover{border-color:#6366f1;background:#eef2ff}\n#ts-app .ts-code-wrap{position:relative;background:#0f172a;border-radius:8px;padding:14px 14px 14px 14px;margin-top:4px}\n#ts-app .ts-code{color:#7dd3fc;font-family:'Courier New',monospace;font-size:12.5px;word-break:break-all;white-space:pre-wrap;line-height:1.6}\n#ts-app .ts-copy-btn{position:absolute;top:10px;right:10px;padding:5px 12px;background:#6366f1;color:#fff;border:none;border-radius:6px;font-size:12px;font-weight:600;cursor:pointer}\n#ts-app .ts-copy-btn:hover{background:#4f46e5}\n#ts-app .ts-copy-btn.copied{background:#10b981}\n#ts-app .ts-preview-controls{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px;align-items:center}\n#ts-app .ts-preview-controls label{font-size:13px;color:#475569}\n#ts-app .ts-sep{border:none;border-top:1.5px solid #e2e8f0;margin:14px 0}\n#ts-app .ts-add-row{display:flex;gap:8px;flex-wrap:wrap;align-items:center}\n\u003c/style\u003e\n\u003c!-- Preview Controls --\u003e\n\u003cdiv class=\"ts-panel\" style=\"margin-bottom:16px\"\u003e\n  \u003ch2\u003eLive Preview\u003c/h2\u003e\n  \u003cdiv class=\"ts-preview-wrap\" id=\"ts-preview-bg\"\u003e\n    \u003cspan id=\"ts-preview-text\"\u003eText Shadow\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ts-preview-controls\"\u003e\n    \u003clabel\u003eText:\u003c/label\u003e\n    \u003cinput type=\"text\" id=\"ts-input-text\" value=\"Text Shadow\" style=\"width:160px\"\u003e\n    \u003clabel\u003eColor:\u003c/label\u003e\n    \u003cinput type=\"color\" id=\"ts-input-textcolor\" value=\"#ffffff\"\u003e\n    \u003clabel\u003eSize:\u003c/label\u003e\n    \u003cinput type=\"range\" id=\"ts-input-fontsize\" min=\"16\" max=\"96\" value=\"52\" style=\"width:100px\"\u003e\n    \u003cspan class=\"ts-val\" id=\"ts-fontsize-val\"\u003e52px\u003c/span\u003e\n    \u003clabel\u003eBG:\u003c/label\u003e\n    \u003cinput type=\"color\" id=\"ts-input-bg\" value=\"#1e293b\"\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Presets --\u003e\n\u003cdiv class=\"ts-panel\" style=\"margin-bottom:16px\"\u003e\n  \u003ch2\u003ePresets\u003c/h2\u003e\n  \u003cdiv class=\"ts-presets\" id=\"ts-presets\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Shadow Layers + CSS Output --\u003e\n\u003cdiv class=\"ts-grid\"\u003e\n  \u003cdiv class=\"ts-panel\"\u003e\n    \u003ch2\u003eShadow Layers\u003c/h2\u003e\n    \u003cdiv class=\"ts-layers\" id=\"ts-layers\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"ts-add-row\"\u003e\n      \u003cbutton class=\"ts-btn ts-btn-primary\" onclick=\"tsAddLayer()\"\u003e+ Add Layer\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ts-panel\"\u003e\n    \u003ch2\u003eGenerated CSS\u003c/h2\u003e\n    \u003cdiv class=\"ts-code-wrap\"\u003e\n      \u003cpre class=\"ts-code\" id=\"ts-css-out\"\u003e\u003c/pre\u003e\n      \u003cbutton class=\"ts-copy-btn\" id=\"ts-copy-btn\" onclick=\"tsCopy()\"\u003eCopy\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n  var layers=[];\n  var lid=0;\n\n  var PRESETS=[\n    {name:\"Neon Glow\",layers:[\n      {h:0,v:0,b:4,c:\"#ff00ff\",a:1},\n      {h:0,v:0,b:16,c:\"#ff00ff\",a:.8},\n      {h:0,v:0,b:32,c:\"#ff00ff\",a:.4}\n    ],bg:\"#0a0a1a\",tc:\"#ff00ff\",fs:52},\n    {name:\"Emboss\",layers:[\n      {h:-2,v:-2,b:4,c:\"#ffffff\",a:.9},\n      {h:2,v:2,b:4,c:\"#000000\",a:.4}\n    ],bg:\"#c0c8d8\",tc:\"#c0c8d8\",fs:52},\n    {name:\"Retro\",layers:[\n      {h:3,v:3,b:0,c:\"#f97316\",a:1},\n      {h:6,v:6,b:0,c:\"#ef4444\",a:1}\n    ],bg:\"#fef9c3\",tc:\"#1e293b\",fs:52},\n    {name:\"Fire\",layers:[\n      {h:0,v:-4,b:8,c:\"#fbbf24\",a:1},\n      {h:0,v:-8,b:16,c:\"#f97316\",a:.9},\n      {h:0,v:-16,b:24,c:\"#ef4444\",a:.6},\n      {h:0,v:-24,b:32,c:\"#dc2626\",a:.3}\n    ],bg:\"#18181b\",tc:\"#fef9c3\",fs:52},\n    {name:\"3D\",layers:[\n      {h:1,v:1,b:0,c:\"#475569\",a:1},\n      {h:2,v:2,b:0,c:\"#64748b\",a:1},\n      {h:3,v:3,b:0,c:\"#94a3b8\",a:1},\n      {h:4,v:4,b:0,c:\"#cbd5e1\",a:1}\n    ],bg:\"#1e293b\",tc:\"#f8fafc\",fs:52},\n    {name:\"Outline\",layers:[\n      {h:1,v:0,b:0,c:\"#000000\",a:1},\n      {h:-1,v:0,b:0,c:\"#000000\",a:1},\n      {h:0,v:1,b:0,c:\"#000000\",a:1},\n      {h:0,v:-1,b:0,c:\"#000000\",a:1}\n    ],bg:\"#f8fafc\",tc:\"#6366f1\",fs:52}\n  ];\n\n  function hexToRgba(hex,a){\n    var r=parseInt(hex.slice(1,3),16),g=parseInt(hex.slice(3,5),16),b=parseInt(hex.slice(5,7),16);\n    return a\u003c1?'rgba('+r+','+g+','+b+','+a+')':'rgb('+r+','+g+','+b+')';\n  }\n\n  function makeCss(){\n    if(!layers.length)return'text-shadow: none;';\n    var parts=layers.map(function(l){\n      return l.h+'px '+l.v+'px '+l.b+'px '+hexToRgba(l.c,l.a);\n    });\n    return'text-shadow:\\n  '+parts.join(',\\n  ')+';';\n  }\n\n  function applyPreview(){\n    var text=document.getElementById('ts-input-text').value||'Text Shadow';\n    var tc=document.getElementById('ts-input-textcolor').value;\n    var fs=document.getElementById('ts-input-fontsize').value;\n    var bg=document.getElementById('ts-input-bg').value;\n    document.getElementById('ts-fontsize-val').textContent=fs+'px';\n    var el=document.getElementById('ts-preview-text');\n    el.textContent=text;\n    el.style.color=tc;\n    el.style.fontSize=fs+'px';\n    el.style.textShadow=layers.map(function(l){\n      return l.h+'px '+l.v+'px '+l.b+'px '+hexToRgba(l.c,l.a);\n    }).join(', ')||'none';\n    document.getElementById('ts-preview-bg').style.background=bg;\n    document.getElementById('ts-css-out').textContent=makeCss();\n  }\n\n  function renderLayers(){\n    var cont=document.getElementById('ts-layers');\n    cont.innerHTML='';\n    if(!layers.length){\n      cont.innerHTML='\u003cp style=\"color:#94a3b8;font-size:13px;text-align:center;padding:16px 0;\"\u003eNo layers yet. Add one or pick a preset.\u003c/p\u003e","title":"CSS Text Shadow Generator"},{"content":" What Is CSS Transform? The transform property lets you visually move, rotate, resize, and skew elements without disrupting document flow. Transforms are GPU-accelerated and animation-friendly — a cornerstone of modern UI effects.\nCSS Transform Generator Use the sliders below to compose a transform in real time. Hit Copy CSS when you are ready.\nPresets:\nFlip Tilt Zoom Rotate 3D Reset Translate Translate X 0px Translate Y 0px \u0026lt;div class=\u0026quot;ctg-section-title\u0026quot;\u0026gt;Rotate\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ctg-row\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;ctg-rot\u0026quot;\u0026gt;Rotate\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;ctg-rot\u0026quot; min=\u0026quot;-360\u0026quot; max=\u0026quot;360\u0026quot; value=\u0026quot;0\u0026quot; step=\u0026quot;1\u0026quot; oninput=\u0026quot;ctgUpdate()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ctg-val\u0026quot; id=\u0026quot;ctg-rot-val\u0026quot;\u0026gt;0deg\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ctg-section-title\u0026quot;\u0026gt;Scale\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ctg-row\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;ctg-sx\u0026quot;\u0026gt;Scale X\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;ctg-sx\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;3\u0026quot; value=\u0026quot;1\u0026quot; step=\u0026quot;0.01\u0026quot; oninput=\u0026quot;ctgUpdate()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ctg-val\u0026quot; id=\u0026quot;ctg-sx-val\u0026quot;\u0026gt;1.00\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ctg-row\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;ctg-sy\u0026quot;\u0026gt;Scale Y\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;ctg-sy\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;3\u0026quot; value=\u0026quot;1\u0026quot; step=\u0026quot;0.01\u0026quot; oninput=\u0026quot;ctgUpdate()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ctg-val\u0026quot; id=\u0026quot;ctg-sy-val\u0026quot;\u0026gt;1.00\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ctg-section-title\u0026quot;\u0026gt;Skew\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ctg-row\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;ctg-skx\u0026quot;\u0026gt;Skew X\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;ctg-skx\u0026quot; min=\u0026quot;-60\u0026quot; max=\u0026quot;60\u0026quot; value=\u0026quot;0\u0026quot; step=\u0026quot;1\u0026quot; oninput=\u0026quot;ctgUpdate()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ctg-val\u0026quot; id=\u0026quot;ctg-skx-val\u0026quot;\u0026gt;0deg\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ctg-row\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;ctg-sky\u0026quot;\u0026gt;Skew Y\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;ctg-sky\u0026quot; min=\u0026quot;-60\u0026quot; max=\u0026quot;60\u0026quot; value=\u0026quot;0\u0026quot; step=\u0026quot;1\u0026quot; oninput=\u0026quot;ctgUpdate()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ctg-val\u0026quot; id=\u0026quot;ctg-sky-val\u0026quot;\u0026gt;0deg\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ctg-section-title\u0026quot;\u0026gt;Perspective\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ctg-perspective-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;ctg-persp-en\u0026quot; onchange=\u0026quot;ctgUpdate()\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;ctg-persp-en\u0026quot;\u0026gt;Enable perspective\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ctg-row\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;ctg-persp\u0026quot;\u0026gt;Perspective\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;ctg-persp\u0026quot; min=\u0026quot;100\u0026quot; max=\u0026quot;2000\u0026quot; value=\u0026quot;600\u0026quot; step=\u0026quot;10\u0026quot; oninput=\u0026quot;ctgUpdate()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ctg-val\u0026quot; id=\u0026quot;ctg-persp-val\u0026quot;\u0026gt;600px\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ctg-preview-panel\u0026quot;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div class=\u0026quot;ctg-preview-label\u0026quot;\u0026gt;Live Preview\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ctg-preview-box\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ctg-subject\u0026quot; id=\u0026quot;ctg-subject\u0026quot;\u0026gt;Box\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div class=\u0026quot;ctg-preview-label\u0026quot;\u0026gt;Generated CSS\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ctg-output\u0026quot; id=\u0026quot;ctg-output\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;ctg-prop\u0026quot;\u0026gt;transform\u0026lt;/span\u0026gt;: \u0026lt;span class=\u0026quot;ctg-val-text\u0026quot;\u0026gt;none\u0026lt;/span\u0026gt;; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ctg-btn-row\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;ctg-btn ctg-btn-primary\u0026quot; onclick=\u0026quot;ctgCopy()\u0026quot;\u0026gt;Copy CSS\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;ctg-btn ctg-btn-secondary\u0026quot; onclick=\u0026quot;ctgApplyPreset('reset')\u0026quot;\u0026gt;Reset All\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ctg-copy-notice\u0026quot; id=\u0026quot;ctg-copy-notice\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Transform Functions Explained Function Syntax Description translate() translate(x, y) Move element along X and Y axes rotate() rotate(deg) Rotate clockwise (positive) or counter-clockwise (negative) scale() scale(x, y) Resize element; 1 = original, -1 = flip skew() skew(xdeg, ydeg) Tilt element along each axis perspective() perspective(px) Add 3D depth for child transforms Multiple Transforms — Order Matters Transforms are applied right to left. translate(50px, 0) rotate(45deg) first rotates then moves, producing a different result than the reversed order.\nPerformance Tips Use transform (and opacity) for animations — they skip layout and paint steps. Add will-change: transform on elements that animate frequently. Avoid transform on elements that are rarely updated — will-change reserves GPU memory. 3D Transforms Quick Reference /* Parent sets the 3D space */ .container { perspective: 600px; } /* Child uses 3D functions */ .card { transform: rotateY(30deg) rotateX(10deg); transform-style: preserve-3d; backface-visibility: hidden; } Related Tools CSS Animation Generator — Build keyframe animations visually CSS Flexbox Generator — Design flex layouts with live preview CSS Box Shadow Generator — Craft multi-layer shadows interactively ","permalink":"https://productivity-works.com/tools/css-transform-generator/","summary":"\u003cdiv id=\"css-transform-gen-root\"\u003e\n\u003cstyle\u003e\n#css-transform-gen-root {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#css-transform-gen-root *,\n#css-transform-gen-root *::before,\n#css-transform-gen-root *::after {\n  box-sizing: border-box;\n}\n#css-transform-gen-root .ctg-layout {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 24px;\n  margin-bottom: 24px;\n}\n@media (max-width: 640px) {\n  #css-transform-gen-root .ctg-layout {\n    grid-template-columns: 1fr;\n  }\n}\n#css-transform-gen-root .ctg-controls {\n  background: #f8f9ff;\n  border: 1px solid #dde1f0;\n  border-radius: 12px;\n  padding: 20px;\n}\n#css-transform-gen-root .ctg-preview-panel {\n  display: flex;\n  flex-direction: column;\n  gap: 16px;\n}\n#css-transform-gen-root .ctg-preview-box {\n  background: #f0f4ff;\n  border: 1px solid #dde1f0;\n  border-radius: 12px;\n  min-height: 280px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  overflow: hidden;\n  position: relative;\n}\n#css-transform-gen-root .ctg-preview-label {\n  font-size: 11px;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #7b82a6;\n  margin-bottom: 12px;\n}\n#css-transform-gen-root .ctg-subject {\n  width: 100px;\n  height: 100px;\n  background: linear-gradient(135deg, #4f6ef7 0%, #7c3aed 100%);\n  border-radius: 8px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: #fff;\n  font-size: 13px;\n  font-weight: 600;\n  transition: transform 0.15s ease;\n  will-change: transform;\n  user-select: none;\n}\n#css-transform-gen-root .ctg-section-title {\n  font-size: 13px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  color: #4f6ef7;\n  margin: 16px 0 10px;\n  padding-bottom: 4px;\n  border-bottom: 1px solid #e2e6fb;\n}\n#css-transform-gen-root .ctg-section-title:first-child {\n  margin-top: 0;\n}\n#css-transform-gen-root .ctg-row {\n  display: grid;\n  grid-template-columns: 110px 1fr 52px;\n  align-items: center;\n  gap: 8px;\n  margin-bottom: 10px;\n}\n#css-transform-gen-root .ctg-row label {\n  font-size: 13px;\n  color: #3a3f5c;\n  font-weight: 500;\n}\n#css-transform-gen-root .ctg-row input[type=\"range\"] {\n  width: 100%;\n  accent-color: #4f6ef7;\n  cursor: pointer;\n}\n#css-transform-gen-root .ctg-row .ctg-val {\n  font-size: 12px;\n  font-weight: 600;\n  color: #4f6ef7;\n  text-align: right;\n  white-space: nowrap;\n}\n#css-transform-gen-root .ctg-output {\n  background: #1a1a2e;\n  border-radius: 10px;\n  padding: 14px 16px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 13px;\n  color: #a8b4ff;\n  word-break: break-all;\n  line-height: 1.6;\n  min-height: 60px;\n}\n#css-transform-gen-root .ctg-output span.ctg-prop {\n  color: #7ee8a2;\n}\n#css-transform-gen-root .ctg-output span.ctg-val-text {\n  color: #ffd97d;\n}\n#css-transform-gen-root .ctg-btn-row {\n  display: flex;\n  gap: 8px;\n  flex-wrap: wrap;\n}\n#css-transform-gen-root .ctg-btn {\n  padding: 9px 18px;\n  border: none;\n  border-radius: 8px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s;\n}\n#css-transform-gen-root .ctg-btn:active {\n  transform: scale(0.97);\n}\n#css-transform-gen-root .ctg-btn-primary {\n  background: #4f6ef7;\n  color: #fff;\n}\n#css-transform-gen-root .ctg-btn-primary:hover {\n  background: #3a57e8;\n}\n#css-transform-gen-root .ctg-btn-secondary {\n  background: #e8eaff;\n  color: #4f6ef7;\n}\n#css-transform-gen-root .ctg-btn-secondary:hover {\n  background: #d4d9ff;\n}\n#css-transform-gen-root .ctg-presets-row {\n  display: flex;\n  gap: 8px;\n  flex-wrap: wrap;\n  margin-bottom: 20px;\n}\n#css-transform-gen-root .ctg-preset-btn {\n  padding: 6px 14px;\n  border: 1.5px solid #dde1f0;\n  border-radius: 20px;\n  background: #fff;\n  font-size: 12px;\n  font-weight: 600;\n  color: #4a4f72;\n  cursor: pointer;\n  transition: border-color 0.15s, background 0.15s, color 0.15s;\n}\n#css-transform-gen-root .ctg-preset-btn:hover {\n  border-color: #4f6ef7;\n  color: #4f6ef7;\n  background: #f0f3ff;\n}\n#css-transform-gen-root .ctg-copy-notice {\n  font-size: 12px;\n  color: #22c55e;\n  font-weight: 600;\n  min-height: 18px;\n  transition: opacity 0.3s;\n}\n#css-transform-gen-root .ctg-perspective-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-bottom: 10px;\n}\n#css-transform-gen-root .ctg-perspective-row input[type=\"checkbox\"] {\n  accent-color: #4f6ef7;\n  width: 15px;\n  height: 15px;\n  cursor: pointer;\n}\n#css-transform-gen-root .ctg-perspective-row label {\n  font-size: 13px;\n  color: #3a3f5c;\n  font-weight: 500;\n  cursor: pointer;\n}\n\u003c/style\u003e\n\u003ch2 id=\"what-is-css-transform\"\u003eWhat Is CSS Transform?\u003c/h2\u003e\n\u003cp\u003eThe \u003ccode\u003etransform\u003c/code\u003e property lets you visually move, rotate, resize, and skew elements without disrupting document flow. Transforms are GPU-accelerated and animation-friendly — a cornerstone of modern UI effects.\u003c/p\u003e","title":"CSS Transform Generator — Visual Playground"},{"content":" Build smooth CSS transitions visually. Select properties, set duration and easing, add multiple transitions, and copy the generated CSS — no coding required.\nHover over the preview element to see your transition in action. Add multiple properties and use presets to get started quickly. Presets Fade Slide Scale Color Change Combined Transition Settings \u0026lt;label for=\u0026quot;tr-property\u0026quot;\u0026gt;Property\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;tr-property\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;all\u0026quot;\u0026gt;all\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;opacity\u0026quot; selected\u0026gt;opacity\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;transform\u0026quot;\u0026gt;transform\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;background-color\u0026quot;\u0026gt;background-color\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;color\u0026quot;\u0026gt;color\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;width\u0026quot;\u0026gt;width\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;height\u0026quot;\u0026gt;height\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;border-radius\u0026quot;\u0026gt;border-radius\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;box-shadow\u0026quot;\u0026gt;box-shadow\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;padding\u0026quot;\u0026gt;padding\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;margin\u0026quot;\u0026gt;margin\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;font-size\u0026quot;\u0026gt;font-size\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;letter-spacing\u0026quot;\u0026gt;letter-spacing\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;border-color\u0026quot;\u0026gt;border-color\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;outline\u0026quot;\u0026gt;outline\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;filter\u0026quot;\u0026gt;filter\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;left\u0026quot;\u0026gt;left\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;top\u0026quot;\u0026gt;top\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;div class=\u0026quot;tr-slider-row\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;tr-slider-header\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;tr-duration\u0026quot;\u0026gt;Duration\u0026lt;/label\u0026gt; \u0026lt;span class=\u0026quot;tr-slider-val\u0026quot; id=\u0026quot;tr-duration-val\u0026quot;\u0026gt;0.3s\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;tr-duration\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;300\u0026quot; value=\u0026quot;30\u0026quot; oninput=\u0026quot;trUpdateDuration(this.value)\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;tr-slider-row\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;tr-slider-header\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;tr-delay\u0026quot;\u0026gt;Delay\u0026lt;/label\u0026gt; \u0026lt;span class=\u0026quot;tr-slider-val\u0026quot; id=\u0026quot;tr-delay-val\u0026quot;\u0026gt;0s\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;tr-delay\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;200\u0026quot; value=\u0026quot;0\u0026quot; oninput=\u0026quot;trUpdateDelay(this.value)\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;label for=\u0026quot;tr-timing\u0026quot;\u0026gt;Timing Function\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;tr-timing\u0026quot; onchange=\u0026quot;trTimingChanged()\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;ease\u0026quot; selected\u0026gt;ease\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;linear\u0026quot;\u0026gt;linear\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;ease-in\u0026quot;\u0026gt;ease-in\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;ease-out\u0026quot;\u0026gt;ease-out\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;ease-in-out\u0026quot;\u0026gt;ease-in-out\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;step-start\u0026quot;\u0026gt;step-start\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;step-end\u0026quot;\u0026gt;step-end\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;cubic-bezier\u0026quot;\u0026gt;cubic-bezier (custom)\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;div class=\u0026quot;tr-bezier-section\u0026quot; id=\u0026quot;tr-bezier-section\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;tr-bezier-labels\u0026quot;\u0026gt; \u0026lt;span\u0026gt;x1\u0026lt;/span\u0026gt;\u0026lt;span\u0026gt;y1\u0026lt;/span\u0026gt;\u0026lt;span\u0026gt;x2\u0026lt;/span\u0026gt;\u0026lt;span\u0026gt;y2\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;tr-bezier-inputs\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;tr-bz-x1\u0026quot; value=\u0026quot;0.25\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;1\u0026quot; step=\u0026quot;0.01\u0026quot; oninput=\u0026quot;trUpdateBezier()\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;tr-bz-y1\u0026quot; value=\u0026quot;0.1\u0026quot; min=\u0026quot;-2\u0026quot; max=\u0026quot;3\u0026quot; step=\u0026quot;0.01\u0026quot; oninput=\u0026quot;trUpdateBezier()\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;tr-bz-x2\u0026quot; value=\u0026quot;0.25\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;1\u0026quot; step=\u0026quot;0.01\u0026quot; oninput=\u0026quot;trUpdateBezier()\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;tr-bz-y2\u0026quot; value=\u0026quot;1\u0026quot; min=\u0026quot;-2\u0026quot; max=\u0026quot;3\u0026quot; step=\u0026quot;0.01\u0026quot; oninput=\u0026quot;trUpdateBezier()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;canvas id=\u0026quot;tr-bezier-canvas\u0026quot; width=\u0026quot;200\u0026quot; height=\u0026quot;160\u0026quot;\u0026gt;\u0026lt;/canvas\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;hr class=\u0026quot;tr-divider\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;tr-btn-row\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;tr-btn tr-btn-primary\u0026quot; onclick=\u0026quot;trAddTransition()\u0026quot;\u0026gt;+ Add Transition\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;tr-btn tr-btn-secondary\u0026quot; onclick=\u0026quot;trClearAll()\u0026quot;\u0026gt;Clear All\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;tr-panel\u0026quot; style=\u0026quot;margin-top:16px;\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Transition List\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;tr-transitions-list\u0026quot; id=\u0026quot;tr-list\u0026quot;\u0026gt; \u0026lt;!-- populated by JS --\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div style=\u0026quot;font-size:12px;color:#94a3b8;\u0026quot; id=\u0026quot;tr-list-empty\u0026quot;\u0026gt;No transitions added yet. Configure and click \u0026quot;Add Transition\u0026quot;.\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Live Preview Hover me hover to trigger The element above uses your generated transitions. \u0026lt;div class=\u0026quot;tr-panel\u0026quot; style=\u0026quot;margin-top:16px;\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Generated CSS\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;tr-output-block\u0026quot; id=\u0026quot;tr-output\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;tr-copy-btn\u0026quot; id=\u0026quot;tr-copy-btn\u0026quot; onclick=\u0026quot;trCopyCSS()\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;span id=\u0026quot;tr-output-content\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div style=\u0026quot;font-size:12px;color:#64748b;\u0026quot;\u0026gt;Paste this into your stylesheet. Apply to the element that should animate.\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; How to Use Select a property — choose which CSS property to animate (opacity, transform, etc.) Set duration — drag the slider from 0 to 3 seconds Set delay — optionally delay when the transition starts (0–2 seconds) Choose a timing function — pick a preset easing or draw a custom cubic-bezier curve Click \u0026ldquo;Add Transition\u0026rdquo; — adds it to the list below Repeat for additional properties Hover the preview to see all transitions in action Copy the CSS and paste it into your stylesheet Tips \u0026amp; Common Patterns Staggering multiple transitions Add different delays to each property to create a cascading effect:\ntransition: opacity 0.3s ease 0s, transform 0.3s ease 0.1s, background-color 0.3s ease 0.2s; Smooth button hover transition: background-color 0.2s ease, box-shadow 0.2s ease, transform 0.15s ease; Performance tip Prefer transform and opacity for transitions — they run on the compositor thread and avoid layout recalculations, giving you 60fps animations without jank.\nCustom easing with cubic-bezier The cubic-bezier editor lets you fine-tune your easing curve. Classic values:\nease-in: cubic-bezier(0.42, 0, 1, 1) ease-out: cubic-bezier(0, 0, 0.58, 1) spring: cubic-bezier(0.34, 1.56, 0.64, 1) sharp: cubic-bezier(0.4, 0, 0.6, 1) Related Tools CSS Animation Generator — build full keyframe animations with @keyframes CSS Transform Generator — visually construct transform values (rotate, scale, skew, translate) ","permalink":"https://productivity-works.com/tools/css-transition-generator/","summary":"\u003cdiv id=\"tr-app\"\u003e\n\u003cstyle\u003e\n#tr-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  color: #1e293b;\n  line-height: 1.6;\n}\n#tr-app *, #tr-app *::before, #tr-app *::after {\n  box-sizing: border-box;\n}\n#tr-app .tr-layout {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 24px;\n  margin-top: 24px;\n}\n@media (max-width: 768px) {\n  #tr-app .tr-layout {\n    grid-template-columns: 1fr;\n  }\n}\n#tr-app .tr-panel {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 20px;\n}\n#tr-app .tr-panel h3 {\n  margin: 0 0 16px 0;\n  font-size: 14px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #64748b;\n}\n#tr-app .tr-full-panel {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 20px;\n  margin-top: 24px;\n}\n#tr-app .tr-full-panel h3 {\n  margin: 0 0 16px 0;\n  font-size: 14px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #64748b;\n}\n#tr-app label {\n  display: block;\n  font-size: 13px;\n  font-weight: 600;\n  color: #475569;\n  margin-bottom: 6px;\n}\n#tr-app select {\n  width: 100%;\n  padding: 8px 12px;\n  border: 1px solid #cbd5e1;\n  border-radius: 8px;\n  background: #fff;\n  font-size: 14px;\n  color: #1e293b;\n  cursor: pointer;\n  outline: none;\n  margin-bottom: 16px;\n  appearance: none;\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2364748b' d='M6 8L1 3h10z'/%3E%3C/svg%3E\");\n  background-repeat: no-repeat;\n  background-position: right 12px center;\n  padding-right: 36px;\n}\n#tr-app select:focus {\n  border-color: #6366f1;\n  box-shadow: 0 0 0 3px rgba(99,102,241,0.12);\n}\n#tr-app .tr-slider-row {\n  margin-bottom: 16px;\n}\n#tr-app .tr-slider-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin-bottom: 6px;\n}\n#tr-app .tr-slider-header label {\n  margin-bottom: 0;\n}\n#tr-app .tr-slider-val {\n  font-size: 13px;\n  font-weight: 700;\n  color: #6366f1;\n  min-width: 48px;\n  text-align: right;\n}\n#tr-app input[type=\"range\"] {\n  width: 100%;\n  accent-color: #6366f1;\n  height: 4px;\n  cursor: pointer;\n}\n#tr-app .tr-bezier-section {\n  margin-top: 4px;\n}\n#tr-app .tr-bezier-inputs {\n  display: grid;\n  grid-template-columns: 1fr 1fr 1fr 1fr;\n  gap: 8px;\n  margin-bottom: 12px;\n}\n#tr-app .tr-bezier-inputs input[type=\"number\"] {\n  width: 100%;\n  padding: 6px 8px;\n  border: 1px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 13px;\n  text-align: center;\n  color: #1e293b;\n  outline: none;\n}\n#tr-app .tr-bezier-inputs input[type=\"number\"]:focus {\n  border-color: #6366f1;\n  box-shadow: 0 0 0 3px rgba(99,102,241,0.12);\n}\n#tr-app .tr-bezier-labels {\n  display: grid;\n  grid-template-columns: 1fr 1fr 1fr 1fr;\n  gap: 8px;\n  margin-bottom: 8px;\n}\n#tr-app .tr-bezier-labels span {\n  font-size: 11px;\n  color: #94a3b8;\n  text-align: center;\n}\n#tr-app canvas {\n  display: block;\n  border: 1px solid #e2e8f0;\n  border-radius: 8px;\n  background: #fff;\n  margin: 0 auto;\n}\n#tr-app .tr-transitions-list {\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n  margin-bottom: 12px;\n  max-height: 240px;\n  overflow-y: auto;\n}\n#tr-app .tr-transition-item {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 8px;\n  padding: 8px 12px;\n  font-size: 13px;\n}\n#tr-app .tr-transition-item span {\n  flex: 1;\n  color: #334155;\n  font-family: \"Courier New\", monospace;\n  font-size: 12px;\n  word-break: break-all;\n}\n#tr-app .tr-btn-remove {\n  background: none;\n  border: none;\n  color: #ef4444;\n  cursor: pointer;\n  font-size: 16px;\n  line-height: 1;\n  padding: 0 4px;\n  flex-shrink: 0;\n}\n#tr-app .tr-btn-remove:hover {\n  color: #b91c1c;\n}\n#tr-app .tr-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  padding: 9px 18px;\n  border-radius: 8px;\n  font-size: 14px;\n  font-weight: 600;\n  border: none;\n  cursor: pointer;\n  transition: background 0.2s, transform 0.1s;\n}\n#tr-app .tr-btn:active {\n  transform: scale(0.97);\n}\n#tr-app .tr-btn-primary {\n  background: #6366f1;\n  color: #fff;\n}\n#tr-app .tr-btn-primary:hover {\n  background: #4f46e5;\n}\n#tr-app .tr-btn-secondary {\n  background: #e2e8f0;\n  color: #475569;\n}\n#tr-app .tr-btn-secondary:hover {\n  background: #cbd5e1;\n}\n#tr-app .tr-btn-row {\n  display: flex;\n  gap: 8px;\n  flex-wrap: wrap;\n}\n#tr-app .tr-presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 0;\n}\n#tr-app .tr-preset-btn {\n  padding: 6px 14px;\n  border-radius: 20px;\n  border: 1.5px solid #6366f1;\n  background: #fff;\n  color: #6366f1;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s;\n}\n#tr-app .tr-preset-btn:hover {\n  background: #6366f1;\n  color: #fff;\n}\n#tr-app .tr-preview-box {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  height: 140px;\n  background: linear-gradient(135deg, #e0e7ff 0%, #f0fdf4 100%);\n  border-radius: 10px;\n  margin-bottom: 12px;\n  position: relative;\n  overflow: hidden;\n}\n#tr-app .tr-preview-el {\n  width: 80px;\n  height: 80px;\n  border-radius: 12px;\n  background: #6366f1;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: #fff;\n  font-size: 12px;\n  font-weight: 700;\n  cursor: pointer;\n  user-select: none;\n}\n#tr-app .tr-preview-hint {\n  position: absolute;\n  bottom: 8px;\n  right: 12px;\n  font-size: 11px;\n  color: #94a3b8;\n}\n#tr-app .tr-output-block {\n  position: relative;\n  background: #0f172a;\n  border-radius: 10px;\n  padding: 20px;\n  font-family: \"Courier New\", monospace;\n  font-size: 13px;\n  line-height: 1.8;\n  color: #e2e8f0;\n  white-space: pre-wrap;\n  word-break: break-all;\n  margin-bottom: 12px;\n  min-height: 80px;\n}\n#tr-app .tr-copy-btn {\n  position: absolute;\n  top: 12px;\n  right: 12px;\n  background: #334155;\n  border: none;\n  border-radius: 6px;\n  color: #94a3b8;\n  padding: 5px 12px;\n  font-size: 12px;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s;\n}\n#tr-app .tr-copy-btn:hover {\n  background: #475569;\n  color: #fff;\n}\n#tr-app .tr-copy-btn.tr-copied {\n  background: #22c55e;\n  color: #fff;\n}\n#tr-app .tr-syntax-prop { color: #93c5fd; }\n#tr-app .tr-syntax-colon { color: #94a3b8; }\n#tr-app .tr-syntax-val { color: #86efac; }\n#tr-app .tr-syntax-semi { color: #94a3b8; }\n#tr-app .tr-info-bar {\n  background: #eff6ff;\n  border: 1px solid #bfdbfe;\n  border-radius: 8px;\n  padding: 12px 16px;\n  font-size: 13px;\n  color: #1d4ed8;\n  margin-bottom: 16px;\n}\n#tr-app .tr-divider {\n  border: none;\n  border-top: 1px solid #e2e8f0;\n  margin: 16px 0;\n}\n\u003c/style\u003e\n\u003cp\u003e\u003cstrong\u003eBuild smooth CSS transitions visually.\u003c/strong\u003e Select properties, set duration and easing, add multiple transitions, and copy the generated CSS — no coding required.\u003c/p\u003e","title":"CSS Transition Generator"},{"content":" Settings Base Font Size (px) Viewport Width (px) Viewport Height (px) Parent / Context Size (px) Used for em and % calculations Value Unit px rem em vw vh % pt cm mm in Click any card to copy the CSS value.\nConvert HTML entities → HTML Entity Converter Format CSS → CSS Minifier ","permalink":"https://productivity-works.com/tools/css-unit-converter/","summary":"\u003cstyle\u003e\n#cuc-app *,#cuc-app *::before,#cuc-app *::after{box-sizing:border-box;margin:0;padding:0}\n#cuc-app{font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;max-width:780px;margin:0 auto;padding:16px;color:#1a1a2e}\n#cuc-app .cuc-settings{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;background:#f8f9ff;border:1.5px solid #d0d5e8;border-radius:10px;padding:14px;margin-bottom:18px}\n#cuc-app .cuc-settings label{display:block;font-size:.78rem;font-weight:600;color:#555;margin-bottom:4px}\n#cuc-app .cuc-settings input[type=number]{width:100%;padding:7px 10px;border:1.5px solid #d0d5e8;border-radius:7px;font-size:.9rem;background:#fff;transition:border-color .2s}\n#cuc-app .cuc-settings input[type=number]:focus{outline:none;border-color:#4f6bed}\n#cuc-app .cuc-input-row{display:flex;gap:10px;margin-bottom:18px;align-items:flex-end}\n#cuc-app .cuc-input-row\u003ediv{flex:1}\n#cuc-app .cuc-input-row label{display:block;font-size:.82rem;font-weight:600;color:#555;margin-bottom:5px}\n#cuc-app .cuc-input-row input[type=number]{width:100%;padding:10px 12px;border:1.5px solid #d0d5e8;border-radius:8px;font-size:1.05rem;background:#fafbff;transition:border-color .2s}\n#cuc-app .cuc-input-row input[type=number]:focus{outline:none;border-color:#4f6bed}\n#cuc-app .cuc-input-row select{width:100%;padding:10px 10px;border:1.5px solid #d0d5e8;border-radius:8px;font-size:1rem;background:#fafbff;cursor:pointer;transition:border-color .2s}\n#cuc-app .cuc-input-row select:focus{outline:none;border-color:#4f6bed}\n#cuc-app .cuc-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:12px;margin-bottom:8px}\n#cuc-app .cuc-card{background:#fff;border:1.5px solid #e2e6f8;border-radius:10px;padding:14px 16px;cursor:pointer;transition:border-color .2s,box-shadow .2s;position:relative}\n#cuc-app .cuc-card:hover{border-color:#4f6bed;box-shadow:0 2px 10px rgba(79,107,237,.12)}\n#cuc-app .cuc-card.cuc-active{border-color:#4f6bed;background:#f0f2ff}\n#cuc-app .cuc-card-unit{font-size:.75rem;font-weight:700;color:#4f6bed;text-transform:uppercase;letter-spacing:.04em;margin-bottom:4px}\n#cuc-app .cuc-card-value{font-size:1.25rem;font-weight:700;color:#1a1a2e;word-break:break-all}\n#cuc-app .cuc-card-full{font-size:.72rem;color:#888;margin-top:2px}\n#cuc-app .cuc-copy-hint{font-size:.72rem;color:#aaa;margin-top:6px}\n#cuc-app .cuc-toast{position:fixed;bottom:24px;right:24px;background:#333;color:#fff;padding:9px 18px;border-radius:8px;font-size:.85rem;opacity:0;pointer-events:none;transition:opacity .3s;z-index:9999}\n#cuc-app .cuc-toast.show{opacity:1}\n#cuc-app .cuc-settings-title{font-size:.8rem;font-weight:700;color:#4f6bed;margin-bottom:2px;grid-column:1/-1}\n#cuc-app .cuc-note{font-size:.78rem;color:#888;margin-top:4px}\n\u003c/style\u003e\n\u003cdiv id=\"cuc-app\"\u003e\n\u003cdiv class=\"cuc-settings\"\u003e\n  \u003cdiv class=\"cuc-settings-title\"\u003eSettings\u003c/div\u003e\n  \u003cdiv\u003e\n    \u003clabel for=\"cuc-base-font\"\u003eBase Font Size (px)\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"cuc-base-font\" value=\"16\" min=\"1\" max=\"100\" step=\"0.5\" oninput=\"cucUpdate()\"\u003e\n  \u003c/div\u003e\n  \u003cdiv\u003e\n    \u003clabel for=\"cuc-vw\"\u003eViewport Width (px)\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"cuc-vw\" value=\"1440\" min=\"1\" max=\"10000\" step=\"1\" oninput=\"cucUpdate()\"\u003e\n  \u003c/div\u003e\n  \u003cdiv\u003e\n    \u003clabel for=\"cuc-vh\"\u003eViewport Height (px)\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"cuc-vh\" value=\"900\" min=\"1\" max=\"10000\" step=\"1\" oninput=\"cucUpdate()\"\u003e\n  \u003c/div\u003e\n  \u003cdiv\u003e\n    \u003clabel for=\"cuc-parent\"\u003eParent / Context Size (px)\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"cuc-parent\" value=\"1440\" min=\"1\" max=\"10000\" step=\"0.5\" oninput=\"cucUpdate()\"\u003e\n    \u003cdiv class=\"cuc-note\"\u003eUsed for em and % calculations\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"cuc-input-row\"\u003e\n  \u003cdiv\u003e\n    \u003clabel for=\"cuc-val\"\u003eValue\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"cuc-val\" value=\"16\" step=\"any\" oninput=\"cucUpdate()\" placeholder=\"Enter value\"\u003e\n  \u003c/div\u003e\n  \u003cdiv style=\"max-width:120px\"\u003e\n    \u003clabel for=\"cuc-unit\"\u003eUnit\u003c/label\u003e\n    \u003cselect id=\"cuc-unit\" onchange=\"cucUpdate()\"\u003e\n      \u003coption value=\"px\"\u003epx\u003c/option\u003e\n      \u003coption value=\"rem\"\u003erem\u003c/option\u003e\n      \u003coption value=\"em\"\u003eem\u003c/option\u003e\n      \u003coption value=\"vw\"\u003evw\u003c/option\u003e\n      \u003coption value=\"vh\"\u003evh\u003c/option\u003e\n      \u003coption value=\"pct\"\u003e%\u003c/option\u003e\n      \u003coption value=\"pt\"\u003ept\u003c/option\u003e\n      \u003coption value=\"cm\"\u003ecm\u003c/option\u003e\n      \u003coption value=\"mm\"\u003emm\u003c/option\u003e\n      \u003coption value=\"in\"\u003ein\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"cuc-grid\" id=\"cuc-grid\"\u003e\u003c/div\u003e\n\u003cp class=\"cuc-note\" style=\"margin-bottom:12px\"\u003eClick any card to copy the CSS value.\u003c/p\u003e","title":"CSS Unit Converter - px rem em vw vh"},{"content":" CSS Variables Generator Build design tokens as CSS custom properties — color, typography, spacing, radius, shadows.\nPresets: Light Modern Dark Mode Ocean Forest Warm \u0026lt;!-- Colors --\u0026gt; \u0026lt;div class=\u0026quot;cv-section\u0026quot; id=\u0026quot;cv-section-colors\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cv-section-title\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-section-icon\u0026quot; style=\u0026quot;background:#ede9fe;\u0026quot;\u0026gt;\u0026amp;#127912;\u0026lt;/span\u0026gt; Colors \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;Primary\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;cv-color-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cv-color-swatch\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;c-primary\u0026quot; value=\u0026quot;#2563eb\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-hex-input\u0026quot; id=\u0026quot;h-primary\u0026quot; value=\u0026quot;#2563eb\u0026quot; maxlength=\u0026quot;7\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;Secondary\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;cv-color-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cv-color-swatch\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;c-secondary\u0026quot; value=\u0026quot;#7c3aed\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-hex-input\u0026quot; id=\u0026quot;h-secondary\u0026quot; value=\u0026quot;#7c3aed\u0026quot; maxlength=\u0026quot;7\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;Accent\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;cv-color-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cv-color-swatch\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;c-accent\u0026quot; value=\u0026quot;#f59e0b\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-hex-input\u0026quot; id=\u0026quot;h-accent\u0026quot; value=\u0026quot;#f59e0b\u0026quot; maxlength=\u0026quot;7\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;Background\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;cv-color-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cv-color-swatch\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;c-bg\u0026quot; value=\u0026quot;#ffffff\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-hex-input\u0026quot; id=\u0026quot;h-bg\u0026quot; value=\u0026quot;#ffffff\u0026quot; maxlength=\u0026quot;7\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;Surface\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;cv-color-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cv-color-swatch\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;c-surface\u0026quot; value=\u0026quot;#f8fafc\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-hex-input\u0026quot; id=\u0026quot;h-surface\u0026quot; value=\u0026quot;#f8fafc\u0026quot; maxlength=\u0026quot;7\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;Text\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;cv-color-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cv-color-swatch\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;c-text\u0026quot; value=\u0026quot;#1e293b\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-hex-input\u0026quot; id=\u0026quot;h-text\u0026quot; value=\u0026quot;#1e293b\u0026quot; maxlength=\u0026quot;7\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;Text Muted\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;cv-color-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cv-color-swatch\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;c-text-muted\u0026quot; value=\u0026quot;#64748b\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-hex-input\u0026quot; id=\u0026quot;h-text-muted\u0026quot; value=\u0026quot;#64748b\u0026quot; maxlength=\u0026quot;7\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;Border\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;cv-color-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cv-color-swatch\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;c-border\u0026quot; value=\u0026quot;#e2e8f0\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-hex-input\u0026quot; id=\u0026quot;h-border\u0026quot; value=\u0026quot;#e2e8f0\u0026quot; maxlength=\u0026quot;7\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Typography --\u0026gt; \u0026lt;div class=\u0026quot;cv-section\u0026quot; id=\u0026quot;cv-section-typography\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cv-section-title\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-section-icon\u0026quot; style=\u0026quot;background:#fef9c3;\u0026quot;\u0026gt;T\u0026lt;/span\u0026gt; Typography \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;Font Family\u0026lt;/span\u0026gt; \u0026lt;select class=\u0026quot;cv-select-input\u0026quot; id=\u0026quot;t-font-family\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\u0026quot;\u0026gt;System UI\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;'Inter', sans-serif\u0026quot;\u0026gt;Inter\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;'Roboto', sans-serif\u0026quot;\u0026gt;Roboto\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;'Poppins', sans-serif\u0026quot;\u0026gt;Poppins\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;'Playfair Display', serif\u0026quot;\u0026gt;Playfair Display\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;Georgia, 'Times New Roman', serif\u0026quot;\u0026gt;Georgia Serif\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;'Fira Code', 'Consolas', monospace\u0026quot;\u0026gt;Fira Code Mono\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;Base Size\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input\u0026quot; id=\u0026quot;t-base\u0026quot; value=\u0026quot;1rem\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;Size SM\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input\u0026quot; id=\u0026quot;t-sm\u0026quot; value=\u0026quot;0.875rem\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;Size LG\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input\u0026quot; id=\u0026quot;t-lg\u0026quot; value=\u0026quot;1.125rem\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;Size XL\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input\u0026quot; id=\u0026quot;t-xl\u0026quot; value=\u0026quot;1.25rem\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;Size 2XL\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input\u0026quot; id=\u0026quot;t-2xl\u0026quot; value=\u0026quot;1.5rem\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;Weight Normal\u0026lt;/span\u0026gt; \u0026lt;select class=\u0026quot;cv-select-input\u0026quot; id=\u0026quot;t-weight-normal\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;300\u0026quot;\u0026gt;300 Light\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;400\u0026quot; selected\u0026gt;400 Regular\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;500\u0026quot;\u0026gt;500 Medium\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;Weight Bold\u0026lt;/span\u0026gt; \u0026lt;select class=\u0026quot;cv-select-input\u0026quot; id=\u0026quot;t-weight-bold\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;600\u0026quot;\u0026gt;600 Semibold\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;700\u0026quot; selected\u0026gt;700 Bold\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;800\u0026quot;\u0026gt;800 Extrabold\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;900\u0026quot;\u0026gt;900 Black\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;Line Height\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input\u0026quot; id=\u0026quot;t-leading\u0026quot; value=\u0026quot;1.6\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;Leading Tight\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input\u0026quot; id=\u0026quot;t-leading-tight\u0026quot; value=\u0026quot;1.25\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Spacing --\u0026gt; \u0026lt;div class=\u0026quot;cv-section\u0026quot; id=\u0026quot;cv-section-spacing\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cv-section-title\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-section-icon\u0026quot; style=\u0026quot;background:#dcfce7;\u0026quot;\u0026gt;\u0026amp;#8596;\u0026lt;/span\u0026gt; Spacing \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;XS\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input cv-spacing\u0026quot; id=\u0026quot;sp-xs\u0026quot; value=\u0026quot;4px\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;SM\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input cv-spacing\u0026quot; id=\u0026quot;sp-sm\u0026quot; value=\u0026quot;8px\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;MD\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input cv-spacing\u0026quot; id=\u0026quot;sp-md\u0026quot; value=\u0026quot;16px\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;LG\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input cv-spacing\u0026quot; id=\u0026quot;sp-lg\u0026quot; value=\u0026quot;24px\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;XL\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input cv-spacing\u0026quot; id=\u0026quot;sp-xl\u0026quot; value=\u0026quot;32px\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;2XL\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input cv-spacing\u0026quot; id=\u0026quot;sp-2xl\u0026quot; value=\u0026quot;48px\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;3XL\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input cv-spacing\u0026quot; id=\u0026quot;sp-3xl\u0026quot; value=\u0026quot;64px\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Border Radius --\u0026gt; \u0026lt;div class=\u0026quot;cv-section\u0026quot; id=\u0026quot;cv-section-radius\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cv-section-title\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-section-icon\u0026quot; style=\u0026quot;background:#fce7f3;\u0026quot;\u0026gt;\u0026amp;#11096;\u0026lt;/span\u0026gt; Border Radius \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;SM\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input cv-radius\u0026quot; id=\u0026quot;r-sm\u0026quot; value=\u0026quot;4px\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;MD\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input cv-radius\u0026quot; id=\u0026quot;r-md\u0026quot; value=\u0026quot;8px\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;LG\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input cv-radius\u0026quot; id=\u0026quot;r-lg\u0026quot; value=\u0026quot;12px\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;XL\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input cv-radius\u0026quot; id=\u0026quot;r-xl\u0026quot; value=\u0026quot;16px\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;Full (pill)\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input cv-radius\u0026quot; id=\u0026quot;r-full\u0026quot; value=\u0026quot;9999px\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Shadows --\u0026gt; \u0026lt;div class=\u0026quot;cv-section\u0026quot; id=\u0026quot;cv-section-shadows\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cv-section-title\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-section-icon\u0026quot; style=\u0026quot;background:#e0f2fe;\u0026quot;\u0026gt;\u0026amp;#9737;\u0026lt;/span\u0026gt; Shadows \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;SM\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input cv-shadow\u0026quot; id=\u0026quot;sh-sm\u0026quot; value=\u0026quot;0 1px 3px rgba(0,0,0,0.1)\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;MD\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input cv-shadow\u0026quot; id=\u0026quot;sh-md\u0026quot; value=\u0026quot;0 4px 12px rgba(0,0,0,0.12)\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;LG\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input cv-shadow\u0026quot; id=\u0026quot;sh-lg\u0026quot; value=\u0026quot;0 8px 24px rgba(0,0,0,0.15)\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-row-label\u0026quot;\u0026gt;XL\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;cv-text-input cv-shadow\u0026quot; id=\u0026quot;sh-xl\u0026quot; value=\u0026quot;0 16px 48px rgba(0,0,0,0.2)\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-preview-title\u0026quot;\u0026gt;Live Preview\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-preview-area\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cv-preview-inner\u0026quot; id=\u0026quot;cv-preview-inner\u0026quot;\u0026gt; \u0026lt;!-- Rendered by JS --\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-output-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cv-output-header\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;cv-output-label\u0026quot;\u0026gt;Generated CSS\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026quot;cv-copy-btn\u0026quot; id=\u0026quot;cv-copy-btn\u0026quot;\u0026gt;Copy CSS\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cv-code-box\u0026quot; id=\u0026quot;cv-code-box\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Related Tools Color Palette Generator CSS Gradient Generator Color Contrast Checker Related Tools Box Shadow Generator Css Animation Generator Css Button Generator ","permalink":"https://productivity-works.com/tools/css-variables-generator/","summary":"\u003cdiv id=\"cssvar-app\"\u003e\n\u003cstyle\u003e\n#cssvar-app *,\n#cssvar-app *::before,\n#cssvar-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\n#cssvar-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 15px;\n  color: #1a1a2e;\n  line-height: 1.5;\n  background: #f4f6fb;\n  border-radius: 12px;\n  padding: 0 0 32px 0;\n  overflow: hidden;\n}\n\n/* ── Header ── */\n#cssvar-app .cv-header {\n  background: linear-gradient(135deg, #2563eb 0%, #7c3aed 100%);\n  color: #fff;\n  padding: 28px 32px 24px;\n  margin-bottom: 0;\n}\n#cssvar-app .cv-header h1 {\n  font-size: 1.55rem;\n  font-weight: 700;\n  letter-spacing: -0.5px;\n  margin-bottom: 4px;\n}\n#cssvar-app .cv-header p {\n  font-size: 0.92rem;\n  opacity: 0.85;\n}\n\n/* ── Preset bar ── */\n#cssvar-app .cv-preset-bar {\n  background: #fff;\n  border-bottom: 1px solid #e2e8f0;\n  padding: 12px 32px;\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  flex-wrap: wrap;\n}\n#cssvar-app .cv-preset-label {\n  font-size: 0.8rem;\n  font-weight: 600;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-right: 4px;\n  white-space: nowrap;\n}\n#cssvar-app .cv-preset-btn {\n  padding: 6px 16px;\n  border-radius: 20px;\n  border: 1.5px solid #cbd5e1;\n  background: #f8fafc;\n  color: #334155;\n  font-size: 0.82rem;\n  font-weight: 500;\n  cursor: pointer;\n  transition: all 0.15s;\n  white-space: nowrap;\n}\n#cssvar-app .cv-preset-btn:hover {\n  border-color: #2563eb;\n  color: #2563eb;\n  background: #eff6ff;\n}\n#cssvar-app .cv-preset-btn.active {\n  background: #2563eb;\n  border-color: #2563eb;\n  color: #fff;\n}\n\n/* ── Main layout ── */\n#cssvar-app .cv-main {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 0;\n}\n@media (max-width: 820px) {\n  #cssvar-app .cv-main {\n    grid-template-columns: 1fr;\n  }\n}\n\n/* ── Panel ── */\n#cssvar-app .cv-panel {\n  padding: 24px 28px;\n  border-right: 1px solid #e2e8f0;\n  overflow-y: auto;\n}\n@media (max-width: 820px) {\n  #cssvar-app .cv-panel {\n    border-right: none;\n    border-bottom: 1px solid #e2e8f0;\n  }\n}\n\n/* ── Section ── */\n#cssvar-app .cv-section {\n  margin-bottom: 28px;\n}\n#cssvar-app .cv-section-title {\n  font-size: 0.78rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #94a3b8;\n  margin-bottom: 14px;\n  padding-bottom: 8px;\n  border-bottom: 1px solid #e2e8f0;\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n#cssvar-app .cv-section-icon {\n  width: 18px;\n  height: 18px;\n  border-radius: 4px;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 11px;\n}\n\n/* ── Row / field ── */\n#cssvar-app .cv-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-bottom: 10px;\n}\n#cssvar-app .cv-row-label {\n  font-size: 0.8rem;\n  color: #475569;\n  width: 140px;\n  flex-shrink: 0;\n  font-weight: 500;\n}\n@media (max-width: 500px) {\n  #cssvar-app .cv-row {\n    flex-wrap: wrap;\n  }\n  #cssvar-app .cv-row-label {\n    width: 100%;\n    margin-bottom: 2px;\n  }\n}\n\n/* Color input */\n#cssvar-app .cv-color-wrap {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  flex: 1;\n}\n#cssvar-app .cv-color-swatch {\n  width: 34px;\n  height: 34px;\n  border-radius: 8px;\n  border: 2px solid #e2e8f0;\n  cursor: pointer;\n  padding: 0;\n  background: transparent;\n  overflow: hidden;\n  flex-shrink: 0;\n}\n#cssvar-app .cv-color-swatch input[type=\"color\"] {\n  width: 44px;\n  height: 44px;\n  border: none;\n  padding: 0;\n  cursor: pointer;\n  transform: translate(-5px, -5px);\n}\n#cssvar-app .cv-hex-input {\n  flex: 1;\n  min-width: 80px;\n  padding: 6px 10px;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 7px;\n  font-size: 0.82rem;\n  font-family: \"Fira Mono\", \"Consolas\", monospace;\n  color: #1e293b;\n  background: #fff;\n  transition: border-color 0.15s;\n  max-width: 110px;\n}\n#cssvar-app .cv-hex-input:focus {\n  outline: none;\n  border-color: #2563eb;\n}\n\n/* Text / select input */\n#cssvar-app .cv-text-input,\n#cssvar-app .cv-select-input {\n  flex: 1;\n  padding: 6px 10px;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 7px;\n  font-size: 0.82rem;\n  color: #1e293b;\n  background: #fff;\n  transition: border-color 0.15s;\n}\n#cssvar-app .cv-text-input:focus,\n#cssvar-app .cv-select-input:focus {\n  outline: none;\n  border-color: #2563eb;\n}\n\n/* ── Preview panel ── */\n#cssvar-app .cv-preview-panel {\n  padding: 24px 28px;\n  background: #f8fafc;\n}\n#cssvar-app .cv-preview-title {\n  font-size: 0.78rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #94a3b8;\n  margin-bottom: 16px;\n  padding-bottom: 8px;\n  border-bottom: 1px solid #e2e8f0;\n}\n\n/* Preview card */\n#cssvar-app .cv-preview-area {\n  border-radius: 10px;\n  overflow: hidden;\n  border: 1.5px solid #e2e8f0;\n  margin-bottom: 16px;\n}\n#cssvar-app .cv-preview-inner {\n  padding: 24px;\n  transition: background 0.3s;\n}\n\n/* Output area */\n#cssvar-app .cv-output-section {\n  margin-top: 0;\n}\n#cssvar-app .cv-output-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 8px;\n}\n#cssvar-app .cv-output-label {\n  font-size: 0.78rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #94a3b8;\n}\n#cssvar-app .cv-copy-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 5px;\n  padding: 7px 16px;\n  background: #2563eb;\n  color: #fff;\n  border: none;\n  border-radius: 7px;\n  font-size: 0.82rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s;\n}\n#cssvar-app .cv-copy-btn:hover {\n  background: #1d4ed8;\n}\n#cssvar-app .cv-copy-btn:active {\n  transform: scale(0.97);\n}\n#cssvar-app .cv-copy-btn.copied {\n  background: #16a34a;\n}\n#cssvar-app .cv-code-box {\n  background: #0f172a;\n  color: #94d2bd;\n  font-family: \"Fira Mono\", \"Consolas\", \"Monaco\", monospace;\n  font-size: 0.76rem;\n  line-height: 1.7;\n  padding: 16px 18px;\n  border-radius: 9px;\n  overflow-x: auto;\n  overflow-y: auto;\n  max-height: 300px;\n  white-space: pre;\n  tab-size: 2;\n}\n\n/* ── Cross-links ── */\n#cssvar-app .cv-links {\n  margin: 24px 28px 0;\n  padding: 18px 20px;\n  background: #eff6ff;\n  border-radius: 10px;\n  border: 1px solid #bfdbfe;\n}\n#cssvar-app .cv-links-title {\n  font-size: 0.8rem;\n  font-weight: 700;\n  color: #1d4ed8;\n  margin-bottom: 8px;\n}\n#cssvar-app .cv-links ul {\n  list-style: none;\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n}\n#cssvar-app .cv-links ul li a {\n  display: inline-block;\n  padding: 5px 14px;\n  background: #fff;\n  border: 1.5px solid #93c5fd;\n  border-radius: 20px;\n  color: #1d4ed8;\n  font-size: 0.82rem;\n  font-weight: 500;\n  text-decoration: none;\n  transition: all 0.15s;\n}\n#cssvar-app .cv-links ul li a:hover {\n  background: #2563eb;\n  color: #fff;\n  border-color: #2563eb;\n}\n\u003c/style\u003e\n\u003cdiv class=\"cv-header\"\u003e\n  \u003ch1\u003eCSS Variables Generator\u003c/h1\u003e\n  \u003cp\u003eBuild design tokens as CSS custom properties — color, typography, spacing, radius, shadows.\u003c/p\u003e","title":"CSS Variables Generator — Design Token Builder"},{"content":"Free, browser-based CSV editor with a spreadsheet-like table interface. Import by pasting or uploading a file, edit cells directly, add/delete rows and columns, sort and filter, then export — all locally with no server involved.\nImport CSV Delimiter: Comma (,)Tab (\\t)Semicolon (;)Pipe (|) First row is header Import from Paste \u0026#128196; Upload .csv file Load Sample + Row + Column Clear All No data yet. Export CSV Generate CSV Copy to Clipboard Download .csv Related Tools JSON to CSV Converter Markdown Table Generator Related Tools Convert JSON to CSV → JSON to CSV Converter Generate markdown tables → Markdown Table Generator Related Articles How to Use AI for Excel Automation 2026 Google Sheets vs Excel 2026: Which Is Better for You? Excel Skills That Will Double Your Salary ","permalink":"https://productivity-works.com/tools/csv-editor/","summary":"\u003cp\u003eFree, browser-based CSV editor with a spreadsheet-like table interface. Import by pasting or uploading a file, edit cells directly, add/delete rows and columns, sort and filter, then export — all locally with no server involved.\u003c/p\u003e\n\u003cstyle\u003e\n#csv-app*,#csv-app*::before,#csv-app*::after{box-sizing:border-box;margin:0;padding:0}\n#csv-app{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;font-size:15px;color:#1f2937;line-height:1.5;max-width:980px;margin:1.5rem auto}\n#csv-app h2{font-size:.95rem;font-weight:700;color:#111827;margin-bottom:.45rem}\n#csv-app .ca-toolbar{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center;background:#f9fafb;border:1px solid #e5e7eb;border-radius:9px;padding:.65rem .9rem;margin-bottom:.9rem}\n#csv-app .ca-btn{padding:.38rem .85rem;border:none;border-radius:6px;font-size:.82rem;font-weight:600;cursor:pointer;transition:background .13s,opacity .13s;white-space:nowrap}\n#csv-app .ca-btn:disabled{opacity:.38;cursor:not-allowed}\n#csv-app .ca-btn-blue{background:#2563eb;color:#fff}\n#csv-app .ca-btn-blue:hover:not(:disabled){background:#1d4ed8}\n#csv-app .ca-btn-green{background:#059669;color:#fff}\n#csv-app .ca-btn-green:hover:not(:disabled){background:#047857}\n#csv-app .ca-btn-red{background:#ef4444;color:#fff}\n#csv-app .ca-btn-red:hover:not(:disabled){background:#dc2626}\n#csv-app .ca-btn-gray{background:#e5e7eb;color:#374151}\n#csv-app .ca-btn-gray:hover:not(:disabled){background:#d1d5db}\n#csv-app .ca-btn-orange{background:#f59e0b;color:#fff}\n#csv-app .ca-btn-orange:hover:not(:disabled){background:#d97706}\n#csv-app .ca-sep{width:1px;height:24px;background:#d1d5db;margin:0 .15rem}\n#csv-app .ca-search{padding:.36rem .7rem;border:1.5px solid #d1d5db;border-radius:6px;font-size:.82rem;color:#374151;outline:none;width:200px;transition:border-color .13s}\n#csv-app .ca-search:focus{border-color:#2563eb;box-shadow:0 0 0 3px rgba(37,99,235,.1)}\n#csv-app .ca-import-area{display:flex;flex-direction:column;gap:.65rem;margin-bottom:1rem}\n#csv-app .ca-import-row{display:flex;flex-wrap:wrap;gap:.5rem;align-items:flex-start}\n#csv-app textarea.ca-paste{width:100%;height:140px;padding:.6rem .75rem;border:1.5px solid #d1d5db;border-radius:8px;font-family:'SFMono-Regular',Consolas,'Liberation Mono',Menlo,monospace;font-size:.78rem;line-height:1.55;color:#1f2937;background:#fff;resize:vertical;outline:none;transition:border-color .13s}\n#csv-app textarea.ca-paste:focus{border-color:#2563eb;box-shadow:0 0 0 3px rgba(37,99,235,.1)}\n#csv-app .ca-file-label{display:inline-flex;align-items:center;gap:.35rem;padding:.38rem .85rem;background:#f3f4f6;border:1.5px dashed #9ca3af;border-radius:7px;font-size:.82rem;font-weight:600;color:#4b5563;cursor:pointer;transition:background .13s,border-color .13s}\n#csv-app .ca-file-label:hover{background:#e5e7eb;border-color:#6b7280}\n#csv-app input[type=file].ca-file-input{display:none}\n#csv-app .ca-opt-row{display:flex;flex-wrap:wrap;gap:.65rem 1.2rem;align-items:center;font-size:.8rem;color:#4b5563}\n#csv-app .ca-opt-row label{display:flex;align-items:center;gap:.3rem;cursor:pointer;user-select:none}\n#csv-app .ca-opt-row select{padding:.25rem .45rem;border:1px solid #d1d5db;border-radius:5px;font-size:.8rem;color:#374151;background:#fff;cursor:pointer}\n#csv-app .ca-opt-row input[type=checkbox]{width:14px;height:14px;accent-color:#2563eb;cursor:pointer}\n#csv-app .ca-table-wrap{overflow:auto;border:1.5px solid #d1d5db;border-radius:10px;max-height:480px;margin-bottom:.75rem;position:relative}\n#csv-app .ca-table-wrap table{border-collapse:collapse;font-size:.8rem;min-width:100%;table-layout:auto}\n#csv-app .ca-table-wrap thead{position:sticky;top:0;z-index:10}\n#csv-app .ca-table-wrap th.ca-th-num{background:#f3f4f6;color:#6b7280;font-weight:600;padding:.38rem .55rem;border-bottom:2px solid #d1d5db;border-right:1px solid #e5e7eb;min-width:38px;text-align:center;position:sticky;left:0;z-index:12}\n#csv-app .ca-table-wrap th.ca-th-col{background:#eff6ff;color:#1e3a8a;font-weight:700;padding:.38rem .5rem;border-bottom:2px solid #bfdbfe;border-right:1px solid #dbeafe;white-space:nowrap;cursor:default;min-width:90px}\n#csv-app .ca-table-wrap th.ca-th-col .ca-th-inner{display:flex;align-items:center;gap:0;min-height:26px}\n#csv-app .ca-table-wrap th.ca-th-col .ca-th-name{flex:1;outline:none;border:none;background:transparent;font:inherit;font-weight:700;color:#1e3a8a;cursor:text;min-width:60px;padding:0 2px}\n#csv-app .ca-table-wrap th.ca-th-col .ca-sort-btn{background:none;border:none;padding:2px 3px;cursor:pointer;font-size:.75rem;color:#6b7280;line-height:1;border-radius:3px;flex-shrink:0}\n#csv-app .ca-table-wrap th.ca-th-col .ca-sort-btn:hover{background:#dbeafe;color:#1d4ed8}\n#csv-app .ca-table-wrap th.ca-th-col .ca-sort-btn.ca-sort-active{color:#2563eb}\n#csv-app .ca-table-wrap th.ca-th-col .ca-del-col-btn{background:none;border:none;padding:2px 3px;cursor:pointer;font-size:.7rem;color:#9ca3af;line-height:1;border-radius:3px;flex-shrink:0;opacity:0;transition:opacity .12s}\n#csv-app .ca-table-wrap th.ca-th-col:hover .ca-del-col-btn{opacity:1}\n#csv-app .ca-table-wrap th.ca-th-col .ca-del-col-btn:hover{background:#fee2e2;color:#dc2626}\n#csv-app .ca-table-wrap td.ca-td-num{background:#f9fafb;color:#9ca3af;font-size:.72rem;text-align:center;padding:.3rem .4rem;border-bottom:1px solid #f3f4f6;border-right:1px solid #e5e7eb;min-width:38px;position:sticky;left:0;z-index:2}\n#csv-app .ca-table-wrap td.ca-td-cell{padding:0;border-bottom:1px solid #f3f4f6;border-right:1px solid #f3f4f6}\n#csv-app .ca-table-wrap td.ca-td-cell input{display:block;width:100%;height:100%;padding:.32rem .55rem;border:none;outline:none;background:transparent;font-family:inherit;font-size:.8rem;color:#1f2937;min-height:30px}\n#csv-app .ca-table-wrap td.ca-td-cell input:focus{background:#eff6ff;box-shadow:inset 0 0 0 2px #2563eb}\n#csv-app .ca-table-wrap tr:nth-child(even) td{background:#f9fafb}\n#csv-app .ca-table-wrap tr:nth-child(even) td.ca-td-num{background:#f3f4f6}\n#csv-app .ca-table-wrap tr:nth-child(even) td.ca-td-cell input{background:transparent}\n#csv-app .ca-table-wrap tr:nth-child(even) td.ca-td-cell input:focus{background:#eff6ff}\n#csv-app .ca-table-wrap td.ca-td-del{padding:.3rem .35rem;border-bottom:1px solid #f3f4f6;text-align:center;vertical-align:middle}\n#csv-app .ca-table-wrap td.ca-td-del button{background:none;border:none;cursor:pointer;color:#d1d5db;font-size:.85rem;line-height:1;border-radius:4px;padding:2px 5px;transition:color .12s,background .12s}\n#csv-app .ca-table-wrap td.ca-td-del button:hover{color:#ef4444;background:#fee2e2}\n#csv-app .ca-table-wrap th.ca-th-del{background:#f3f4f6;border-bottom:2px solid #d1d5db;border-right:1px solid #e5e7eb;padding:.38rem .35rem;min-width:32px}\n#csv-app .ca-status{display:flex;justify-content:space-between;align-items:center;font-size:.78rem;color:#6b7280;margin-bottom:.5rem;flex-wrap:wrap;gap:.3rem}\n#csv-app .ca-status .ca-filtered-note{color:#d97706;font-weight:600}\n#csv-app .ca-msg{font-size:.8rem;padding:.3rem .65rem;border-radius:5px;margin-bottom:.5rem;display:none}\n#csv-app .ca-msg.err{background:#fef2f2;color:#dc2626;border:1px solid #fca5a5;display:block}\n#csv-app .ca-msg.ok{background:#ecfdf5;color:#059669;border:1px solid #6ee7b7;display:block}\n#csv-app .ca-export-wrap{display:flex;flex-direction:column;gap:.5rem}\n#csv-app .ca-export-row{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center}\n#csv-app textarea.ca-export-ta{width:100%;height:120px;padding:.55rem .7rem;border:1.5px solid #d1d5db;border-radius:8px;font-family:'SFMono-Regular',Consolas,'Liberation Mono',Menlo,monospace;font-size:.76rem;line-height:1.5;color:#374151;background:#f8fafc;resize:vertical;outline:none}\n#csv-app .ca-empty{text-align:center;padding:2.5rem 1rem;color:#9ca3af;font-size:.88rem}\n#csv-app .ca-related{background:#f8fafc;border:1.5px solid #e5e7eb;border-radius:10px;padding:1rem 1.2rem;margin-top:1.6rem}\n#csv-app .ca-related h3{font-size:.9rem;font-weight:700;margin-bottom:.6rem;color:#374151}\n#csv-app .ca-related-links{display:flex;flex-wrap:wrap;gap:.45rem}\n#csv-app .ca-related-links a{display:inline-block;padding:.32rem .8rem;background:#eff6ff;color:#1d4ed8;border:1px solid #bfdbfe;border-radius:6px;font-size:.8rem;font-weight:600;text-decoration:none;transition:background .13s}\n#csv-app .ca-related-links a:hover{background:#dbeafe}\n@media(max-width:640px){#csv-app .ca-search{width:100%}#csv-app .ca-toolbar{gap:.4rem}#csv-app .ca-sep{display:none}}\n\u003c/style\u003e\n\u003cdiv id=\"csv-app\"\u003e\n\u003cdiv class=\"ca-import-area\"\u003e\n  \u003ch2\u003eImport CSV\u003c/h2\u003e\n  \u003cdiv class=\"ca-opt-row\"\u003e\n    \u003clabel\u003eDelimiter: \u003cselect id=\"ca-delim\"\u003e\u003coption value=\",\"\u003eComma (,)\u003c/option\u003e\u003coption value=\"\t\"\u003eTab (\\t)\u003c/option\u003e\u003coption value=\";\"\u003eSemicolon (;)\u003c/option\u003e\u003coption value=\"|\"\u003ePipe (|)\u003c/option\u003e\u003c/select\u003e\u003c/label\u003e\n    \u003clabel\u003e\u003cinput type=\"checkbox\" id=\"ca-has-header\" checked\u003e First row is header\u003c/label\u003e\n  \u003c/div\u003e\n  \u003ctextarea class=\"ca-paste\" id=\"ca-paste\" placeholder=\"Paste CSV here (e.g. name,age,city\u0026#10;Alice,30,New York\u0026#10;Bob,25,London)\"\u003e\u003c/textarea\u003e\n  \u003cdiv class=\"ca-import-row\"\u003e\n    \u003cbutton class=\"ca-btn ca-btn-blue\" id=\"ca-import-paste-btn\"\u003eImport from Paste\u003c/button\u003e\n    \u003clabel class=\"ca-file-label\"\u003e\u003cspan\u003e\u0026#128196; Upload .csv file\u003c/span\u003e\u003cinput type=\"file\" class=\"ca-file-input\" id=\"ca-file-input\" accept=\".csv,text/csv,text/plain\"\u003e\u003c/label\u003e\n    \u003cbutton class=\"ca-btn ca-btn-gray\" id=\"ca-load-sample-btn\"\u003eLoad Sample\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ca-msg\" id=\"ca-msg\"\u003e\u003c/div\u003e\n\u003cdiv id=\"ca-editor-section\" style=\"display:none\"\u003e\n  \u003cdiv class=\"ca-toolbar\"\u003e\n    \u003cinput class=\"ca-search\" id=\"ca-search\" type=\"search\" placeholder=\"Search / filter rows...\"\u003e\n    \u003cdiv class=\"ca-sep\"\u003e\u003c/div\u003e\n    \u003cbutton class=\"ca-btn ca-btn-green\" id=\"ca-add-row-btn\"\u003e+ Row\u003c/button\u003e\n    \u003cbutton class=\"ca-btn ca-btn-green\" id=\"ca-add-col-btn\"\u003e+ Column\u003c/button\u003e\n    \u003cdiv class=\"ca-sep\"\u003e\u003c/div\u003e\n    \u003cbutton class=\"ca-btn ca-btn-gray\" id=\"ca-clear-btn\"\u003eClear All\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ca-status\"\u003e\n    \u003cspan id=\"ca-status-text\"\u003e\u003c/span\u003e\n    \u003cspan id=\"ca-filtered-note\" class=\"ca-filtered-note\" style=\"display:none\"\u003e\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ca-table-wrap\" id=\"ca-table-wrap\"\u003e\n    \u003cdiv class=\"ca-empty\"\u003eNo data yet.\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ca-export-wrap\" style=\"margin-top:.9rem\"\u003e\n    \u003ch2\u003eExport CSV\u003c/h2\u003e\n    \u003cdiv class=\"ca-export-row\"\u003e\n      \u003cbutton class=\"ca-btn ca-btn-blue\" id=\"ca-generate-btn\"\u003eGenerate CSV\u003c/button\u003e\n      \u003cbutton class=\"ca-btn ca-btn-green\" id=\"ca-copy-btn\" disabled\u003eCopy to Clipboard\u003c/button\u003e\n      \u003cbutton class=\"ca-btn ca-btn-orange\" id=\"ca-download-btn\" disabled\u003eDownload .csv\u003c/button\u003e\n    \u003c/div\u003e\n    \u003ctextarea class=\"ca-export-ta\" id=\"ca-export-ta\" readonly placeholder=\"Click 'Generate CSV' to preview the output here...\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ca-related\"\u003e\n  \u003ch3\u003eRelated Tools\u003c/h3\u003e\n  \u003cdiv class=\"ca-related-links\"\u003e\n    \u003ca href=\"/tools/json-to-csv/\"\u003eJSON to CSV Converter\u003c/a\u003e\n    \u003ca href=\"/tools/markdown-table-generator/\"\u003eMarkdown Table Generator\u003c/a\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n'use strict';\n\n// ── State ───────────────────────────────────────────────────────────────────\nvar state = {\n  headers: [],   // string[]\n  rows: [],      // string[][]  (all data rows, original order)\n  sortCol: -1,\n  sortDir: 1,    // 1=asc, -1=desc\n  filter: '',\n  lastCsv: ''\n};\n\n// ── Elements ────────────────────────────────────────────────────────────────\nvar app          = document.getElementById('csv-app');\nvar pasteEl      = document.getElementById('ca-paste');\nvar fileInput    = document.getElementById('ca-file-input');\nvar delimEl      = document.getElementById('ca-delim');\nvar headerCb     = document.getElementById('ca-has-header');\nvar importBtn    = document.getElementById('ca-import-paste-btn');\nvar sampleBtn    = document.getElementById('ca-load-sample-btn');\nvar searchEl     = document.getElementById('ca-search');\nvar addRowBtn    = document.getElementById('ca-add-row-btn');\nvar addColBtn    = document.getElementById('ca-add-col-btn');\nvar clearBtn     = document.getElementById('ca-clear-btn');\nvar tableWrap    = document.getElementById('ca-table-wrap');\nvar statusText   = document.getElementById('ca-status-text');\nvar filteredNote = document.getElementById('ca-filtered-note');\nvar editorSec    = document.getElementById('ca-editor-section');\nvar msgEl        = document.getElementById('ca-msg');\nvar generateBtn  = document.getElementById('ca-generate-btn');\nvar copyBtn      = document.getElementById('ca-copy-btn');\nvar downloadBtn  = document.getElementById('ca-download-btn');\nvar exportTa     = document.getElementById('ca-export-ta');\n\n// ── RFC 4180 Parser ─────────────────────────────────────────────────────────\nfunction parseCSV(text, delim) {\n  delim = delim || ',';\n  var rows = [], row = [], field = '', inQuote = false, i = 0, c, nc;\n  text = text.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\n  if (text.length \u0026\u0026 text[text.length-1] !== '\\n') text += '\\n';\n  while (i \u003c text.length) {\n    c = text[i];\n    if (inQuote) {\n      if (c === '\"') {\n        nc = text[i+1];\n        if (nc === '\"') { field += '\"'; i += 2; continue; }\n        inQuote = false; i++; continue;\n      }\n      field += c; i++; continue;\n    }\n    if (c === '\"') { inQuote = true; i++; continue; }\n    if (c === delim) { row.push(field); field = ''; i++; continue; }\n    if (c === '\\n') {\n      row.push(field); field = '';\n      // skip empty trailing row\n      if (i \u003c text.length - 1 || row.some(function(f){return f!==''})) rows.push(row);\n      row = []; i++; continue;\n    }\n    field += c; i++;\n  }\n  return rows;\n}\n\n// ── RFC 4180 Serializer ─────────────────────────────────────────────────────\nfunction serializeCSV(headers, rows, delim) {\n  delim = delim || ',';\n  function escField(f) {\n    f = String(f == null ? '' : f);\n    if (f.indexOf(delim) !== -1 || f.indexOf('\"') !== -1 || f.indexOf('\\n') !== -1 || f.indexOf('\\r') !== -1) {\n      return '\"' + f.replace(/\"/g, '\"\"') + '\"';\n    }\n    return f;\n  }\n  var lines = [];\n  if (headers.length) lines.push(headers.map(escField).join(delim));\n  rows.forEach(function(r){ lines.push(r.map(escField).join(delim)); });\n  return lines.join('\\r\\n');\n}\n\n// ── Normalize rows to header width ─────────────────────────────────────────\nfunction normalizeRows() {\n  var w = state.headers.length;\n  state.rows.forEach(function(r) {\n    while (r.length \u003c w) r.push('');\n    r.length = w;\n  });\n}\n\n// ── Filtered / sorted view ─────────────────────────────────────────────────\nfunction getView() {\n  var rows = state.rows.slice();\n  // filter\n  var q = state.filter.trim().toLowerCase();\n  if (q) {\n    rows = rows.filter(function(r) {\n      return r.some(function(c){ return String(c).toLowerCase().indexOf(q) !== -1; });\n    });\n  }\n  // sort\n  if (state.sortCol \u003e= 0 \u0026\u0026 state.sortCol \u003c state.headers.length) {\n    var col = state.sortCol, dir = state.sortDir;\n    rows = rows.slice().sort(function(a, b) {\n      var av = a[col] || '', bv = b[col] || '';\n      var an = parseFloat(av), bn = parseFloat(bv);\n      if (!isNaN(an) \u0026\u0026 !isNaN(bn)) return (an - bn) * dir;\n      return av.localeCompare(bv) * dir;\n    });\n  }\n  return rows;\n}\n\n// ── Render table ────────────────────────────────────────────────────────────\nfunction render() {\n  var view = getView();\n  var filtered = state.filter.trim() !== '' || state.sortCol \u003e= 0;\n\n  // status\n  statusText.textContent = state.rows.length + ' row' + (state.rows.length !== 1 ? 's' : '') + ', ' +\n    state.headers.length + ' column' + (state.headers.length !== 1 ? 's' : '');\n  if (state.filter.trim()) {\n    filteredNote.textContent = 'Showing ' + view.length + ' of ' + state.rows.length + ' rows (filtered)';\n    filteredNote.style.display = '';\n  } else {\n    filteredNote.style.display = 'none';\n  }\n\n  if (!state.headers.length \u0026\u0026 !state.rows.length) {\n    tableWrap.innerHTML = '\u003cdiv class=\"ca-empty\"\u003eNo data. Import a CSV or click \"+ Row\" / \"+ Column\" to start.\u003c/div\u003e';\n    return;\n  }\n\n  // build table\n  var html = '\u003ctable\u003e\u003cthead\u003e\u003ctr\u003e';\n  html += '\u003cth class=\"ca-th-num\"\u003e#\u003c/th\u003e';\n  state.headers.forEach(function(h, ci) {\n    var sortIcon = '⇅';\n    var sortCls = '';\n    if (state.sortCol === ci) { sortIcon = state.sortDir === 1 ? '↑' : '↓'; sortCls = ' ca-sort-active'; }\n    html += '\u003cth class=\"ca-th-col\" data-ci=\"' + ci + '\"\u003e' +\n      '\u003cdiv class=\"ca-th-inner\"\u003e' +\n      '\u003cinput class=\"ca-th-name\" data-ci=\"' + ci + '\" value=\"' + escAttr(h) + '\" title=\"Rename column\"\u003e' +\n      '\u003cbutton class=\"ca-sort-btn' + sortCls + '\" data-ci=\"' + ci + '\" title=\"Sort\"\u003e' + sortIcon + '\u003c/button\u003e' +\n      '\u003cbutton class=\"ca-del-col-btn\" data-ci=\"' + ci + '\" title=\"Delete column\"\u003e✕\u003c/button\u003e' +\n      '\u003c/div\u003e\u003c/th\u003e';\n  });\n  html += '\u003cth class=\"ca-th-del\"\u003e\u003c/th\u003e';\n  html += '\u003c/tr\u003e\u003c/thead\u003e\u003ctbody\u003e';\n\n  view.forEach(function(row, vi) {\n    // find original index for row identity (for filtering we just use view rows directly,\n    // but edits need to map back; we store original row reference)\n    var origIdx = state.rows.indexOf(row);\n    html += '\u003ctr\u003e';\n    html += '\u003ctd class=\"ca-td-num\"\u003e' + (vi + 1) + '\u003c/td\u003e';\n    state.headers.forEach(function(_, ci) {\n      var val = row[ci] != null ? row[ci] : '';\n      html += '\u003ctd class=\"ca-td-cell\"\u003e\u003cinput type=\"text\" value=\"' + escAttr(val) + '\" data-orig=\"' + origIdx + '\" data-ci=\"' + ci + '\"\u003e\u003c/td\u003e';\n    });\n    html += '\u003ctd class=\"ca-td-del\"\u003e\u003cbutton data-orig=\"' + origIdx + '\" title=\"Delete row\"\u003e✕\u003c/button\u003e\u003c/td\u003e';\n    html += '\u003c/tr\u003e';\n  });\n\n  html += '\u003c/tbody\u003e\u003c/table\u003e';\n  tableWrap.innerHTML = html;\n\n  // ── Bind events ─────────────────────────────────────────────────────────\n  // Header rename\n  tableWrap.querySelectorAll('.ca-th-name').forEach(function(inp) {\n    inp.addEventListener('change', function() {\n      state.headers[+this.dataset.ci] = this.value;\n      invalidateExport();\n    });\n    inp.addEventListener('keydown', function(e){ if(e.key==='Enter') this.blur(); });\n  });\n  // Sort buttons\n  tableWrap.querySelectorAll('.ca-sort-btn').forEach(function(btn) {\n    btn.addEventListener('click', function() {\n      var ci = +this.dataset.ci;\n      if (state.sortCol === ci) {\n        if (state.sortDir === 1) { state.sortDir = -1; }\n        else { state.sortCol = -1; state.sortDir = 1; }\n      } else { state.sortCol = ci; state.sortDir = 1; }\n      render();\n    });\n  });\n  // Delete column\n  tableWrap.querySelectorAll('.ca-del-col-btn').forEach(function(btn) {\n    btn.addEventListener('click', function() {\n      var ci = +this.dataset.ci;\n      if (!confirm('Delete column \"' + state.headers[ci] + '\"?')) return;\n      state.headers.splice(ci, 1);\n      state.rows.forEach(function(r){ r.splice(ci, 1); });\n      if (state.sortCol === ci) { state.sortCol = -1; }\n      else if (state.sortCol \u003e ci) { state.sortCol--; }\n      invalidateExport(); render();\n    });\n  });\n  // Cell edit\n  tableWrap.querySelectorAll('.ca-td-cell input').forEach(function(inp) {\n    inp.addEventListener('change', function() {\n      var orig = +this.dataset.orig, ci = +this.dataset.ci;\n      if (state.rows[orig]) { state.rows[orig][ci] = this.value; invalidateExport(); }\n    });\n    inp.addEventListener('keydown', function(e){\n      if (e.key === 'Enter') { e.preventDefault(); moveFocus(this, 1, 0); }\n      if (e.key === 'Tab' \u0026\u0026 !e.shiftKey) { e.preventDefault(); moveFocus(this, 0, 1); }\n      if (e.key === 'Tab' \u0026\u0026 e.shiftKey) { e.preventDefault(); moveFocus(this, 0, -1); }\n    });\n  });\n  // Delete row\n  tableWrap.querySelectorAll('.ca-td-del button').forEach(function(btn) {\n    btn.addEventListener('click', function() {\n      var orig = +this.dataset.orig;\n      state.rows.splice(orig, 1);\n      invalidateExport(); render();\n    });\n  });\n}\n\nfunction moveFocus(inp, dr, dc) {\n  var inputs = Array.from(tableWrap.querySelectorAll('.ca-td-cell input'));\n  var idx = inputs.indexOf(inp);\n  if (idx \u003c 0) return;\n  var cols = state.headers.length;\n  var newIdx = idx + dr * cols + dc;\n  if (newIdx \u003e= 0 \u0026\u0026 newIdx \u003c inputs.length) inputs[newIdx].focus();\n}\n\nfunction escAttr(s) {\n  return String(s == null ? '' : s).replace(/\u0026/g,'\u0026amp;').replace(/\"/g,'\u0026quot;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n}\n\nfunction showMsg(txt, type) {\n  msgEl.textContent = txt;\n  msgEl.className = 'ca-msg ' + (type || 'ok');\n  clearTimeout(msgEl._t);\n  msgEl._t = setTimeout(function(){ msgEl.className = 'ca-msg'; }, 3500);\n}\n\nfunction invalidateExport() {\n  state.lastCsv = '';\n  exportTa.value = '';\n  copyBtn.disabled = true;\n  downloadBtn.disabled = true;\n}\n\nfunction showEditor() { editorSec.style.display = ''; }\n\n// ── Import logic ────────────────────────────────────────────────────────────\nfunction importText(text) {\n  var delim = delimEl.value;\n  var rows = parseCSV(text.trim(), delim);\n  if (!rows.length) { showMsg('No data found in the input.', 'err'); return; }\n  var hasHeader = headerCb.checked;\n  if (hasHeader \u0026\u0026 rows.length \u003e 0) {\n    state.headers = rows[0].map(function(h){ return h.trim(); });\n    state.rows = rows.slice(1).map(function(r){ return r.slice(); });\n  } else {\n    // generate col names\n    var maxCols = rows.reduce(function(m,r){ return Math.max(m, r.length); }, 0);\n    state.headers = [];\n    for (var i=0;i\u003cmaxCols;i++) state.headers.push('Column ' + (i+1));\n    state.rows = rows.map(function(r){ return r.slice(); });\n  }\n  normalizeRows();\n  state.sortCol = -1; state.sortDir = 1; state.filter = ''; searchEl.value = '';\n  invalidateExport();\n  showEditor();\n  render();\n  showMsg('Imported ' + state.rows.length + ' row(s) and ' + state.headers.length + ' column(s).', 'ok');\n}\n\nimportBtn.addEventListener('click', function() {\n  var text = pasteEl.value;\n  if (!text.trim()) { showMsg('Please paste CSV text first.', 'err'); return; }\n  importText(text);\n});\n\nfileInput.addEventListener('change', function() {\n  var file = this.files[0];\n  if (!file) return;\n  var reader = new FileReader();\n  reader.onload = function(e) { importText(e.target.result); };\n  reader.onerror = function() { showMsg('Could not read file.', 'err'); };\n  reader.readAsText(file, 'UTF-8');\n  fileInput.value = '';\n});\n\nsampleBtn.addEventListener('click', function() {\n  pasteEl.value = 'Name,Age,City,Country,Score\\nAlice,30,New York,USA,92.5\\nBob,25,London,UK,87.0\\nCarla,34,Tokyo,Japan,95.5\\n\"Smith, John\",28,\"Paris, France\",France,80.0\\nEva,22,Sydney,Australia,78.5\\nFrank,45,Berlin,Germany,88.0';\n  delimEl.value = ',';\n  headerCb.checked = true;\n  showMsg('Sample data loaded. Click \"Import from Paste\".', 'ok');\n});\n\n// ── Search / filter ────────────────────────────────────────────────────────\nsearchEl.addEventListener('input', function() {\n  state.filter = this.value;\n  render();\n});\n\n// ── Add row / column ────────────────────────────────────────────────────────\naddRowBtn.addEventListener('click', function() {\n  if (!state.headers.length) {\n    state.headers = ['Column 1'];\n  }\n  var newRow = [];\n  for (var i=0;i\u003cstate.headers.length;i++) newRow.push('');\n  state.rows.push(newRow);\n  invalidateExport(); showEditor(); render();\n  // scroll to bottom\n  setTimeout(function(){ tableWrap.scrollTop = tableWrap.scrollHeight; }, 30);\n});\n\naddColBtn.addEventListener('click', function() {\n  var name = prompt('New column name:', 'Column ' + (state.headers.length + 1));\n  if (name === null) return;\n  name = name.trim() || ('Column ' + (state.headers.length + 1));\n  state.headers.push(name);\n  state.rows.forEach(function(r){ r.push(''); });\n  invalidateExport(); showEditor(); render();\n});\n\nclearBtn.addEventListener('click', function() {\n  if (!confirm('Clear all data?')) return;\n  state.headers = []; state.rows = [];\n  state.sortCol = -1; state.sortDir = 1; state.filter = '';\n  searchEl.value = ''; pasteEl.value = '';\n  invalidateExport(); render();\n  editorSec.style.display = 'none';\n});\n\n// ── Export ───────────────────────────────────────────────────────────────\ngenerateBtn.addEventListener('click', function() {\n  var delim = delimEl.value;\n  // export all rows in original order, not filtered view\n  var csv = serializeCSV(state.headers, state.rows, delim);\n  state.lastCsv = csv;\n  exportTa.value = csv;\n  copyBtn.disabled = false;\n  downloadBtn.disabled = false;\n  showMsg('CSV generated.', 'ok');\n});\n\ncopyBtn.addEventListener('click', function() {\n  if (!state.lastCsv) return;\n  if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n    navigator.clipboard.writeText(state.lastCsv).then(function() {\n      showMsg('Copied to clipboard!', 'ok');\n    }).catch(function() { fallbackCopy(); });\n  } else { fallbackCopy(); }\n});\n\nfunction fallbackCopy() {\n  exportTa.select();\n  try { document.execCommand('copy'); showMsg('Copied to clipboard!', 'ok'); }\n  catch(e) { showMsg('Copy failed. Please select and copy manually.', 'err'); }\n}\n\ndownloadBtn.addEventListener('click', function() {\n  if (!state.lastCsv) return;\n  var blob = new Blob([state.lastCsv], {type:'text/csv;charset=utf-8;'});\n  var url = URL.createObjectURL(blob);\n  var a = document.createElement('a');\n  a.href = url; a.download = 'data.csv';\n  document.body.appendChild(a); a.click();\n  setTimeout(function(){ document.body.removeChild(a); URL.revokeObjectURL(url); }, 500);\n  showMsg('Download started.', 'ok');\n});\n\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch3 id=\"related-tools\"\u003eRelated Tools\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003eConvert JSON to CSV → \u003ca href=\"https://productivity-works.com/tools/json-to-csv/\"\u003eJSON to CSV Converter\u003c/a\u003e\n\nGenerate markdown tables → \u003ca href=\"https://productivity-works.com/tools/markdown-table-generator/\"\u003eMarkdown Table Generator\u003c/a\u003e\n\u003c/p\u003e","title":"CSV Editor"},{"content":"Convert CSV data to JSON instantly — or reverse it. Paste text, upload a file, pick your format, and download the result. Everything runs in your browser; no data is sent to any server.\nRelated tools: JSON to CSV Converter · JSON Formatter CSV → JSON JSON → CSV Options Delimiter Auto-detect Comma (,) Tab (\\t) Semicolon (;) Pipe (|) Custom… Custom char Output format Array of objects Array of arrays Nested (group by first col) First row as headers Type inference Pretty print CSV Input drag \u0026amp; drop .csv Upload .csv No file chosen Convert to JSON Clear Load sample Rows: 0 Columns: 0 Delimiter detected: — Preview Table JSON Output Copied! Copy JSON Download .json Records: 0 Size: 0 B Options Output delimiter Comma (,) Tab (\\t) Semicolon (;) Pipe (|) Include header row Quote all strings JSON Input Convert to CSV Clear Load sample CSV Output Copied! Copy CSV Download .csv Rows: 0 Columns: 0 Size: 0 B How It Works CSV → JSON\nPaste CSV text or upload a .csv file (drag and drop supported). Choose a delimiter — or leave it on Auto-detect and the tool will count occurrences on the first line to find the right separator. Toggle First row as headers to control whether row 1 becomes object keys or data. Pick an output format: Array of objects — most common; each row becomes { \u0026quot;col\u0026quot;: \u0026quot;val\u0026quot;, … }. Array of arrays — compact; rows stay as plain arrays. Nested — groups rows into an object keyed by the first column\u0026rsquo;s value. Type inference automatically converts numbers, true/false, and empty/null/n/a cells to proper JSON types. Hit Convert, review the preview table, then Copy or Download. JSON → CSV\nSwitch to the JSON → CSV tab, paste any flat JSON array, choose your output delimiter, and download the result.\nQuoted fields with commas inside (e.g. \u0026quot;Lee, James\u0026quot;) are handled correctly per RFC 4180.\nRelated tools: JSON to CSV Converter · JSON Formatter Related Articles How to Use AI for Excel Automation 2026 How to Use ChatGPT for Data Analysis 2026 Google Sheets vs Excel 2026: Which Is Better for You? ","permalink":"https://productivity-works.com/tools/csv-to-json/","summary":"\u003cp\u003eConvert CSV data to JSON instantly — or reverse it. Paste text, upload a file, pick your format, and download the result. Everything runs in your browser; no data is sent to any server.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eRelated tools:\u003c/strong\u003e \u003ca href=\"https://productivity-works.com/tools/json-to-csv/\"\u003eJSON to CSV Converter\u003c/a\u003e\n  ·  \u003ca href=\"https://productivity-works.com/tools/json-formatter/\"\u003eJSON Formatter\u003c/a\u003e\n\u003c/p\u003e\n\u003chr\u003e\n\u003cdiv id=\"cj-app\"\u003e\n\u003cstyle\u003e\n/* ── Reset / base ── */\n#cj-app *, #cj-app *::before, #cj-app *::after { box-sizing: border-box; margin: 0; padding: 0; }\n#cj-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 14px;\n  color: #1e293b;\n  line-height: 1.6;\n}\n\n/* ── Layout ── */\n#cj-app .cj-card {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 20px;\n  margin-bottom: 16px;\n}\n\n/* ── Section headers ── */\n#cj-app .cj-section-title {\n  font-size: 13px;\n  font-weight: 700;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: .05em;\n  margin-bottom: 10px;\n}\n\n/* ── Mode tabs ── */\n#cj-app .cj-tabs {\n  display: flex;\n  gap: 4px;\n  margin-bottom: 16px;\n  background: #f1f5f9;\n  border-radius: 8px;\n  padding: 4px;\n}\n#cj-app .cj-tab {\n  flex: 1;\n  padding: 8px 12px;\n  border: none;\n  border-radius: 6px;\n  background: transparent;\n  font-size: 13px;\n  font-weight: 600;\n  color: #64748b;\n  cursor: pointer;\n  transition: all .15s;\n}\n#cj-app .cj-tab.active {\n  background: #fff;\n  color: #0f172a;\n  box-shadow: 0 1px 3px rgba(0,0,0,.12);\n}\n\n/* ── Controls row ── */\n#cj-app .cj-controls {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  align-items: flex-end;\n  margin-bottom: 14px;\n}\n#cj-app .cj-control-group {\n  display: flex;\n  flex-direction: column;\n  gap: 4px;\n}\n#cj-app .cj-label {\n  font-size: 12px;\n  font-weight: 600;\n  color: #475569;\n}\n#cj-app select, #cj-app input[type=\"text\"] {\n  border: 1px solid #cbd5e1;\n  border-radius: 6px;\n  padding: 6px 10px;\n  font-size: 13px;\n  color: #1e293b;\n  background: #fff;\n  outline: none;\n  transition: border-color .15s;\n}\n#cj-app select:focus, #cj-app input[type=\"text\"]:focus {\n  border-color: #3b82f6;\n}\n#cj-app .cj-toggle-label {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 13px;\n  color: #1e293b;\n  cursor: pointer;\n  user-select: none;\n}\n#cj-app .cj-toggle-label input[type=\"checkbox\"] {\n  width: 15px;\n  height: 15px;\n  accent-color: #3b82f6;\n  cursor: pointer;\n}\n\n/* ── Textarea / file drop ── */\n#cj-app .cj-drop-zone {\n  position: relative;\n  border: 2px dashed #cbd5e1;\n  border-radius: 8px;\n  transition: border-color .15s, background .15s;\n}\n#cj-app .cj-drop-zone.drag-over {\n  border-color: #3b82f6;\n  background: #eff6ff;\n}\n#cj-app .cj-textarea {\n  width: 100%;\n  min-height: 160px;\n  border: none;\n  border-radius: 8px;\n  padding: 12px;\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  font-size: 12.5px;\n  color: #0f172a;\n  background: transparent;\n  resize: vertical;\n  outline: none;\n  display: block;\n}\n#cj-app .cj-drop-hint {\n  position: absolute;\n  bottom: 8px;\n  right: 12px;\n  font-size: 11px;\n  color: #94a3b8;\n  pointer-events: none;\n}\n\n/* ── File upload button ── */\n#cj-app .cj-file-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-top: 8px;\n}\n#cj-app .cj-btn-file {\n  display: inline-flex;\n  align-items: center;\n  gap: 5px;\n  padding: 6px 14px;\n  border: 1px solid #94a3b8;\n  border-radius: 6px;\n  background: #f8fafc;\n  font-size: 12px;\n  font-weight: 600;\n  color: #475569;\n  cursor: pointer;\n  transition: background .15s;\n}\n#cj-app .cj-btn-file:hover { background: #f1f5f9; }\n#cj-app #cj-file-input { display: none; }\n#cj-app .cj-file-name {\n  font-size: 12px;\n  color: #64748b;\n  font-style: italic;\n}\n\n/* ── Action buttons ── */\n#cj-app .cj-actions {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-top: 12px;\n}\n#cj-app .cj-btn {\n  padding: 8px 18px;\n  border: none;\n  border-radius: 7px;\n  font-size: 13px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: opacity .15s, transform .1s;\n}\n#cj-app .cj-btn:hover { opacity: .88; }\n#cj-app .cj-btn:active { transform: scale(.97); }\n#cj-app .cj-btn-primary {\n  background: #3b82f6;\n  color: #fff;\n}\n#cj-app .cj-btn-secondary {\n  background: #f1f5f9;\n  color: #334155;\n  border: 1px solid #cbd5e1;\n}\n#cj-app .cj-btn-success {\n  background: #10b981;\n  color: #fff;\n}\n#cj-app .cj-btn-sm {\n  padding: 5px 12px;\n  font-size: 12px;\n}\n\n/* ── Stats bar ── */\n#cj-app .cj-stats {\n  display: flex;\n  gap: 16px;\n  font-size: 12px;\n  color: #64748b;\n  margin-top: 8px;\n  flex-wrap: wrap;\n}\n#cj-app .cj-stat-item strong { color: #0f172a; }\n\n/* ── Output area ── */\n#cj-app .cj-output-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 8px;\n  flex-wrap: wrap;\n  gap: 8px;\n}\n#cj-app .cj-output-textarea {\n  width: 100%;\n  min-height: 220px;\n  border: 1px solid #e2e8f0;\n  border-radius: 8px;\n  padding: 12px;\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  font-size: 12px;\n  color: #0f172a;\n  background: #f8fafc;\n  resize: vertical;\n  outline: none;\n}\n\n/* ── Preview table ── */\n#cj-app .cj-table-wrap {\n  overflow-x: auto;\n  border: 1px solid #e2e8f0;\n  border-radius: 8px;\n  max-height: 280px;\n  overflow-y: auto;\n}\n#cj-app .cj-table {\n  border-collapse: collapse;\n  width: 100%;\n  font-size: 12px;\n}\n#cj-app .cj-table th {\n  background: #f1f5f9;\n  color: #475569;\n  font-weight: 700;\n  padding: 7px 12px;\n  text-align: left;\n  border-bottom: 1px solid #e2e8f0;\n  white-space: nowrap;\n  position: sticky;\n  top: 0;\n}\n#cj-app .cj-table td {\n  padding: 5px 12px;\n  border-bottom: 1px solid #f1f5f9;\n  white-space: nowrap;\n  max-width: 200px;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n#cj-app .cj-table tr:hover td { background: #f8fafc; }\n#cj-app .cj-table .cj-type-num { color: #0891b2; }\n#cj-app .cj-table .cj-type-bool { color: #7c3aed; }\n#cj-app .cj-table .cj-type-null { color: #94a3b8; font-style: italic; }\n\n/* ── Message / error ── */\n#cj-app .cj-msg {\n  padding: 10px 14px;\n  border-radius: 7px;\n  font-size: 13px;\n  font-weight: 500;\n  margin-top: 10px;\n  display: none;\n}\n#cj-app .cj-msg.show { display: block; }\n#cj-app .cj-msg.error { background: #fef2f2; color: #b91c1c; border: 1px solid #fecaca; }\n#cj-app .cj-msg.success { background: #f0fdf4; color: #166534; border: 1px solid #bbf7d0; }\n\n/* ── Copy feedback ── */\n#cj-app .cj-copy-feedback {\n  font-size: 12px;\n  color: #10b981;\n  font-weight: 600;\n  opacity: 0;\n  transition: opacity .3s;\n}\n#cj-app .cj-copy-feedback.show { opacity: 1; }\n\n/* ── Responsive ── */\n@media (max-width: 560px) {\n  #cj-app .cj-controls { flex-direction: column; align-items: stretch; }\n  #cj-app .cj-output-header { flex-direction: column; align-items: flex-start; }\n}\n\u003c/style\u003e\n\u003c!-- ═══════════════════════════════════════════\n     MODE TABS\n═══════════════════════════════════════════ --\u003e\n\u003cdiv class=\"cj-tabs\"\u003e\n  \u003cbutton class=\"cj-tab active\" id=\"cj-tab-csv2json\" onclick=\"cjSetMode('csv2json')\"\u003eCSV → JSON\u003c/button\u003e\n  \u003cbutton class=\"cj-tab\" id=\"cj-tab-json2csv\" onclick=\"cjSetMode('json2csv')\"\u003eJSON → CSV\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- ═══════════════════════════════════════════\n     CSV → JSON PANEL\n═══════════════════════════════════════════ --\u003e\n\u003cdiv id=\"cj-panel-csv2json\"\u003e\n  \u003c!-- Options card --\u003e\n  \u003cdiv class=\"cj-card\"\u003e\n    \u003cdiv class=\"cj-section-title\"\u003eOptions\u003c/div\u003e\n    \u003cdiv class=\"cj-controls\"\u003e\n      \u003c!-- Delimiter --\u003e\n      \u003cdiv class=\"cj-control-group\"\u003e\n        \u003cspan class=\"cj-label\"\u003eDelimiter\u003c/span\u003e\n        \u003cselect id=\"cj-delimiter\" onchange=\"cjOnDelimiterChange()\"\u003e\n          \u003coption value=\"auto\"\u003eAuto-detect\u003c/option\u003e\n          \u003coption value=\",\"\u003eComma (,)\u003c/option\u003e\n          \u003coption value=\"\t\"\u003eTab (\\t)\u003c/option\u003e\n          \u003coption value=\";\"\u003eSemicolon (;)\u003c/option\u003e\n          \u003coption value=\"|\"\u003ePipe (|)\u003c/option\u003e\n          \u003coption value=\"custom\"\u003eCustom…\u003c/option\u003e\n        \u003c/select\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"cj-control-group\" id=\"cj-custom-delim-group\" style=\"display:none;\"\u003e\n        \u003cspan class=\"cj-label\"\u003eCustom char\u003c/span\u003e\n        \u003cinput type=\"text\" id=\"cj-custom-delim\" maxlength=\"3\" placeholder=\"e.g. ~\" style=\"width:80px;\" /\u003e\n      \u003c/div\u003e\n      \u003c!-- Output format --\u003e\n      \u003cdiv class=\"cj-control-group\"\u003e\n        \u003cspan class=\"cj-label\"\u003eOutput format\u003c/span\u003e\n        \u003cselect id=\"cj-format\"\u003e\n          \u003coption value=\"objects\"\u003eArray of objects\u003c/option\u003e\n          \u003coption value=\"arrays\"\u003eArray of arrays\u003c/option\u003e\n          \u003coption value=\"nested\"\u003eNested (group by first col)\u003c/option\u003e\n        \u003c/select\u003e\n      \u003c/div\u003e\n      \u003c!-- Toggles --\u003e\n      \u003cdiv class=\"cj-control-group\" style=\"justify-content:flex-end;gap:8px;\"\u003e\n        \u003clabel class=\"cj-toggle-label\"\u003e\n          \u003cinput type=\"checkbox\" id=\"cj-headers\" checked /\u003e\n          First row as headers\n        \u003c/label\u003e\n        \u003clabel class=\"cj-toggle-label\"\u003e\n          \u003cinput type=\"checkbox\" id=\"cj-typeinfer\" checked /\u003e\n          Type inference\n        \u003c/label\u003e\n        \u003clabel class=\"cj-toggle-label\"\u003e\n          \u003cinput type=\"checkbox\" id=\"cj-pretty\" checked /\u003e\n          Pretty print\n        \u003c/label\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Input card --\u003e\n  \u003cdiv class=\"cj-card\"\u003e\n    \u003cdiv class=\"cj-section-title\"\u003eCSV Input\u003c/div\u003e\n    \u003cdiv class=\"cj-drop-zone\" id=\"cj-drop-zone\"\u003e\n      \u003ctextarea class=\"cj-textarea\" id=\"cj-csv-input\"\n        placeholder=\"Paste CSV here, or drag \u0026amp; drop a .csv file…\u0026#10;\u0026#10;Example:\u0026#10;name,age,active\u0026#10;Alice,30,true\u0026#10;Bob,25,false\"\u003e\u003c/textarea\u003e\n      \u003cspan class=\"cj-drop-hint\"\u003edrag \u0026amp; drop .csv\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cj-file-row\"\u003e\n      \u003clabel class=\"cj-btn-file\" for=\"cj-file-input\"\u003e\n        \u003csvg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\"\u003e\u003cpath d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/\u003e\u003cpolyline points=\"17 8 12 3 7 8\"/\u003e\u003cline x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"/\u003e\u003c/svg\u003e\n        Upload .csv\n      \u003c/label\u003e\n      \u003cinput type=\"file\" id=\"cj-file-input\" accept=\".csv,text/csv,text/plain\" /\u003e\n      \u003cspan class=\"cj-file-name\" id=\"cj-file-name\"\u003eNo file chosen\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cj-actions\"\u003e\n      \u003cbutton class=\"cj-btn cj-btn-primary\" onclick=\"cjConvert()\"\u003eConvert to JSON\u003c/button\u003e\n      \u003cbutton class=\"cj-btn cj-btn-secondary\" onclick=\"cjClearInput()\"\u003eClear\u003c/button\u003e\n      \u003cbutton class=\"cj-btn cj-btn-secondary\" onclick=\"cjLoadSample()\"\u003eLoad sample\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cj-msg\" id=\"cj-input-msg\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"cj-stats\" id=\"cj-input-stats\" style=\"display:none;\"\u003e\n      \u003cspan class=\"cj-stat-item\"\u003eRows: \u003cstrong id=\"cj-row-count\"\u003e0\u003c/strong\u003e\u003c/span\u003e\n      \u003cspan class=\"cj-stat-item\"\u003eColumns: \u003cstrong id=\"cj-col-count\"\u003e0\u003c/strong\u003e\u003c/span\u003e\n      \u003cspan class=\"cj-stat-item\"\u003eDelimiter detected: \u003cstrong id=\"cj-detected-delim\"\u003e—\u003c/strong\u003e\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Preview card --\u003e\n  \u003cdiv class=\"cj-card\" id=\"cj-preview-card\" style=\"display:none;\"\u003e\n    \u003cdiv class=\"cj-section-title\"\u003ePreview Table\u003c/div\u003e\n    \u003cdiv class=\"cj-table-wrap\" id=\"cj-table-wrap\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Output card --\u003e\n  \u003cdiv class=\"cj-card\" id=\"cj-output-card\" style=\"display:none;\"\u003e\n    \u003cdiv class=\"cj-output-header\"\u003e\n      \u003cdiv class=\"cj-section-title\" style=\"margin-bottom:0;\"\u003eJSON Output\u003c/div\u003e\n      \u003cdiv style=\"display:flex;align-items:center;gap:8px;flex-wrap:wrap;\"\u003e\n        \u003cspan class=\"cj-copy-feedback\" id=\"cj-copy-feedback\"\u003eCopied!\u003c/span\u003e\n        \u003cbutton class=\"cj-btn cj-btn-secondary cj-btn-sm\" onclick=\"cjCopy()\"\u003eCopy JSON\u003c/button\u003e\n        \u003cbutton class=\"cj-btn cj-btn-success cj-btn-sm\" onclick=\"cjDownloadJSON()\"\u003eDownload .json\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003ctextarea class=\"cj-output-textarea\" id=\"cj-json-output\" readonly\u003e\u003c/textarea\u003e\n    \u003cdiv class=\"cj-stats\" id=\"cj-output-stats\"\u003e\n      \u003cspan class=\"cj-stat-item\"\u003eRecords: \u003cstrong id=\"cj-record-count\"\u003e0\u003c/strong\u003e\u003c/span\u003e\n      \u003cspan class=\"cj-stat-item\"\u003eSize: \u003cstrong id=\"cj-output-size\"\u003e0 B\u003c/strong\u003e\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\u003c!-- /panel csv2json --\u003e\n\u003c!-- ═══════════════════════════════════════════\n     JSON → CSV PANEL\n═══════════════════════════════════════════ --\u003e\n\u003cdiv id=\"cj-panel-json2csv\" style=\"display:none;\"\u003e\n  \u003cdiv class=\"cj-card\"\u003e\n    \u003cdiv class=\"cj-section-title\"\u003eOptions\u003c/div\u003e\n    \u003cdiv class=\"cj-controls\"\u003e\n      \u003cdiv class=\"cj-control-group\"\u003e\n        \u003cspan class=\"cj-label\"\u003eOutput delimiter\u003c/span\u003e\n        \u003cselect id=\"cj-j2c-delimiter\"\u003e\n          \u003coption value=\",\"\u003eComma (,)\u003c/option\u003e\n          \u003coption value=\"\t\"\u003eTab (\\t)\u003c/option\u003e\n          \u003coption value=\";\"\u003eSemicolon (;)\u003c/option\u003e\n          \u003coption value=\"|\"\u003ePipe (|)\u003c/option\u003e\n        \u003c/select\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"cj-control-group\" style=\"justify-content:flex-end;gap:8px;\"\u003e\n        \u003clabel class=\"cj-toggle-label\"\u003e\n          \u003cinput type=\"checkbox\" id=\"cj-j2c-headers\" checked /\u003e\n          Include header row\n        \u003c/label\u003e\n        \u003clabel class=\"cj-toggle-label\"\u003e\n          \u003cinput type=\"checkbox\" id=\"cj-j2c-quote\" checked /\u003e\n          Quote all strings\n        \u003c/label\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cj-card\"\u003e\n    \u003cdiv class=\"cj-section-title\"\u003eJSON Input\u003c/div\u003e\n    \u003cdiv class=\"cj-drop-zone\"\u003e\n      \u003ctextarea class=\"cj-textarea\" id=\"cj-json-input\"\n        placeholder='Paste a JSON array here…\u0026#10;\u0026#10;Example:\u0026#10;[{\"name\":\"Alice\",\"age\":30},{\"name\":\"Bob\",\"age\":25}]'\u003e\u003c/textarea\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cj-actions\"\u003e\n      \u003cbutton class=\"cj-btn cj-btn-primary\" onclick=\"cjConvertJ2C()\"\u003eConvert to CSV\u003c/button\u003e\n      \u003cbutton class=\"cj-btn cj-btn-secondary\" onclick=\"document.getElementById('cj-json-input').value=''\"\u003eClear\u003c/button\u003e\n      \u003cbutton class=\"cj-btn cj-btn-secondary\" onclick=\"cjLoadJ2CSample()\"\u003eLoad sample\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"cj-msg\" id=\"cj-j2c-input-msg\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"cj-card\" id=\"cj-j2c-output-card\" style=\"display:none;\"\u003e\n    \u003cdiv class=\"cj-output-header\"\u003e\n      \u003cdiv class=\"cj-section-title\" style=\"margin-bottom:0;\"\u003eCSV Output\u003c/div\u003e\n      \u003cdiv style=\"display:flex;align-items:center;gap:8px;flex-wrap:wrap;\"\u003e\n        \u003cspan class=\"cj-copy-feedback\" id=\"cj-j2c-copy-feedback\"\u003eCopied!\u003c/span\u003e\n        \u003cbutton class=\"cj-btn cj-btn-secondary cj-btn-sm\" onclick=\"cjCopyCSV()\"\u003eCopy CSV\u003c/button\u003e\n        \u003cbutton class=\"cj-btn cj-btn-success cj-btn-sm\" onclick=\"cjDownloadCSV()\"\u003eDownload .csv\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003ctextarea class=\"cj-output-textarea\" id=\"cj-csv-output\" readonly\u003e\u003c/textarea\u003e\n    \u003cdiv class=\"cj-stats\" id=\"cj-j2c-output-stats\"\u003e\n      \u003cspan class=\"cj-stat-item\"\u003eRows: \u003cstrong id=\"cj-j2c-row-count\"\u003e0\u003c/strong\u003e\u003c/span\u003e\n      \u003cspan class=\"cj-stat-item\"\u003eColumns: \u003cstrong id=\"cj-j2c-col-count\"\u003e0\u003c/strong\u003e\u003c/span\u003e\n      \u003cspan class=\"cj-stat-item\"\u003eSize: \u003cstrong id=\"cj-j2c-size\"\u003e0 B\u003c/strong\u003e\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\u003c!-- /panel json2csv --\u003e\n\u003c/div\u003e\u003c!-- /#cj-app --\u003e\n\u003cscript\u003e\n(function () {\n  'use strict';\n\n  /* ── Utilities ── */\n  function showMsg(id, text, type) {\n    var el = document.getElementById(id);\n    el.textContent = text;\n    el.className = 'cj-msg show ' + type;\n  }\n  function hideMsg(id) {\n    var el = document.getElementById(id);\n    el.className = 'cj-msg';\n  }\n  function show(id) { var el = document.getElementById(id); if (el) el.style.display = ''; }\n  function hide(id) { var el = document.getElementById(id); if (el) el.style.display = 'none'; }\n  function formatBytes(n) {\n    if (n \u003c 1024) return n + ' B';\n    if (n \u003c 1048576) return (n / 1024).toFixed(1) + ' KB';\n    return (n / 1048576).toFixed(2) + ' MB';\n  }\n\n  /* ── Mode switching ── */\n  window.cjSetMode = function (mode) {\n    ['csv2json', 'json2csv'].forEach(function (m) {\n      document.getElementById('cj-tab-' + m).classList.toggle('active', m === mode);\n      document.getElementById('cj-panel-' + m).style.display = m === mode ? '' : 'none';\n    });\n  };\n\n  /* ── Delimiter auto-detect ── */\n  function detectDelimiter(text) {\n    var candidates = [',', '\\t', ';', '|'];\n    var firstLine = text.split('\\n')[0] || '';\n    var best = ','; var bestCount = 0;\n    candidates.forEach(function (d) {\n      var count = firstLine.split(d).length - 1;\n      if (count \u003e bestCount) { bestCount = count; best = d; }\n    });\n    return best;\n  }\n\n  function getDelimiter() {\n    var sel = document.getElementById('cj-delimiter').value;\n    if (sel === 'auto') return null; // signal: auto\n    if (sel === 'custom') return document.getElementById('cj-custom-delim').value || ',';\n    return sel;\n  }\n\n  window.cjOnDelimiterChange = function () {\n    var sel = document.getElementById('cj-delimiter').value;\n    document.getElementById('cj-custom-delim-group').style.display = sel === 'custom' ? '' : 'none';\n  };\n\n  /* ── CSV Parser (RFC 4180-aware) ── */\n  function parseCSV(text, delimiter) {\n    var rows = [];\n    var len = text.length;\n    var row = [];\n    var field = '';\n    var inQuote = false;\n    var i = 0;\n    var d = delimiter;\n    var dLen = d.length;\n\n    while (i \u003c len) {\n      var ch = text[i];\n\n      if (inQuote) {\n        if (ch === '\"') {\n          if (text[i + 1] === '\"') { field += '\"'; i += 2; continue; }\n          inQuote = false; i++; continue;\n        }\n        field += ch; i++; continue;\n      }\n\n      if (ch === '\"') { inQuote = true; i++; continue; }\n\n      // Check for delimiter (supports multi-char in future)\n      if (text.substr(i, dLen) === d) {\n        row.push(field); field = ''; i += dLen; continue;\n      }\n\n      if (ch === '\\r') {\n        if (text[i + 1] === '\\n') i++;\n        row.push(field); rows.push(row); row = []; field = ''; i++; continue;\n      }\n      if (ch === '\\n') {\n        row.push(field); rows.push(row); row = []; field = ''; i++; continue;\n      }\n\n      field += ch; i++;\n    }\n    // Last field/row\n    if (field !== '' || row.length \u003e 0) { row.push(field); rows.push(row); }\n    // Drop trailing empty row\n    if (rows.length \u0026\u0026 rows[rows.length - 1].every(function (c) { return c === ''; })) rows.pop();\n    return rows;\n  }\n\n  /* ── Type inference ── */\n  function inferValue(s) {\n    if (s === '' || s.toLowerCase() === 'null' || s.toLowerCase() === 'n/a') return null;\n    if (s.toLowerCase() === 'true') return true;\n    if (s.toLowerCase() === 'false') return false;\n    if (s !== '' \u0026\u0026 !isNaN(Number(s)) \u0026\u0026 s.trim() !== '') return Number(s);\n    return s;\n  }\n\n  /* ── Main convert CSV → JSON ── */\n  window.cjConvert = function () {\n    hideMsg('cj-input-msg');\n    var raw = document.getElementById('cj-csv-input').value.trim();\n    if (!raw) { showMsg('cj-input-msg', 'Please paste some CSV data or upload a file.', 'error'); return; }\n\n    var delimSel = getDelimiter();\n    var delim = delimSel === null ? detectDelimiter(raw) : delimSel;\n    var displayDelim = delim === '\\t' ? '\\\\t' : delim;\n\n    var rows = parseCSV(raw, delim);\n    if (!rows.length) { showMsg('cj-input-msg', 'Could not parse any rows.', 'error'); return; }\n\n    var useHeaders = document.getElementById('cj-headers').checked;\n    var useTypeInfer = document.getElementById('cj-typeinfer').checked;\n    var pretty = document.getElementById('cj-pretty').checked;\n    var format = document.getElementById('cj-format').value;\n\n    var headers = useHeaders ? rows[0] : rows[0].map(function (_, i) { return 'col' + (i + 1); });\n    var dataRows = useHeaders ? rows.slice(1) : rows;\n\n    // Stats\n    document.getElementById('cj-row-count').textContent = dataRows.length;\n    document.getElementById('cj-col-count').textContent = headers.length;\n    document.getElementById('cj-detected-delim').textContent = delimSel === null ? '\"' + displayDelim + '\" (auto)' : '\"' + displayDelim + '\"';\n    document.getElementById('cj-input-stats').style.display = '';\n\n    // Build output\n    var result;\n    if (format === 'arrays') {\n      result = useHeaders ? [headers].concat(dataRows.map(function (r) {\n        return r.map(function (c) { return useTypeInfer ? inferValue(c) : c; });\n      })) : dataRows.map(function (r) {\n        return r.map(function (c) { return useTypeInfer ? inferValue(c) : c; });\n      });\n    } else if (format === 'nested') {\n      result = {};\n      dataRows.forEach(function (r) {\n        var key = r[0] || '';\n        var obj = {};\n        headers.slice(1).forEach(function (h, hi) {\n          obj[h] = useTypeInfer ? inferValue(r[hi + 1] || '') : (r[hi + 1] || '');\n        });\n        result[key] = obj;\n      });\n    } else {\n      result = dataRows.map(function (r) {\n        var obj = {};\n        headers.forEach(function (h, hi) {\n          obj[h] = useTypeInfer ? inferValue(r[hi] || '') : (r[hi] || '');\n        });\n        return obj;\n      });\n    }\n\n    var jsonStr = pretty ? JSON.stringify(result, null, 2) : JSON.stringify(result);\n    document.getElementById('cj-json-output').value = jsonStr;\n    document.getElementById('cj-record-count').textContent = Array.isArray(result) ? result.length : Object.keys(result).length;\n    document.getElementById('cj-output-size').textContent = formatBytes(new Blob([jsonStr]).size);\n\n    show('cj-output-card');\n    buildPreviewTable(headers, dataRows, useTypeInfer);\n  };\n\n  /* ── Preview table ── */\n  function buildPreviewTable(headers, dataRows, useTypeInfer) {\n    var maxRows = Math.min(dataRows.length, 50);\n    var html = '\u003ctable class=\"cj-table\"\u003e\u003cthead\u003e\u003ctr\u003e';\n    headers.forEach(function (h) { html += '\u003cth\u003e' + escHtml(h) + '\u003c/th\u003e'; });\n    html += '\u003c/tr\u003e\u003c/thead\u003e\u003ctbody\u003e';\n    for (var i = 0; i \u003c maxRows; i++) {\n      html += '\u003ctr\u003e';\n      headers.forEach(function (_, hi) {\n        var raw = dataRows[i][hi] !== undefined ? dataRows[i][hi] : '';\n        var val = useTypeInfer ? inferValue(raw) : raw;\n        var cls = '';\n        if (typeof val === 'number') cls = 'cj-type-num';\n        else if (typeof val === 'boolean') cls = 'cj-type-bool';\n        else if (val === null) cls = 'cj-type-null';\n        var display = val === null ? 'null' : String(val);\n        html += '\u003ctd class=\"' + cls + '\"\u003e' + escHtml(display) + '\u003c/td\u003e';\n      });\n      html += '\u003c/tr\u003e';\n    }\n    html += '\u003c/tbody\u003e\u003c/table\u003e';\n    if (dataRows.length \u003e 50) html += '\u003cp style=\"font-size:11px;color:#94a3b8;padding:6px 12px;\"\u003eShowing first 50 of ' + dataRows.length + ' rows\u003c/p\u003e","title":"CSV to JSON Converter"},{"content":" Currency Converter Convert any amount between 12 major world currencies using reference exchange rates.\nAmount From \u0026#8644; To — * Rates are approximate reference values. Not for financial transactions. Quick Conversion Table Amount (From Currency) Result (To Currency) All Currencies vs. 1 Unit Shows how much 1 unit of your From currency buys in every currency.\nCurrency Rate (1 unit of From) Reference rates as of approx. May 2025. Exchange rates fluctuate daily due to market conditions, inflation, interest rates, and geopolitical events. Always check a live rate source (e.g., your bank or XE.com) before making any financial decision. Related free tools\nPercentage Calculator Unit Converter Tax Calculator Compound Interest Related Tools Dividend Income Calculator Forex Profit Calculator Related Articles Best Forex Brokers 2026: Platforms for Beginners \u0026amp; Traders Best FX Brokers for English Speakers in Japan 2026: What to Look For ","permalink":"https://productivity-works.com/tools/currency-converter/","summary":"\u003cdiv id=\"crc-app\"\u003e\n\u003cstyle\u003e\n#crc-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 760px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#crc-app h2 {\n  font-size: 1.25rem;\n  font-weight: 700;\n  margin: 1.5rem 0 0.75rem;\n  color: #1a1a2e;\n}\n#crc-app .crc-card {\n  background: #ffffff;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 1.5rem;\n  margin-bottom: 1.25rem;\n  box-shadow: 0 2px 8px rgba(0,0,0,0.06);\n}\n#crc-app .crc-row {\n  display: flex;\n  gap: 0.75rem;\n  align-items: flex-end;\n  flex-wrap: wrap;\n}\n#crc-app .crc-field {\n  display: flex;\n  flex-direction: column;\n  gap: 0.3rem;\n  flex: 1;\n  min-width: 120px;\n}\n#crc-app .crc-field label {\n  font-size: 0.78rem;\n  font-weight: 600;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#crc-app .crc-field input,\n#crc-app .crc-field select {\n  padding: 0.6rem 0.75rem;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 1rem;\n  background: #f8fafc;\n  color: #1a1a2e;\n  outline: none;\n  transition: border-color 0.15s;\n}\n#crc-app .crc-field input:focus,\n#crc-app .crc-field select:focus {\n  border-color: #3b82f6;\n  background: #fff;\n}\n#crc-app .crc-swap-btn {\n  background: #3b82f6;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  padding: 0.6rem 1rem;\n  font-size: 1.15rem;\n  cursor: pointer;\n  transition: background 0.15s;\n  align-self: flex-end;\n  height: 42px;\n}\n#crc-app .crc-swap-btn:hover {\n  background: #2563eb;\n}\n#crc-app .crc-result-box {\n  margin-top: 1rem;\n  padding: 1rem 1.25rem;\n  background: linear-gradient(135deg, #eff6ff 0%, #f0fdf4 100%);\n  border-radius: 10px;\n  border: 1px solid #bfdbfe;\n}\n#crc-app .crc-result-main {\n  font-size: 2rem;\n  font-weight: 800;\n  color: #1d4ed8;\n  line-height: 1.1;\n}\n#crc-app .crc-result-sub {\n  font-size: 0.85rem;\n  color: #64748b;\n  margin-top: 0.3rem;\n}\n#crc-app .crc-disclaimer {\n  font-size: 0.75rem;\n  color: #94a3b8;\n  margin-top: 0.5rem;\n  font-style: italic;\n}\n#crc-app table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.9rem;\n}\n#crc-app table th {\n  background: #f1f5f9;\n  color: #475569;\n  font-weight: 700;\n  text-align: left;\n  padding: 0.55rem 0.75rem;\n  border-bottom: 2px solid #e2e8f0;\n  font-size: 0.8rem;\n  text-transform: uppercase;\n  letter-spacing: 0.03em;\n}\n#crc-app table td {\n  padding: 0.5rem 0.75rem;\n  border-bottom: 1px solid #f1f5f9;\n  color: #334155;\n}\n#crc-app table tr:last-child td {\n  border-bottom: none;\n}\n#crc-app table tr:hover td {\n  background: #f8fafc;\n}\n#crc-app .crc-flag {\n  margin-right: 0.35rem;\n}\n#crc-app .crc-highlight td {\n  font-weight: 700;\n  color: #1d4ed8;\n  background: #eff6ff !important;\n}\n#crc-app .crc-note {\n  font-size: 0.78rem;\n  color: #94a3b8;\n  padding: 0.6rem 0;\n  line-height: 1.6;\n}\n#crc-app .crc-related {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 1rem 1.25rem;\n  margin-top: 1rem;\n}\n#crc-app .crc-related p {\n  margin: 0 0 0.4rem;\n  font-weight: 700;\n  font-size: 0.9rem;\n  color: #475569;\n}\n#crc-app .crc-related a {\n  display: inline-block;\n  margin: 0.2rem 0.4rem 0.2rem 0;\n  padding: 0.3rem 0.75rem;\n  background: #fff;\n  border: 1px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 0.82rem;\n  color: #3b82f6;\n  text-decoration: none;\n  transition: border-color 0.15s, background 0.15s;\n}\n#crc-app .crc-related a:hover {\n  background: #eff6ff;\n  border-color: #3b82f6;\n}\n\u003c/style\u003e\n\u003ch2 id=\"currency-converter\"\u003eCurrency Converter\u003c/h2\u003e\n\u003cp\u003eConvert any amount between 12 major world currencies using reference exchange rates.\u003c/p\u003e","title":"Currency Converter - Exchange Rate Calculator"},{"content":" Daily Planner \u0026#8592; \u0026#8594; Today \u0026#128438; Print \u0026lt;!-- Add Block Form --\u0026gt; \u0026lt;div class=\u0026quot;dp-card\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Add Time Block\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;dp-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Title\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;dp-title\u0026quot; placeholder=\u0026quot;e.g. Team standup\u0026quot; maxlength=\u0026quot;60\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;dp-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Category\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;dp-cat-chips\u0026quot; id=\u0026quot;dp-cat-chips\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;select id=\u0026quot;dp-cat-select\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;Work\u0026quot;\u0026gt;Work\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;Meeting\u0026quot;\u0026gt;Meeting\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;Personal\u0026quot;\u0026gt;Personal\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;Exercise\u0026quot;\u0026gt;Exercise\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;Break\u0026quot;\u0026gt;Break\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;Other\u0026quot;\u0026gt;Other\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;dp-time-row\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;dp-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Start\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;time\u0026quot; id=\u0026quot;dp-start\u0026quot; value=\u0026quot;09:00\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;dp-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;End\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;time\u0026quot; id=\u0026quot;dp-end\u0026quot; value=\u0026quot;10:00\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;dp-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Color\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;dp-color-row\u0026quot; id=\u0026quot;dp-color-row\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;dp-btn dp-btn-primary\u0026quot; style=\u0026quot;width:100%;margin-top:4px\u0026quot; onclick=\u0026quot;dpAddBlock()\u0026quot;\u0026gt;+ Add Block\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Summary --\u0026gt; \u0026lt;div class=\u0026quot;dp-card\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Summary\u0026lt;/h3\u0026gt; \u0026lt;ul class=\u0026quot;dp-summary-list\u0026quot; id=\u0026quot;dp-summary\u0026quot;\u0026gt;\u0026lt;/ul\u0026gt; \u0026lt;div class=\u0026quot;dp-total-hrs\u0026quot; id=\u0026quot;dp-total-hrs\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt; Total planned: \u0026lt;span id=\u0026quot;dp-total-val\u0026quot;\u0026gt;0h 0m\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Focus your sessions with → Pomodoro Timer Build lasting habits → Habit Tracker Organize your tasks → Kanban Board Related Articles Best ChatGPT Prompts for Productivity 2026 Best Remote Work Tools in 2026: The Complete Stack Best Productivity Apps for Remote Workers 2026: Tested and Ranked ","permalink":"https://productivity-works.com/tools/daily-planner/","summary":"\u003cdiv id=\"dp-app\"\u003e\n\u003cstyle\u003e\n/* ── Reset \u0026 base ── */\n#dp-app *,#dp-app *::before,#dp-app *::after{box-sizing:border-box;margin:0;padding:0}\n#dp-app{\n  font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;\n  font-size:15px;line-height:1.5;\n  color:#e2e8f0;\n  background:#0f172a;\n  border-radius:12px;\n  padding:24px;\n  max-width:960px;\n  margin:0 auto;\n}\n\u003cp\u003e/* ── Header ── */\n#dp-app .dp-header{\ndisplay:flex;flex-wrap:wrap;align-items:center;gap:12px;\nmargin-bottom:24px;padding-bottom:16px;\nborder-bottom:1px solid #1e293b;\n}\n#dp-app .dp-header h2{font-size:1.35rem;font-weight:700;color:#f1f5f9;flex:1}\n#dp-app .dp-date-nav{display:flex;align-items:center;gap:8px}\n#dp-app .dp-date-nav button{\nbackground:#1e293b;border:none;color:#94a3b8;\npadding:6px 10px;border-radius:7px;cursor:pointer;font-size:1rem;\ntransition:background 0.15s,color 0.15s;\n}\n#dp-app .dp-date-nav button:hover{background:#334155;color:#f1f5f9}\n#dp-app .dp-date-input{\nbackground:#1e293b;border:1px solid #334155;color:#e2e8f0;\npadding:7px 10px;border-radius:7px;font-size:0.875rem;outline:none;\ntransition:border-color 0.15s;\n}\n#dp-app .dp-date-input:focus{border-color:#6366f1}\n#dp-app .dp-btn{\npadding:8px 18px;border-radius:8px;border:none;cursor:pointer;\nfont-size:0.875rem;font-weight:600;transition:opacity 0.15s;\n}\n#dp-app .dp-btn:hover{opacity:0.85}\n#dp-app .dp-btn-primary{background:#6366f1;color:#fff}\n#dp-app .dp-btn-sm{padding:5px 12px;font-size:0.8rem;border-radius:6px}\n#dp-app .dp-btn-danger{background:#ef4444;color:#fff}\n#dp-app .dp-btn-ghost{background:transparent;border:1px solid #334155;color:#94a3b8}\n#dp-app .dp-btn-ghost:hover{border-color:#6366f1;color:#e2e8f0}\n#dp-app .dp-btn-print{background:#0f766e;color:#fff}\u003c/p\u003e","title":"Daily Planner - Schedule Builder"},{"content":"Convert any file to a Base64 data URL, or decode a data URL back to a downloadable file — all in your browser, no server, completely private.\nFile → Data URL Data URL → File 📁 Drag \u0026amp; drop any file here, or click to browse\nImages, SVG, fonts, JSON, PDF, audio, video — any file type File name — MIME type — Original size — Encoded size — Size increase — \u0026lt;div id=\u0026quot;du-preview-wrap\u0026quot;\u0026gt; \u0026lt;img id=\u0026quot;du-preview-img\u0026quot; src=\u0026quot;\u0026quot; alt=\u0026quot;Preview\u0026quot;\u0026gt; \u0026lt;div id=\u0026quot;du-preview-label\u0026quot;\u0026gt;Image preview\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;du-output-block\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;du-output-header\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;du-output-title\u0026quot;\u0026gt;Data URL (data:mime;base64,...)\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026quot;du-copy-btn\u0026quot; data-target=\u0026quot;du-out-dataurl\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;textarea id=\u0026quot;du-out-dataurl\u0026quot; readonly spellcheck=\u0026quot;false\u0026quot; placeholder=\u0026quot;Data URL will appear here...\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;du-output-block\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;du-output-header\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;du-output-title\u0026quot;\u0026gt;Base64 string only\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026quot;du-copy-btn\u0026quot; data-target=\u0026quot;du-out-b64\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;textarea id=\u0026quot;du-out-b64\u0026quot; readonly spellcheck=\u0026quot;false\u0026quot; placeholder=\u0026quot;Base64 string will appear here...\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;du-output-block\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;du-output-header\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;du-output-title\u0026quot;\u0026gt;CSS background-image\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026quot;du-copy-btn\u0026quot; data-target=\u0026quot;du-out-css\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;textarea id=\u0026quot;du-out-css\u0026quot; readonly spellcheck=\u0026quot;false\u0026quot; rows=\u0026quot;2\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;du-output-block\u0026quot; id=\u0026quot;du-img-src-block\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;du-output-header\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;du-output-title\u0026quot;\u0026gt;HTML \u0026amp;lt;img\u0026amp;gt; src\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026quot;du-copy-btn\u0026quot; data-target=\u0026quot;du-out-html\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;textarea id=\u0026quot;du-out-html\u0026quot; readonly spellcheck=\u0026quot;false\u0026quot; rows=\u0026quot;2\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;du-output-block\u0026quot; id=\u0026quot;du-font-block\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;du-output-header\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;du-output-title\u0026quot;\u0026gt;CSS @font-face src\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026quot;du-copy-btn\u0026quot; data-target=\u0026quot;du-out-font\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;textarea id=\u0026quot;du-out-font\u0026quot; readonly spellcheck=\u0026quot;false\u0026quot; rows=\u0026quot;3\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;du-enc-actions\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;du-btn-secondary\u0026quot; id=\u0026quot;du-reset-btn\u0026quot;\u0026gt;Convert another file\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; Paste a data URL or bare Base64 string: Decode \u0026amp; Download Detected MIME — Decoded size — Decoded image preview ⬇ Download file Common use cases CSS background-image HTML img src Font embedding (@font-face) SVG embedding Email templates JSON / API payloads Offline web apps Favicon embedding How to use File to Data URL Drag and drop any file onto the upload zone, or click to open the file picker. The tool reads the file using the browser FileReader API — nothing is sent to any server. Copy the output format you need: full data URL, bare Base64, CSS background-image, HTML \u0026lt;img\u0026gt; tag, or CSS @font-face (for font files). Data URL to File Switch to the Data URL → File tab. Paste your data:mime;base64,... string (or a bare Base64 string). Click Decode \u0026amp; Download to save the original file. Output formats Format When to use Data URL Direct value for src, href, or url() Base64 string APIs, databases, JSON payloads CSS background-image Paste straight into a stylesheet HTML \u0026lt;img\u0026gt; tag Paste straight into HTML CSS @font-face Embed a web font without external requests Why use data URLs? Embedding a file as a Base64 data URI lets you ship a self-contained HTML file, email template, or CSS stylesheet with no external HTTP requests. Common scenarios:\nSVG icons in CSS — no extra network round-trip Fonts in email — some clients block external resources Offline web apps — embed assets directly in the app shell API payloads — pass binary data as JSON strings Favicon embedding — inline tiny icons without a separate file The trade-off is roughly a ~33% size increase (every 3 bytes become 4 Base64 characters). Keep embedded assets small — under ~50 KB is a good rule of thumb for production use.\nPrivacy All conversion happens entirely in your browser using the FileReader and atob/btoa APIs. No file data is ever sent to a server.\nRelated tools: Base64 Encoder / Decoder · Image to Base64 · Base64 to Image ","permalink":"https://productivity-works.com/tools/data-url-converter/","summary":"\u003cp\u003eConvert any file to a Base64 data URL, or decode a data URL back to a downloadable file — all in your browser, no server, completely private.\u003c/p\u003e\n\u003cdiv id=\"du-app\"\u003e\n\u003cstyle\u003e\n  #du-app *,\n  #du-app *::before,\n  #du-app *::after {\n    box-sizing: border-box;\n    margin: 0;\n    padding: 0;\n  }\n  #du-app {\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n    color: #1a1a2e;\n    max-width: 860px;\n    margin: 0 auto;\n    padding: 0 0 48px;\n  }\n  #du-tabs {\n    display: flex;\n    gap: 8px;\n    margin-bottom: 24px;\n  }\n  .du-tab {\n    flex: 1;\n    padding: 12px 20px;\n    border: 2px solid #7c3aed;\n    border-radius: 10px;\n    background: #fff;\n    color: #7c3aed;\n    font-size: 0.97rem;\n    font-weight: 600;\n    cursor: pointer;\n    transition: background 0.18s, color 0.18s;\n    text-align: center;\n  }\n  .du-tab.du-active {\n    background: #7c3aed;\n    color: #fff;\n  }\n  .du-tab:hover:not(.du-active) {\n    background: #f5f3ff;\n  }\n  .du-panel {\n    display: none;\n  }\n  .du-panel.du-active {\n    display: block;\n  }\n  /* Drop zone */\n  #du-drop-zone {\n    border: 2.5px dashed #7c3aed;\n    border-radius: 14px;\n    padding: 48px 24px;\n    text-align: center;\n    cursor: pointer;\n    background: #f5f3ff;\n    transition: background 0.2s, border-color 0.2s;\n    position: relative;\n  }\n  #du-drop-zone.du-dragover {\n    background: #ede9fe;\n    border-color: #5b21b6;\n  }\n  #du-drop-icon {\n    font-size: 48px;\n    line-height: 1;\n    margin-bottom: 12px;\n  }\n  #du-drop-zone p {\n    color: #6d28d9;\n    font-size: 1.05rem;\n    font-weight: 500;\n  }\n  #du-drop-zone small {\n    display: block;\n    margin-top: 6px;\n    color: #8b5cf6;\n    font-size: 0.82rem;\n  }\n  #du-file-input {\n    position: absolute;\n    inset: 0;\n    opacity: 0;\n    cursor: pointer;\n    width: 100%;\n    height: 100%;\n  }\n  /* Results — encode */\n  #du-enc-results {\n    display: none;\n  }\n  #du-info-bar {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 12px;\n    margin-bottom: 20px;\n    padding: 14px 18px;\n    background: #f5f3ff;\n    border-radius: 10px;\n    border: 1px solid #ddd6fe;\n    font-size: 0.88rem;\n  }\n  .du-info-item {\n    display: flex;\n    flex-direction: column;\n    gap: 2px;\n  }\n  .du-info-label {\n    font-weight: 600;\n    color: #5b21b6;\n    font-size: 0.78rem;\n    text-transform: uppercase;\n    letter-spacing: 0.04em;\n  }\n  .du-info-value {\n    color: #1a1a2e;\n    font-size: 0.9rem;\n  }\n  #du-size-increase {\n    font-weight: 700;\n    color: #7c3aed;\n  }\n  /* Image preview */\n  #du-preview-wrap {\n    display: none;\n    margin-bottom: 20px;\n    text-align: center;\n  }\n  #du-preview-wrap img {\n    max-width: 100%;\n    max-height: 220px;\n    border-radius: 10px;\n    border: 1px solid #ddd6fe;\n    box-shadow: 0 2px 12px rgba(124,58,237,0.10);\n  }\n  #du-preview-label {\n    font-size: 0.8rem;\n    color: #8b5cf6;\n    margin-top: 6px;\n  }\n  /* Output blocks */\n  .du-output-block {\n    margin-bottom: 18px;\n  }\n  .du-output-header {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    margin-bottom: 6px;\n  }\n  .du-output-title {\n    font-size: 0.85rem;\n    font-weight: 700;\n    color: #5b21b6;\n    text-transform: uppercase;\n    letter-spacing: 0.04em;\n  }\n  .du-copy-btn {\n    padding: 5px 14px;\n    background: #7c3aed;\n    color: #fff;\n    border: none;\n    border-radius: 6px;\n    font-size: 0.8rem;\n    font-weight: 600;\n    cursor: pointer;\n    transition: background 0.18s;\n  }\n  .du-copy-btn:hover {\n    background: #6d28d9;\n  }\n  .du-copy-btn.du-copied {\n    background: #16a34a;\n  }\n  .du-output-block textarea {\n    width: 100%;\n    height: 100px;\n    padding: 10px 12px;\n    border: 1.5px solid #ddd6fe;\n    border-radius: 8px;\n    font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n    font-size: 0.78rem;\n    color: #1a1a2e;\n    resize: vertical;\n    background: #fafaf9;\n    line-height: 1.5;\n  }\n  .du-output-block textarea:focus {\n    outline: none;\n    border-color: #7c3aed;\n  }\n  /* Actions row */\n  #du-enc-actions {\n    display: flex;\n    gap: 10px;\n    margin-top: 8px;\n  }\n  .du-btn-secondary {\n    padding: 9px 20px;\n    background: #fff;\n    color: #7c3aed;\n    border: 2px solid #7c3aed;\n    border-radius: 8px;\n    font-size: 0.9rem;\n    font-weight: 600;\n    cursor: pointer;\n    transition: background 0.18s;\n  }\n  .du-btn-secondary:hover {\n    background: #f5f3ff;\n  }\n  /* Decode panel */\n  #du-dec-panel label {\n    display: block;\n    font-size: 0.85rem;\n    font-weight: 700;\n    color: #5b21b6;\n    text-transform: uppercase;\n    letter-spacing: 0.04em;\n    margin-bottom: 6px;\n  }\n  #du-paste-area {\n    width: 100%;\n    height: 130px;\n    padding: 10px 12px;\n    border: 2px solid #ddd6fe;\n    border-radius: 10px;\n    font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n    font-size: 0.8rem;\n    color: #1a1a2e;\n    resize: vertical;\n    background: #f5f3ff;\n    margin-bottom: 14px;\n    line-height: 1.5;\n    transition: border-color 0.18s;\n  }\n  #du-paste-area:focus {\n    outline: none;\n    border-color: #7c3aed;\n  }\n  #du-dec-btn {\n    padding: 10px 28px;\n    background: #7c3aed;\n    color: #fff;\n    border: none;\n    border-radius: 8px;\n    font-size: 0.97rem;\n    font-weight: 700;\n    cursor: pointer;\n    transition: background 0.18s;\n    margin-bottom: 18px;\n  }\n  #du-dec-btn:hover {\n    background: #6d28d9;\n  }\n  #du-dec-error {\n    display: none;\n    padding: 10px 16px;\n    background: #fef2f2;\n    border: 1px solid #fca5a5;\n    border-radius: 8px;\n    color: #991b1b;\n    font-size: 0.88rem;\n    font-weight: 500;\n    margin-bottom: 14px;\n  }\n  #du-dec-results {\n    display: none;\n  }\n  #du-dec-info {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 12px;\n    margin-bottom: 18px;\n    padding: 14px 18px;\n    background: #f0fdf4;\n    border-radius: 10px;\n    border: 1px solid #bbf7d0;\n    font-size: 0.88rem;\n  }\n  #du-dec-preview-wrap {\n    display: none;\n    margin-bottom: 18px;\n    text-align: center;\n  }\n  #du-dec-preview-wrap img {\n    max-width: 100%;\n    max-height: 220px;\n    border-radius: 10px;\n    border: 1px solid #ddd6fe;\n    box-shadow: 0 2px 12px rgba(124,58,237,0.10);\n  }\n  #du-dec-preview-label {\n    font-size: 0.8rem;\n    color: #16a34a;\n    margin-top: 6px;\n  }\n  #du-download-btn {\n    display: inline-flex;\n    align-items: center;\n    gap: 8px;\n    padding: 10px 26px;\n    background: #16a34a;\n    color: #fff;\n    border: none;\n    border-radius: 8px;\n    font-size: 0.97rem;\n    font-weight: 700;\n    cursor: pointer;\n    text-decoration: none;\n    transition: background 0.18s;\n  }\n  #du-download-btn:hover {\n    background: #15803d;\n  }\n  /* Common use cases */\n  #du-usecases {\n    margin-top: 32px;\n    padding: 18px 20px;\n    background: #f5f3ff;\n    border-radius: 12px;\n    border: 1px solid #ddd6fe;\n  }\n  #du-usecases h3 {\n    font-size: 0.95rem;\n    font-weight: 700;\n    color: #5b21b6;\n    margin-bottom: 12px;\n  }\n  #du-usecases ul {\n    list-style: none;\n    display: flex;\n    flex-wrap: wrap;\n    gap: 8px;\n  }\n  #du-usecases li {\n    padding: 5px 12px;\n    background: #ede9fe;\n    border-radius: 20px;\n    font-size: 0.82rem;\n    color: #5b21b6;\n    font-weight: 500;\n  }\n  @media (max-width: 520px) {\n    #du-info-bar { flex-direction: column; gap: 8px; }\n    #du-tabs { flex-direction: column; }\n    #du-enc-actions { flex-direction: column; }\n  }\n\u003c/style\u003e\n\u003cdiv id=\"du-tabs\"\u003e\n  \u003cbutton class=\"du-tab du-active\" id=\"du-tab-enc\" onclick=\"(function(){document.getElementById('du-enc-panel').classList.add('du-active');document.getElementById('du-dec-panel').classList.remove('du-active');document.getElementById('du-tab-enc').classList.add('du-active');document.getElementById('du-tab-dec').classList.remove('du-active');})()\"\u003e\n    File → Data URL\n  \u003c/button\u003e\n  \u003cbutton class=\"du-tab\" id=\"du-tab-dec\" onclick=\"(function(){document.getElementById('du-dec-panel').classList.add('du-active');document.getElementById('du-enc-panel').classList.remove('du-active');document.getElementById('du-tab-dec').classList.add('du-active');document.getElementById('du-tab-enc').classList.remove('du-active');})()\"\u003e\n    Data URL → File\n  \u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- ENCODE PANEL --\u003e\n\u003cdiv class=\"du-panel du-active\" id=\"du-enc-panel\"\u003e\n  \u003cdiv id=\"du-drop-zone\"\u003e\n    \u003cinput type=\"file\" id=\"du-file-input\" aria-label=\"Choose a file to convert\"\u003e\n    \u003cdiv id=\"du-drop-icon\"\u003e📁\u003c/div\u003e\n    \u003cp\u003eDrag \u0026amp; drop any file here, or click to browse\u003c/p\u003e","title":"Data URL Converter"},{"content":"Free online date calculator with three modes: find the difference between two dates, add or subtract time from a date, and calculate business days. No sign-up required.\nDate Difference Add / Subtract Business Days Start Date End Date Calculate Difference Please enter both dates. Base Date Amount Unit Days Weeks Months Years Operation + Add − Subtract Calculate Date Please enter a base date and amount. Start Date End Date Weekends (Saturday \u0026amp; Sunday) are excluded. Public holidays are not automatically excluded.\nCalculate Business Days Please enter both dates. Related Free Tools Age Calculator Calendar Generator How to Use the Date Calculator Mode 1 — Date Difference: Enter a start date and an end date, then click \u0026ldquo;Calculate Difference.\u0026rdquo; The tool returns the gap in years, months, days, total days, weeks, and hours.\nMode 2 — Add / Subtract: Pick a base date, choose an amount and unit (days, weeks, months, or years), select Add or Subtract, and click \u0026ldquo;Calculate Date.\u0026rdquo; The resulting date and day of week are displayed instantly.\nMode 3 — Business Days: Enter two dates to count how many working days (Monday–Friday) fall between them, excluding weekends. Public holidays must be excluded manually.\nFrequently Asked Questions How is the date difference calculated? The calculator uses the exact calendar difference. Years and months are counted by calendar position; remaining days are counted precisely including leap years.\nAre public holidays excluded in Business Days mode? No. The tool automatically removes Saturdays and Sundays. If you need to exclude local public holidays, subtract them from the business-day count manually.\nCan I subtract dates across different years? Yes. The calculator handles multi-year spans, leap years, and month-length differences automatically.\nWhat is the day of week used for? Every result shows the day of the week (e.g., Monday) for quick reference — useful for scheduling meetings or deadlines.\nRelated: Age Calculator · Calendar Generator ","permalink":"https://productivity-works.com/tools/date-calculator/","summary":"\u003cp\u003eFree online date calculator with three modes: find the difference between two dates, add or subtract time from a date, and calculate business days. No sign-up required.\u003c/p\u003e\n\u003cdiv id=\"dt-app\"\u003e\n\u003cstyle\u003e\n#dt-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 700px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#dt-app * {\n  box-sizing: border-box;\n}\n#dt-app .dt-tabs {\n  display: flex;\n  gap: 0;\n  border-bottom: 2px solid #e0e0e0;\n  margin-bottom: 24px;\n}\n#dt-app .dt-tab {\n  flex: 1;\n  padding: 12px 8px;\n  background: none;\n  border: none;\n  border-bottom: 3px solid transparent;\n  margin-bottom: -2px;\n  font-size: 14px;\n  font-weight: 600;\n  color: #777;\n  cursor: pointer;\n  transition: all 0.2s;\n  text-align: center;\n}\n#dt-app .dt-tab:hover {\n  color: #4f46e5;\n  background: #f5f3ff;\n}\n#dt-app .dt-tab.active {\n  color: #4f46e5;\n  border-bottom-color: #4f46e5;\n  background: none;\n}\n#dt-app .dt-panel {\n  display: none;\n}\n#dt-app .dt-panel.active {\n  display: block;\n}\n#dt-app .dt-card {\n  background: #f9f9ff;\n  border: 1px solid #e8e8f0;\n  border-radius: 12px;\n  padding: 24px;\n  margin-bottom: 20px;\n}\n#dt-app .dt-row {\n  display: flex;\n  gap: 16px;\n  flex-wrap: wrap;\n  margin-bottom: 16px;\n  align-items: flex-end;\n}\n#dt-app .dt-field {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n  flex: 1;\n  min-width: 160px;\n}\n#dt-app .dt-field label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #555;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#dt-app .dt-field input,\n#dt-app .dt-field select {\n  padding: 10px 12px;\n  border: 1.5px solid #d0d0e0;\n  border-radius: 8px;\n  font-size: 15px;\n  background: #fff;\n  color: #1a1a2e;\n  outline: none;\n  transition: border-color 0.2s;\n  width: 100%;\n}\n#dt-app .dt-field input:focus,\n#dt-app .dt-field select:focus {\n  border-color: #4f46e5;\n}\n#dt-app .dt-btn {\n  display: inline-block;\n  padding: 11px 28px;\n  background: #4f46e5;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 15px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n#dt-app .dt-btn:hover {\n  background: #4338ca;\n}\n#dt-app .dt-result {\n  background: #fff;\n  border: 1.5px solid #4f46e5;\n  border-radius: 10px;\n  padding: 20px 24px;\n  margin-top: 16px;\n  display: none;\n}\n#dt-app .dt-result.show {\n  display: block;\n}\n#dt-app .dt-result-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));\n  gap: 12px;\n  margin-bottom: 12px;\n}\n#dt-app .dt-result-item {\n  background: #f5f3ff;\n  border-radius: 8px;\n  padding: 14px 12px;\n  text-align: center;\n}\n#dt-app .dt-result-item .val {\n  font-size: 26px;\n  font-weight: 800;\n  color: #4f46e5;\n  line-height: 1.1;\n}\n#dt-app .dt-result-item .lbl {\n  font-size: 12px;\n  color: #666;\n  margin-top: 4px;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n#dt-app .dt-result-main {\n  font-size: 17px;\n  font-weight: 700;\n  color: #1a1a2e;\n  margin-bottom: 8px;\n}\n#dt-app .dt-result-sub {\n  font-size: 14px;\n  color: #555;\n  line-height: 1.6;\n}\n#dt-app .dt-dow-badge {\n  display: inline-block;\n  background: #e0f2fe;\n  color: #0369a1;\n  border-radius: 6px;\n  padding: 3px 10px;\n  font-size: 13px;\n  font-weight: 700;\n  margin-left: 6px;\n}\n#dt-app .dt-error {\n  color: #dc2626;\n  font-size: 14px;\n  margin-top: 10px;\n  display: none;\n}\n#dt-app .dt-error.show {\n  display: block;\n}\n#dt-app .dt-op-toggle {\n  display: flex;\n  gap: 0;\n  border: 1.5px solid #d0d0e0;\n  border-radius: 8px;\n  overflow: hidden;\n  background: #fff;\n}\n#dt-app .dt-op-btn {\n  flex: 1;\n  padding: 10px;\n  background: none;\n  border: none;\n  font-size: 14px;\n  font-weight: 600;\n  color: #777;\n  cursor: pointer;\n  transition: all 0.2s;\n}\n#dt-app .dt-op-btn.active {\n  background: #4f46e5;\n  color: #fff;\n}\n#dt-app .dt-related {\n  margin-top: 32px;\n  padding-top: 24px;\n  border-top: 1px solid #e0e0e0;\n}\n#dt-app .dt-related h3 {\n  font-size: 16px;\n  font-weight: 700;\n  margin: 0 0 14px;\n  color: #1a1a2e;\n}\n#dt-app .dt-related-links {\n  display: flex;\n  gap: 12px;\n  flex-wrap: wrap;\n}\n#dt-app .dt-related-link {\n  display: inline-block;\n  padding: 9px 18px;\n  background: #f5f3ff;\n  border: 1.5px solid #c7d2fe;\n  border-radius: 8px;\n  color: #4f46e5;\n  font-size: 14px;\n  font-weight: 600;\n  text-decoration: none;\n  transition: background 0.2s;\n}\n#dt-app .dt-related-link:hover {\n  background: #ede9fe;\n}\n#dt-app .dt-hint {\n  font-size: 13px;\n  color: #888;\n  margin-top: 10px;\n}\n@media (max-width: 500px) {\n  #dt-app .dt-tab { font-size: 12px; padding: 10px 4px; }\n  #dt-app .dt-result-item .val { font-size: 20px; }\n}\n\u003c/style\u003e\n\u003c!-- Tab navigation --\u003e\n\u003cdiv class=\"dt-tabs\"\u003e\n  \u003cbutton class=\"dt-tab active\" onclick=\"dtSwitchTab(0, this)\"\u003eDate Difference\u003c/button\u003e\n  \u003cbutton class=\"dt-tab\" onclick=\"dtSwitchTab(1, this)\"\u003eAdd / Subtract\u003c/button\u003e\n  \u003cbutton class=\"dt-tab\" onclick=\"dtSwitchTab(2, this)\"\u003eBusiness Days\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Panel 0: Date Difference --\u003e\n\u003cdiv class=\"dt-panel active\" id=\"dt-panel-0\"\u003e\n  \u003cdiv class=\"dt-card\"\u003e\n    \u003cdiv class=\"dt-row\"\u003e\n      \u003cdiv class=\"dt-field\"\u003e\n        \u003clabel\u003eStart Date\u003c/label\u003e\n        \u003cinput type=\"date\" id=\"dd-start\" /\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"dt-field\"\u003e\n        \u003clabel\u003eEnd Date\u003c/label\u003e\n        \u003cinput type=\"date\" id=\"dd-end\" /\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cbutton class=\"dt-btn\" onclick=\"dtCalcDiff()\"\u003eCalculate Difference\u003c/button\u003e\n    \u003cdiv class=\"dt-error\" id=\"dd-error\"\u003ePlease enter both dates.\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"dt-result\" id=\"dd-result\"\u003e\n    \u003cdiv class=\"dt-result-main\" id=\"dd-main\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"dt-result-grid\" id=\"dd-grid\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"dt-result-sub\" id=\"dd-sub\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Panel 1: Add / Subtract --\u003e\n\u003cdiv class=\"dt-panel\" id=\"dt-panel-1\"\u003e\n  \u003cdiv class=\"dt-card\"\u003e\n    \u003cdiv class=\"dt-row\"\u003e\n      \u003cdiv class=\"dt-field\"\u003e\n        \u003clabel\u003eBase Date\u003c/label\u003e\n        \u003cinput type=\"date\" id=\"as-date\" /\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"dt-row\"\u003e\n      \u003cdiv class=\"dt-field\" style=\"max-width:100px;\"\u003e\n        \u003clabel\u003eAmount\u003c/label\u003e\n        \u003cinput type=\"number\" id=\"as-amount\" value=\"30\" min=\"0\" max=\"9999\" /\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"dt-field\" style=\"max-width:160px;\"\u003e\n        \u003clabel\u003eUnit\u003c/label\u003e\n        \u003cselect id=\"as-unit\"\u003e\n          \u003coption value=\"days\"\u003eDays\u003c/option\u003e\n          \u003coption value=\"weeks\"\u003eWeeks\u003c/option\u003e\n          \u003coption value=\"months\"\u003eMonths\u003c/option\u003e\n          \u003coption value=\"years\"\u003eYears\u003c/option\u003e\n        \u003c/select\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"dt-field\" style=\"max-width:180px;\"\u003e\n        \u003clabel\u003eOperation\u003c/label\u003e\n        \u003cdiv class=\"dt-op-toggle\"\u003e\n          \u003cbutton class=\"dt-op-btn active\" id=\"as-add-btn\" onclick=\"dtSetOp('add')\"\u003e+ Add\u003c/button\u003e\n          \u003cbutton class=\"dt-op-btn\" id=\"as-sub-btn\" onclick=\"dtSetOp('sub')\"\u003e− Subtract\u003c/button\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cbutton class=\"dt-btn\" onclick=\"dtCalcAddSub()\"\u003eCalculate Date\u003c/button\u003e\n    \u003cdiv class=\"dt-error\" id=\"as-error\"\u003ePlease enter a base date and amount.\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"dt-result\" id=\"as-result\"\u003e\n    \u003cdiv class=\"dt-result-main\" id=\"as-main\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"dt-result-sub\" id=\"as-sub\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Panel 2: Business Days --\u003e\n\u003cdiv class=\"dt-panel\" id=\"dt-panel-2\"\u003e\n  \u003cdiv class=\"dt-card\"\u003e\n    \u003cdiv class=\"dt-row\"\u003e\n      \u003cdiv class=\"dt-field\"\u003e\n        \u003clabel\u003eStart Date\u003c/label\u003e\n        \u003cinput type=\"date\" id=\"bd-start\" /\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"dt-field\"\u003e\n        \u003clabel\u003eEnd Date\u003c/label\u003e\n        \u003cinput type=\"date\" id=\"bd-end\" /\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cp class=\"dt-hint\"\u003eWeekends (Saturday \u0026amp; Sunday) are excluded. Public holidays are not automatically excluded.\u003c/p\u003e","title":"Date Calculator"},{"content":" Free tool — instantly calculate days until any date, business days, and more.\nDays Until Days Between Add / Subtract Days How Many Days Until...? Quick presets: Christmas New Year's Day Halloween Valentine's Day Thanksgiving July 4th My Birthday Target Date Count business days only (exclude Sat \u0026 Sun) Please select a target date. Calculate — Days Until — Weeks — Months (approx.) — Business Days Year Progress 0% Days Between Two Dates From Date To Date Exclude weekends Please select both dates. Calculate — Total Days — Weeks — Months (approx.) — Business Days Add or Subtract Days from a Date Start Date Number of Days Operation Add days (future date) Subtract days (past date) Skip weekends (business days only) Please fill in the start date and number of days. Calculate Result Date — Related Tools Time Zone Converter Age Calculator Work Hours Calculator Deadline Calculator Week Number Calculator Related Tools Age Calculator Countdown Timer Date Calculator ","permalink":"https://productivity-works.com/tools/days-until-calculator/","summary":"\u003cdiv id=\"duc-app\"\u003e\n\u003cstyle\u003e\n#duc-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 760px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#duc-app * { box-sizing: border-box; }\n#duc-app h2 {\n  font-size: 1.25rem;\n  font-weight: 700;\n  margin: 0 0 1rem 0;\n  color: #1a1a2e;\n}\n#duc-app .duc-tabs {\n  display: flex;\n  gap: 0;\n  border-bottom: 2px solid #e2e8f0;\n  margin-bottom: 1.5rem;\n}\n#duc-app .duc-tab {\n  padding: 0.6rem 1.1rem;\n  font-size: 0.9rem;\n  font-weight: 600;\n  color: #64748b;\n  background: none;\n  border: none;\n  border-bottom: 3px solid transparent;\n  cursor: pointer;\n  transition: all 0.2s;\n  margin-bottom: -2px;\n}\n#duc-app .duc-tab:hover { color: #3b82f6; }\n#duc-app .duc-tab.active {\n  color: #3b82f6;\n  border-bottom-color: #3b82f6;\n}\n#duc-app .duc-panel { display: none; }\n#duc-app .duc-panel.active { display: block; }\n#duc-app .duc-card {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 1.5rem;\n  margin-bottom: 1rem;\n}\n#duc-app label {\n  display: block;\n  font-size: 0.85rem;\n  font-weight: 600;\n  color: #374151;\n  margin-bottom: 0.4rem;\n}\n#duc-app input[type=\"date\"],\n#duc-app input[type=\"number\"],\n#duc-app select {\n  width: 100%;\n  padding: 0.6rem 0.85rem;\n  border: 1.5px solid #d1d5db;\n  border-radius: 8px;\n  font-size: 0.95rem;\n  color: #1a1a2e;\n  background: #fff;\n  transition: border-color 0.2s;\n}\n#duc-app input[type=\"date\"]:focus,\n#duc-app input[type=\"number\"]:focus,\n#duc-app select:focus {\n  outline: none;\n  border-color: #3b82f6;\n}\n#duc-app .duc-row {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1rem;\n  margin-bottom: 1rem;\n}\n@media (max-width: 520px) {\n  #duc-app .duc-row { grid-template-columns: 1fr; }\n}\n#duc-app .duc-field { margin-bottom: 1rem; }\n#duc-app .duc-check-row {\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n  margin-bottom: 1rem;\n}\n#duc-app .duc-check-row input[type=\"checkbox\"] {\n  width: 16px;\n  height: 16px;\n  cursor: pointer;\n}\n#duc-app .duc-check-row label {\n  margin: 0;\n  font-size: 0.88rem;\n  cursor: pointer;\n}\n#duc-app .duc-btn {\n  display: inline-block;\n  padding: 0.65rem 1.6rem;\n  background: #3b82f6;\n  color: #fff;\n  font-size: 0.95rem;\n  font-weight: 700;\n  border: none;\n  border-radius: 8px;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n#duc-app .duc-btn:hover { background: #2563eb; }\n#duc-app .duc-results {\n  display: none;\n  margin-top: 1.25rem;\n}\n#duc-app .duc-results.show { display: block; }\n#duc-app .duc-result-grid {\n  display: grid;\n  grid-template-columns: repeat(2, 1fr);\n  gap: 0.75rem;\n  margin-bottom: 1rem;\n}\n@media (max-width: 520px) {\n  #duc-app .duc-result-grid { grid-template-columns: 1fr; }\n}\n#duc-app .duc-result-box {\n  background: #fff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 1rem;\n  text-align: center;\n}\n#duc-app .duc-result-box.highlight {\n  background: linear-gradient(135deg, #3b82f6 0%, #6366f1 100%);\n  border-color: transparent;\n  color: #fff;\n}\n#duc-app .duc-result-box .rval {\n  font-size: 2rem;\n  font-weight: 800;\n  line-height: 1.1;\n}\n#duc-app .duc-result-box.highlight .rval { color: #fff; }\n#duc-app .duc-result-box .rlabel {\n  font-size: 0.78rem;\n  font-weight: 600;\n  color: #64748b;\n  margin-top: 0.2rem;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#duc-app .duc-result-box.highlight .rlabel { color: rgba(255,255,255,0.85); }\n#duc-app .duc-progress-wrap {\n  background: #fff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 1rem 1.25rem;\n  margin-bottom: 0.75rem;\n}\n#duc-app .duc-progress-label {\n  display: flex;\n  justify-content: space-between;\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #64748b;\n  margin-bottom: 0.5rem;\n}\n#duc-app .duc-progress-bar {\n  height: 10px;\n  background: #e2e8f0;\n  border-radius: 99px;\n  overflow: hidden;\n}\n#duc-app .duc-progress-fill {\n  height: 100%;\n  background: linear-gradient(90deg, #3b82f6, #6366f1);\n  border-radius: 99px;\n  transition: width 0.6s ease;\n}\n#duc-app .duc-message {\n  background: #eff6ff;\n  border: 1px solid #bfdbfe;\n  border-radius: 8px;\n  padding: 0.75rem 1rem;\n  font-size: 0.9rem;\n  color: #1e40af;\n  margin-top: 0.5rem;\n}\n#duc-app .duc-presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem;\n  margin-bottom: 1rem;\n}\n#duc-app .duc-preset-btn {\n  padding: 0.35rem 0.85rem;\n  font-size: 0.8rem;\n  font-weight: 600;\n  background: #fff;\n  border: 1.5px solid #d1d5db;\n  border-radius: 20px;\n  cursor: pointer;\n  color: #374151;\n  transition: all 0.2s;\n}\n#duc-app .duc-preset-btn:hover {\n  background: #3b82f6;\n  border-color: #3b82f6;\n  color: #fff;\n}\n#duc-app .duc-error {\n  color: #dc2626;\n  font-size: 0.85rem;\n  margin-top: 0.5rem;\n  display: none;\n}\n#duc-app .duc-error.show { display: block; }\n#duc-app .duc-related {\n  margin-top: 2rem;\n  padding-top: 1.5rem;\n  border-top: 1px solid #e2e8f0;\n}\n#duc-app .duc-related h3 {\n  font-size: 1rem;\n  font-weight: 700;\n  margin: 0 0 0.75rem 0;\n  color: #374151;\n}\n#duc-app .duc-related-links {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem;\n}\n#duc-app .duc-related-links a {\n  display: inline-block;\n  padding: 0.4rem 0.9rem;\n  background: #f1f5f9;\n  border: 1px solid #e2e8f0;\n  border-radius: 6px;\n  font-size: 0.85rem;\n  color: #3b82f6;\n  text-decoration: none;\n  transition: all 0.2s;\n}\n#duc-app .duc-related-links a:hover {\n  background: #3b82f6;\n  color: #fff;\n  border-color: #3b82f6;\n}\n#duc-app .duc-add-result {\n  display: none;\n  background: #fff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 1rem 1.25rem;\n  margin-top: 1rem;\n  font-size: 0.95rem;\n}\n#duc-app .duc-add-result.show { display: block; }\n#duc-app .duc-add-result .big-date {\n  font-size: 1.4rem;\n  font-weight: 800;\n  color: #3b82f6;\n  margin-top: 0.3rem;\n}\n\u003c/style\u003e\n\u003cp\u003e\u003cstrong\u003eFree tool\u003c/strong\u003e — instantly calculate days until any date, business days, and more.\u003c/p\u003e","title":"Days Until Calculator - Count Days to Any Date"},{"content":" DNS Configuration Helper Enter your domain name, choose a preset configuration, and get ready-to-use DNS records.\nYour Domain Name Configuration Preset\nGitHub Pages Vercel Netlify Cloudflare Google Workspace Microsoft 365 CDN (Basic) Email Only Generate DNS Records\nDNS Record Type Reference A A Record — IPv4 Address Maps a hostname to an IPv4 address. The most fundamental DNS record — when someone visits your domain, the A record tells DNS which server IP to connect to. example.com. 3600 IN A 93.184.216.34 www.example.com. 3600 IN A 93.184.216.34 Use when: Pointing your domain to a web server, mail server, or any IPv4 address. AAAA AAAA Record — IPv6 Address Maps a hostname to an IPv6 address. Functions identically to an A record but for modern IPv6 infrastructure. Recommended to add alongside A records for full dual-stack support. example.com. 3600 IN AAAA 2606:2800:220:1:248:1893:25c8:1946 Use when: Your server supports IPv6 (GitHub Pages, Cloudflare, most modern CDNs). CNAME CNAME Record — Canonical Name Creates an alias from one hostname to another. Instead of an IP, a CNAME points to another domain name. Cannot be used at the zone apex (@) in standard DNS — use ALIAS/ANAME or flatten at the registrar. The target must ultimately resolve to an A or AAAA record. www.example.com. 3600 IN CNAME example.com. blog.example.com. 3600 IN CNAME my-site.netlify.app. Use when: Pointing subdomains to hosting providers (Vercel, Netlify, GitHub Pages www subdomain, CDN hostnames). MX MX Record — Mail Exchanger Specifies the mail servers responsible for receiving email for a domain. Multiple MX records can exist with different priority values — lower numbers = higher priority. The value must be a hostname (not an IP address). example.com. 3600 IN MX 1 aspmx.l.google.com. example.com. 3600 IN MX 5 alt1.aspmx.l.google.com. example.com. 3600 IN MX 10 alt2.aspmx.l.google.com. Use when: Configuring email delivery — Google Workspace, Microsoft 365, Fastmail, ProtonMail, etc. TXT TXT Record — Text Stores arbitrary text data. Widely used for domain verification, email authentication (SPF, DKIM, DMARC), and service-specific configuration. A domain can have multiple TXT records. # SPF — authorize mail senders example.com. 3600 IN TXT \"v=spf1 include:_spf.google.com ~all\" DMARC — email policy _dmarc.example.com. 3600 IN TXT \u0026ldquo;v=DMARC1; p=reject; rua=mailto:dmarc@example.com\u0026rdquo;\nDKIM — signing key selector._domainkey.example.com. 3600 IN TXT \u0026ldquo;v=DKIM1; k=rsa; p=MIIBIjAN\u0026hellip;\u0026rdquo;\nDomain verification example.com. 3600 IN TXT \u0026ldquo;google-site-verification=abc123\u0026rdquo;\nUse when: SPF/DKIM/DMARC email authentication, domain ownership verification, service configuration. NS NS Record — Name Server Delegates a domain or subdomain to a set of authoritative name servers. NS records at the zone apex are set by your domain registrar. You can also delegate subdomains to different DNS providers using NS records. example.com. 86400 IN NS ns1.cloudflare.com. example.com. 86400 IN NS ns2.cloudflare.com. Subdomain delegation staging.example.com. 3600 IN NS ns1.otherprovider.com.\nUse when: Changing DNS providers, delegating subdomain DNS management to another provider. SOA SOA Record — Start of Authority Contains administrative information about a DNS zone: primary name server, responsible party's email, serial number, and refresh/retry/expire timers. Every DNS zone must have exactly one SOA record. Usually managed automatically by your DNS provider. example.com. 86400 IN SOA ns1.example.com. admin.example.com. ( 2025051601 ; Serial (YYYYMMDDnn) 3600 ; Refresh (1 hour) 900 ; Retry (15 min) 604800 ; Expire (7 days) 300 ) ; Minimum TTL (5 min) Use when: Typically auto-managed. Manually adjust serial/TTL values in self-hosted DNS (BIND, PowerDNS). SRV SRV Record — Service Locator Specifies the location of servers for specific services, including the service name, protocol, priority, weight, port, and target. Format: _service._proto.name TTL IN SRV priority weight port target. # Microsoft Teams / Skype for Business _sip._tls.example.com. 3600 IN SRV 100 1 443 sipdir.online.lync.com. _sipfederationtls._tcp.example.com. 3600 IN SRV 100 1 5061 sipfed.online.lync.com. XMPP (Jabber) _xmpp-client._tcp.example.com. 3600 IN SRV 5 0 5222 xmpp.example.com.\nUse when: Microsoft 365 (Teams/Lync), VoIP, XMPP, and any protocol requiring service discovery. CAA CAA Record — Certification Authority Authorization Controls which Certificate Authorities (CAs) are permitted to issue SSL/TLS certificates for your domain. Prevents unauthorized certificate issuance. Three tag types: issue (single domain), issuewild (wildcard), iodef (violation reporting). example.com. 3600 IN CAA 0 issue \"letsencrypt.org\" example.com. 3600 IN CAA 0 issuewild \"letsencrypt.org\" example.com. 3600 IN CAA 0 issue \"digicert.com\" example.com. 3600 IN CAA 0 iodef \"mailto:security@example.com\" Use when: Strengthening SSL certificate security, required by some compliance frameworks (PCI-DSS). PTR PTR Record — Pointer (Reverse DNS) The reverse of an A record — maps an IP address back to a hostname. Lives in the in-addr.arpa (IPv4) or ip6.arpa (IPv6) zones. Configured with your ISP or hosting provider, not your domain registrar. Critical for mail server reputation. # IPv4 reverse DNS (34.216.184.93 → example.com) 34.216.184.93.in-addr.arpa. 3600 IN PTR mail.example.com. IPv6 reverse DNS 6.4.9.1.8.c.5.2.3.9.8.1.8.4.2.0.1.0.0.0.0.2.2.0.0.8.2.6.0.6.2.ip6.arpa. 3600 IN PTR example.com.\nUse when: Setting up a mail server (anti-spam), network diagnostics, compliance requirements. Quick TTL Reference TTL Value Seconds When to Use 300 5 min During migrations — propagates changes quickly 900 15 min Active testing or frequent changes 3600 1 hour Standard for most records 86400 24 hours Stable records (NS, SOA) 172800 48 hours Very stable, rarely-changed records Tip: Lower TTL before a planned migration so DNS changes propagate quickly. Raise it again once stable.\nCommon DNS Troubleshooting Changes not propagating? DNS propagation takes time equal to the record\u0026rsquo;s TTL. If your TTL was 86400 (24h), you may wait up to 24 hours. Use whatsmydns.net to check propagation status globally.\nEmail going to spam? Ensure SPF, DKIM, and DMARC TXT records are all configured. A missing PTR (reverse DNS) record on your mail server is also a common cause.\nCNAME conflict? You cannot have a CNAME and any other record type for the same hostname. At the zone apex (@), use ALIAS/ANAME records (Cloudflare, Route 53) or A records directly.\nCertificate errors after moving hosts? Update your CAA records to allow the new host\u0026rsquo;s CA, and ensure A/CNAME records point to the new server before requesting a new certificate.\nRelated Tools IP Address Info Robots.txt Generator .htaccess Generator ","permalink":"https://productivity-works.com/tools/dns-record-guide/","summary":"\u003cdiv id=\"dns-app\"\u003e\n\u003cstyle\u003e\n#dns-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  color: #1a1a2e;\n  line-height: 1.6;\n}\n#dns-app *, #dns-app *::before, #dns-app *::after {\n  box-sizing: border-box;\n}\n#dns-app h2 {\n  font-size: 1.4rem;\n  font-weight: 700;\n  margin: 2rem 0 1rem;\n  color: #1a1a2e;\n  border-bottom: 2px solid #e0e7ff;\n  padding-bottom: 0.4rem;\n}\n#dns-app h3 {\n  font-size: 1.05rem;\n  font-weight: 600;\n  margin: 0 0 0.4rem;\n  color: #1a1a2e;\n}\n\n/* ---------- Config Generator ---------- */\n#dns-config-generator {\n  background: linear-gradient(135deg, #f0f4ff 0%, #e8f5e9 100%);\n  border: 1px solid #c7d7fc;\n  border-radius: 12px;\n  padding: 1.5rem;\n  margin-bottom: 2rem;\n}\n#dns-config-generator label {\n  display: block;\n  font-weight: 600;\n  font-size: 0.9rem;\n  color: #374151;\n  margin-bottom: 0.3rem;\n}\n#dns-domain-input {\n  width: 100%;\n  padding: 0.6rem 0.9rem;\n  border: 1px solid #a5b4fc;\n  border-radius: 8px;\n  font-size: 1rem;\n  margin-bottom: 1rem;\n  outline: none;\n  transition: border 0.2s;\n  background: #fff;\n}\n#dns-domain-input:focus {\n  border-color: #6366f1;\n  box-shadow: 0 0 0 3px rgba(99,102,241,0.15);\n}\n#dns-preset-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));\n  gap: 0.5rem;\n  margin-bottom: 1rem;\n}\n.dns-preset-btn {\n  padding: 0.55rem 0.7rem;\n  border: 2px solid #c7d7fc;\n  border-radius: 8px;\n  background: #fff;\n  cursor: pointer;\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #374151;\n  transition: all 0.15s;\n  text-align: center;\n}\n.dns-preset-btn:hover,\n.dns-preset-btn.dns-preset-active {\n  background: #6366f1;\n  border-color: #6366f1;\n  color: #fff;\n}\n#dns-generate-btn {\n  display: inline-block;\n  padding: 0.65rem 1.6rem;\n  background: #6366f1;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 0.95rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n#dns-generate-btn:hover { background: #4f46e5; }\n\n/* ---------- Results ---------- */\n#dns-results {\n  margin-top: 1.5rem;\n  display: none;\n}\n#dns-results.dns-visible { display: block; }\n.dns-result-header {\n  font-weight: 700;\n  font-size: 1rem;\n  color: #4f46e5;\n  margin-bottom: 0.8rem;\n}\n.dns-record-card {\n  background: #fff;\n  border: 1px solid #e0e7ff;\n  border-radius: 10px;\n  padding: 1rem 1.1rem;\n  margin-bottom: 0.75rem;\n  position: relative;\n}\n.dns-record-type-badge {\n  display: inline-block;\n  padding: 0.2rem 0.55rem;\n  border-radius: 5px;\n  font-size: 0.72rem;\n  font-weight: 800;\n  letter-spacing: 0.04em;\n  margin-right: 0.5rem;\n  vertical-align: middle;\n}\n.dns-badge-A    { background: #dbeafe; color: #1d4ed8; }\n.dns-badge-AAAA { background: #ede9fe; color: #6d28d9; }\n.dns-badge-CNAME{ background: #dcfce7; color: #166534; }\n.dns-badge-MX   { background: #fef9c3; color: #854d0e; }\n.dns-badge-TXT  { background: #fce7f3; color: #9d174d; }\n.dns-badge-NS   { background: #e0f2fe; color: #0369a1; }\n.dns-badge-SRV  { background: #ffedd5; color: #9a3412; }\n.dns-badge-CAA  { background: #f3e8ff; color: #7e22ce; }\n.dns-badge-PTR  { background: #d1fae5; color: #065f46; }\n.dns-badge-SOA  { background: #fee2e2; color: #991b1b; }\n.dns-record-row {\n  display: grid;\n  grid-template-columns: 1fr 1fr 1fr auto;\n  gap: 0.5rem;\n  align-items: center;\n  margin-top: 0.5rem;\n  font-size: 0.85rem;\n}\n.dns-record-label {\n  font-size: 0.72rem;\n  font-weight: 700;\n  color: #6b7280;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 0.1rem;\n}\n.dns-record-value {\n  font-family: \"SF Mono\", \"Fira Code\", \"Fira Mono\", monospace;\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 5px;\n  padding: 0.25rem 0.5rem;\n  word-break: break-all;\n  font-size: 0.82rem;\n  color: #1e293b;\n}\n.dns-copy-btn {\n  padding: 0.3rem 0.7rem;\n  background: #6366f1;\n  color: #fff;\n  border: none;\n  border-radius: 6px;\n  cursor: pointer;\n  font-size: 0.78rem;\n  font-weight: 600;\n  white-space: nowrap;\n  transition: background 0.15s;\n  align-self: end;\n}\n.dns-copy-btn:hover  { background: #4f46e5; }\n.dns-copy-btn.copied { background: #16a34a; }\n.dns-record-note {\n  font-size: 0.78rem;\n  color: #6b7280;\n  margin-top: 0.5rem;\n  font-style: italic;\n}\n\n/* ---------- Reference Table ---------- */\n#dns-reference-section {\n  margin-top: 2.5rem;\n}\n.dns-type-card {\n  background: #fff;\n  border: 1px solid #e0e7ff;\n  border-radius: 10px;\n  padding: 1.1rem 1.2rem;\n  margin-bottom: 1rem;\n}\n.dns-type-card-header {\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n  margin-bottom: 0.6rem;\n}\n.dns-type-desc {\n  font-size: 0.88rem;\n  color: #374151;\n  margin-bottom: 0.5rem;\n}\n.dns-type-example {\n  font-family: \"SF Mono\", \"Fira Code\", monospace;\n  font-size: 0.8rem;\n  background: #f1f5f9;\n  border-left: 3px solid #6366f1;\n  padding: 0.5rem 0.8rem;\n  border-radius: 0 6px 6px 0;\n  color: #1e293b;\n  white-space: pre;\n  overflow-x: auto;\n}\n.dns-type-use {\n  font-size: 0.78rem;\n  color: #6b7280;\n  margin-top: 0.4rem;\n}\n\n/* ---------- Cross-links ---------- */\n#dns-crosslinks {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.6rem;\n  margin-top: 2.5rem;\n}\n.dns-crosslink {\n  display: inline-block;\n  padding: 0.45rem 1rem;\n  background: #e0e7ff;\n  color: #4338ca;\n  border-radius: 20px;\n  font-size: 0.85rem;\n  font-weight: 600;\n  text-decoration: none;\n  transition: background 0.15s;\n}\n.dns-crosslink:hover {\n  background: #c7d7fc;\n  text-decoration: none;\n}\n\n/* ---------- Responsive ---------- */\n@media (max-width: 600px) {\n  #dns-preset-grid {\n    grid-template-columns: repeat(2, 1fr);\n  }\n  .dns-record-row {\n    grid-template-columns: 1fr 1fr;\n    grid-template-rows: auto auto auto;\n  }\n  .dns-copy-btn {\n    grid-column: 1 / -1;\n    width: 100%;\n    margin-top: 0.3rem;\n  }\n}\n\u003c/style\u003e\n\u003ch2 id=\"dns-configuration-helper\"\u003eDNS Configuration Helper\u003c/h2\u003e\n\u003cp\u003eEnter your domain name, choose a preset configuration, and get ready-to-use DNS records.\u003c/p\u003e","title":"DNS Record Guide — Configuration Helper"},{"content":" The old \"multiply by 7\" rule is a myth. Dogs age rapidly in their first two years, then slow down. A 1-year-old dog is roughly equivalent to a 15-year-old human. This calculator uses size-adjusted research to give you a far more accurate estimate. Enter Your Dog's Details Age (Years) Age (Months) Breed Size Small (\u0026lt;10 kg / 22 lbs) Medium (10-25 kg / 22-55 lbs) Large (25-45 kg / 55-99 lbs) Giant (\u0026gt;45 kg / \u0026gt;99 lbs) Calculate Please enter a valid age (0-30 years). 🐾 -- Human-equivalent years -- Dog's Actual Age -- Size Category -- Avg. Lifespan --% Life Stage % Life Stage Timeline Puppy Junior Adult Mature Senior Geriatric Did You Know? Full Age Comparison Table Dog Age Small Medium Large Giant Related Free Tools BMI Calculator Calorie Calculator Sleep Calculator Life Expectancy Calculator Pet Food Calculator Related Tools Age Calculator Pregnancy Due Date Calculator ","permalink":"https://productivity-works.com/tools/dog-age-calculator/","summary":"\u003cdiv id=\"da-app\"\u003e\n\u003cstyle\u003e\n#da-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 780px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#da-app *, #da-app *::before, #da-app *::after {\n  box-sizing: border-box;\n}\n#da-app h2 {\n  font-size: 1.4rem;\n  font-weight: 700;\n  margin: 1.6rem 0 0.8rem;\n  color: #1a1a2e;\n}\n#da-app .da-intro {\n  background: linear-gradient(135deg, #fff8f0 0%, #fff3e0 100%);\n  border-left: 4px solid #f4a226;\n  border-radius: 0 10px 10px 0;\n  padding: 14px 18px;\n  margin-bottom: 24px;\n  font-size: 0.95rem;\n  color: #5a4a2a;\n  line-height: 1.6;\n}\n#da-app .da-card {\n  background: #fff;\n  border: 1px solid #e8e8f0;\n  border-radius: 14px;\n  padding: 24px;\n  margin-bottom: 20px;\n  box-shadow: 0 2px 12px rgba(0,0,0,0.06);\n}\n#da-app .da-form-row {\n  display: flex;\n  gap: 16px;\n  flex-wrap: wrap;\n  align-items: flex-end;\n  margin-bottom: 18px;\n}\n#da-app .da-field {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n  flex: 1;\n  min-width: 130px;\n}\n#da-app .da-field label {\n  font-size: 0.85rem;\n  font-weight: 600;\n  color: #555;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#da-app .da-field input,\n#da-app .da-field select {\n  padding: 10px 14px;\n  border: 2px solid #e0e0ea;\n  border-radius: 8px;\n  font-size: 1rem;\n  color: #1a1a2e;\n  background: #fafafa;\n  transition: border-color 0.2s;\n  outline: none;\n}\n#da-app .da-field input:focus,\n#da-app .da-field select:focus {\n  border-color: #f4a226;\n  background: #fff;\n}\n#da-app .da-btn {\n  padding: 12px 28px;\n  background: linear-gradient(135deg, #f4a226 0%, #e8891a 100%);\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 1rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: transform 0.15s, box-shadow 0.15s;\n  white-space: nowrap;\n  align-self: flex-end;\n}\n#da-app .da-btn:hover {\n  transform: translateY(-1px);\n  box-shadow: 0 4px 16px rgba(244,162,38,0.4);\n}\n#da-app .da-btn:active {\n  transform: translateY(0);\n}\n#da-app .da-result {\n  display: none;\n  animation: daFadeIn 0.4s ease;\n}\n#da-app .da-result.active {\n  display: block;\n}\n@keyframes daFadeIn {\n  from { opacity: 0; transform: translateY(10px); }\n  to   { opacity: 1; transform: translateY(0); }\n}\n#da-app .da-result-hero {\n  background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);\n  border-radius: 14px;\n  padding: 28px 24px;\n  text-align: center;\n  color: #fff;\n  margin-bottom: 20px;\n  position: relative;\n  overflow: hidden;\n}\n#da-app .da-result-hero::before {\n  content: '';\n  position: absolute;\n  top: -40px; right: -40px;\n  width: 160px; height: 160px;\n  background: rgba(244,162,38,0.12);\n  border-radius: 50%;\n}\n#da-app .da-result-hero .da-paw {\n  font-size: 2.4rem;\n  margin-bottom: 8px;\n}\n#da-app .da-result-hero .da-human-age {\n  font-size: 3.6rem;\n  font-weight: 800;\n  color: #f4a226;\n  line-height: 1;\n  margin-bottom: 4px;\n}\n#da-app .da-result-hero .da-human-age-label {\n  font-size: 1rem;\n  color: rgba(255,255,255,0.7);\n  margin-bottom: 16px;\n}\n#da-app .da-stage-badge {\n  display: inline-block;\n  padding: 6px 18px;\n  border-radius: 50px;\n  font-size: 0.9rem;\n  font-weight: 700;\n  letter-spacing: 0.06em;\n  text-transform: uppercase;\n}\n#da-app .da-stage-puppy    { background: #4caf50; color: #fff; }\n#da-app .da-stage-junior   { background: #8bc34a; color: #fff; }\n#da-app .da-stage-adult    { background: #2196f3; color: #fff; }\n#da-app .da-stage-mature   { background: #ff9800; color: #fff; }\n#da-app .da-stage-senior   { background: #f44336; color: #fff; }\n#da-app .da-stage-geriatric{ background: #9c27b0; color: #fff; }\n#da-app .da-meta-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n  gap: 14px;\n  margin-bottom: 20px;\n}\n#da-app .da-meta-item {\n  background: #f8f8fc;\n  border-radius: 10px;\n  padding: 14px 16px;\n  text-align: center;\n}\n#da-app .da-meta-value {\n  font-size: 1.5rem;\n  font-weight: 700;\n  color: #f4a226;\n  margin-bottom: 2px;\n}\n#da-app .da-meta-label {\n  font-size: 0.78rem;\n  color: #888;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n/* Timeline */\n#da-app .da-timeline {\n  position: relative;\n  padding: 10px 0;\n  margin: 8px 0 20px;\n}\n#da-app .da-timeline-track {\n  height: 10px;\n  border-radius: 5px;\n  background: linear-gradient(to right,\n    #4caf50 0%, #4caf50 8%,\n    #8bc34a 8%, #8bc34a 20%,\n    #2196f3 20%, #2196f3 55%,\n    #ff9800 55%, #ff9800 72%,\n    #f44336 72%, #f44336 88%,\n    #9c27b0 88%, #9c27b0 100%\n  );\n  position: relative;\n}\n#da-app .da-timeline-marker {\n  position: absolute;\n  top: -5px;\n  width: 20px;\n  height: 20px;\n  background: #fff;\n  border: 3px solid #1a1a2e;\n  border-radius: 50%;\n  transform: translateX(-50%);\n  transition: left 0.5s ease;\n  box-shadow: 0 2px 8px rgba(0,0,0,0.2);\n}\n#da-app .da-timeline-labels {\n  display: flex;\n  justify-content: space-between;\n  margin-top: 8px;\n  font-size: 0.72rem;\n  color: #888;\n}\n/* Fun facts */\n#da-app .da-fact {\n  background: #f0f7ff;\n  border-radius: 10px;\n  padding: 14px 18px;\n  margin-bottom: 10px;\n  font-size: 0.92rem;\n  color: #1a3a5c;\n  line-height: 1.6;\n  border-left: 3px solid #2196f3;\n}\n#da-app .da-fact-icon {\n  font-weight: 700;\n  color: #2196f3;\n  margin-right: 6px;\n}\n/* Comparison table */\n#da-app .da-table-wrap {\n  overflow-x: auto;\n  -webkit-overflow-scrolling: touch;\n  border-radius: 10px;\n  border: 1px solid #e8e8f0;\n}\n#da-app table.da-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.88rem;\n}\n#da-app table.da-table thead tr {\n  background: #1a1a2e;\n  color: #fff;\n}\n#da-app table.da-table th {\n  padding: 10px 14px;\n  text-align: center;\n  font-weight: 600;\n  letter-spacing: 0.04em;\n  font-size: 0.82rem;\n}\n#da-app table.da-table th:first-child {\n  text-align: left;\n}\n#da-app table.da-table td {\n  padding: 9px 14px;\n  text-align: center;\n  border-bottom: 1px solid #f0f0f6;\n  color: #333;\n}\n#da-app table.da-table td:first-child {\n  text-align: left;\n  font-weight: 600;\n  color: #1a1a2e;\n}\n#da-app table.da-table tbody tr:nth-child(even) {\n  background: #fafafa;\n}\n#da-app table.da-table tbody tr.da-highlight {\n  background: #fff8e1 !important;\n  font-weight: 700;\n}\n#da-app table.da-table tbody tr:last-child td {\n  border-bottom: none;\n}\n/* Related tools */\n#da-app .da-related {\n  background: #f8f8fc;\n  border-radius: 12px;\n  padding: 18px 20px;\n  margin-top: 24px;\n}\n#da-app .da-related h3 {\n  font-size: 1rem;\n  font-weight: 700;\n  margin: 0 0 12px;\n  color: #1a1a2e;\n}\n#da-app .da-related ul {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n}\n#da-app .da-related li a {\n  display: inline-block;\n  padding: 6px 14px;\n  background: #fff;\n  border: 1px solid #e0e0ea;\n  border-radius: 50px;\n  font-size: 0.88rem;\n  color: #f4a226;\n  text-decoration: none;\n  font-weight: 600;\n  transition: background 0.15s, color 0.15s;\n}\n#da-app .da-related li a:hover {\n  background: #f4a226;\n  color: #fff;\n  border-color: #f4a226;\n}\n#da-app .da-error {\n  color: #e53935;\n  font-size: 0.88rem;\n  margin-top: 6px;\n  display: none;\n}\n@media (max-width: 480px) {\n  #da-app .da-result-hero .da-human-age { font-size: 2.8rem; }\n  #da-app .da-form-row { flex-direction: column; }\n  #da-app .da-btn { width: 100%; }\n}\n\u003c/style\u003e\n\u003cdiv class=\"da-intro\"\u003e\nThe old \"multiply by 7\" rule is a myth. Dogs age rapidly in their first two years, then slow down. A 1-year-old dog is roughly equivalent to a 15-year-old human. This calculator uses size-adjusted research to give you a far more accurate estimate.\n\u003c/div\u003e\n\u003c!-- Input Card --\u003e\n\u003cdiv class=\"da-card\"\u003e\n  \u003ch2 style=\"margin-top:0\"\u003eEnter Your Dog's Details\u003c/h2\u003e\n  \u003cdiv class=\"da-form-row\"\u003e\n    \u003cdiv class=\"da-field\"\u003e\n      \u003clabel for=\"da-years\"\u003eAge (Years)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"da-years\" min=\"0\" max=\"30\" placeholder=\"e.g. 5\" /\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"da-field\"\u003e\n      \u003clabel for=\"da-months\"\u003eAge (Months)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"da-months\" min=\"0\" max=\"11\" placeholder=\"0-11\" /\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"da-field\" style=\"min-width:180px\"\u003e\n      \u003clabel for=\"da-size\"\u003eBreed Size\u003c/label\u003e\n      \u003cselect id=\"da-size\"\u003e\n        \u003coption value=\"small\"\u003eSmall (\u0026lt;10 kg / 22 lbs)\u003c/option\u003e\n        \u003coption value=\"medium\" selected\u003eMedium (10-25 kg / 22-55 lbs)\u003c/option\u003e\n        \u003coption value=\"large\"\u003eLarge (25-45 kg / 55-99 lbs)\u003c/option\u003e\n        \u003coption value=\"giant\"\u003eGiant (\u0026gt;45 kg / \u0026gt;99 lbs)\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n    \u003cbutton class=\"da-btn\" onclick=\"daCalculate()\"\u003eCalculate\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"da-error\" id=\"da-error\"\u003ePlease enter a valid age (0-30 years).\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Result --\u003e\n\u003cdiv class=\"da-result\" id=\"da-result\"\u003e\n  \u003cdiv class=\"da-result-hero\"\u003e\n    \u003cdiv class=\"da-paw\"\u003e🐾\u003c/div\u003e\n    \u003cdiv class=\"da-human-age\" id=\"da-human-age\"\u003e--\u003c/div\u003e\n    \u003cdiv class=\"da-human-age-label\"\u003eHuman-equivalent years\u003c/div\u003e\n    \u003cspan class=\"da-stage-badge\" id=\"da-stage-badge\"\u003e\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"da-meta-grid\"\u003e\n    \u003cdiv class=\"da-meta-item\"\u003e\n      \u003cdiv class=\"da-meta-value\" id=\"da-dog-age-display\"\u003e--\u003c/div\u003e\n      \u003cdiv class=\"da-meta-label\"\u003eDog's Actual Age\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"da-meta-item\"\u003e\n      \u003cdiv class=\"da-meta-value\" id=\"da-size-display\"\u003e--\u003c/div\u003e\n      \u003cdiv class=\"da-meta-label\"\u003eSize Category\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"da-meta-item\"\u003e\n      \u003cdiv class=\"da-meta-value\" id=\"da-lifespan\"\u003e--\u003c/div\u003e\n      \u003cdiv class=\"da-meta-label\"\u003eAvg. Lifespan\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"da-meta-item\"\u003e\n      \u003cdiv class=\"da-meta-value\" id=\"da-life-pct\"\u003e--%\u003c/div\u003e\n      \u003cdiv class=\"da-meta-label\"\u003eLife Stage %\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Life stage timeline --\u003e\n  \u003cdiv class=\"da-card\" style=\"padding:18px 20px\"\u003e\n    \u003cstrong style=\"font-size:0.9rem;color:#555;text-transform:uppercase;letter-spacing:0.05em\"\u003eLife Stage Timeline\u003c/strong\u003e\n    \u003cdiv class=\"da-timeline\" style=\"margin-top:14px\"\u003e\n      \u003cdiv class=\"da-timeline-track\"\u003e\n        \u003cdiv class=\"da-timeline-marker\" id=\"da-marker\"\u003e\u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"da-timeline-labels\"\u003e\n        \u003cspan\u003ePuppy\u003c/span\u003e\n        \u003cspan\u003eJunior\u003c/span\u003e\n        \u003cspan\u003eAdult\u003c/span\u003e\n        \u003cspan\u003eMature\u003c/span\u003e\n        \u003cspan\u003eSenior\u003c/span\u003e\n        \u003cspan\u003eGeriatric\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Fun Facts --\u003e\n  \u003ch2\u003eDid You Know?\u003c/h2\u003e\n  \u003cdiv id=\"da-facts\"\u003e\u003c/div\u003e\n  \u003c!-- Comparison Table --\u003e\n  \u003ch2\u003eFull Age Comparison Table\u003c/h2\u003e\n  \u003cdiv class=\"da-table-wrap\"\u003e\n    \u003ctable class=\"da-table\" id=\"da-table\"\u003e\n      \u003cthead\u003e\n        \u003ctr\u003e\n          \u003cth\u003eDog Age\u003c/th\u003e\n          \u003cth\u003eSmall\u003c/th\u003e\n          \u003cth\u003eMedium\u003c/th\u003e\n          \u003cth\u003eLarge\u003c/th\u003e\n          \u003cth\u003eGiant\u003c/th\u003e\n        \u003c/tr\u003e\n      \u003c/thead\u003e\n      \u003ctbody id=\"da-tbody\"\u003e\u003c/tbody\u003e\n    \u003c/table\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\u003c!-- /.da-result --\u003e\n\u003c!-- Related Tools --\u003e\n\u003cdiv class=\"da-related\"\u003e\n  \u003ch3\u003eRelated Free Tools\u003c/h3\u003e\n  \u003cul\u003e\n    \u003cli\u003e\u003ca href=\"/tools/bmi-calculator/\"\u003eBMI Calculator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/calorie-calculator/\"\u003eCalorie Calculator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/sleep-calculator/\"\u003eSleep Calculator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/retirement-calculator/\"\u003eLife Expectancy Calculator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/calorie-calculator/\"\u003ePet Food Calculator\u003c/a\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  // Size-adjusted human-equivalent age tables\n  // Based on research from American Veterinary Medical Association (AVMA)\n  // and studies on epigenetic clocks (Horvath et al., 2020 Cell Systems)\n  // Format: [small, medium, large, giant]\n  var AGE_TABLE = {\n    // dog_age_months: [small, medium, large, giant]\n    1:  [1, 1, 1, 1],\n    2:  [3, 3, 3, 3],\n    3:  [5, 5, 5, 5],\n    4:  [7, 7, 7, 7],\n    5:  [9, 9, 9, 9],\n    6:  [10, 10, 10, 10],\n    8:  [11, 11, 11, 11],\n    10: [12, 12, 12, 12],\n    12: [15, 15, 15, 15],  // 1 year\n    18: [18, 18, 19, 20],\n    24: [22, 22, 24, 26],  // 2 years\n    36: [26, 28, 30, 34],  // 3 years\n    48: [30, 34, 36, 42],  // 4 years\n    60: [34, 38, 42, 50],  // 5 years\n    72: [38, 42, 48, 58],  // 6 years\n    84: [42, 46, 54, 66],  // 7 years\n    96: [46, 50, 60, 74],  // 8 years\n    108:[50, 54, 66, 82],  // 9 years\n    120:[56, 60, 72, 90],  // 10 years\n    132:[60, 65, 78, 98],  // 11 years\n    144:[64, 70, 84, 106], // 12 years\n    156:[68, 75, 90, 114], // 13 years\n    168:[72, 80, 96, 122], // 14 years\n    180:[76, 85, 102, 130],// 15 years\n    192:[80, 90, 108, 138],// 16 years\n    204:[84, 95, 114, 145],// 17 years\n    216:[88, 100, 120, 152],// 18 years\n    228:[92, 105, 126, 159],// 19 years\n    240:[96, 110, 132, 166] // 20 years\n  };\n\n  var SIZE_INDEX = { small: 0, medium: 1, large: 2, giant: 3 };\n  var SIZE_LABELS = { small: 'Small', medium: 'Medium', large: 'Large', giant: 'Giant' };\n  var LIFESPANS = { small: '14-16 yrs', medium: '12-14 yrs', large: '10-12 yrs', giant: '8-10 yrs' };\n  var LIFESPAN_YRS = { small: 15, medium: 13, large: 11, giant: 9 };\n\n  function interpolate(totalMonths, sizeIdx) {\n    var keys = Object.keys(AGE_TABLE).map(Number).sort(function(a,b){return a-b;});\n    if (totalMonths \u003c= 0) return 0;\n    if (totalMonths \u003e= keys[keys.length-1]) {\n      return AGE_TABLE[keys[keys.length-1]][sizeIdx];\n    }\n    for (var i = 0; i \u003c keys.length - 1; i++) {\n      if (totalMonths \u003e= keys[i] \u0026\u0026 totalMonths \u003c= keys[i+1]) {\n        var lo = keys[i], hi = keys[i+1];\n        var loVal = AGE_TABLE[lo][sizeIdx];\n        var hiVal = AGE_TABLE[hi][sizeIdx];\n        var ratio = (totalMonths - lo) / (hi - lo);\n        return Math.round(loVal + ratio * (hiVal - loVal));\n      }\n    }\n    return 0;\n  }\n\n  function getStage(totalMonths, size) {\n    var lifespan = LIFESPAN_YRS[size] * 12;\n    var ratio = totalMonths / lifespan;\n    if (totalMonths \u003c 12)        return { label: 'Puppy',    cls: 'da-stage-puppy',     pct: ratio };\n    if (totalMonths \u003c 24)        return { label: 'Junior',   cls: 'da-stage-junior',    pct: ratio };\n    if (ratio \u003c 0.50)            return { label: 'Adult',    cls: 'da-stage-adult',     pct: ratio };\n    if (ratio \u003c 0.68)            return { label: 'Mature',   cls: 'da-stage-mature',    pct: ratio };\n    if (ratio \u003c 0.85)            return { label: 'Senior',   cls: 'da-stage-senior',    pct: ratio };\n    return                              { label: 'Geriatric', cls: 'da-stage-geriatric', pct: ratio };\n  }\n\n  var FACTS = [\n    '\u003cspan class=\"da-fact-icon\"\u003eScience\u003c/span\u003eA 2020 study in \u003cem\u003eCell Systems\u003c/em\u003e mapped dog aging using DNA methylation patterns, finding dogs age very rapidly in early life — a 1-year-old dog has a similar epigenetic profile to a 30-year-old human.',\n    '\u003cspan class=\"da-fact-icon\"\u003eSize\u003c/span\u003eLarger dogs age faster than smaller ones. A Great Dane may be \"old\" at 7, while a Chihuahua might remain spry into its early teens. The exact reason is still being researched — faster growth may accelerate cellular aging.',\n    '\u003cspan class=\"da-fact-icon\"\u003eRecord\u003c/span\u003eThe oldest verified dog on record was Bobi, a Rafeiro do Alentejo from Portugal, who lived to 31 years and 165 days — equivalent to over 200 human years by some estimates.',\n    '\u003cspan class=\"da-fact-icon\"\u003eFirst year\u003c/span\u003eIn your dog\\'s very first year, they go from newborn to sexually mature — equivalent to roughly 15 human years of development in just 12 months.',\n    '\u003cspan class=\"da-fact-icon\"\u003eSenior care\u003c/span\u003eMost veterinarians classify dogs as \"senior\" at age 7. Once in the senior stage, bi-annual vet check-ups (instead of annual) are recommended to catch age-related conditions early.'\n  ];\n\n  function buildFacts() {\n    var html = '';\n    FACTS.forEach(function(f) {\n      html += '\u003cdiv class=\"da-fact\"\u003e' + f + '\u003c/div\u003e';\n    });\n    document.getElementById('da-facts').innerHTML = html;\n  }\n\n  function buildTable(highlightMonths, size) {\n    var sizeIdx = SIZE_INDEX[size];\n    var rows = '';\n    for (var yr = 1; yr \u003c= 20; yr++) {\n      var m = yr * 12;\n      var isHL = (highlightMonths \u003e= (m - 6) \u0026\u0026 highlightMonths \u003c (m + 6));\n      rows += '\u003ctr' + (isHL ? ' class=\"da-highlight\"' : '') + '\u003e';\n      rows += '\u003ctd\u003e' + yr + ' yr' + (yr === 1 ? '' : 's') + '\u003c/td\u003e';\n      var sizes = ['small','medium','large','giant'];\n      sizes.forEach(function(s) {\n        var val = interpolate(m, SIZE_INDEX[s]);\n        rows += '\u003ctd\u003e' + val + '\u003c/td\u003e';\n      });\n      rows += '\u003c/tr\u003e';\n    }\n    document.getElementById('da-tbody').innerHTML = rows;\n  }\n\n  window.daCalculate = function() {\n    var yrsEl = document.getElementById('da-years');\n    var mosEl = document.getElementById('da-months');\n    var sizeEl = document.getElementById('da-size');\n    var errEl = document.getElementById('da-error');\n\n    var yrs = parseFloat(yrsEl.value) || 0;\n    var mos = parseFloat(mosEl.value) || 0;\n    var size = sizeEl.value;\n\n    if (yrs \u003c 0 || yrs \u003e 30 || mos \u003c 0 || mos \u003e 11 || (yrs === 0 \u0026\u0026 mos === 0)) {\n      errEl.style.display = 'block';\n      return;\n    }\n    errEl.style.display = 'none';\n\n    var totalMonths = Math.round(yrs * 12 + mos);\n    var sizeIdx = SIZE_INDEX[size];\n    var humanAge = interpolate(totalMonths, sizeIdx);\n    var stage = getStage(totalMonths, size);\n    var lifePct = Math.min(100, Math.round(stage.pct * 100));\n\n    // Display age string\n    var dogAgeStr = yrs \u003e 0 ? yrs + ' yr' + (yrs !== 1 ? 's' : '') : '';\n    if (mos \u003e 0) dogAgeStr += (dogAgeStr ? ' ' : '') + mos + ' mo' + (mos !== 1 ? 's' : '');\n\n    document.getElementById('da-human-age').textContent = humanAge;\n    document.getElementById('da-dog-age-display').textContent = dogAgeStr || '\u003c 1 mo';\n    document.getElementById('da-size-display').textContent = SIZE_LABELS[size];\n    document.getElementById('da-lifespan').textContent = LIFESPANS[size];\n    document.getElementById('da-life-pct').textContent = lifePct + '%';\n\n    var badge = document.getElementById('da-stage-badge');\n    badge.textContent = stage.label;\n    badge.className = 'da-stage-badge ' + stage.cls;\n\n    // Timeline marker position (capped 2%-96%)\n    var markerPct = Math.max(2, Math.min(96, lifePct));\n    document.getElementById('da-marker').style.left = markerPct + '%';\n\n    buildFacts();\n    buildTable(totalMonths, size);\n\n    var resultEl = document.getElementById('da-result');\n    resultEl.classList.add('active');\n    resultEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n  };\n\n  // Allow Enter key to trigger calculation\n  ['da-years','da-months','da-size'].forEach(function(id) {\n    var el = document.getElementById(id);\n    if (el) el.addEventListener('keydown', function(e) {\n      if (e.key === 'Enter') window.daCalculate();\n    });\n  });\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"https://productivity-works.com/tools/age-calculator/\"\u003eAge Calculator\u003c/a\u003e\n\n\u003ca href=\"https://productivity-works.com/tools/pregnancy-due-date-calculator/\"\u003ePregnancy Due Date Calculator\u003c/a\u003e\n\u003c/p\u003e","title":"Dog Age Calculator - Convert Dog Years to Human Years"},{"content":" File Size \u0026 Speed Input Quick file size presets: MP3 Song (5 MB) Photo (10 MB) Document (25 MB) CD Image (700 MB) HD Movie (5 GB) 4K Movie (25 GB) PC Game (50 GB) Console Game (100 GB) File Size Unit KB (Kilobytes) MB (Megabytes) GB (Gigabytes) TB (Terabytes) Quick speed presets: 3G — 1 Mbps 3G — 5 Mbps 4G LTE — 25 Mbps 4G+ — 50 Mbps Wi-Fi — 100 Mbps Wi-Fi — 300 Mbps Fiber — 500 Mbps Fiber — 1 Gbps Internet Speed (Mbps) Please enter a valid file size and speed greater than zero. Calculate Download Time Speed Unit Converter Enter a value in any field — all others update automatically. Mbps MBps (MB/s) Kbps KBps (KB/s) Gbps Comparison Table Connection Type Speed — — — Table updates automatically when you calculate a file size above. Related Free Tools Bandwidth Calculator — plan your network capacity File Size Converter — bytes, KB, MB, GB, TB What is a good internet speed? Complete guide Data Usage Calculator — estimate your monthly usage Related Articles Best Internet Service Providers 2026: Speed \u0026amp; Price Compared ","permalink":"https://productivity-works.com/tools/download-time-calculator/","summary":"\u003cdiv id=\"dt-app\"\u003e\n\u003cstyle\u003e\n#dt-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #1a202c;\n}\n#dt-app * { box-sizing: border-box; }\n#dt-app h2 {\n  font-size: 1.3rem;\n  font-weight: 700;\n  margin: 1.6rem 0 0.8rem;\n  color: #2d3748;\n  border-left: 4px solid #4299e1;\n  padding-left: 0.6rem;\n}\n#dt-app .card {\n  background: #f7fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 1.4rem 1.6rem;\n  margin-bottom: 1.4rem;\n}\n#dt-app .row {\n  display: flex;\n  gap: 1rem;\n  flex-wrap: wrap;\n  align-items: flex-end;\n  margin-bottom: 1rem;\n}\n#dt-app .field {\n  display: flex;\n  flex-direction: column;\n  gap: 0.3rem;\n}\n#dt-app label {\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #4a5568;\n  text-transform: uppercase;\n  letter-spacing: 0.03em;\n}\n#dt-app input[type=\"number\"] {\n  padding: 0.55rem 0.75rem;\n  border: 1.5px solid #cbd5e0;\n  border-radius: 7px;\n  font-size: 1rem;\n  width: 160px;\n  background: #fff;\n  transition: border-color 0.2s;\n}\n#dt-app input[type=\"number\"]:focus {\n  outline: none;\n  border-color: #4299e1;\n  box-shadow: 0 0 0 3px rgba(66,153,225,0.15);\n}\n#dt-app select {\n  padding: 0.55rem 0.75rem;\n  border: 1.5px solid #cbd5e0;\n  border-radius: 7px;\n  font-size: 0.95rem;\n  background: #fff;\n  cursor: pointer;\n  transition: border-color 0.2s;\n}\n#dt-app select:focus {\n  outline: none;\n  border-color: #4299e1;\n}\n#dt-app .presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem;\n  margin-bottom: 0.8rem;\n}\n#dt-app .preset-btn {\n  padding: 0.38rem 0.85rem;\n  border: 1.5px solid #bee3f8;\n  border-radius: 20px;\n  background: #ebf8ff;\n  color: #2b6cb0;\n  font-size: 0.82rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.18s;\n  white-space: nowrap;\n}\n#dt-app .preset-btn:hover {\n  background: #4299e1;\n  color: #fff;\n  border-color: #4299e1;\n}\n#dt-app .preset-btn.active {\n  background: #2b6cb0;\n  color: #fff;\n  border-color: #2b6cb0;\n}\n#dt-app .calc-btn {\n  padding: 0.65rem 2rem;\n  background: linear-gradient(135deg, #4299e1, #2b6cb0);\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 1rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: opacity 0.2s, transform 0.1s;\n  margin-top: 0.5rem;\n}\n#dt-app .calc-btn:hover { opacity: 0.9; transform: translateY(-1px); }\n#dt-app .calc-btn:active { transform: translateY(0); }\n#dt-app .result-box {\n  background: linear-gradient(135deg, #ebf8ff, #e6fffa);\n  border: 2px solid #4299e1;\n  border-radius: 10px;\n  padding: 1.2rem 1.6rem;\n  margin-top: 1rem;\n  display: none;\n}\n#dt-app .result-box.show { display: block; }\n#dt-app .result-time {\n  font-size: 2.2rem;\n  font-weight: 800;\n  color: #2b6cb0;\n  margin-bottom: 0.3rem;\n}\n#dt-app .result-detail {\n  font-size: 0.9rem;\n  color: #4a5568;\n}\n#dt-app .converter-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n  gap: 0.8rem;\n}\n#dt-app .conv-field {\n  display: flex;\n  flex-direction: column;\n  gap: 0.3rem;\n}\n#dt-app .conv-field input {\n  width: 100%;\n}\n#dt-app table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.9rem;\n}\n#dt-app th {\n  background: #4299e1;\n  color: #fff;\n  padding: 0.6rem 0.8rem;\n  text-align: left;\n  font-weight: 600;\n}\n#dt-app th:first-child { border-radius: 6px 0 0 0; }\n#dt-app th:last-child { border-radius: 0 6px 0 0; }\n#dt-app td {\n  padding: 0.55rem 0.8rem;\n  border-bottom: 1px solid #e2e8f0;\n}\n#dt-app tr:nth-child(even) td { background: #f7fafc; }\n#dt-app tr:hover td { background: #ebf8ff; }\n#dt-app .highlight-row td { font-weight: 700; color: #2b6cb0; }\n#dt-app .section-label {\n  font-size: 0.78rem;\n  color: #718096;\n  margin-bottom: 0.5rem;\n}\n#dt-app .error-msg {\n  color: #e53e3e;\n  font-size: 0.85rem;\n  margin-top: 0.4rem;\n  display: none;\n}\n#dt-app .related-links {\n  background: #f0fff4;\n  border: 1px solid #9ae6b4;\n  border-radius: 8px;\n  padding: 1rem 1.4rem;\n  margin-top: 1.4rem;\n}\n#dt-app .related-links h3 {\n  font-size: 1rem;\n  font-weight: 700;\n  color: #276749;\n  margin: 0 0 0.6rem;\n}\n#dt-app .related-links ul {\n  margin: 0;\n  padding-left: 1.2rem;\n}\n#dt-app .related-links li { margin-bottom: 0.3rem; font-size: 0.9rem; }\n@media (max-width: 600px) {\n  #dt-app input[type=\"number\"] { width: 130px; }\n  #dt-app .result-time { font-size: 1.6rem; }\n}\n\u003c/style\u003e\n\u003ch2\u003eFile Size \u0026 Speed Input\u003c/h2\u003e\n\u003cdiv class=\"card\"\u003e\n  \u003cdiv class=\"section-label\"\u003eQuick file size presets:\u003c/div\u003e\n  \u003cdiv class=\"presets\" id=\"size-presets\"\u003e\n    \u003cbutton class=\"preset-btn\" onclick=\"dtSetSize(5,'MB','MP3 Song')\"\u003eMP3 Song (5 MB)\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" onclick=\"dtSetSize(10,'MB','Photo')\"\u003ePhoto (10 MB)\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" onclick=\"dtSetSize(25,'MB','Document')\"\u003eDocument (25 MB)\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" onclick=\"dtSetSize(700,'MB','CD Image')\"\u003eCD Image (700 MB)\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" onclick=\"dtSetSize(5,'GB','HD Movie')\"\u003eHD Movie (5 GB)\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" onclick=\"dtSetSize(25,'GB','4K Movie')\"\u003e4K Movie (25 GB)\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" onclick=\"dtSetSize(50,'GB','PC Game')\"\u003ePC Game (50 GB)\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" onclick=\"dtSetSize(100,'GB','Console Game')\"\u003eConsole Game (100 GB)\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"row\"\u003e\n    \u003cdiv class=\"field\"\u003e\n      \u003clabel\u003eFile Size\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"dt-filesize\" min=\"0\" step=\"any\" placeholder=\"e.g. 1.5\" value=\"\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"field\"\u003e\n      \u003clabel\u003eUnit\u003c/label\u003e\n      \u003cselect id=\"dt-sizeunit\"\u003e\n        \u003coption value=\"KB\"\u003eKB (Kilobytes)\u003c/option\u003e\n        \u003coption value=\"MB\" selected\u003eMB (Megabytes)\u003c/option\u003e\n        \u003coption value=\"GB\"\u003eGB (Gigabytes)\u003c/option\u003e\n        \u003coption value=\"TB\"\u003eTB (Terabytes)\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"section-label\" style=\"margin-top:0.8rem;\"\u003eQuick speed presets:\u003c/div\u003e\n  \u003cdiv class=\"presets\" id=\"speed-presets\"\u003e\n    \u003cbutton class=\"preset-btn\" onclick=\"dtSetSpeed(1,'3G (Slow)')\"\u003e3G — 1 Mbps\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" onclick=\"dtSetSpeed(5,'3G (Typical)')\"\u003e3G — 5 Mbps\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" onclick=\"dtSetSpeed(25,'4G LTE')\"\u003e4G LTE — 25 Mbps\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" onclick=\"dtSetSpeed(50,'4G+ LTE')\"\u003e4G+ — 50 Mbps\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" onclick=\"dtSetSpeed(100,'Wi-Fi (100)')\"\u003eWi-Fi — 100 Mbps\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" onclick=\"dtSetSpeed(300,'Wi-Fi (300)')\"\u003eWi-Fi — 300 Mbps\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" onclick=\"dtSetSpeed(500,'Fiber (500)')\"\u003eFiber — 500 Mbps\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" onclick=\"dtSetSpeed(1000,'Fiber (1 Gbps)')\"\u003eFiber — 1 Gbps\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"row\"\u003e\n    \u003cdiv class=\"field\"\u003e\n      \u003clabel\u003eInternet Speed (Mbps)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"dt-speed\" min=\"0.01\" step=\"any\" placeholder=\"e.g. 100\" value=\"\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"error-msg\" id=\"dt-error\"\u003ePlease enter a valid file size and speed greater than zero.\u003c/div\u003e\n  \u003cbutton class=\"calc-btn\" onclick=\"dtCalculate()\"\u003eCalculate Download Time\u003c/button\u003e\n  \u003cdiv class=\"result-box\" id=\"dt-result\"\u003e\n    \u003cdiv class=\"result-time\" id=\"dt-result-time\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"result-detail\" id=\"dt-result-detail\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003ch2\u003eSpeed Unit Converter\u003c/h2\u003e\n\u003cdiv class=\"card\"\u003e\n  \u003cdiv class=\"section-label\"\u003eEnter a value in any field — all others update automatically.\u003c/div\u003e\n  \u003cdiv class=\"converter-grid\"\u003e\n    \u003cdiv class=\"conv-field\"\u003e\n      \u003clabel\u003eMbps\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"cv-mbps\" min=\"0\" step=\"any\" placeholder=\"e.g. 100\" oninput=\"dtConvert('mbps')\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"conv-field\"\u003e\n      \u003clabel\u003eMBps (MB/s)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"cv-mbps2\" min=\"0\" step=\"any\" placeholder=\"\" oninput=\"dtConvert('mbps2')\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"conv-field\"\u003e\n      \u003clabel\u003eKbps\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"cv-kbps\" min=\"0\" step=\"any\" placeholder=\"\" oninput=\"dtConvert('kbps')\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"conv-field\"\u003e\n      \u003clabel\u003eKBps (KB/s)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"cv-kbps2\" min=\"0\" step=\"any\" placeholder=\"\" oninput=\"dtConvert('kbps2')\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"conv-field\"\u003e\n      \u003clabel\u003eGbps\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"cv-gbps\" min=\"0\" step=\"any\" placeholder=\"\" oninput=\"dtConvert('gbps')\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003ch2\u003eComparison Table\u003c/h2\u003e\n\u003cdiv class=\"card\" style=\"padding: 0.6rem 0; overflow-x:auto;\"\u003e\n  \u003ctable id=\"dt-table\"\u003e\n    \u003cthead\u003e\n      \u003ctr\u003e\n        \u003cth\u003eConnection Type\u003c/th\u003e\n        \u003cth\u003eSpeed\u003c/th\u003e\n        \u003cth id=\"th-col1\"\u003e—\u003c/th\u003e\n        \u003cth id=\"th-col2\"\u003e—\u003c/th\u003e\n        \u003cth id=\"th-col3\"\u003e—\u003c/th\u003e\n      \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody id=\"dt-tbody\"\u003e\u003c/tbody\u003e\n  \u003c/table\u003e\n  \u003cdiv style=\"font-size:0.78rem;color:#718096;padding:0.5rem 0.8rem 0;\"\u003e\n    Table updates automatically when you calculate a file size above.\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"related-links\"\u003e\n  \u003ch3\u003eRelated Free Tools\u003c/h3\u003e\n  \u003cul\u003e\n    \u003cli\u003e\u003ca href=\"/tools/download-time-calculator/\"\u003eBandwidth Calculator — plan your network capacity\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/byte-converter/\"\u003eFile Size Converter — bytes, KB, MB, GB, TB\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/download-time-calculator/\"\u003eWhat is a good internet speed? Complete guide\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/byte-converter/\"\u003eData Usage Calculator — estimate your monthly usage\u003c/a\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var dtActiveSize = null;\n  var dtActiveSpeed = null;\n\n  window.dtSetSize = function(val, unit, label) {\n    document.getElementById('dt-filesize').value = val;\n    document.getElementById('dt-sizeunit').value = unit;\n    document.querySelectorAll('#size-presets .preset-btn').forEach(function(b) { b.classList.remove('active'); });\n    event.target.classList.add('active');\n    dtActiveSize = label;\n  };\n\n  window.dtSetSpeed = function(val, label) {\n    document.getElementById('dt-speed').value = val;\n    document.querySelectorAll('#speed-presets .preset-btn').forEach(function(b) { b.classList.remove('active'); });\n    event.target.classList.add('active');\n    dtActiveSpeed = label;\n  };\n\n  function dtToBytes(val, unit) {\n    var u = { KB: 1024, MB: 1024*1024, GB: 1024*1024*1024, TB: 1024*1024*1024*1024 };\n    return val * (u[unit] || 1);\n  }\n\n  function dtFormatTime(seconds) {\n    seconds = Math.round(seconds);\n    if (seconds \u003c 1) return 'Less than 1 second';\n    var d = Math.floor(seconds / 86400);\n    var h = Math.floor((seconds % 86400) / 3600);\n    var m = Math.floor((seconds % 3600) / 60);\n    var s = seconds % 60;\n    var parts = [];\n    if (d \u003e 0) parts.push(d + ' day' + (d \u003e 1 ? 's' : ''));\n    if (h \u003e 0) parts.push(h + ' hour' + (h \u003e 1 ? 's' : ''));\n    if (m \u003e 0) parts.push(m + ' minute' + (m \u003e 1 ? 's' : ''));\n    if (s \u003e 0 \u0026\u0026 d === 0) parts.push(s + ' second' + (s \u003e 1 ? 's' : ''));\n    return parts.join(', ');\n  }\n\n  function dtFormatTimeShort(seconds) {\n    seconds = Math.round(seconds);\n    if (seconds \u003c 1) return '\u003c 1s';\n    var d = Math.floor(seconds / 86400);\n    var h = Math.floor((seconds % 86400) / 3600);\n    var m = Math.floor((seconds % 3600) / 60);\n    var s = seconds % 60;\n    if (d \u003e 0) return d + 'd ' + h + 'h ' + m + 'm';\n    if (h \u003e 0) return h + 'h ' + m + 'm ' + s + 's';\n    if (m \u003e 0) return m + 'm ' + s + 's';\n    return s + 's';\n  }\n\n  function dtFormatSize(bytes) {\n    if (bytes \u003e= 1024*1024*1024*1024) return (bytes/(1024*1024*1024*1024)).toFixed(2) + ' TB';\n    if (bytes \u003e= 1024*1024*1024) return (bytes/(1024*1024*1024)).toFixed(2) + ' GB';\n    if (bytes \u003e= 1024*1024) return (bytes/(1024*1024)).toFixed(2) + ' MB';\n    if (bytes \u003e= 1024) return (bytes/1024).toFixed(2) + ' KB';\n    return bytes + ' B';\n  }\n\n  window.dtCalculate = function() {\n    var sizeVal = parseFloat(document.getElementById('dt-filesize').value);\n    var sizeUnit = document.getElementById('dt-sizeunit').value;\n    var speedMbps = parseFloat(document.getElementById('dt-speed').value);\n    var errEl = document.getElementById('dt-error');\n    var resBox = document.getElementById('dt-result');\n\n    if (!sizeVal || sizeVal \u003c= 0 || !speedMbps || speedMbps \u003c= 0) {\n      errEl.style.display = 'block';\n      resBox.classList.remove('show');\n      return;\n    }\n    errEl.style.display = 'none';\n\n    var bytes = dtToBytes(sizeVal, sizeUnit);\n    var bits = bytes * 8;\n    var bitsPerSec = speedMbps * 1000000;\n    var seconds = bits / bitsPerSec;\n\n    document.getElementById('dt-result-time').textContent = dtFormatTime(seconds);\n    document.getElementById('dt-result-detail').innerHTML =\n      'File: \u003cstrong\u003e' + dtFormatSize(bytes) + '\u003c/strong\u003e \u0026nbsp;|\u0026nbsp; ' +\n      'Speed: \u003cstrong\u003e' + speedMbps + ' Mbps\u003c/strong\u003e (' + (speedMbps/8).toFixed(2) + ' MB/s) \u0026nbsp;|\u0026nbsp; ' +\n      'Total seconds: \u003cstrong\u003e' + Math.round(seconds).toLocaleString() + '\u003c/strong\u003e';\n    resBox.classList.add('show');\n\n    dtBuildTable(bytes, sizeVal, sizeUnit);\n  };\n\n  function dtBuildTable(bytes, sizeVal, sizeUnit) {\n    var cols = [\n      { label: sizeVal + ' ' + sizeUnit, bytes: bytes },\n      { label: dtFormatSize(bytes * 5), bytes: bytes * 5 },\n      { label: dtFormatSize(bytes * 20), bytes: bytes * 20 }\n    ];\n    document.getElementById('th-col1').textContent = cols[0].label;\n    document.getElementById('th-col2').textContent = cols[1].label;\n    document.getElementById('th-col3').textContent = cols[2].label;\n\n    var speeds = [\n      { name: '3G (Slow)', mbps: 1 },\n      { name: '3G (Typical)', mbps: 5 },\n      { name: '4G LTE', mbps: 25 },\n      { name: '4G+ LTE', mbps: 50 },\n      { name: 'Wi-Fi (100 Mbps)', mbps: 100 },\n      { name: 'Wi-Fi (300 Mbps)', mbps: 300 },\n      { name: 'Fiber (500 Mbps)', mbps: 500 },\n      { name: 'Fiber (1 Gbps)', mbps: 1000 }\n    ];\n\n    var currentSpeed = parseFloat(document.getElementById('dt-speed').value);\n    var tbody = document.getElementById('dt-tbody');\n    tbody.innerHTML = '';\n\n    speeds.forEach(function(sp) {\n      var bps = sp.mbps * 1000000;\n      var tr = document.createElement('tr');\n      if (sp.mbps === currentSpeed) tr.className = 'highlight-row';\n      var tdName = document.createElement('td');\n      tdName.textContent = sp.name;\n      var tdSpeed = document.createElement('td');\n      tdSpeed.textContent = sp.mbps \u003e= 1000 ? (sp.mbps/1000) + ' Gbps' : sp.mbps + ' Mbps';\n      tr.appendChild(tdName);\n      tr.appendChild(tdSpeed);\n      cols.forEach(function(col) {\n        var td = document.createElement('td');\n        var secs = (col.bytes * 8) / bps;\n        td.textContent = dtFormatTimeShort(secs);\n        tr.appendChild(td);\n      });\n      tbody.appendChild(tr);\n    });\n  }\n\n  // Initialize empty table\n  dtBuildTable(1024*1024*100, 100, 'MB');\n  document.getElementById('th-col1').textContent = '100 MB';\n  document.getElementById('th-col2').textContent = '500 MB';\n  document.getElementById('th-col3').textContent = '2 GB';\n\n  window.dtConvert = function(source) {\n    var mbps, val;\n    var fields = {\n      mbps:  document.getElementById('cv-mbps'),\n      mbps2: document.getElementById('cv-mbps2'),\n      kbps:  document.getElementById('cv-kbps'),\n      kbps2: document.getElementById('cv-kbps2'),\n      gbps:  document.getElementById('cv-gbps')\n    };\n    val = parseFloat(fields[source].value);\n    if (isNaN(val) || val \u003c 0) return;\n    // Convert source to Mbps\n    switch(source) {\n      case 'mbps':  mbps = val; break;\n      case 'mbps2': mbps = val * 8; break;\n      case 'kbps':  mbps = val / 1000; break;\n      case 'kbps2': mbps = val * 8 / 1000; break;\n      case 'gbps':  mbps = val * 1000; break;\n    }\n    // Fill others\n    if (source !== 'mbps')  fields.mbps.value  = +mbps.toPrecision(6);\n    if (source !== 'mbps2') fields.mbps2.value = +(mbps / 8).toPrecision(6);\n    if (source !== 'kbps')  fields.kbps.value  = +(mbps * 1000).toPrecision(6);\n    if (source !== 'kbps2') fields.kbps2.value = +(mbps * 1000 / 8).toPrecision(6);\n    if (source !== 'gbps')  fields.gbps.value  = +(mbps / 1000).toPrecision(6);\n  };\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003ch2 id=\"related-articles\"\u003eRelated Articles\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://productivity-works.com/posts/best-internet-service-providers-2026/\"\u003eBest Internet Service Providers 2026: Speed \u0026amp; Price Compared\u003c/a\u003e\n\u003c/li\u003e\n\u003c/ul\u003e","title":"Download Time Calculator - Estimate File Transfer Speed"},{"content":" Single Appliance Multi-Appliance Single Appliance Calculator \u0026lt;h3\u0026gt;Quick Presets\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;ec-presets\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;ec-preset-btn\u0026quot; onclick=\u0026quot;ecApplyPreset(65, 'Laptop')\u0026quot;\u0026gt;Laptop (65W)\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;ec-preset-btn\u0026quot; onclick=\u0026quot;ecApplyPreset(300, 'Desktop PC')\u0026quot;\u0026gt;Desktop (300W)\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;ec-preset-btn\u0026quot; onclick=\u0026quot;ecApplyPreset(100, 'LED TV')\u0026quot;\u0026gt;LED TV (100W)\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;ec-preset-btn\u0026quot; onclick=\u0026quot;ecApplyPreset(150, 'Refrigerator')\u0026quot;\u0026gt;Fridge (150W)\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;ec-preset-btn\u0026quot; onclick=\u0026quot;ecApplyPreset(1500,'Air Conditioner')\u0026quot;\u0026gt;AC (1500W)\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;ec-preset-btn\u0026quot; onclick=\u0026quot;ecApplyPreset(500, 'Washer')\u0026quot;\u0026gt;Washer (500W)\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;ec-preset-btn\u0026quot; onclick=\u0026quot;ecApplyPreset(1000,'Microwave')\u0026quot;\u0026gt;Microwave (1000W)\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ec-grid\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ec-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Appliance Name\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;ec-name\u0026quot; value=\u0026quot;My Appliance\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ec-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Wattage (W)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;ec-watts\u0026quot; value=\u0026quot;65\u0026quot; min=\u0026quot;1\u0026quot; oninput=\u0026quot;ecCalcSingle()\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ec-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Hours Used / Day\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;ec-hours\u0026quot; value=\u0026quot;8\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;24\u0026quot; step=\u0026quot;0.5\u0026quot; oninput=\u0026quot;ecCalcSingle()\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ec-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Electricity Rate ($/kWh)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;ec-rate\u0026quot; value=\u0026quot;0.13\u0026quot; min=\u0026quot;0.01\u0026quot; step=\u0026quot;0.01\u0026quot; oninput=\u0026quot;ecCalcSingle()\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ec-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Days / Month\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;ec-days\u0026quot; value=\u0026quot;30\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;31\u0026quot; oninput=\u0026quot;ecCalcSingle()\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Daily Cost $0.00 0.00 kWh/day Monthly Cost $0.00 0.00 kWh/month Yearly Cost $0.00 0.00 kWh/year Cost Comparison Chart Multi-Appliance Calculator \u0026lt;h3\u0026gt;Quick Add Presets\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;ec-presets\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;ec-preset-btn\u0026quot; onclick=\u0026quot;ecMultiAddPreset(65, 'Laptop')\u0026quot;\u0026gt;+ Laptop (65W)\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;ec-preset-btn\u0026quot; onclick=\u0026quot;ecMultiAddPreset(300, 'Desktop PC')\u0026quot;\u0026gt;+ Desktop (300W)\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;ec-preset-btn\u0026quot; onclick=\u0026quot;ecMultiAddPreset(100, 'LED TV')\u0026quot;\u0026gt;+ LED TV (100W)\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;ec-preset-btn\u0026quot; onclick=\u0026quot;ecMultiAddPreset(150, 'Refrigerator')\u0026quot;\u0026gt;+ Fridge (150W)\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;ec-preset-btn\u0026quot; onclick=\u0026quot;ecMultiAddPreset(1500,'Air Conditioner')\u0026quot;\u0026gt;+ AC (1500W)\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;ec-preset-btn\u0026quot; onclick=\u0026quot;ecMultiAddPreset(500, 'Washer')\u0026quot;\u0026gt;+ Washer (500W)\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;ec-preset-btn\u0026quot; onclick=\u0026quot;ecMultiAddPreset(1000,'Microwave')\u0026quot;\u0026gt;+ Microwave (1000W)\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ec-field\u0026quot; style=\u0026quot;max-width:260px; margin-bottom:1rem;\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Electricity Rate ($/kWh)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;ec-multi-rate\u0026quot; value=\u0026quot;0.13\u0026quot; min=\u0026quot;0.01\u0026quot; step=\u0026quot;0.01\u0026quot; oninput=\u0026quot;ecCalcMulti()\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;ec-appliance-list\u0026quot; class=\u0026quot;ec-appliance-list\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ec-row-actions\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;ec-btn ec-btn-success\u0026quot; onclick=\u0026quot;ecMultiAddRow()\u0026quot;\u0026gt;+ Add Appliance\u0026lt;/button\u0026gt; \u0026lt;span class=\u0026quot;ec-notice\u0026quot;\u0026gt;Each row: name, watts, hours/day, days/month\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; Total (all appliances) $0.00Daily $0.00Monthly $0.00Yearly 0.00kWh/mo Appliance Cost Breakdown Energy Saving Tips Unplug Standby DevicesIdle electronics can draw 5–10% of your total electricity. Unplug chargers and TVs when not in use. Switch to LED BulbsLEDs use up to 80% less energy than incandescent bulbs and last 25x longer. Optimize AC UsageEvery 1°C increase in AC setpoint saves ~3% on cooling costs. Use ceiling fans to feel cooler. Run Full LoadsWashers and dishwashers use similar energy regardless of load size. Always run full loads. Smart Power StripsUse smart strips to automatically cut power to peripherals when your main device turns off. Off-Peak HoursSome utilities offer time-of-use rates. Shift heavy loads (laundry, dishwasher) to off-peak hours. \u0026#8594;Calculate ROI ROI Calculator \u0026#8594;Plan your budget 50/30/20 Budget Calculator \u0026#8594;Calculate loan payments Loan EMI Calculator ","permalink":"https://productivity-works.com/tools/electricity-calculator/","summary":"\u003cdiv id=\"ec-app\"\u003e\n\u003cstyle\u003e\n#ec-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #e2e8f0;\n}\n\u003cp\u003e#ec-app * {\nbox-sizing: border-box;\n}\u003c/p\u003e\n\u003cp\u003e#ec-app h2 {\nfont-size: 1.25rem;\nfont-weight: 700;\ncolor: #f1f5f9;\nmargin: 0 0 1rem 0;\npadding-bottom: 0.5rem;\nborder-bottom: 2px solid #334155;\n}\u003c/p\u003e\n\u003cp\u003e#ec-app h3 {\nfont-size: 1rem;\nfont-weight: 600;\ncolor: #cbd5e1;\nmargin: 0 0 0.75rem 0;\n}\u003c/p\u003e\n\u003cp\u003e/* Tabs */\n#ec-app .ec-tabs {\ndisplay: flex;\ngap: 0.5rem;\nmargin-bottom: 1.5rem;\n}\u003c/p\u003e\n\u003cp\u003e#ec-app .ec-tab {\npadding: 0.5rem 1.25rem;\nborder-radius: 6px;\nborder: 1px solid #334155;\nbackground: #1e293b;\ncolor: #94a3b8;\ncursor: pointer;\nfont-size: 0.875rem;\nfont-weight: 500;\ntransition: all 0.15s ease;\n}\u003c/p\u003e","title":"Electricity Cost Calculator"},{"content":" Enter your appliances below, set your electricity rate, and instantly see your estimated monthly and yearly power bill — with a visual breakdown by appliance. Electricity Rate Rate per kWh: $/kWh \u0026nbsp;(check your utility bill for the exact rate) Add Appliance Quick presets Appliance Name Watts (W) Hours/Day Days/Month + Add Appliance Your Appliances Appliance Watts Hrs/Day Days/Mo kWh/Mo Cost/Mo Cost/Yr No appliances added yet. Use presets above or enter your own. Total kWh / Month 0 kilowatt-hours Monthly Cost $0.00 estimated Yearly Cost $0.00 projected annual Cost Breakdown Add appliances to see breakdown Tips to Reduce Your Electricity Bill Unplug standby devices — chargers, TVs on standby, and game consoles can draw 5–20W continuously even when \"off\". Switch to LED bulbs — LED bulbs use up to 80% less energy than incandescent, lasting 15–25x longer. Use smart power strips — automatically cut power to devices when the main device (e.g. TV) turns off. Optimize your air conditioner — set to 78°F (26°C) in summer; each degree lower adds ~3% to cooling costs. Run laundry in cold water — about 90% of a washing machine's energy goes to heating water. Check your refrigerator seals — worn gaskets force the compressor to work harder; replace if they fail the \"paper test\". Use time-of-use rates — if your utility offers TOU pricing, run dishwashers and dryers during off-peak hours. Related Free Tools Hourly Rate Calculator Savings Goal Calculator Pomodoro Timer BMI Calculator Unit Converter Related Tools Carbon Footprint Calculator Cooking Unit Converter Fuel Cost Calculator ","permalink":"https://productivity-works.com/tools/electricity-cost-calculator/","summary":"\u003cstyle\u003e\n#ec-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#ec-app * { box-sizing: border-box; }\n#ec-app h2 {\n  font-size: 1.35rem;\n  font-weight: 700;\n  margin: 1.5rem 0 0.75rem;\n  color: #1a1a2e;\n}\n#ec-app .ec-intro {\n  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n  color: #fff;\n  border-radius: 12px;\n  padding: 1.25rem 1.5rem;\n  margin-bottom: 1.5rem;\n  font-size: 0.97rem;\n  line-height: 1.6;\n}\n#ec-app .ec-rate-row {\n  display: flex;\n  align-items: center;\n  gap: 0.75rem;\n  background: #f0f4ff;\n  border: 1.5px solid #c7d2fe;\n  border-radius: 10px;\n  padding: 0.85rem 1.1rem;\n  margin-bottom: 1.25rem;\n  flex-wrap: wrap;\n}\n#ec-app .ec-rate-row label {\n  font-weight: 600;\n  font-size: 0.95rem;\n  color: #3730a3;\n  white-space: nowrap;\n}\n#ec-app .ec-rate-row input {\n  width: 110px;\n  padding: 0.45rem 0.65rem;\n  border: 1.5px solid #a5b4fc;\n  border-radius: 7px;\n  font-size: 1rem;\n  background: #fff;\n  color: #1a1a2e;\n  outline: none;\n  transition: border-color 0.2s;\n}\n#ec-app .ec-rate-row input:focus { border-color: #6366f1; }\n#ec-app .ec-rate-row span { font-size: 0.9rem; color: #6366f1; font-weight: 500; }\n\n/* Presets */\n#ec-app .ec-presets {\n  margin-bottom: 1.25rem;\n}\n#ec-app .ec-presets-title {\n  font-size: 0.88rem;\n  font-weight: 600;\n  color: #6b7280;\n  margin-bottom: 0.5rem;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#ec-app .ec-preset-grid {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.45rem;\n}\n#ec-app .ec-preset-btn {\n  background: #fff;\n  border: 1.5px solid #e0e7ff;\n  border-radius: 20px;\n  padding: 0.35rem 0.85rem;\n  font-size: 0.85rem;\n  cursor: pointer;\n  color: #4f46e5;\n  font-weight: 500;\n  transition: background 0.15s, border-color 0.15s;\n}\n#ec-app .ec-preset-btn:hover {\n  background: #eef2ff;\n  border-color: #6366f1;\n}\n\n/* Add form */\n#ec-app .ec-add-form {\n  background: #f9fafb;\n  border: 1.5px solid #e5e7eb;\n  border-radius: 12px;\n  padding: 1.1rem 1.25rem;\n  margin-bottom: 1.25rem;\n}\n#ec-app .ec-add-form .ec-form-grid {\n  display: grid;\n  grid-template-columns: 2fr 1fr 1fr 1fr;\n  gap: 0.6rem;\n  align-items: end;\n}\n@media (max-width: 600px) {\n  #ec-app .ec-add-form .ec-form-grid {\n    grid-template-columns: 1fr 1fr;\n  }\n}\n#ec-app .ec-form-group { display: flex; flex-direction: column; gap: 0.3rem; }\n#ec-app .ec-form-group label {\n  font-size: 0.8rem;\n  font-weight: 600;\n  color: #6b7280;\n  text-transform: uppercase;\n  letter-spacing: 0.03em;\n}\n#ec-app .ec-form-group input {\n  padding: 0.5rem 0.7rem;\n  border: 1.5px solid #d1d5db;\n  border-radius: 7px;\n  font-size: 0.97rem;\n  background: #fff;\n  color: #1a1a2e;\n  outline: none;\n  transition: border-color 0.2s;\n}\n#ec-app .ec-form-group input:focus { border-color: #6366f1; }\n#ec-app .ec-add-btn {\n  background: linear-gradient(135deg, #6366f1, #8b5cf6);\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  padding: 0.55rem 1.2rem;\n  font-size: 0.97rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: opacity 0.2s;\n  white-space: nowrap;\n  margin-top: 0.6rem;\n}\n#ec-app .ec-add-btn:hover { opacity: 0.88; }\n\n/* Table */\n#ec-app .ec-table-wrap {\n  overflow-x: auto;\n  border-radius: 10px;\n  border: 1.5px solid #e5e7eb;\n  margin-bottom: 1.5rem;\n}\n#ec-app .ec-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.93rem;\n}\n#ec-app .ec-table th {\n  background: #f3f4f6;\n  padding: 0.65rem 0.9rem;\n  text-align: left;\n  font-size: 0.8rem;\n  font-weight: 700;\n  color: #6b7280;\n  text-transform: uppercase;\n  letter-spacing: 0.03em;\n  white-space: nowrap;\n}\n#ec-app .ec-table td {\n  padding: 0.6rem 0.9rem;\n  border-top: 1px solid #f0f0f0;\n  vertical-align: middle;\n}\n#ec-app .ec-table tr:hover td { background: #f9fafb; }\n#ec-app .ec-table .ec-color-dot {\n  display: inline-block;\n  width: 10px;\n  height: 10px;\n  border-radius: 50%;\n  margin-right: 6px;\n  vertical-align: middle;\n}\n#ec-app .ec-del-btn {\n  background: none;\n  border: none;\n  color: #ef4444;\n  cursor: pointer;\n  font-size: 1.1rem;\n  padding: 0.1rem 0.4rem;\n  border-radius: 5px;\n  transition: background 0.15s;\n}\n#ec-app .ec-del-btn:hover { background: #fee2e2; }\n#ec-app .ec-empty {\n  text-align: center;\n  color: #9ca3af;\n  padding: 2rem;\n  font-size: 0.95rem;\n}\n\n/* Summary */\n#ec-app .ec-summary {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));\n  gap: 1rem;\n  margin-bottom: 1.5rem;\n}\n#ec-app .ec-summary-card {\n  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n  color: #fff;\n  border-radius: 12px;\n  padding: 1.1rem 1.25rem;\n  text-align: center;\n}\n#ec-app .ec-summary-card.green {\n  background: linear-gradient(135deg, #11998e, #38ef7d);\n}\n#ec-app .ec-summary-card.orange {\n  background: linear-gradient(135deg, #f7971e, #ffd200);\n  color: #1a1a2e;\n}\n#ec-app .ec-summary-card .ec-card-label {\n  font-size: 0.82rem;\n  font-weight: 600;\n  opacity: 0.85;\n  margin-bottom: 0.35rem;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#ec-app .ec-summary-card .ec-card-value {\n  font-size: 1.7rem;\n  font-weight: 800;\n  line-height: 1.1;\n}\n#ec-app .ec-summary-card .ec-card-sub {\n  font-size: 0.78rem;\n  opacity: 0.75;\n  margin-top: 0.2rem;\n}\n\n/* Chart */\n#ec-app .ec-chart-section {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1.5rem;\n  align-items: start;\n  margin-bottom: 1.5rem;\n}\n@media (max-width: 600px) {\n  #ec-app .ec-chart-section { grid-template-columns: 1fr; }\n}\n#ec-app canvas {\n  display: block;\n  max-width: 100%;\n}\n#ec-app .ec-legend { list-style: none; padding: 0; margin: 0; }\n#ec-app .ec-legend li {\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n  font-size: 0.9rem;\n  margin-bottom: 0.45rem;\n  color: #374151;\n}\n#ec-app .ec-legend-dot {\n  width: 12px;\n  height: 12px;\n  border-radius: 3px;\n  flex-shrink: 0;\n}\n#ec-app .ec-legend-pct {\n  margin-left: auto;\n  font-weight: 700;\n  color: #4f46e5;\n}\n\n/* Tips */\n#ec-app .ec-tips {\n  background: #f0fdf4;\n  border: 1.5px solid #bbf7d0;\n  border-radius: 12px;\n  padding: 1.1rem 1.25rem;\n  margin-bottom: 1.5rem;\n}\n#ec-app .ec-tips h3 {\n  margin: 0 0 0.7rem;\n  font-size: 1rem;\n  font-weight: 700;\n  color: #15803d;\n}\n#ec-app .ec-tips ul {\n  margin: 0;\n  padding-left: 1.25rem;\n}\n#ec-app .ec-tips li {\n  font-size: 0.92rem;\n  color: #166534;\n  margin-bottom: 0.35rem;\n  line-height: 1.5;\n}\n\n/* Related tools */\n#ec-app .ec-related {\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 1.1rem 1.25rem;\n}\n#ec-app .ec-related h3 {\n  margin: 0 0 0.7rem;\n  font-size: 1rem;\n  font-weight: 700;\n  color: #1e293b;\n}\n#ec-app .ec-related ul {\n  margin: 0;\n  padding: 0;\n  list-style: none;\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem;\n}\n#ec-app .ec-related li a {\n  display: inline-block;\n  background: #fff;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 20px;\n  padding: 0.35rem 0.85rem;\n  font-size: 0.88rem;\n  color: #4f46e5;\n  text-decoration: none;\n  font-weight: 500;\n  transition: border-color 0.15s, background 0.15s;\n}\n#ec-app .ec-related li a:hover {\n  border-color: #6366f1;\n  background: #eef2ff;\n}\n\u003c/style\u003e\n\u003cdiv id=\"ec-app\"\u003e\n\u003cdiv class=\"ec-intro\"\u003e\n  Enter your appliances below, set your electricity rate, and instantly see your estimated monthly and yearly power bill — with a visual breakdown by appliance.\n\u003c/div\u003e\n\u003ch2\u003eElectricity Rate\u003c/h2\u003e\n\u003cdiv class=\"ec-rate-row\"\u003e\n  \u003clabel for=\"ec-rate\"\u003eRate per kWh:\u003c/label\u003e\n  \u003cinput type=\"number\" id=\"ec-rate\" value=\"0.13\" min=\"0.01\" step=\"0.01\"\u003e\n  \u003cspan\u003e$/kWh \u0026nbsp;(check your utility bill for the exact rate)\u003c/span\u003e\n\u003c/div\u003e\n\u003ch2\u003eAdd Appliance\u003c/h2\u003e\n\u003cdiv class=\"ec-presets\"\u003e\n  \u003cdiv class=\"ec-presets-title\"\u003eQuick presets\u003c/div\u003e\n  \u003cdiv class=\"ec-preset-grid\" id=\"ec-preset-grid\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ec-add-form\"\u003e\n  \u003cdiv class=\"ec-form-grid\"\u003e\n    \u003cdiv class=\"ec-form-group\"\u003e\n      \u003clabel for=\"ec-name\"\u003eAppliance Name\u003c/label\u003e\n      \u003cinput type=\"text\" id=\"ec-name\" placeholder=\"e.g. Living Room TV\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ec-form-group\"\u003e\n      \u003clabel for=\"ec-watts\"\u003eWatts (W)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"ec-watts\" placeholder=\"e.g. 100\" min=\"0\" step=\"1\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ec-form-group\"\u003e\n      \u003clabel for=\"ec-hours\"\u003eHours/Day\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"ec-hours\" placeholder=\"e.g. 4\" min=\"0\" max=\"24\" step=\"0.5\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ec-form-group\"\u003e\n      \u003clabel for=\"ec-days\"\u003eDays/Month\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"ec-days\" placeholder=\"e.g. 30\" min=\"1\" max=\"31\" step=\"1\" value=\"30\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cbutton class=\"ec-add-btn\" onclick=\"ecAddAppliance()\"\u003e+ Add Appliance\u003c/button\u003e\n\u003c/div\u003e\n\u003ch2\u003eYour Appliances\u003c/h2\u003e\n\u003cdiv class=\"ec-table-wrap\"\u003e\n  \u003ctable class=\"ec-table\"\u003e\n    \u003cthead\u003e\n      \u003ctr\u003e\n        \u003cth\u003eAppliance\u003c/th\u003e\n        \u003cth\u003eWatts\u003c/th\u003e\n        \u003cth\u003eHrs/Day\u003c/th\u003e\n        \u003cth\u003eDays/Mo\u003c/th\u003e\n        \u003cth\u003ekWh/Mo\u003c/th\u003e\n        \u003cth\u003eCost/Mo\u003c/th\u003e\n        \u003cth\u003eCost/Yr\u003c/th\u003e\n        \u003cth\u003e\u003c/th\u003e\n      \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody id=\"ec-tbody\"\u003e\n      \u003ctr\u003e\u003ctd colspan=\"8\" class=\"ec-empty\"\u003eNo appliances added yet. Use presets above or enter your own.\u003c/td\u003e\u003c/tr\u003e\n    \u003c/tbody\u003e\n  \u003c/table\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ec-summary\" id=\"ec-summary\"\u003e\n  \u003cdiv class=\"ec-summary-card\"\u003e\n    \u003cdiv class=\"ec-card-label\"\u003eTotal kWh / Month\u003c/div\u003e\n    \u003cdiv class=\"ec-card-value\" id=\"ec-total-kwh\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"ec-card-sub\"\u003ekilowatt-hours\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ec-summary-card green\"\u003e\n    \u003cdiv class=\"ec-card-label\"\u003eMonthly Cost\u003c/div\u003e\n    \u003cdiv class=\"ec-card-value\" id=\"ec-total-monthly\"\u003e$0.00\u003c/div\u003e\n    \u003cdiv class=\"ec-card-sub\"\u003eestimated\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ec-summary-card orange\"\u003e\n    \u003cdiv class=\"ec-card-label\"\u003eYearly Cost\u003c/div\u003e\n    \u003cdiv class=\"ec-card-value\" id=\"ec-total-yearly\"\u003e$0.00\u003c/div\u003e\n    \u003cdiv class=\"ec-card-sub\"\u003eprojected annual\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003ch2\u003eCost Breakdown\u003c/h2\u003e\n\u003cdiv class=\"ec-chart-section\"\u003e\n  \u003ccanvas id=\"ec-pie\" width=\"260\" height=\"260\"\u003e\u003c/canvas\u003e\n  \u003cul class=\"ec-legend\" id=\"ec-legend\"\u003e\n    \u003cli style=\"color:#9ca3af\"\u003eAdd appliances to see breakdown\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ec-tips\"\u003e\n  \u003ch3\u003eTips to Reduce Your Electricity Bill\u003c/h3\u003e\n  \u003cul\u003e\n    \u003cli\u003e\u003cstrong\u003eUnplug standby devices\u003c/strong\u003e — chargers, TVs on standby, and game consoles can draw 5–20W continuously even when \"off\".\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003eSwitch to LED bulbs\u003c/strong\u003e — LED bulbs use up to 80% less energy than incandescent, lasting 15–25x longer.\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003eUse smart power strips\u003c/strong\u003e — automatically cut power to devices when the main device (e.g. TV) turns off.\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003eOptimize your air conditioner\u003c/strong\u003e — set to 78°F (26°C) in summer; each degree lower adds ~3% to cooling costs.\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003eRun laundry in cold water\u003c/strong\u003e — about 90% of a washing machine's energy goes to heating water.\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003eCheck your refrigerator seals\u003c/strong\u003e — worn gaskets force the compressor to work harder; replace if they fail the \"paper test\".\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003eUse time-of-use rates\u003c/strong\u003e — if your utility offers TOU pricing, run dishwashers and dryers during off-peak hours.\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ec-related\"\u003e\n  \u003ch3\u003eRelated Free Tools\u003c/h3\u003e\n  \u003cul\u003e\n    \u003cli\u003e\u003ca href=\"/tools/hourly-to-salary-calculator/\"\u003eHourly Rate Calculator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/savings-goal-calculator/\"\u003eSavings Goal Calculator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/pomodoro-timer/\"\u003ePomodoro Timer\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/bmi-calculator/\"\u003eBMI Calculator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/unit-converter/\"\u003eUnit Converter\u003c/a\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var COLORS = [\n    '#6366f1','#8b5cf6','#ec4899','#f59e0b','#10b981',\n    '#3b82f6','#ef4444','#14b8a6','#f97316','#84cc16',\n    '#06b6d4','#a855f7','#eab308','#22c55e','#e11d48'\n  ];\n\n  var PRESETS = [\n    { name: \"Refrigerator\", watts: 150, hours: 24, days: 30 },\n    { name: \"Central AC\", watts: 3500, hours: 8, days: 22 },\n    { name: \"Window AC\", watts: 900, hours: 8, days: 22 },\n    { name: \"Electric Heater\", watts: 1500, hours: 6, days: 20 },\n    { name: \"Clothes Dryer\", watts: 5000, hours: 0.75, days: 12 },\n    { name: \"Washing Machine\", watts: 500, hours: 0.75, days: 12 },\n    { name: \"Dishwasher\", watts: 1200, hours: 1, days: 15 },\n    { name: \"TV (50\\\")\", watts: 100, hours: 4, days: 30 },\n    { name: \"Desktop PC\", watts: 200, hours: 8, days: 22 },\n    { name: \"Laptop\", watts: 50, hours: 8, days: 22 },\n    { name: \"LED Light Bulb\", watts: 10, hours: 5, days: 30 },\n    { name: \"Microwave\", watts: 1000, hours: 0.25, days: 30 },\n    { name: \"Electric Oven\", watts: 2400, hours: 1, days: 15 },\n    { name: \"Water Heater\", watts: 4000, hours: 3, days: 30 },\n    { name: \"EV Charger (L2)\", watts: 7200, hours: 2, days: 15 }\n  ];\n\n  var appliances = [];\n\n  function init() {\n    var grid = document.getElementById('ec-preset-grid');\n    PRESETS.forEach(function(p, i) {\n      var btn = document.createElement('button');\n      btn.className = 'ec-preset-btn';\n      btn.textContent = p.name;\n      btn.onclick = function() { ecFillPreset(p); };\n      grid.appendChild(btn);\n    });\n    render();\n  }\n\n  window.ecFillPreset = function(p) {\n    document.getElementById('ec-name').value = p.name;\n    document.getElementById('ec-watts').value = p.watts;\n    document.getElementById('ec-hours').value = p.hours;\n    document.getElementById('ec-days').value = p.days;\n  };\n\n  window.ecAddAppliance = function() {\n    var name = document.getElementById('ec-name').value.trim();\n    var watts = parseFloat(document.getElementById('ec-watts').value);\n    var hours = parseFloat(document.getElementById('ec-hours').value);\n    var days = parseFloat(document.getElementById('ec-days').value);\n    if (!name) { alert('Please enter an appliance name.'); return; }\n    if (isNaN(watts) || watts \u003c 0) { alert('Please enter valid wattage.'); return; }\n    if (isNaN(hours) || hours \u003c 0) { alert('Please enter valid hours/day.'); return; }\n    if (isNaN(days) || days \u003c 1) { alert('Please enter valid days/month.'); return; }\n    appliances.push({ name: name, watts: watts, hours: hours, days: days, color: COLORS[appliances.length % COLORS.length] });\n    document.getElementById('ec-name').value = '';\n    document.getElementById('ec-watts').value = '';\n    document.getElementById('ec-hours').value = '';\n    document.getElementById('ec-days').value = '30';\n    render();\n  };\n\n  window.ecRemove = function(i) {\n    appliances.splice(i, 1);\n    appliances.forEach(function(a, idx) { a.color = COLORS[idx % COLORS.length]; });\n    render();\n  };\n\n  function getRate() {\n    return parseFloat(document.getElementById('ec-rate').value) || 0.13;\n  }\n\n  function calcKwh(a) {\n    return (a.watts / 1000) * a.hours * a.days;\n  }\n\n  function fmt(n) { return '$' + n.toFixed(2); }\n\n  function render() {\n    var rate = getRate();\n    var tbody = document.getElementById('ec-tbody');\n\n    if (appliances.length === 0) {\n      tbody.innerHTML = '\u003ctr\u003e\u003ctd colspan=\"8\" class=\"ec-empty\"\u003eNo appliances added yet. Use presets above or enter your own.\u003c/td\u003e\u003c/tr\u003e';\n      document.getElementById('ec-total-kwh').textContent = '0';\n      document.getElementById('ec-total-monthly').textContent = '$0.00';\n      document.getElementById('ec-total-yearly').textContent = '$0.00';\n      document.getElementById('ec-legend').innerHTML = '\u003cli style=\"color:#9ca3af\"\u003eAdd appliances to see breakdown\u003c/li\u003e';\n      drawPie([]);\n      return;\n    }\n\n    var totalKwh = 0, totalMonth = 0;\n    var rows = appliances.map(function(a, i) {\n      var kwh = calcKwh(a);\n      var costM = kwh * rate;\n      var costY = costM * 12;\n      totalKwh += kwh;\n      totalMonth += costM;\n      return '\u003ctr\u003e' +\n        '\u003ctd\u003e\u003cspan class=\"ec-color-dot\" style=\"background:' + a.color + '\"\u003e\u003c/span\u003e' + escHtml(a.name) + '\u003c/td\u003e' +\n        '\u003ctd\u003e' + a.watts + '\u003c/td\u003e' +\n        '\u003ctd\u003e' + a.hours + '\u003c/td\u003e' +\n        '\u003ctd\u003e' + a.days + '\u003c/td\u003e' +\n        '\u003ctd\u003e' + kwh.toFixed(1) + '\u003c/td\u003e' +\n        '\u003ctd\u003e' + fmt(costM) + '\u003c/td\u003e' +\n        '\u003ctd\u003e' + fmt(costY) + '\u003c/td\u003e' +\n        '\u003ctd\u003e\u003cbutton class=\"ec-del-btn\" onclick=\"ecRemove(' + i + ')\" title=\"Remove\"\u003e\u0026#10005;\u003c/button\u003e\u003c/td\u003e' +\n        '\u003c/tr\u003e';\n    });\n\n    tbody.innerHTML = rows.join('');\n    document.getElementById('ec-total-kwh').textContent = totalKwh.toFixed(1);\n    document.getElementById('ec-total-monthly').textContent = fmt(totalMonth);\n    document.getElementById('ec-total-yearly').textContent = fmt(totalMonth * 12);\n\n    var legend = document.getElementById('ec-legend');\n    legend.innerHTML = appliances.map(function(a) {\n      var kwh = calcKwh(a);\n      var pct = totalKwh \u003e 0 ? (kwh / totalKwh * 100).toFixed(1) : '0.0';\n      return '\u003cli\u003e' +\n        '\u003cspan class=\"ec-legend-dot\" style=\"background:' + a.color + '\"\u003e\u003c/span\u003e' +\n        escHtml(a.name) +\n        '\u003cspan class=\"ec-legend-pct\"\u003e' + pct + '%\u003c/span\u003e' +\n        '\u003c/li\u003e';\n    }).join('');\n\n    drawPie(appliances.map(function(a) { return { label: a.name, value: calcKwh(a), color: a.color }; }));\n  }\n\n  function drawPie(data) {\n    var canvas = document.getElementById('ec-pie');\n    var ctx = canvas.getContext('2d');\n    var W = canvas.width, H = canvas.height;\n    ctx.clearRect(0, 0, W, H);\n\n    var total = data.reduce(function(s, d) { return s + d.value; }, 0);\n    if (total === 0 || data.length === 0) {\n      ctx.beginPath();\n      ctx.arc(W/2, H/2, W/2 - 10, 0, Math.PI*2);\n      ctx.fillStyle = '#f3f4f6';\n      ctx.fill();\n      ctx.fillStyle = '#9ca3af';\n      ctx.font = '14px sans-serif';\n      ctx.textAlign = 'center';\n      ctx.fillText('No data yet', W/2, H/2 + 5);\n      return;\n    }\n\n    var startAngle = -Math.PI / 2;\n    var cx = W/2, cy = H/2, r = W/2 - 10;\n    data.forEach(function(d) {\n      var slice = (d.value / total) * Math.PI * 2;\n      ctx.beginPath();\n      ctx.moveTo(cx, cy);\n      ctx.arc(cx, cy, r, startAngle, startAngle + slice);\n      ctx.closePath();\n      ctx.fillStyle = d.color;\n      ctx.fill();\n      ctx.strokeStyle = '#fff';\n      ctx.lineWidth = 2;\n      ctx.stroke();\n      startAngle += slice;\n    });\n\n    // Center hole\n    ctx.beginPath();\n    ctx.arc(cx, cy, r * 0.42, 0, Math.PI*2);\n    ctx.fillStyle = '#fff';\n    ctx.fill();\n    ctx.fillStyle = '#374151';\n    ctx.font = 'bold 13px sans-serif';\n    ctx.textAlign = 'center';\n    ctx.fillText('kWh/Mo', cx, cy - 6);\n    ctx.font = 'bold 16px sans-serif';\n    ctx.fillStyle = '#4f46e5';\n    var totalKwh = data.reduce(function(s,d){return s+d.value;},0);\n    ctx.fillText(totalKwh.toFixed(1), cx, cy + 14);\n  }\n\n  function escHtml(s) {\n    return s.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;').replace(/\"/g,'\u0026quot;');\n  }\n\n  document.getElementById('ec-rate').addEventListener('input', render);\n\n  init();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"https://productivity-works.com/tools/carbon-footprint-calculator/\"\u003eCarbon Footprint Calculator\u003c/a\u003e\n\n\u003ca href=\"https://productivity-works.com/tools/cooking-unit-converter/\"\u003eCooking Unit Converter\u003c/a\u003e\n\n\u003ca href=\"https://productivity-works.com/tools/fuel-cost-calculator/\"\u003eFuel Cost Calculator\u003c/a\u003e\n\u003c/p\u003e","title":"Electricity Cost Calculator - Estimate Your Power Bill"},{"content":"Create a polished, professional email signature in seconds — no account required, no data sent to any server. Fill in your details, pick a template, and copy the HTML directly into Gmail, Outlook, or Apple Mail.\nYour Details \u0026lt;div class=\u0026quot;sa-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Full Name\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;sa-name\u0026quot; placeholder=\u0026quot;Jane Smith\u0026quot; oninput=\u0026quot;saRender()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sa-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Job Title\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;sa-title\u0026quot; placeholder=\u0026quot;Senior Product Designer\u0026quot; oninput=\u0026quot;saRender()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sa-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Company\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;sa-company\u0026quot; placeholder=\u0026quot;Acme Inc.\u0026quot; oninput=\u0026quot;saRender()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sa-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Phone\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;sa-phone\u0026quot; placeholder=\u0026quot;+1 (555) 000-0000\u0026quot; oninput=\u0026quot;saRender()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sa-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Email\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;email\u0026quot; id=\u0026quot;sa-email\u0026quot; placeholder=\u0026quot;jane@acme.com\u0026quot; oninput=\u0026quot;saRender()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sa-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Website\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;sa-website\u0026quot; placeholder=\u0026quot;https://acme.com\u0026quot; oninput=\u0026quot;saRender()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sa-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;LinkedIn URL\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;sa-linkedin\u0026quot; placeholder=\u0026quot;https://linkedin.com/in/janesmith\u0026quot; oninput=\u0026quot;saRender()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sa-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Twitter / X Handle\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;sa-twitter\u0026quot; placeholder=\u0026quot;@janesmith\u0026quot; oninput=\u0026quot;saRender()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sa-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;GitHub Username\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;sa-github\u0026quot; placeholder=\u0026quot;janesmith\u0026quot; oninput=\u0026quot;saRender()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sa-panel\u0026quot; style=\u0026quot;margin-top:20px;\u0026quot;\u0026gt; \u0026lt;h2\u0026gt;Style Options\u0026lt;/h2\u0026gt; \u0026lt;div class=\u0026quot;sa-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Layout\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;sa-layout\u0026quot; onchange=\u0026quot;saRender()\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;horizontal\u0026quot;\u0026gt;Horizontal (photo left, text right)\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;vertical\u0026quot;\u0026gt;Vertical (stacked)\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sa-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Color Scheme\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;sa-swatches\u0026quot; id=\u0026quot;sa-swatches\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;sa-swatch active\u0026quot; data-color=\u0026quot;#6366f1\u0026quot; style=\u0026quot;background:#6366f1\u0026quot; onclick=\u0026quot;saPick(this)\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;sa-swatch\u0026quot; data-color=\u0026quot;#0ea5e9\u0026quot; style=\u0026quot;background:#0ea5e9\u0026quot; onclick=\u0026quot;saPick(this)\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;sa-swatch\u0026quot; data-color=\u0026quot;#10b981\u0026quot; style=\u0026quot;background:#10b981\u0026quot; onclick=\u0026quot;saPick(this)\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;sa-swatch\u0026quot; data-color=\u0026quot;#f59e0b\u0026quot; style=\u0026quot;background:#f59e0b\u0026quot; onclick=\u0026quot;saPick(this)\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;sa-swatch\u0026quot; data-color=\u0026quot;#ef4444\u0026quot; style=\u0026quot;background:#ef4444\u0026quot; onclick=\u0026quot;saPick(this)\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;sa-swatch\u0026quot; data-color=\u0026quot;#8b5cf6\u0026quot; style=\u0026quot;background:#8b5cf6\u0026quot; onclick=\u0026quot;saPick(this)\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;sa-swatch\u0026quot; data-color=\u0026quot;#1e293b\u0026quot; style=\u0026quot;background:#1e293b\u0026quot; onclick=\u0026quot;saPick(this)\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;sa-swatch\u0026quot; data-color=\u0026quot;#64748b\u0026quot; style=\u0026quot;background:#64748b\u0026quot; onclick=\u0026quot;saPick(this)\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sa-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Font\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;sa-font\u0026quot; onchange=\u0026quot;saRender()\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;Arial, Helvetica, sans-serif\u0026quot;\u0026gt;Arial (safe)\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;Georgia, serif\u0026quot;\u0026gt;Georgia (classic)\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;'Courier New', monospace\u0026quot;\u0026gt;Courier New (dev)\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;Verdana, Geneva, sans-serif\u0026quot;\u0026gt;Verdana\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;Trebuchet MS, sans-serif\u0026quot;\u0026gt;Trebuchet MS\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sa-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Separator Style\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;sa-sep\u0026quot; onchange=\u0026quot;saRender()\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;solid\u0026quot;\u0026gt;Solid line\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;dashed\u0026quot;\u0026gt;Dashed line\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;dotted\u0026quot;\u0026gt;Dotted line\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;none\u0026quot;\u0026gt;No separator\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sa-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Photo Placeholder\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;sa-photo\u0026quot; onchange=\u0026quot;saRender()\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;circle\u0026quot;\u0026gt;Circle avatar\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;square\u0026quot;\u0026gt;Square avatar\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;none\u0026quot;\u0026gt;No photo\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Template Corporate Creative Minimal Developer \u0026lt;div class=\u0026quot;sa-preview-label\u0026quot;\u0026gt;Live Preview\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sa-preview-wrap\u0026quot;\u0026gt; \u0026lt;div id=\u0026quot;sa-preview\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sa-actions\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;sa-btn sa-btn-primary\u0026quot; onclick=\u0026quot;saCopyHTML()\u0026quot;\u0026gt;Copy as HTML\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;sa-btn sa-btn-secondary\u0026quot; onclick=\u0026quot;saToggleCode()\u0026quot;\u0026gt;View HTML\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;sa-btn sa-btn-secondary\u0026quot; onclick=\u0026quot;saReset()\u0026quot;\u0026gt;Reset\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;span class=\u0026quot;sa-toast\u0026quot; id=\u0026quot;sa-toast\u0026quot;\u0026gt;Copied to clipboard!\u0026lt;/span\u0026gt; \u0026lt;textarea class=\u0026quot;sa-html-out\u0026quot; id=\u0026quot;sa-html-out\u0026quot; readonly\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;/div\u0026gt; How to Install Your Signature Gmail Click Copy as HTML above. Open Gmail → Settings (gear icon) → See all settings. Under the General tab, scroll to Signature and click Create new. Give it a name (e.g., \"Professional\"), then click inside the signature box. Switch to HTML mode: in the toolbar, click the ⋮ More options menu → Edit as HTML. Paste your copied HTML and click OK. Scroll down and click Save Changes. Outlook (Windows \u0026amp; Mac) Click Copy as HTML above. Open a New Email in Outlook. Go to Insert → Signature → Signatures… Click New, name it, then click inside the editing area. Open Notepad, paste the HTML, and save it as sig.htm. In the Outlook signature editor, click the source / HTML button and paste your HTML, or drag the saved .htm file. Click OK and set as default if desired. Apple Mail (macOS) Click Copy as HTML above. Open Mail → Settings → Signatures. Select your account, click + to add a new signature. Type a placeholder (e.g., \"x\") in the signature body. Quit Mail, then open Terminal and run:\nopen ~/Library/Mail Navigate to V10 (or your version) → [Account folder] → Data → Signatures. Open the .mailsignature file in a text editor, find the placeholder, and replace the body content with your copied HTML. Save and reopen Mail. Related Free Tools Meta Tag Generator Privacy Policy Generator HTML Table Generator Tips for a Great Email Signature Keep it concise. Aim for 4–6 lines of information. Recruiters and clients scan quickly — a wall of text undermines professionalism.\nUse email-safe fonts. The generator defaults to web-safe fonts (Arial, Georgia, Verdana) that render correctly in every email client without loading external fonts.\nAvoid images when possible. Many email clients block images by default. If you need a logo, host it at a stable URL and use an \u0026lt;img\u0026gt; tag — the HTML output makes this easy to add manually.\nTest before sending. Send yourself a test email after installing your signature. Check it on mobile and desktop.\nUpdate regularly. Changed roles or phone numbers? Refresh your signature so contacts always have accurate information.\nRelated Tools Email Template Builder Email Validator Related Articles Best AI Tools for Writing Emails Professionally ","permalink":"https://productivity-works.com/tools/email-signature-generator/","summary":"\u003cp\u003eCreate a polished, professional email signature in seconds — no account required, no data sent to any server. Fill in your details, pick a template, and copy the HTML directly into Gmail, Outlook, or Apple Mail.\u003c/p\u003e\n\u003cdiv id=\"sig-app\"\u003e\n\u003cstyle\u003e\n/* ── Scoped styles: all selectors prefixed with #sig-app ── */\n#sig-app *,\n#sig-app *::before,\n#sig-app *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\u003cp\u003e#sig-app {\nfont-family: -apple-system, BlinkMacSystemFont, \u0026ldquo;Segoe UI\u0026rdquo;, Roboto, sans-serif;\nfont-size: 15px;\ncolor: #1a1a1a;\nline-height: 1.5;\nmax-width: 960px;\nmargin: 0 auto;\npadding: 0 16px 48px;\n}\u003c/p\u003e","title":"Email Signature Generator — Free Professional Tool"},{"content":"Build responsive, email-client-safe HTML email templates visually — with live preview, preset themes, and one-click HTML copy.\nEmail Template Builder Build email-safe HTML with inline CSS\n\u0026lt;!-- PRESETS --\u0026gt; \u0026lt;div class=\u0026quot;et-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;et-section-title\u0026quot;\u0026gt;Preset Templates\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-presets\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;et-preset-btn\u0026quot; onclick=\u0026quot;etApplyPreset('newsletter')\u0026quot;\u0026gt;Newsletter\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;et-preset-btn\u0026quot; onclick=\u0026quot;etApplyPreset('announcement')\u0026quot;\u0026gt;Announcement\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;et-preset-btn\u0026quot; onclick=\u0026quot;etApplyPreset('welcome')\u0026quot;\u0026gt;Welcome Email\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;et-preset-btn\u0026quot; onclick=\u0026quot;etApplyPreset('receipt')\u0026quot;\u0026gt;Receipt / Order\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- HEADER --\u0026gt; \u0026lt;div class=\u0026quot;et-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;et-section-title\u0026quot;\u0026gt;Header\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-toggle-row\u0026quot;\u0026gt; \u0026lt;label class=\u0026quot;et-toggle\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;et-show-logo\u0026quot; checked onchange=\u0026quot;etUpdate()\u0026quot;\u0026gt;\u0026lt;span class=\u0026quot;et-toggle-slider\u0026quot;\u0026gt;\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;label for=\u0026quot;et-show-logo\u0026quot;\u0026gt;Show Logo\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-field\u0026quot; id=\u0026quot;et-logo-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Logo URL\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;url\u0026quot; id=\u0026quot;et-logo-url\u0026quot; placeholder=\u0026quot;https://example.com/logo.png\u0026quot; oninput=\u0026quot;etUpdate()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Logo Alt Text\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;et-logo-alt\u0026quot; value=\u0026quot;Company Logo\u0026quot; oninput=\u0026quot;etUpdate()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Header Background\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;et-color-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;et-header-bg-pick\u0026quot; value=\u0026quot;#6366f1\u0026quot; oninput=\u0026quot;etSyncColor('header-bg')\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;et-header-bg\u0026quot; value=\u0026quot;#6366f1\u0026quot; oninput=\u0026quot;etSyncPick('header-bg');etUpdate()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Header Text / Logo Padding\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;et-header-text\u0026quot; value=\u0026quot;Your Company\u0026quot; oninput=\u0026quot;etUpdate()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- HERO IMAGE --\u0026gt; \u0026lt;div class=\u0026quot;et-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;et-section-title\u0026quot;\u0026gt;Hero Image\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-toggle-row\u0026quot;\u0026gt; \u0026lt;label class=\u0026quot;et-toggle\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;et-show-hero\u0026quot; onchange=\u0026quot;etUpdate()\u0026quot;\u0026gt;\u0026lt;span class=\u0026quot;et-toggle-slider\u0026quot;\u0026gt;\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;label for=\u0026quot;et-show-hero\u0026quot;\u0026gt;Show Hero Image\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Hero Image URL\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;url\u0026quot; id=\u0026quot;et-hero-url\u0026quot; placeholder=\u0026quot;https://example.com/hero.jpg\u0026quot; oninput=\u0026quot;etUpdate()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Hero Alt Text\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;et-hero-alt\u0026quot; value=\u0026quot;Hero Image\u0026quot; oninput=\u0026quot;etUpdate()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- CONTENT --\u0026gt; \u0026lt;div class=\u0026quot;et-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;et-section-title\u0026quot;\u0026gt;Content\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Heading\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;et-heading\u0026quot; value=\u0026quot;Welcome to Our Newsletter\u0026quot; oninput=\u0026quot;etUpdate()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Body Text\u0026lt;/label\u0026gt; \u0026lt;textarea id=\u0026quot;et-body\u0026quot; oninput=\u0026quot;etUpdate()\u0026quot;\u0026gt;Thank you for subscribing! We're excited to share the latest updates, tips, and news with you. Stay tuned for great content coming your way.\u0026lt;/textarea\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- CTA BUTTON --\u0026gt; \u0026lt;div class=\u0026quot;et-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;et-section-title\u0026quot;\u0026gt;CTA Button\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-toggle-row\u0026quot;\u0026gt; \u0026lt;label class=\u0026quot;et-toggle\u0026quot;\u0026gt;\u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;et-show-cta\u0026quot; checked onchange=\u0026quot;etUpdate()\u0026quot;\u0026gt;\u0026lt;span class=\u0026quot;et-toggle-slider\u0026quot;\u0026gt;\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;label for=\u0026quot;et-show-cta\u0026quot;\u0026gt;Show Button\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Button Text\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;et-cta-text\u0026quot; value=\u0026quot;Read More\u0026quot; oninput=\u0026quot;etUpdate()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Button URL\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;url\u0026quot; id=\u0026quot;et-cta-url\u0026quot; value=\u0026quot;https://example.com\u0026quot; oninput=\u0026quot;etUpdate()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Button Color\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;et-color-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;et-btn-bg-pick\u0026quot; value=\u0026quot;#6366f1\u0026quot; oninput=\u0026quot;etSyncColor('btn-bg')\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;et-btn-bg\u0026quot; value=\u0026quot;#6366f1\u0026quot; oninput=\u0026quot;etSyncPick('btn-bg');etUpdate()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Button Text Color\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;et-color-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;et-btn-color-pick\u0026quot; value=\u0026quot;#ffffff\u0026quot; oninput=\u0026quot;etSyncColor('btn-color')\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;et-btn-color\u0026quot; value=\u0026quot;#ffffff\u0026quot; oninput=\u0026quot;etSyncPick('btn-color');etUpdate()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- STYLE --\u0026gt; \u0026lt;div class=\u0026quot;et-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;et-section-title\u0026quot;\u0026gt;Style\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Font Family\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;et-font\u0026quot; onchange=\u0026quot;etUpdate()\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;Arial, Helvetica, sans-serif\u0026quot;\u0026gt;Arial (safe)\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;'Georgia', Times, serif\u0026quot;\u0026gt;Georgia (serif)\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;'Trebuchet MS', sans-serif\u0026quot;\u0026gt;Trebuchet MS\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;Verdana, Geneva, sans-serif\u0026quot;\u0026gt;Verdana\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;'Courier New', Courier, monospace\u0026quot;\u0026gt;Courier New (mono)\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Body Background\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;et-color-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;et-body-bg-pick\u0026quot; value=\u0026quot;#f4f4f5\u0026quot; oninput=\u0026quot;etSyncColor('body-bg')\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;et-body-bg\u0026quot; value=\u0026quot;#f4f4f5\u0026quot; oninput=\u0026quot;etSyncPick('body-bg');etUpdate()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Card Background\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;et-color-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;et-card-bg-pick\u0026quot; value=\u0026quot;#ffffff\u0026quot; oninput=\u0026quot;etSyncColor('card-bg')\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;et-card-bg\u0026quot; value=\u0026quot;#ffffff\u0026quot; oninput=\u0026quot;etSyncPick('card-bg');etUpdate()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Text Color\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;et-color-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;et-text-color-pick\u0026quot; value=\u0026quot;#374151\u0026quot; oninput=\u0026quot;etSyncColor('text-color')\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;et-text-color\u0026quot; value=\u0026quot;#374151\u0026quot; oninput=\u0026quot;etSyncPick('text-color');etUpdate()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- FOOTER --\u0026gt; \u0026lt;div class=\u0026quot;et-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;et-section-title\u0026quot;\u0026gt;Footer\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;et-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Footer Text\u0026lt;/label\u0026gt; \u0026lt;textarea id=\u0026quot;et-footer\u0026quot; oninput=\u0026quot;etUpdate()\u0026quot;\u0026gt;© 2025 Your Company. All rights reserved. 123 Main Street, City, Country Unsubscribe | Privacy Policy Footer Background Preview: Desktop (600px) Mobile (320px) HTML Output Copy HTML HTML copied to clipboard! Related Tools Format HTML → HTML Beautifier Generate meta tags → Meta Tag Generator Related Articles Best AI Tools for Small Business Owners 2026: Complete Guide Best AI Tools for Writing Emails Professionally ChatGPT Prompts for Real Estate Agents: 30 Templates (2026) ","permalink":"https://productivity-works.com/tools/email-template-builder/","summary":"\u003cp\u003eBuild responsive, email-client-safe HTML email templates visually — with live preview, preset themes, and one-click HTML copy.\u003c/p\u003e\n\u003cdiv id=\"et-app\"\u003e\n\u003cstyle\u003e\n#et-app *,#et-app *::before,#et-app *::after{box-sizing:border-box;margin:0;padding:0}\n#et-app{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:#1e293b;background:#f1f5f9;min-height:100vh;padding:0}\n#et-app .et-wrap{display:grid;grid-template-columns:340px 1fr;gap:0;min-height:600px;background:#f1f5f9}\n#et-app .et-panel{background:#fff;border-right:1px solid #e2e8f0;overflow-y:auto;max-height:900px;padding:0}\n#et-app .et-panel-header{padding:16px 18px;background:linear-gradient(135deg,#6366f1 0%,#8b5cf6 100%);color:#fff}\n#et-app .et-panel-header h2{font-size:15px;font-weight:700;margin-bottom:2px}\n#et-app .et-panel-header p{font-size:12px;opacity:.85}\n#et-app .et-section{padding:14px 16px;border-bottom:1px solid #f1f5f9}\n#et-app .et-section-title{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:#94a3b8;margin-bottom:10px}\n#et-app .et-field{margin-bottom:10px}\n#et-app .et-field label{display:block;font-size:12px;font-weight:600;color:#475569;margin-bottom:4px}\n#et-app .et-field input[type=text],#et-app .et-field input[type=url],#et-app .et-field textarea,#et-app .et-field select{width:100%;padding:7px 10px;border:1px solid #cbd5e1;border-radius:6px;font-size:13px;color:#1e293b;background:#fff;outline:none;transition:border .15s}\n#et-app .et-field input[type=text]:focus,#et-app .et-field input[type=url]:focus,#et-app .et-field textarea:focus,#et-app .et-field select:focus{border-color:#6366f1;box-shadow:0 0 0 3px rgba(99,102,241,.12)}\n#et-app .et-field textarea{resize:vertical;min-height:70px;font-family:inherit}\n#et-app .et-color-row{display:flex;gap:8px;align-items:center}\n#et-app .et-color-row input[type=color]{width:36px;height:34px;padding:2px;border:1px solid #cbd5e1;border-radius:6px;cursor:pointer;background:#fff;flex-shrink:0}\n#et-app .et-color-row input[type=text]{flex:1}\n#et-app .et-toggle-row{display:flex;align-items:center;gap:8px;margin-bottom:8px}\n#et-app .et-toggle-row label{font-size:12px;font-weight:600;color:#475569;cursor:pointer}\n#et-app .et-toggle{position:relative;width:34px;height:18px;flex-shrink:0}\n#et-app .et-toggle input{opacity:0;width:0;height:0;position:absolute}\n#et-app .et-toggle-slider{position:absolute;inset:0;background:#cbd5e1;border-radius:9px;transition:.2s;cursor:pointer}\n#et-app .et-toggle-slider::before{content:'';position:absolute;width:12px;height:12px;left:3px;top:3px;background:#fff;border-radius:50%;transition:.2s}\n#et-app .et-toggle input:checked+.et-toggle-slider{background:#6366f1}\n#et-app .et-toggle input:checked+.et-toggle-slider::before{transform:translateX(16px)}\n#et-app .et-presets{display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-bottom:4px}\n#et-app .et-preset-btn{padding:7px 6px;border:1.5px solid #e2e8f0;border-radius:7px;background:#f8fafc;font-size:11px;font-weight:600;color:#475569;cursor:pointer;text-align:center;transition:all .15s}\n#et-app .et-preset-btn:hover{border-color:#6366f1;color:#6366f1;background:#eef2ff}\n#et-app .et-preview-area{display:flex;flex-direction:column;overflow:hidden}\n#et-app .et-preview-toolbar{display:flex;align-items:center;gap:8px;padding:12px 16px;background:#fff;border-bottom:1px solid #e2e8f0;flex-wrap:wrap}\n#et-app .et-preview-toolbar span{font-size:12px;font-weight:600;color:#64748b}\n#et-app .et-view-btn{padding:5px 12px;border:1.5px solid #e2e8f0;border-radius:6px;background:#f8fafc;font-size:12px;font-weight:600;color:#475569;cursor:pointer;transition:all .15s}\n#et-app .et-view-btn.active,#et-app .et-view-btn:hover{background:#6366f1;border-color:#6366f1;color:#fff}\n#et-app .et-copy-btn{margin-left:auto;padding:6px 16px;background:linear-gradient(135deg,#6366f1 0%,#8b5cf6 100%);color:#fff;border:none;border-radius:7px;font-size:12px;font-weight:700;cursor:pointer;transition:opacity .15s}\n#et-app .et-copy-btn:hover{opacity:.88}\n#et-app .et-preview-frame{flex:1;padding:20px;background:#e2e8f0;display:flex;justify-content:center;align-items:flex-start;overflow-y:auto;min-height:500px}\n#et-app .et-preview-shell{background:#fff;border-radius:8px;box-shadow:0 4px 24px rgba(0,0,0,.13);overflow:hidden;transition:width .25s}\n#et-app .et-preview-shell iframe{display:block;border:none;width:100%;height:700px}\n#et-app .et-html-out{display:none;padding:16px;background:#0f172a;border-radius:8px;margin:16px;overflow:auto}\n#et-app .et-html-out pre{font-family:'Courier New',monospace;font-size:11px;color:#94a3b8;white-space:pre-wrap;word-break:break-all}\n#et-app .et-toast{position:fixed;bottom:24px;right:24px;padding:10px 20px;background:#10b981;color:#fff;border-radius:8px;font-size:13px;font-weight:600;box-shadow:0 4px 16px rgba(0,0,0,.18);z-index:9999;opacity:0;transform:translateY(8px);transition:all .25s;pointer-events:none}\n#et-app .et-toast.show{opacity:1;transform:translateY(0)}\n@media(max-width:700px){\n  #et-app .et-wrap{grid-template-columns:1fr}\n  #et-app .et-panel{max-height:none;border-right:none;border-bottom:1px solid #e2e8f0}\n}\n\u003c/style\u003e\n\u003cdiv class=\"et-wrap\"\u003e\n  \u003c!-- LEFT PANEL --\u003e\n  \u003cdiv class=\"et-panel\"\u003e\n    \u003cdiv class=\"et-panel-header\"\u003e\n      \u003ch2\u003eEmail Template Builder\u003c/h2\u003e\n      \u003cp\u003eBuild email-safe HTML with inline CSS\u003c/p\u003e","title":"Email Template Builder"},{"content":" Single Email Bulk Validation Validate Clear Validate All Clear One email per line Copy Valid Emails Copied! Related Free Tools Password Generator Word Counter JSON Formatter URL Encoder / Decoder Base64 Encoder Related Tools Email Signature Generator Email Template Builder ","permalink":"https://productivity-works.com/tools/email-validator/","summary":"\u003cdiv id=\"ev-app\"\u003e\n\u003cstyle\u003e\n#ev-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 780px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#ev-app h2 {\n  font-size: 1.4rem;\n  font-weight: 700;\n  margin: 1.6rem 0 0.8rem;\n  color: #1a1a2e;\n}\n#ev-app .ev-tabs {\n  display: flex;\n  gap: 0;\n  border-bottom: 2px solid #e2e8f0;\n  margin-bottom: 1.4rem;\n}\n#ev-app .ev-tab {\n  padding: 0.6rem 1.4rem;\n  cursor: pointer;\n  font-size: 0.95rem;\n  font-weight: 600;\n  color: #64748b;\n  border-bottom: 3px solid transparent;\n  margin-bottom: -2px;\n  background: none;\n  border-top: none;\n  border-left: none;\n  border-right: none;\n  transition: color 0.2s, border-color 0.2s;\n}\n#ev-app .ev-tab.active {\n  color: #2563eb;\n  border-bottom-color: #2563eb;\n}\n#ev-app .ev-tab:hover:not(.active) {\n  color: #334155;\n}\n#ev-app .ev-panel {\n  display: none;\n}\n#ev-app .ev-panel.active {\n  display: block;\n}\n#ev-app .ev-input-row {\n  display: flex;\n  gap: 0.6rem;\n  margin-bottom: 1rem;\n}\n#ev-app input[type=\"email\"],\n#ev-app input[type=\"text\"] {\n  flex: 1;\n  padding: 0.65rem 1rem;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 1rem;\n  outline: none;\n  transition: border-color 0.2s;\n  background: #f8fafc;\n}\n#ev-app input[type=\"email\"]:focus,\n#ev-app input[type=\"text\"]:focus {\n  border-color: #2563eb;\n  background: #fff;\n}\n#ev-app textarea {\n  width: 100%;\n  min-height: 140px;\n  padding: 0.7rem 1rem;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 0.95rem;\n  font-family: \"Courier New\", monospace;\n  outline: none;\n  resize: vertical;\n  background: #f8fafc;\n  box-sizing: border-box;\n  transition: border-color 0.2s;\n}\n#ev-app textarea:focus {\n  border-color: #2563eb;\n  background: #fff;\n}\n#ev-app .ev-btn {\n  padding: 0.65rem 1.4rem;\n  background: #2563eb;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 0.97rem;\n  font-weight: 600;\n  cursor: pointer;\n  white-space: nowrap;\n  transition: background 0.2s;\n}\n#ev-app .ev-btn:hover {\n  background: #1d4ed8;\n}\n#ev-app .ev-btn.secondary {\n  background: #f1f5f9;\n  color: #334155;\n  border: 1.5px solid #cbd5e1;\n}\n#ev-app .ev-btn.secondary:hover {\n  background: #e2e8f0;\n}\n#ev-app .ev-btn.success {\n  background: #16a34a;\n}\n#ev-app .ev-btn.success:hover {\n  background: #15803d;\n}\n#ev-app .ev-result-single {\n  margin-top: 1rem;\n  padding: 1rem 1.2rem;\n  border-radius: 10px;\n  display: none;\n}\n#ev-app .ev-result-single.valid {\n  background: #f0fdf4;\n  border: 1.5px solid #86efac;\n  display: block;\n}\n#ev-app .ev-result-single.invalid {\n  background: #fff1f2;\n  border: 1.5px solid #fca5a5;\n  display: block;\n}\n#ev-app .ev-result-single .ev-status {\n  font-size: 1.05rem;\n  font-weight: 700;\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n  margin-bottom: 0.5rem;\n}\n#ev-app .ev-result-single.valid .ev-status { color: #16a34a; }\n#ev-app .ev-result-single.invalid .ev-status { color: #dc2626; }\n#ev-app .ev-result-single .ev-detail {\n  font-size: 0.9rem;\n  color: #475569;\n  margin: 0.2rem 0;\n}\n#ev-app .ev-suggestion {\n  margin-top: 0.5rem;\n  padding: 0.5rem 0.8rem;\n  background: #fefce8;\n  border: 1px solid #fde68a;\n  border-radius: 6px;\n  font-size: 0.88rem;\n  color: #92400e;\n}\n#ev-app .ev-suggestion a {\n  color: #d97706;\n  font-weight: 600;\n  cursor: pointer;\n  text-decoration: underline;\n}\n#ev-app .ev-provider-badge {\n  display: inline-block;\n  padding: 0.2rem 0.6rem;\n  border-radius: 20px;\n  font-size: 0.8rem;\n  font-weight: 600;\n  margin-top: 0.4rem;\n}\n#ev-app .ev-provider-gmail { background: #fce8e6; color: #c5221f; }\n#ev-app .ev-provider-yahoo { background: #e8f0fe; color: #1a73e8; }\n#ev-app .ev-provider-outlook { background: #e3f2fd; color: #0077d7; }\n#ev-app .ev-provider-icloud { background: #f3e8ff; color: #7c3aed; }\n#ev-app .ev-provider-proton { background: #ecfdf5; color: #059669; }\n#ev-app .ev-provider-other { background: #f1f5f9; color: #475569; }\n#ev-app .ev-bulk-controls {\n  display: flex;\n  gap: 0.6rem;\n  margin-bottom: 0.8rem;\n  flex-wrap: wrap;\n  align-items: center;\n}\n#ev-app .ev-bulk-results {\n  margin-top: 1rem;\n}\n#ev-app .ev-bulk-summary {\n  display: flex;\n  gap: 1rem;\n  margin-bottom: 1rem;\n  flex-wrap: wrap;\n}\n#ev-app .ev-summary-card {\n  flex: 1;\n  min-width: 120px;\n  padding: 0.7rem 1rem;\n  border-radius: 8px;\n  text-align: center;\n}\n#ev-app .ev-summary-card .ev-num {\n  font-size: 1.8rem;\n  font-weight: 800;\n  line-height: 1;\n}\n#ev-app .ev-summary-card .ev-label {\n  font-size: 0.8rem;\n  margin-top: 0.2rem;\n  font-weight: 500;\n}\n#ev-app .ev-summary-card.total { background: #f1f5f9; color: #334155; }\n#ev-app .ev-summary-card.valid { background: #f0fdf4; color: #16a34a; }\n#ev-app .ev-summary-card.invalid { background: #fff1f2; color: #dc2626; }\n#ev-app .ev-bulk-list {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n  max-height: 320px;\n  overflow-y: auto;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 8px;\n}\n#ev-app .ev-bulk-list li {\n  display: flex;\n  align-items: center;\n  gap: 0.7rem;\n  padding: 0.55rem 0.9rem;\n  border-bottom: 1px solid #f1f5f9;\n  font-size: 0.9rem;\n}\n#ev-app .ev-bulk-list li:last-child { border-bottom: none; }\n#ev-app .ev-bulk-list li.valid { background: #fafffe; }\n#ev-app .ev-bulk-list li.invalid { background: #fffafa; }\n#ev-app .ev-icon { font-size: 1rem; flex-shrink: 0; }\n#ev-app .ev-email-text { font-family: \"Courier New\", monospace; flex: 1; word-break: break-all; }\n#ev-app .ev-reason { font-size: 0.78rem; color: #94a3b8; margin-left: auto; text-align: right; }\n#ev-app .ev-copy-area { margin-top: 0.8rem; display: flex; gap: 0.6rem; align-items: center; flex-wrap: wrap; }\n#ev-app .ev-copy-feedback { font-size: 0.85rem; color: #16a34a; font-weight: 600; display: none; }\n#ev-app .ev-footer-links {\n  margin-top: 2.5rem;\n  padding-top: 1.2rem;\n  border-top: 1.5px solid #e2e8f0;\n}\n#ev-app .ev-footer-links h3 {\n  font-size: 1rem;\n  font-weight: 700;\n  margin-bottom: 0.6rem;\n  color: #334155;\n}\n#ev-app .ev-footer-links ul {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem 1.4rem;\n}\n#ev-app .ev-footer-links ul li a {\n  color: #2563eb;\n  text-decoration: none;\n  font-size: 0.92rem;\n}\n#ev-app .ev-footer-links ul li a:hover { text-decoration: underline; }\n@media (max-width: 520px) {\n  #ev-app .ev-input-row { flex-direction: column; }\n  #ev-app .ev-btn { width: 100%; }\n  #ev-app .ev-bulk-controls { flex-direction: column; }\n}\n\u003c/style\u003e\n\u003cdiv class=\"ev-tabs\"\u003e\n  \u003cbutton class=\"ev-tab active\" onclick=\"evSwitchTab('single', this)\"\u003eSingle Email\u003c/button\u003e\n  \u003cbutton class=\"ev-tab\" onclick=\"evSwitchTab('bulk', this)\"\u003eBulk Validation\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Single Panel --\u003e\n\u003cdiv id=\"ev-panel-single\" class=\"ev-panel active\"\u003e\n  \u003cdiv class=\"ev-input-row\"\u003e\n    \u003cinput type=\"text\" id=\"ev-single-input\" placeholder=\"Enter email address (e.g. user@example.com)\" onkeydown=\"if(event.key==='Enter')evValidateSingle()\"\u003e\n    \u003cbutton class=\"ev-btn\" onclick=\"evValidateSingle()\"\u003eValidate\u003c/button\u003e\n    \u003cbutton class=\"ev-btn secondary\" onclick=\"evClearSingle()\"\u003eClear\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv id=\"ev-single-result\" class=\"ev-result-single\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Bulk Panel --\u003e\n\u003cdiv id=\"ev-panel-bulk\" class=\"ev-panel\"\u003e\n  \u003cdiv class=\"ev-bulk-controls\"\u003e\n    \u003cbutton class=\"ev-btn\" onclick=\"evValidateBulk()\"\u003eValidate All\u003c/button\u003e\n    \u003cbutton class=\"ev-btn secondary\" onclick=\"evClearBulk()\"\u003eClear\u003c/button\u003e\n    \u003cspan style=\"font-size:0.85rem;color:#64748b;\"\u003eOne email per line\u003c/span\u003e\n  \u003c/div\u003e\n  \u003ctextarea id=\"ev-bulk-input\" placeholder=\"user@gmail.com\u0026#10;contact@company.com\u0026#10;invalid-email\u0026#10;test@yahoo.co.jp\u0026#10;...\"\u003e\u003c/textarea\u003e\n  \u003cdiv id=\"ev-bulk-results\" class=\"ev-bulk-results\" style=\"display:none;\"\u003e\n    \u003cdiv id=\"ev-bulk-summary\" class=\"ev-bulk-summary\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"ev-copy-area\"\u003e\n      \u003cbutton class=\"ev-btn success\" onclick=\"evCopyValid()\"\u003eCopy Valid Emails\u003c/button\u003e\n      \u003cspan id=\"ev-copy-feedback\" class=\"ev-copy-feedback\"\u003eCopied!\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cul id=\"ev-bulk-list\" class=\"ev-bulk-list\"\u003e\u003c/ul\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ev-footer-links\"\u003e\n  \u003ch3\u003eRelated Free Tools\u003c/h3\u003e\n  \u003cul\u003e\n    \u003cli\u003e\u003ca href=\"/tools/password-generator/\"\u003ePassword Generator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/word-counter/\"\u003eWord Counter\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/json-formatter/\"\u003eJSON Formatter\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/url-encoder/\"\u003eURL Encoder / Decoder\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/base64-encoder/\"\u003eBase64 Encoder\u003c/a\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  \"use strict\";\n\n  // --- Typo map: wrong domain → correct domain ---\n  var TYPO_MAP = {\n    \"gmial.com\": \"gmail.com\",\n    \"gmai.com\": \"gmail.com\",\n    \"gmail.con\": \"gmail.com\",\n    \"gmail.cmo\": \"gmail.com\",\n    \"gmaill.com\": \"gmail.com\",\n    \"gmailcom\": \"gmail.com\",\n    \"gnail.com\": \"gmail.com\",\n    \"gamil.com\": \"gmail.com\",\n    \"yahooo.com\": \"yahoo.com\",\n    \"yaho.com\": \"yahoo.com\",\n    \"yahoo.con\": \"yahoo.com\",\n    \"yahoo.cmo\": \"yahoo.com\",\n    \"yahooo.co.jp\": \"yahoo.co.jp\",\n    \"yaho.co.jp\": \"yahoo.co.jp\",\n    \"hotmial.com\": \"hotmail.com\",\n    \"hotmail.con\": \"hotmail.com\",\n    \"hotmal.com\": \"hotmail.com\",\n    \"outllook.com\": \"outlook.com\",\n    \"outlok.com\": \"outlook.com\",\n    \"outlook.con\": \"outlook.com\",\n    \"outlookcom\": \"outlook.com\",\n    \"icoud.com\": \"icloud.com\",\n    \"iclod.com\": \"icloud.com\",\n    \"protonmal.com\": \"protonmail.com\",\n    \"protonmial.com\": \"protonmail.com\"\n  };\n\n  // --- Provider detection ---\n  var PROVIDERS = {\n    \"gmail.com\": { name: \"Gmail\", cls: \"ev-provider-gmail\" },\n    \"googlemail.com\": { name: \"Gmail\", cls: \"ev-provider-gmail\" },\n    \"yahoo.com\": { name: \"Yahoo Mail\", cls: \"ev-provider-yahoo\" },\n    \"yahoo.co.jp\": { name: \"Yahoo Mail JP\", cls: \"ev-provider-yahoo\" },\n    \"yahoo.co.uk\": { name: \"Yahoo Mail UK\", cls: \"ev-provider-yahoo\" },\n    \"ymail.com\": { name: \"Yahoo Mail\", cls: \"ev-provider-yahoo\" },\n    \"outlook.com\": { name: \"Outlook\", cls: \"ev-provider-outlook\" },\n    \"hotmail.com\": { name: \"Hotmail / Outlook\", cls: \"ev-provider-outlook\" },\n    \"live.com\": { name: \"Outlook / Live\", cls: \"ev-provider-outlook\" },\n    \"msn.com\": { name: \"MSN / Outlook\", cls: \"ev-provider-outlook\" },\n    \"icloud.com\": { name: \"iCloud Mail\", cls: \"ev-provider-icloud\" },\n    \"me.com\": { name: \"iCloud Mail\", cls: \"ev-provider-icloud\" },\n    \"mac.com\": { name: \"iCloud Mail\", cls: \"ev-provider-icloud\" },\n    \"protonmail.com\": { name: \"Proton Mail\", cls: \"ev-provider-proton\" },\n    \"pm.me\": { name: \"Proton Mail\", cls: \"ev-provider-proton\" }\n  };\n\n  // --- Core validator ---\n  function validateEmail(raw) {\n    var email = raw.trim();\n    var result = {\n      email: email,\n      valid: false,\n      reason: \"\",\n      suggestion: null,\n      provider: null,\n      providerCls: null\n    };\n\n    if (!email) {\n      result.reason = \"Email address is empty.\";\n      return result;\n    }\n\n    // Basic structure check\n    var atCount = (email.match(/@/g) || []).length;\n    if (atCount === 0) {\n      result.reason = \"Missing @ symbol.\";\n      return result;\n    }\n    if (atCount \u003e 1) {\n      result.reason = \"Multiple @ symbols found.\";\n      return result;\n    }\n\n    var parts = email.split(\"@\");\n    var local = parts[0];\n    var domain = parts[1];\n\n    // Local part checks\n    if (!local || local.length === 0) {\n      result.reason = \"Nothing before the @ symbol.\";\n      return result;\n    }\n    if (local.length \u003e 64) {\n      result.reason = \"Local part (before @) exceeds 64 characters.\";\n      return result;\n    }\n    if (/^\\./.test(local) || /\\.$/.test(local)) {\n      result.reason = \"Local part cannot start or end with a dot.\";\n      return result;\n    }\n    if (/\\.{2,}/.test(local)) {\n      result.reason = \"Local part contains consecutive dots.\";\n      return result;\n    }\n    if (!/^[a-zA-Z0-9!#$%\u0026'*+/=?^_`{|}~.-]+$/.test(local)) {\n      result.reason = \"Local part contains invalid characters.\";\n      return result;\n    }\n\n    // Domain checks\n    if (!domain || domain.length === 0) {\n      result.reason = \"Nothing after the @ symbol.\";\n      return result;\n    }\n    if (domain.length \u003e 255) {\n      result.reason = \"Domain exceeds maximum length.\";\n      return result;\n    }\n    if (!domain.includes(\".\")) {\n      result.reason = \"Domain has no dot (e.g. missing .com).\";\n      // check for missing dot typos like \"gmailcom\"\n      var possibleFixed = null;\n      Object.keys(TYPO_MAP).forEach(function (t) {\n        if (domain.toLowerCase() === t.replace(\".\", \"\")) {\n          possibleFixed = local + \"@\" + TYPO_MAP[t];\n        }\n      });\n      if (possibleFixed) result.suggestion = possibleFixed;\n      return result;\n    }\n    if (/^-|-$/.test(domain.split(\".\")[0])) {\n      result.reason = \"Domain label cannot start or end with a hyphen.\";\n      return result;\n    }\n    if (/\\.{2,}/.test(domain)) {\n      result.reason = \"Domain contains consecutive dots.\";\n      return result;\n    }\n    if (/^\\.|\\.$/.test(domain)) {\n      result.reason = \"Domain cannot start or end with a dot.\";\n      return result;\n    }\n    var tld = domain.split(\".\").pop();\n    if (!tld || tld.length \u003c 2) {\n      result.reason = \"TLD (e.g. .com) is too short or missing.\";\n      return result;\n    }\n    if (!/^[a-zA-Z]{2,}$/.test(tld)) {\n      result.reason = \"TLD contains invalid characters.\";\n      return result;\n    }\n    if (!/^[a-zA-Z0-9.-]+$/.test(domain)) {\n      result.reason = \"Domain contains invalid characters.\";\n      return result;\n    }\n\n    // Full regex sanity check\n    var re = /^[a-zA-Z0-9!#$%\u0026'*+/=?^_`{|}~.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$/;\n    if (!re.test(email)) {\n      result.reason = \"Email format is not valid.\";\n      return result;\n    }\n\n    // Typo detection\n    var domainLower = domain.toLowerCase();\n    if (TYPO_MAP[domainLower]) {\n      result.suggestion = local + \"@\" + TYPO_MAP[domainLower];\n    }\n\n    // Provider detection\n    if (PROVIDERS[domainLower]) {\n      result.provider = PROVIDERS[domainLower].name;\n      result.providerCls = PROVIDERS[domainLower].cls;\n    } else {\n      result.provider = \"Custom Domain\";\n      result.providerCls = \"ev-provider-other\";\n    }\n\n    result.valid = true;\n    result.reason = \"Syntax and domain structure look correct.\";\n    return result;\n  }\n\n  // --- Tab switcher ---\n  window.evSwitchTab = function (tab, btn) {\n    document.querySelectorAll(\"#ev-app .ev-panel\").forEach(function (p) { p.classList.remove(\"active\"); });\n    document.querySelectorAll(\"#ev-app .ev-tab\").forEach(function (b) { b.classList.remove(\"active\"); });\n    document.getElementById(\"ev-panel-\" + tab).classList.add(\"active\");\n    btn.classList.add(\"active\");\n  };\n\n  // --- Single validation ---\n  window.evValidateSingle = function () {\n    var input = document.getElementById(\"ev-single-input\").value;\n    var r = validateEmail(input);\n    var el = document.getElementById(\"ev-single-result\");\n    el.className = \"ev-result-single \" + (r.valid ? \"valid\" : \"invalid\");\n\n    var html = '\u003cdiv class=\"ev-status\"\u003e' +\n      (r.valid ? \"\u0026#10003;\" : \"\u0026#10007;\") +\n      \" \u003cspan\u003e\" + (r.valid ? \"Valid Email Address\" : \"Invalid Email Address\") + \"\u003c/span\u003e\u003c/div\u003e\";\n    html += '\u003cdiv class=\"ev-detail\"\u003e\u003cstrong\u003eEmail:\u003c/strong\u003e ' + escHtml(r.email) + \"\u003c/div\u003e\";\n    html += '\u003cdiv class=\"ev-detail\"\u003e\u003cstrong\u003eResult:\u003c/strong\u003e ' + escHtml(r.reason) + \"\u003c/div\u003e\";\n\n    if (r.valid \u0026\u0026 r.provider) {\n      html += '\u003cspan class=\"ev-provider-badge ' + r.providerCls + '\"\u003e' + escHtml(r.provider) + \"\u003c/span\u003e\";\n    }\n    if (r.suggestion) {\n      html += '\u003cdiv class=\"ev-suggestion\"\u003eDid you mean: \u003ca onclick=\"evUseSuggestion(\\'' + escAttr(r.suggestion) + '\\')\"\u003e' + escHtml(r.suggestion) + \"\u003c/a\u003e?\u003c/div\u003e\";\n    }\n\n    el.innerHTML = html;\n  };\n\n  window.evUseSuggestion = function (suggested) {\n    document.getElementById(\"ev-single-input\").value = suggested;\n    evValidateSingle();\n  };\n\n  window.evClearSingle = function () {\n    document.getElementById(\"ev-single-input\").value = \"\";\n    var el = document.getElementById(\"ev-single-result\");\n    el.className = \"ev-result-single\";\n    el.innerHTML = \"\";\n  };\n\n  // --- Bulk validation ---\n  var bulkResults = [];\n\n  window.evValidateBulk = function () {\n    var raw = document.getElementById(\"ev-bulk-input\").value;\n    var lines = raw.split(\"\\n\").map(function (l) { return l.trim(); }).filter(function (l) { return l.length \u003e 0; });\n\n    if (lines.length === 0) return;\n\n    bulkResults = lines.map(validateEmail);\n\n    var valid = bulkResults.filter(function (r) { return r.valid; });\n    var invalid = bulkResults.filter(function (r) { return !r.valid; });\n\n    // Summary\n    var summary = document.getElementById(\"ev-bulk-summary\");\n    summary.innerHTML =\n      '\u003cdiv class=\"ev-summary-card total\"\u003e\u003cdiv class=\"ev-num\"\u003e' + bulkResults.length + '\u003c/div\u003e\u003cdiv class=\"ev-label\"\u003eTotal\u003c/div\u003e\u003c/div\u003e' +\n      '\u003cdiv class=\"ev-summary-card valid\"\u003e\u003cdiv class=\"ev-num\"\u003e' + valid.length + '\u003c/div\u003e\u003cdiv class=\"ev-label\"\u003eValid\u003c/div\u003e\u003c/div\u003e' +\n      '\u003cdiv class=\"ev-summary-card invalid\"\u003e\u003cdiv class=\"ev-num\"\u003e' + invalid.length + '\u003c/div\u003e\u003cdiv class=\"ev-label\"\u003eInvalid\u003c/div\u003e\u003c/div\u003e';\n\n    // List\n    var list = document.getElementById(\"ev-bulk-list\");\n    list.innerHTML = bulkResults.map(function (r) {\n      return '\u003cli class=\"' + (r.valid ? \"valid\" : \"invalid\") + '\"\u003e' +\n        '\u003cspan class=\"ev-icon\"\u003e' + (r.valid ? \"\u0026#10003;\" : \"\u0026#10007;\") + \"\u003c/span\u003e\" +\n        '\u003cspan class=\"ev-email-text\"\u003e' + escHtml(r.email) + \"\u003c/span\u003e\" +\n        (r.provider ? '\u003cspan class=\"ev-provider-badge ' + r.providerCls + '\" style=\"font-size:0.72rem;padding:0.1rem 0.4rem;\"\u003e' + escHtml(r.provider) + \"\u003c/span\u003e\" : \"\") +\n        '\u003cspan class=\"ev-reason\"\u003e' + escHtml(r.reason) + \"\u003c/span\u003e\" +\n        \"\u003c/li\u003e\";\n    }).join(\"\");\n\n    document.getElementById(\"ev-bulk-results\").style.display = \"block\";\n  };\n\n  window.evClearBulk = function () {\n    document.getElementById(\"ev-bulk-input\").value = \"\";\n    document.getElementById(\"ev-bulk-results\").style.display = \"none\";\n    bulkResults = [];\n  };\n\n  window.evCopyValid = function () {\n    var validEmails = bulkResults.filter(function (r) { return r.valid; }).map(function (r) { return r.email; });\n    if (validEmails.length === 0) return;\n    var text = validEmails.join(\"\\n\");\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(text).then(showCopied);\n    } else {\n      var ta = document.createElement(\"textarea\");\n      ta.value = text;\n      ta.style.position = \"fixed\";\n      ta.style.opacity = \"0\";\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand(\"copy\");\n      document.body.removeChild(ta);\n      showCopied();\n    }\n  };\n\n  function showCopied() {\n    var fb = document.getElementById(\"ev-copy-feedback\");\n    fb.style.display = \"inline\";\n    setTimeout(function () { fb.style.display = \"none\"; }, 2000);\n  }\n\n  function escHtml(s) {\n    return String(s).replace(/\u0026/g, \"\u0026amp;\").replace(/\u003c/g, \"\u0026lt;\").replace(/\u003e/g, \"\u0026gt;\").replace(/\"/g, \"\u0026quot;\");\n  }\n  function escAttr(s) {\n    return String(s).replace(/'/g, \"\\\\'\");\n  }\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"https://productivity-works.com/tools/email-signature-generator/\"\u003eEmail Signature Generator\u003c/a\u003e\n\n\u003ca href=\"https://productivity-works.com/tools/email-template-builder/\"\u003eEmail Template Builder\u003c/a\u003e\n\u003c/p\u003e","title":"Email Validator - Check Email Address Format"},{"content":" Emoji Picker Browse, search, and copy emojis instantly. Click any emoji to copy it.\nName Unicode HTML Entity Category Copy Emoji Skin tone: \u0026#x270B; \u0026#x270B; \u0026#x270B; \u0026#x270B; \u0026#x270B; All Smileys People Animals Food Travel Objects Symbols Flags Recently Used No recent emojis yet All Emojis Related: Encode special characters with our HTML Entity Encoder ","permalink":"https://productivity-works.com/tools/emoji-picker/","summary":"\u003cdiv id=\"emoji-app\"\u003e\n\u003cstyle\u003e\n#emoji-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 0 0 40px 0;\n  color: #f1f5f9;\n}\n\n#emoji-app * {\n  box-sizing: border-box;\n}\n\n.ep-hero {\n  background: linear-gradient(135deg, #78350f 0%, #92400e 40%, #b45309 100%);\n  border-radius: 16px;\n  padding: 36px 32px;\n  margin-bottom: 28px;\n  text-align: center;\n}\n\n.ep-hero h1 {\n  font-size: 2rem;\n  font-weight: 800;\n  color: #fef9c3;\n  margin: 0 0 8px 0;\n}\n\n.ep-hero p {\n  color: #fde68a;\n  margin: 0;\n  font-size: 1rem;\n}\n\n.ep-controls {\n  display: flex;\n  flex-direction: column;\n  gap: 12px;\n  margin-bottom: 20px;\n}\n\n.ep-search-row {\n  display: flex;\n  gap: 12px;\n  align-items: center;\n  flex-wrap: wrap;\n}\n\n.ep-search {\n  flex: 1;\n  min-width: 200px;\n  padding: 10px 16px;\n  border-radius: 10px;\n  border: 2px solid #eab308;\n  background: #1e293b;\n  color: #f1f5f9;\n  font-size: 1rem;\n  outline: none;\n  transition: border-color 0.2s;\n}\n\n.ep-search::placeholder {\n  color: #64748b;\n}\n\n.ep-search:focus {\n  border-color: #fbbf24;\n}\n\n.ep-tone-label {\n  color: #fde68a;\n  font-size: 0.85rem;\n  font-weight: 600;\n  white-space: nowrap;\n}\n\n.ep-tone-buttons {\n  display: flex;\n  gap: 6px;\n}\n\n.ep-tone-btn {\n  width: 30px;\n  height: 30px;\n  border-radius: 50%;\n  border: 2px solid transparent;\n  cursor: pointer;\n  font-size: 1.1rem;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  background: #1e293b;\n  transition: border-color 0.2s, transform 0.1s;\n}\n\n.ep-tone-btn:hover {\n  transform: scale(1.15);\n}\n\n.ep-tone-btn.active {\n  border-color: #eab308;\n}\n\n.ep-tabs {\n  display: flex;\n  gap: 6px;\n  flex-wrap: wrap;\n  margin-bottom: 16px;\n}\n\n.ep-tab {\n  padding: 6px 14px;\n  border-radius: 20px;\n  border: 2px solid #334155;\n  background: #1e293b;\n  color: #94a3b8;\n  cursor: pointer;\n  font-size: 0.85rem;\n  font-weight: 600;\n  transition: all 0.2s;\n  white-space: nowrap;\n}\n\n.ep-tab:hover {\n  border-color: #eab308;\n  color: #fde68a;\n}\n\n.ep-tab.active {\n  background: #eab308;\n  border-color: #eab308;\n  color: #1e293b;\n}\n\n.ep-section-title {\n  font-size: 0.8rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #eab308;\n  margin: 0 0 10px 0;\n}\n\n.ep-recent {\n  background: #1e293b;\n  border-radius: 12px;\n  padding: 14px 16px;\n  margin-bottom: 16px;\n  border: 1px solid #334155;\n}\n\n.ep-recent-grid {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 4px;\n}\n\n.ep-grid {\n  background: #1e293b;\n  border-radius: 12px;\n  padding: 14px 16px;\n  border: 1px solid #334155;\n  margin-bottom: 16px;\n}\n\n.ep-emoji-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(44px, 1fr));\n  gap: 4px;\n}\n\n.ep-emoji-btn {\n  width: 44px;\n  height: 44px;\n  border-radius: 8px;\n  border: none;\n  background: transparent;\n  font-size: 1.5rem;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  transition: background 0.15s, transform 0.1s;\n  line-height: 1;\n}\n\n.ep-emoji-btn:hover {\n  background: #334155;\n  transform: scale(1.2);\n}\n\n.ep-emoji-btn:active {\n  transform: scale(0.95);\n}\n\n.ep-info-panel {\n  background: #1e293b;\n  border-radius: 12px;\n  padding: 16px 20px;\n  border: 1px solid #eab308;\n  margin-bottom: 16px;\n  display: none;\n}\n\n.ep-info-panel.visible {\n  display: block;\n}\n\n.ep-info-emoji {\n  font-size: 3rem;\n  line-height: 1;\n  margin-bottom: 10px;\n  text-align: center;\n}\n\n.ep-info-rows {\n  display: grid;\n  grid-template-columns: auto 1fr;\n  gap: 6px 12px;\n  font-size: 0.875rem;\n}\n\n.ep-info-label {\n  color: #eab308;\n  font-weight: 700;\n}\n\n.ep-info-value {\n  color: #cbd5e1;\n  font-family: monospace;\n  word-break: break-all;\n}\n\n.ep-copy-btn {\n  display: block;\n  width: 100%;\n  margin-top: 12px;\n  padding: 10px;\n  background: #eab308;\n  color: #1e293b;\n  border: none;\n  border-radius: 8px;\n  font-size: 1rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n\n.ep-copy-btn:hover {\n  background: #fbbf24;\n}\n\n.ep-empty {\n  text-align: center;\n  color: #475569;\n  padding: 32px 0;\n  font-size: 1rem;\n}\n\n.ep-toast {\n  position: fixed;\n  bottom: 32px;\n  left: 50%;\n  transform: translateX(-50%) translateY(20px);\n  background: #eab308;\n  color: #1e293b;\n  padding: 10px 24px;\n  border-radius: 50px;\n  font-weight: 700;\n  font-size: 0.95rem;\n  opacity: 0;\n  transition: opacity 0.3s, transform 0.3s;\n  pointer-events: none;\n  z-index: 9999;\n  white-space: nowrap;\n}\n\n.ep-toast.show {\n  opacity: 1;\n  transform: translateX(-50%) translateY(0);\n}\n\n.ep-no-recent {\n  color: #475569;\n  font-size: 0.85rem;\n  font-style: italic;\n}\n\n@media (max-width: 600px) {\n  .ep-hero h1 { font-size: 1.4rem; }\n  .ep-emoji-grid { grid-template-columns: repeat(auto-fill, minmax(40px, 1fr)); }\n  .ep-emoji-btn { width: 40px; height: 40px; font-size: 1.3rem; }\n}\n\u003c/style\u003e\n\u003cdiv class=\"ep-hero\"\u003e\n  \u003ch1\u003eEmoji Picker\u003c/h1\u003e\n  \u003cp\u003eBrowse, search, and copy emojis instantly. Click any emoji to copy it.\u003c/p\u003e","title":"Emoji Picker - Copy \u0026 Search Emojis Free"},{"content":" Emoji Search \u0026 Copy Search 1500+ emojis — click any emoji to copy instantly\nSkin: \u0026#x270B; \u0026#x270B;\u0026#x1F3FB; \u0026#x270B;\u0026#x1F3FC; \u0026#x270B;\u0026#x1F3FD; \u0026#x270B;\u0026#x1F3FE; \u0026#x270B;\u0026#x1F3FF; Recently Used \u0026#x1F600; Grinning Face Unicode U+1F600 Copy HTML Entity \u0026#amp;#128512; Copy CSS Value \\1F600 Copy Copy Emoji No emojis found. Try a different keyword. Also useful: browse Unicode symbols with our Unicode Character Map or use the original Emoji Picker for a compact popup-style experience.\n","permalink":"https://productivity-works.com/tools/emoji-search/","summary":"\u003cdiv id=\"es-app\"\u003e\n\u003cstyle\u003e\n#es-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 960px;\n  margin: 0 auto;\n  padding: 0 0 40px 0;\n  color: #f1f5f9;\n}\n#es-app * { box-sizing: border-box; }\n\n.es-hero {\n  background: linear-gradient(135deg, #1e3a5f 0%, #1e40af 50%, #1d4ed8 100%);\n  border-radius: 16px;\n  padding: 36px 32px;\n  margin-bottom: 24px;\n  text-align: center;\n}\n.es-hero h1 { font-size: 2rem; font-weight: 800; color: #bfdbfe; margin: 0 0 8px 0; }\n.es-hero p  { color: #93c5fd; margin: 0; font-size: 1rem; }\n\n.es-search-row {\n  display: flex;\n  gap: 10px;\n  align-items: center;\n  flex-wrap: wrap;\n  margin-bottom: 14px;\n}\n.es-search {\n  flex: 1;\n  min-width: 220px;\n  padding: 11px 16px;\n  border-radius: 10px;\n  border: 2px solid #3b82f6;\n  background: #1e293b;\n  color: #f1f5f9;\n  font-size: 1rem;\n  outline: none;\n  transition: border-color 0.2s;\n}\n.es-search::placeholder { color: #64748b; }\n.es-search:focus { border-color: #60a5fa; }\n\n.es-tone-wrap {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  flex-shrink: 0;\n}\n.es-tone-label { color: #93c5fd; font-size: 0.82rem; font-weight: 600; white-space: nowrap; }\n.es-tone-buttons { display: flex; gap: 5px; }\n.es-tone-btn {\n  width: 28px; height: 28px;\n  border-radius: 50%;\n  border: 2px solid transparent;\n  cursor: pointer;\n  font-size: 1rem;\n  display: flex; align-items: center; justify-content: center;\n  background: #1e293b;\n  transition: border-color 0.2s, transform 0.1s;\n}\n.es-tone-btn:hover { transform: scale(1.2); }\n.es-tone-btn.active { border-color: #3b82f6; }\n\n.es-tabs {\n  display: flex;\n  gap: 6px;\n  flex-wrap: wrap;\n  margin-bottom: 14px;\n}\n.es-tab {\n  padding: 6px 13px;\n  border-radius: 20px;\n  border: 2px solid #334155;\n  background: #1e293b;\n  color: #94a3b8;\n  cursor: pointer;\n  font-size: 0.82rem;\n  font-weight: 600;\n  transition: all 0.2s;\n  white-space: nowrap;\n}\n.es-tab:hover { border-color: #3b82f6; color: #93c5fd; }\n.es-tab.active { background: #3b82f6; border-color: #3b82f6; color: #fff; }\n\n.es-section-label {\n  font-size: 0.75rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #3b82f6;\n  margin: 0 0 8px 0;\n}\n\n.es-recent-box {\n  background: #1e293b;\n  border-radius: 12px;\n  padding: 12px 14px;\n  margin-bottom: 14px;\n  border: 1px solid #334155;\n}\n.es-recent-row { display: flex; flex-wrap: wrap; gap: 3px; }\n\n.es-grid-box {\n  background: #1e293b;\n  border-radius: 12px;\n  padding: 12px 14px;\n  border: 1px solid #334155;\n  margin-bottom: 14px;\n}\n.es-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(44px, 1fr));\n  gap: 3px;\n}\n.es-cell {\n  width: 44px; height: 44px;\n  display: flex; align-items: center; justify-content: center;\n  font-size: 1.5rem;\n  border-radius: 8px;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s;\n  position: relative;\n  border: 2px solid transparent;\n}\n.es-cell:hover { background: #334155; transform: scale(1.18); z-index: 2; }\n.es-cell.selected { border-color: #3b82f6; background: #1e3a5f; }\n\n.es-detail {\n  background: #1e293b;\n  border-radius: 12px;\n  padding: 18px 20px;\n  border: 1px solid #3b82f6;\n  margin-bottom: 14px;\n  display: none;\n}\n.es-detail.visible { display: block; }\n.es-detail-inner { display: flex; align-items: flex-start; gap: 20px; flex-wrap: wrap; }\n.es-preview { font-size: 4rem; line-height: 1; flex-shrink: 0; }\n.es-info { flex: 1; min-width: 200px; }\n.es-info-name { font-size: 1.1rem; font-weight: 700; color: #bfdbfe; margin-bottom: 10px; }\n.es-info-row {\n  display: flex; align-items: center; gap: 8px;\n  margin-bottom: 7px; flex-wrap: wrap;\n}\n.es-info-label { color: #64748b; font-size: 0.8rem; width: 80px; flex-shrink: 0; }\n.es-info-val {\n  font-family: 'Courier New', monospace;\n  font-size: 0.88rem;\n  color: #93c5fd;\n  background: #0f172a;\n  padding: 3px 8px;\n  border-radius: 5px;\n}\n.es-copy-btn {\n  padding: 3px 10px;\n  border-radius: 6px;\n  border: 1px solid #3b82f6;\n  background: transparent;\n  color: #60a5fa;\n  font-size: 0.78rem;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n.es-copy-btn:hover { background: #1e3a5f; }\n.es-copy-emoji-btn {\n  margin-top: 10px;\n  padding: 8px 20px;\n  border-radius: 8px;\n  border: none;\n  background: #3b82f6;\n  color: #fff;\n  font-size: 0.95rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n.es-copy-emoji-btn:hover { background: #2563eb; }\n\n.es-empty { color: #64748b; font-size: 0.95rem; text-align: center; padding: 20px 0; }\n\n.es-toast {\n  position: fixed;\n  bottom: 24px; left: 50%;\n  transform: translateX(-50%) translateY(20px);\n  background: #1d4ed8;\n  color: #fff;\n  padding: 10px 22px;\n  border-radius: 10px;\n  font-size: 0.9rem;\n  font-weight: 600;\n  opacity: 0;\n  transition: opacity 0.25s, transform 0.25s;\n  pointer-events: none;\n  z-index: 9999;\n  white-space: nowrap;\n}\n.es-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }\n\n.es-count { color: #64748b; font-size: 0.8rem; margin-bottom: 8px; }\n\n@media (max-width: 600px) {\n  .es-hero { padding: 24px 16px; }\n  .es-hero h1 { font-size: 1.4rem; }\n  .es-cell { width: 38px; height: 38px; font-size: 1.3rem; }\n  .es-preview { font-size: 3rem; }\n}\n\u003c/style\u003e\n\u003cdiv class=\"es-hero\"\u003e\n  \u003ch1\u003eEmoji Search \u0026 Copy\u003c/h1\u003e\n  \u003cp\u003eSearch 1500+ emojis — click any emoji to copy instantly\u003c/p\u003e","title":"Emoji Search \u0026 Copy Tool"},{"content":" \u0026#128197; Event Countdown Event Name Target Date \u0026amp; Time Quick Presets \u0026#9654; Start Countdown + Add to List \u0026#128279; Share Link Set an event above and click \"Start Countdown\" to begin. \u0026#128203; My Countdowns No countdowns added yet. Configure an event and click \"+ Add to List\". Link copied to clipboard! How to Use the Event Countdown Calculator Name your event — Type a descriptive label such as \u0026ldquo;Product Launch\u0026rdquo; or \u0026ldquo;Summer Vacation\u0026rdquo; in the Event Name field. Pick a date and time — Use the date/time picker to set the exact moment you\u0026rsquo;re counting down to. Time precision gives you hours, minutes, and seconds on top of days. Use a preset — Click any of the quick-preset chips (New Year, Christmas, Halloween, etc.) to instantly load that event. Click \u0026ldquo;Start Countdown\u0026rdquo; — The display updates every second with days, hours, minutes, and seconds remaining. Track progress — The progress bar shows what percentage of time has elapsed since you started tracking. Add to list — Click + Add to List to save multiple events and see them sorted by date with live day counts. Share your countdown — Click Share Link to copy a URL that opens this page pre-loaded with your event. Great for sharing with a team or on social media. Past dates — If you enter a date that has already passed, the tool shows \u0026ldquo;X days ago\u0026rdquo; instead. Confetti at zero — When the countdown reaches exactly zero, a confetti animation fires automatically. What Is the Difference Between This and a Countdown Timer? The Countdown Timer on this site is a simple stopwatch-style tool: you set a duration (e.g. 25 minutes) and it counts down that span. It is designed for focused work sessions, cooking, workouts, and short intervals.\nThe Event Countdown tool here is date-anchored: you pick a specific calendar date and time in the future and the tool shows you precisely how far away that moment is — in days, hours, minutes, and seconds — and continues updating even if you reload the page (via URL params).\nFeature Countdown Timer Event Countdown Input Duration (hh:mm:ss) Specific date \u0026amp; time Use case Short sessions Future events \u0026amp; deadlines Day display No Yes Shareable link No Yes Past date support No Yes (shows \u0026ldquo;X days ago\u0026rdquo;) Multiple events No Yes (pinned list) Popular Events to Count Down To Category Examples Holidays New Year, Christmas, Thanksgiving, Halloween Work Project deadlines, product launches, performance reviews Personal Birthdays, anniversaries, weddings Travel Flight departures, hotel check-in, end of vacation Education Exam dates, graduation, semester start Sports Race day, game day, season opener Related Tools Simple timer for work sessions → Countdown Timer Calculate your exact age → Age Calculator ","permalink":"https://productivity-works.com/tools/event-countdown/","summary":"\u003cdiv id=\"ec-app\"\u003e\n\u003cstyle\u003e\n/* =============================================\n   EVENT COUNTDOWN - Teal + Amber Theme\n   ============================================= */\n\n#ec-app {\n  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #0f172a;\n}\n\n#ec-app * {\n  box-sizing: border-box;\n}\n\n/* --- Input Panel --- */\n#ec-app .ec-panel {\n  background: linear-gradient(135deg, #0d9488 0%, #0f766e 100%);\n  border-radius: 16px;\n  padding: 28px 28px 24px;\n  color: #fff;\n  margin-bottom: 24px;\n  box-shadow: 0 8px 32px rgba(13,148,136,0.28);\n}\n\n#ec-app .ec-panel h2 {\n  margin: 0 0 20px;\n  font-size: 1.3rem;\n  font-weight: 700;\n  letter-spacing: .02em;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  color: #f0fdfa;\n}\n\n#ec-app .ec-form-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 12px;\n  align-items: flex-end;\n  margin-bottom: 14px;\n}\n\n#ec-app .ec-form-group {\n  display: flex;\n  flex-direction: column;\n  gap: 5px;\n  flex: 1;\n  min-width: 160px;\n}\n\n#ec-app .ec-form-group label {\n  font-size: .8rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: .06em;\n  color: #ccfbf1;\n}\n\n#ec-app .ec-input {\n  padding: 9px 12px;\n  border-radius: 8px;\n  border: 2px solid rgba(255,255,255,0.25);\n  background: rgba(255,255,255,0.15);\n  color: #fff;\n  font-size: .95rem;\n  outline: none;\n  transition: border-color .2s;\n  width: 100%;\n}\n\n#ec-app .ec-input::placeholder { color: rgba(255,255,255,0.55); }\n#ec-app .ec-input:focus { border-color: rgba(255,255,255,0.7); }\n\n#ec-app input[type=\"date\"]::-webkit-calendar-picker-indicator,\n#ec-app input[type=\"datetime-local\"]::-webkit-calendar-picker-indicator {\n  filter: invert(1) opacity(0.7);\n  cursor: pointer;\n}\n\n/* Preset chips */\n#ec-app .ec-presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 16px;\n}\n\n#ec-app .ec-chip {\n  background: rgba(255,255,255,0.15);\n  border: 1px solid rgba(255,255,255,0.3);\n  color: #fff;\n  border-radius: 20px;\n  padding: 4px 12px;\n  font-size: .82rem;\n  cursor: pointer;\n  transition: background .2s, transform .1s;\n  white-space: nowrap;\n}\n\n#ec-app .ec-chip:hover {\n  background: rgba(255,255,255,0.3);\n  transform: translateY(-1px);\n}\n\n/* Action buttons */\n#ec-app .ec-btn-row {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n}\n\n#ec-app .ec-btn {\n  padding: 10px 20px;\n  border-radius: 8px;\n  border: none;\n  font-size: .9rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: transform .15s, opacity .15s;\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n\n#ec-app .ec-btn:hover { transform: translateY(-1px); opacity: .9; }\n#ec-app .ec-btn:active { transform: translateY(0); }\n\n#ec-app .ec-btn-primary {\n  background: #fbbf24;\n  color: #0f172a;\n}\n\n#ec-app .ec-btn-secondary {\n  background: rgba(255,255,255,0.2);\n  color: #fff;\n  border: 1px solid rgba(255,255,255,0.35);\n}\n\n#ec-app .ec-btn-share {\n  background: rgba(255,255,255,0.15);\n  color: #fff;\n  border: 1px solid rgba(255,255,255,0.35);\n}\n\n/* --- Main Display --- */\n#ec-app .ec-display {\n  background: #fff;\n  border-radius: 16px;\n  padding: 32px 28px;\n  box-shadow: 0 2px 16px rgba(0,0,0,0.08);\n  margin-bottom: 24px;\n  text-align: center;\n  border: 1px solid #e2e8f0;\n  min-height: 180px;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n}\n\n#ec-app .ec-event-label {\n  font-size: 1.1rem;\n  font-weight: 700;\n  color: #0f766e;\n  margin-bottom: 8px;\n  letter-spacing: .01em;\n}\n\n#ec-app .ec-placeholder {\n  color: #94a3b8;\n  font-size: 1rem;\n}\n\n/* Countdown blocks */\n#ec-app .ec-blocks {\n  display: flex;\n  gap: 16px;\n  justify-content: center;\n  flex-wrap: wrap;\n  margin: 12px 0 16px;\n}\n\n#ec-app .ec-block {\n  background: linear-gradient(135deg, #f0fdfa, #ccfbf1);\n  border-radius: 12px;\n  padding: 16px 20px 12px;\n  min-width: 90px;\n  border: 1px solid #99f6e4;\n}\n\n#ec-app .ec-block-num {\n  font-size: 2.6rem;\n  font-weight: 800;\n  color: #0d9488;\n  line-height: 1;\n  font-variant-numeric: tabular-nums;\n  display: block;\n}\n\n#ec-app .ec-block-label {\n  font-size: .72rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: .08em;\n  color: #5eead4;\n  display: block;\n  margin-top: 4px;\n}\n\n/* Past date / zero state */\n#ec-app .ec-past-msg {\n  font-size: 1.8rem;\n  font-weight: 800;\n  color: #7c3aed;\n}\n\n#ec-app .ec-zero-msg {\n  font-size: 1.6rem;\n  font-weight: 800;\n  color: #f59e0b;\n  animation: ec-pulse 0.8s ease-in-out infinite alternate;\n}\n\n@keyframes ec-pulse {\n  from { transform: scale(1); }\n  to   { transform: scale(1.04); }\n}\n\n/* Progress bar */\n#ec-app .ec-progress-wrap {\n  width: 100%;\n  max-width: 420px;\n  margin-top: 4px;\n}\n\n#ec-app .ec-progress-labels {\n  display: flex;\n  justify-content: space-between;\n  font-size: .75rem;\n  color: #94a3b8;\n  margin-bottom: 4px;\n}\n\n#ec-app .ec-progress-track {\n  background: #e2e8f0;\n  border-radius: 99px;\n  height: 8px;\n  overflow: hidden;\n}\n\n#ec-app .ec-progress-bar {\n  height: 100%;\n  background: linear-gradient(90deg, #0d9488, #fbbf24);\n  border-radius: 99px;\n  transition: width .5s ease;\n}\n\n#ec-app .ec-progress-pct {\n  font-size: .78rem;\n  color: #64748b;\n  margin-top: 4px;\n}\n\n/* Share toast */\n#ec-app .ec-toast {\n  position: fixed;\n  bottom: 28px;\n  left: 50%;\n  transform: translateX(-50%) translateY(80px);\n  background: #0f172a;\n  color: #fff;\n  padding: 10px 20px;\n  border-radius: 8px;\n  font-size: .9rem;\n  font-weight: 600;\n  transition: transform .3s ease;\n  z-index: 9999;\n  pointer-events: none;\n}\n\n#ec-app .ec-toast.ec-toast-show {\n  transform: translateX(-50%) translateY(0);\n}\n\n/* --- List Panel --- */\n#ec-app .ec-list-panel {\n  background: #fff;\n  border-radius: 16px;\n  padding: 24px 24px 16px;\n  box-shadow: 0 2px 16px rgba(0,0,0,0.08);\n  border: 1px solid #e2e8f0;\n  margin-bottom: 24px;\n}\n\n#ec-app .ec-list-panel h3 {\n  margin: 0 0 16px;\n  font-size: 1rem;\n  font-weight: 700;\n  color: #0f766e;\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n\n#ec-app .ec-list-empty {\n  color: #94a3b8;\n  font-size: .9rem;\n  text-align: center;\n  padding: 12px 0;\n}\n\n#ec-app .ec-list {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n}\n\n#ec-app .ec-list-item {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  padding: 12px 14px;\n  border-radius: 10px;\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  transition: box-shadow .2s;\n}\n\n#ec-app .ec-list-item:hover {\n  box-shadow: 0 2px 8px rgba(0,0,0,0.08);\n}\n\n#ec-app .ec-list-item-name {\n  flex: 1;\n  font-weight: 600;\n  font-size: .9rem;\n  color: #1e293b;\n}\n\n#ec-app .ec-list-item-date {\n  font-size: .78rem;\n  color: #94a3b8;\n}\n\n#ec-app .ec-list-item-days {\n  font-weight: 800;\n  font-size: 1.05rem;\n  color: #0d9488;\n  white-space: nowrap;\n  min-width: 80px;\n  text-align: right;\n}\n\n#ec-app .ec-list-item-days.past { color: #7c3aed; }\n#ec-app .ec-list-item-days.today { color: #f59e0b; }\n\n#ec-app .ec-list-item-del {\n  background: none;\n  border: none;\n  cursor: pointer;\n  color: #cbd5e1;\n  font-size: 1rem;\n  padding: 2px 4px;\n  border-radius: 4px;\n  transition: color .2s;\n  line-height: 1;\n}\n\n#ec-app .ec-list-item-del:hover { color: #ef4444; }\n\n/* Confetti canvas */\n#ec-confetti-canvas {\n  position: fixed;\n  top: 0; left: 0;\n  width: 100%; height: 100%;\n  pointer-events: none;\n  z-index: 9998;\n}\n\n/* Responsive */\n@media (max-width: 520px) {\n  #ec-app .ec-block-num { font-size: 2rem; }\n  #ec-app .ec-block { min-width: 70px; padding: 12px 12px 10px; }\n  #ec-app .ec-blocks { gap: 10px; }\n  #ec-app .ec-panel { padding: 20px 16px 18px; }\n  #ec-app .ec-display { padding: 24px 16px; }\n}\n\u003c/style\u003e\n\u003c!-- Input Panel --\u003e\n\u003cdiv class=\"ec-panel\"\u003e\n  \u003ch2\u003e\u0026#128197; Event Countdown\u003c/h2\u003e\n  \u003cdiv class=\"ec-form-row\"\u003e\n    \u003cdiv class=\"ec-form-group\" style=\"flex:2;min-width:200px;\"\u003e\n      \u003clabel for=\"ec-name\"\u003eEvent Name\u003c/label\u003e\n      \u003cinput type=\"text\" id=\"ec-name\" class=\"ec-input\" placeholder=\"e.g. Project Launch, Vacation...\" maxlength=\"60\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ec-form-group\"\u003e\n      \u003clabel for=\"ec-date\"\u003eTarget Date \u0026amp; Time\u003c/label\u003e\n      \u003cinput type=\"datetime-local\" id=\"ec-date\" class=\"ec-input\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv style=\"margin-bottom:14px;\"\u003e\n    \u003cdiv style=\"font-size:.8rem;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:#ccfbf1;margin-bottom:8px;\"\u003eQuick Presets\u003c/div\u003e\n    \u003cdiv class=\"ec-presets\" id=\"ec-presets\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ec-btn-row\"\u003e\n    \u003cbutton class=\"ec-btn ec-btn-primary\" id=\"ec-start-btn\"\u003e\u0026#9654; Start Countdown\u003c/button\u003e\n    \u003cbutton class=\"ec-btn ec-btn-secondary\" id=\"ec-add-btn\"\u003e+ Add to List\u003c/button\u003e\n    \u003cbutton class=\"ec-btn ec-btn-share\" id=\"ec-share-btn\"\u003e\u0026#128279; Share Link\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Main Display --\u003e\n\u003cdiv class=\"ec-display\" id=\"ec-display\"\u003e\n  \u003cdiv class=\"ec-placeholder\"\u003eSet an event above and click \"Start Countdown\" to begin.\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- List Panel --\u003e\n\u003cdiv class=\"ec-list-panel\"\u003e\n  \u003ch3\u003e\u0026#128203; My Countdowns\u003c/h3\u003e\n  \u003cul class=\"ec-list\" id=\"ec-list\"\u003e\n    \u003cli class=\"ec-list-empty\" id=\"ec-list-empty\"\u003eNo countdowns added yet. Configure an event and click \"+ Add to List\".\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003c!-- Confetti canvas --\u003e\n\u003cp\u003e\u003ccanvas id=\"ec-confetti-canvas\" style=\"display:none;\"\u003e\u003c/canvas\u003e\u003c/p\u003e","title":"Event Countdown - Days Until Calculator"},{"content":" Add Transaction Date Type + Income - Expense Description Amount ($) Category Food Transport Housing Entertainment Health Education Other Add Transaction Monthly Overview Income $0.00 Expenses $0.00 Balance $0.00 Transactions Month Category All Categories Food Transport Housing Entertainment Health Education Other Type All Income Expense Export CSV Date Description Category Type Amount Edit Transaction Date Type + Income - Expense Description Amount ($) Category Food Transport Housing Entertainment Health Education Other Cancel Save Changes Related Tools Budget Planner — Plan your monthly budget with the 50/30/20 rule Split Bill Calculator — Split costs fairly among a group Related Articles 7 Best Budgeting Apps in 2026 (Free \u0026amp; Paid Compared) Best Budgeting Apps 2026: Full Comparison 10 Ways ChatGPT Can Save You $500/Month ","permalink":"https://productivity-works.com/tools/expense-tracker/","summary":"\u003cdiv id=\"et-app\"\u003e\n\u003cstyle\u003e\n#et-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  max-width: 760px;\n  margin: 0 auto;\n  color: #1e293b;\n  box-sizing: border-box;\n}\n#et-app *, #et-app *::before, #et-app *::after {\n  box-sizing: border-box;\n}\n#et-app h2 {\n  font-size: 1.05rem;\n  font-weight: 700;\n  color: #334155;\n  margin: 24px 0 10px;\n  padding-bottom: 6px;\n  border-bottom: 2px solid #e2e8f0;\n}\n#et-app .et-card {\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 18px 20px;\n  margin-bottom: 16px;\n}\n#et-app label {\n  display: block;\n  font-size: 12px;\n  font-weight: 600;\n  color: #64748b;\n  margin-bottom: 4px;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#et-app input, #et-app select, #et-app textarea {\n  width: 100%;\n  padding: 9px 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  font-size: 14px;\n  color: #1e293b;\n  background: #fff;\n  outline: none;\n  transition: border-color 0.15s;\n}\n#et-app input:focus, #et-app select:focus {\n  border-color: #3b82f6;\n}\n#et-app .et-grid {\n  display: grid;\n  gap: 12px;\n}\n#et-app .et-grid-2 { grid-template-columns: 1fr 1fr; }\n#et-app .et-grid-3 { grid-template-columns: 1fr 1fr 1fr; }\n#et-app .et-grid-4 { grid-template-columns: 1fr 1fr 1fr 1fr; }\n@media (max-width: 560px) {\n  #et-app .et-grid-2,\n  #et-app .et-grid-3,\n  #et-app .et-grid-4 { grid-template-columns: 1fr; }\n}\n#et-app .et-type-toggle {\n  display: flex;\n  gap: 0;\n  border-radius: 8px;\n  overflow: hidden;\n  border: 1.5px solid #cbd5e1;\n  width: 100%;\n}\n#et-app .et-type-toggle button {\n  flex: 1;\n  padding: 9px;\n  border: none;\n  background: #fff;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s;\n  color: #64748b;\n}\n#et-app .et-type-toggle button.active-income {\n  background: #22c55e;\n  color: #fff;\n}\n#et-app .et-type-toggle button.active-expense {\n  background: #ef4444;\n  color: #fff;\n}\n#et-app .et-btn {\n  display: inline-block;\n  padding: 10px 22px;\n  border: none;\n  border-radius: 8px;\n  font-size: 14px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: opacity 0.15s;\n}\n#et-app .et-btn:hover { opacity: 0.85; }\n#et-app .et-btn-primary { background: #3b82f6; color: #fff; }\n#et-app .et-btn-sm {\n  padding: 5px 12px;\n  font-size: 12px;\n  border-radius: 6px;\n  font-weight: 600;\n  border: none;\n  cursor: pointer;\n}\n#et-app .et-btn-delete { background: #fee2e2; color: #dc2626; }\n#et-app .et-btn-edit { background: #eff6ff; color: #2563eb; }\n#et-app .et-btn-export { background: #f0fdf4; color: #16a34a; border: 1.5px solid #bbf7d0; }\n\n/* Summary bar */\n#et-app .et-summary-bar {\n  display: grid;\n  grid-template-columns: 1fr 1fr 1fr;\n  gap: 12px;\n  margin-bottom: 16px;\n}\n#et-app .et-summary-card {\n  border-radius: 10px;\n  padding: 14px 16px;\n  text-align: center;\n}\n#et-app .et-summary-card .et-s-label {\n  font-size: 11px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 4px;\n}\n#et-app .et-summary-card .et-s-value {\n  font-size: 1.35rem;\n  font-weight: 800;\n}\n#et-app .et-income-card { background: #f0fdf4; border: 1.5px solid #bbf7d0; }\n#et-app .et-income-card .et-s-label { color: #16a34a; }\n#et-app .et-income-card .et-s-value { color: #15803d; }\n#et-app .et-expense-card { background: #fef2f2; border: 1.5px solid #fecaca; }\n#et-app .et-expense-card .et-s-label { color: #dc2626; }\n#et-app .et-expense-card .et-s-value { color: #b91c1c; }\n#et-app .et-balance-card { background: #eff6ff; border: 1.5px solid #bfdbfe; }\n#et-app .et-balance-card .et-s-label { color: #2563eb; }\n#et-app .et-balance-card .et-s-value { color: #1d4ed8; }\n#et-app .et-balance-neg { background: #fef9c3; border-color: #fde68a; }\n#et-app .et-balance-neg .et-s-label { color: #b45309; }\n#et-app .et-balance-neg .et-s-value { color: #92400e; }\n\n/* Filter row */\n#et-app .et-filter-row {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n  align-items: flex-end;\n  margin-bottom: 14px;\n}\n#et-app .et-filter-row \u003e div { flex: 1; min-width: 130px; }\n\n/* Table */\n#et-app .et-table-wrap { overflow-x: auto; }\n#et-app table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 13.5px;\n}\n#et-app thead th {\n  background: #f1f5f9;\n  padding: 9px 12px;\n  text-align: left;\n  font-size: 11px;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  color: #64748b;\n  font-weight: 700;\n  white-space: nowrap;\n}\n#et-app tbody tr {\n  border-bottom: 1px solid #f1f5f9;\n  transition: background 0.1s;\n}\n#et-app tbody tr:hover { background: #f8fafc; }\n#et-app tbody td { padding: 9px 12px; vertical-align: middle; }\n#et-app .et-badge {\n  display: inline-block;\n  padding: 2px 9px;\n  border-radius: 99px;\n  font-size: 11px;\n  font-weight: 700;\n}\n#et-app .et-badge-income { background: #dcfce7; color: #16a34a; }\n#et-app .et-badge-expense { background: #fee2e2; color: #dc2626; }\n#et-app .et-amount-income { color: #16a34a; font-weight: 700; }\n#et-app .et-amount-expense { color: #dc2626; font-weight: 700; }\n#et-app .et-empty {\n  text-align: center;\n  padding: 32px;\n  color: #94a3b8;\n  font-size: 14px;\n}\n\n/* Chart */\n#et-app .et-chart-wrap {\n  display: flex;\n  gap: 24px;\n  align-items: center;\n  flex-wrap: wrap;\n}\n#et-app canvas { display: block; }\n#et-app .et-legend { flex: 1; min-width: 180px; }\n#et-app .et-legend-item {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin-bottom: 7px;\n  font-size: 13px;\n}\n#et-app .et-legend-dot {\n  width: 12px;\n  height: 12px;\n  border-radius: 3px;\n  flex-shrink: 0;\n}\n#et-app .et-legend-label { flex: 1; color: #475569; }\n#et-app .et-legend-val { font-weight: 700; color: #1e293b; }\n\n/* Category breakdown */\n#et-app .et-cat-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-bottom: 8px;\n}\n#et-app .et-cat-bar-bg {\n  flex: 1;\n  height: 8px;\n  background: #e2e8f0;\n  border-radius: 99px;\n  overflow: hidden;\n}\n#et-app .et-cat-bar-fill {\n  height: 100%;\n  border-radius: 99px;\n  transition: width 0.3s;\n}\n#et-app .et-cat-name { font-size: 13px; color: #475569; min-width: 90px; }\n#et-app .et-cat-amt { font-size: 13px; font-weight: 700; color: #1e293b; min-width: 70px; text-align: right; }\n\n/* Edit modal */\n#et-app .et-modal-overlay {\n  display: none;\n  position: fixed;\n  inset: 0;\n  background: rgba(0,0,0,0.4);\n  z-index: 9999;\n  align-items: center;\n  justify-content: center;\n}\n#et-app .et-modal-overlay.open { display: flex; }\n#et-app .et-modal {\n  background: #fff;\n  border-radius: 12px;\n  padding: 24px;\n  width: 100%;\n  max-width: 460px;\n  margin: 16px;\n  box-shadow: 0 20px 60px rgba(0,0,0,0.2);\n}\n#et-app .et-modal h3 { margin: 0 0 16px; font-size: 1.1rem; color: #1e293b; }\n#et-app .et-modal-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 16px; }\n#et-app .et-btn-secondary { background: #f1f5f9; color: #475569; }\n\u003c/style\u003e\n\u003ch2\u003eAdd Transaction\u003c/h2\u003e\n\u003cdiv class=\"et-card\"\u003e\n  \u003cdiv class=\"et-grid et-grid-2\" style=\"margin-bottom:12px;\"\u003e\n    \u003cdiv\u003e\n      \u003clabel\u003eDate\u003c/label\u003e\n      \u003cinput type=\"date\" id=\"et-date\"\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003clabel\u003eType\u003c/label\u003e\n      \u003cdiv class=\"et-type-toggle\"\u003e\n        \u003cbutton id=\"et-btn-income\" class=\"active-income\" onclick=\"etSetType('income')\"\u003e+ Income\u003c/button\u003e\n        \u003cbutton id=\"et-btn-expense\" onclick=\"etSetType('expense')\"\u003e- Expense\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"et-grid et-grid-3\" style=\"margin-bottom:12px;\"\u003e\n    \u003cdiv style=\"grid-column:span 2;\"\u003e\n      \u003clabel\u003eDescription\u003c/label\u003e\n      \u003cinput type=\"text\" id=\"et-desc\" placeholder=\"e.g. Grocery run\"\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003clabel\u003eAmount ($)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"et-amount\" placeholder=\"0.00\" min=\"0\" step=\"0.01\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"et-grid et-grid-2\" style=\"margin-bottom:14px;\"\u003e\n    \u003cdiv\u003e\n      \u003clabel\u003eCategory\u003c/label\u003e\n      \u003cselect id=\"et-category\"\u003e\n        \u003coption value=\"Food\"\u003eFood\u003c/option\u003e\n        \u003coption value=\"Transport\"\u003eTransport\u003c/option\u003e\n        \u003coption value=\"Housing\"\u003eHousing\u003c/option\u003e\n        \u003coption value=\"Entertainment\"\u003eEntertainment\u003c/option\u003e\n        \u003coption value=\"Health\"\u003eHealth\u003c/option\u003e\n        \u003coption value=\"Education\"\u003eEducation\u003c/option\u003e\n        \u003coption value=\"Other\"\u003eOther\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n    \u003cdiv style=\"display:flex;align-items:flex-end;\"\u003e\n      \u003cbutton class=\"et-btn et-btn-primary\" onclick=\"etAddTransaction()\" style=\"width:100%;\"\u003eAdd Transaction\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003ch2\u003eMonthly Overview\u003c/h2\u003e\n\u003cdiv class=\"et-summary-bar\"\u003e\n  \u003cdiv class=\"et-summary-card et-income-card\"\u003e\n    \u003cdiv class=\"et-s-label\"\u003eIncome\u003c/div\u003e\n    \u003cdiv class=\"et-s-value\" id=\"et-total-income\"\u003e$0.00\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"et-summary-card et-expense-card\"\u003e\n    \u003cdiv class=\"et-s-label\"\u003eExpenses\u003c/div\u003e\n    \u003cdiv class=\"et-s-value\" id=\"et-total-expense\"\u003e$0.00\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"et-summary-card et-balance-card\" id=\"et-balance-card\"\u003e\n    \u003cdiv class=\"et-s-label\"\u003eBalance\u003c/div\u003e\n    \u003cdiv class=\"et-s-value\" id=\"et-balance\"\u003e$0.00\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"et-card\" id=\"et-chart-section\" style=\"display:none;\"\u003e\n  \u003cdiv class=\"et-chart-wrap\"\u003e\n    \u003ccanvas id=\"et-donut\" width=\"160\" height=\"160\"\u003e\u003c/canvas\u003e\n    \u003cdiv class=\"et-legend\" id=\"et-legend\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv style=\"margin-top:18px;\" id=\"et-cat-breakdown\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch2\u003eTransactions\u003c/h2\u003e\n\u003cdiv class=\"et-filter-row\"\u003e\n  \u003cdiv\u003e\n    \u003clabel\u003eMonth\u003c/label\u003e\n    \u003cinput type=\"month\" id=\"et-filter-month\"\u003e\n  \u003c/div\u003e\n  \u003cdiv\u003e\n    \u003clabel\u003eCategory\u003c/label\u003e\n    \u003cselect id=\"et-filter-cat\"\u003e\n      \u003coption value=\"\"\u003eAll Categories\u003c/option\u003e\n      \u003coption value=\"Food\"\u003eFood\u003c/option\u003e\n      \u003coption value=\"Transport\"\u003eTransport\u003c/option\u003e\n      \u003coption value=\"Housing\"\u003eHousing\u003c/option\u003e\n      \u003coption value=\"Entertainment\"\u003eEntertainment\u003c/option\u003e\n      \u003coption value=\"Health\"\u003eHealth\u003c/option\u003e\n      \u003coption value=\"Education\"\u003eEducation\u003c/option\u003e\n      \u003coption value=\"Other\"\u003eOther\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n  \u003cdiv\u003e\n    \u003clabel\u003eType\u003c/label\u003e\n    \u003cselect id=\"et-filter-type\"\u003e\n      \u003coption value=\"\"\u003eAll\u003c/option\u003e\n      \u003coption value=\"income\"\u003eIncome\u003c/option\u003e\n      \u003coption value=\"expense\"\u003eExpense\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n  \u003cdiv style=\"display:flex;align-items:flex-end;gap:8px;\"\u003e\n    \u003cbutton class=\"et-btn et-btn-sm et-btn-export\" onclick=\"etExportCSV()\"\u003eExport CSV\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"et-table-wrap et-card\" style=\"padding:0;overflow:hidden;\"\u003e\n  \u003ctable\u003e\n    \u003cthead\u003e\n      \u003ctr\u003e\n        \u003cth\u003eDate\u003c/th\u003e\n        \u003cth\u003eDescription\u003c/th\u003e\n        \u003cth\u003eCategory\u003c/th\u003e\n        \u003cth\u003eType\u003c/th\u003e\n        \u003cth style=\"text-align:right;\"\u003eAmount\u003c/th\u003e\n        \u003cth\u003e\u003c/th\u003e\n      \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody id=\"et-tbody\"\u003e\u003c/tbody\u003e\n  \u003c/table\u003e\n\u003c/div\u003e\n\u003c!-- Edit Modal --\u003e\n\u003cdiv class=\"et-modal-overlay\" id=\"et-modal\"\u003e\n  \u003cdiv class=\"et-modal\"\u003e\n    \u003ch3\u003eEdit Transaction\u003c/h3\u003e\n    \u003cdiv class=\"et-grid et-grid-2\" style=\"margin-bottom:12px;\"\u003e\n      \u003cdiv\u003e\n        \u003clabel\u003eDate\u003c/label\u003e\n        \u003cinput type=\"date\" id=\"et-edit-date\"\u003e\n      \u003c/div\u003e\n      \u003cdiv\u003e\n        \u003clabel\u003eType\u003c/label\u003e\n        \u003cdiv class=\"et-type-toggle\"\u003e\n          \u003cbutton id=\"et-edit-btn-income\" class=\"active-income\" onclick=\"etEditSetType('income')\"\u003e+ Income\u003c/button\u003e\n          \u003cbutton id=\"et-edit-btn-expense\" onclick=\"etEditSetType('expense')\"\u003e- Expense\u003c/button\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"et-grid et-grid-2\" style=\"margin-bottom:12px;\"\u003e\n      \u003cdiv style=\"grid-column:span 2;\"\u003e\n        \u003clabel\u003eDescription\u003c/label\u003e\n        \u003cinput type=\"text\" id=\"et-edit-desc\"\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"et-grid et-grid-2\" style=\"margin-bottom:12px;\"\u003e\n      \u003cdiv\u003e\n        \u003clabel\u003eAmount ($)\u003c/label\u003e\n        \u003cinput type=\"number\" id=\"et-edit-amount\" min=\"0\" step=\"0.01\"\u003e\n      \u003c/div\u003e\n      \u003cdiv\u003e\n        \u003clabel\u003eCategory\u003c/label\u003e\n        \u003cselect id=\"et-edit-category\"\u003e\n          \u003coption value=\"Food\"\u003eFood\u003c/option\u003e\n          \u003coption value=\"Transport\"\u003eTransport\u003c/option\u003e\n          \u003coption value=\"Housing\"\u003eHousing\u003c/option\u003e\n          \u003coption value=\"Entertainment\"\u003eEntertainment\u003c/option\u003e\n          \u003coption value=\"Health\"\u003eHealth\u003c/option\u003e\n          \u003coption value=\"Education\"\u003eEducation\u003c/option\u003e\n          \u003coption value=\"Other\"\u003eOther\u003c/option\u003e\n        \u003c/select\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"et-modal-actions\"\u003e\n      \u003cbutton class=\"et-btn et-btn-sm et-btn-secondary\" onclick=\"etCloseModal()\"\u003eCancel\u003c/button\u003e\n      \u003cbutton class=\"et-btn et-btn-sm et-btn-primary\" onclick=\"etSaveEdit()\"\u003eSave Changes\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  'use strict';\n\n  var STORAGE_KEY = 'et_transactions_v1';\n  var COLORS = {\n    Food:          '#f97316',\n    Transport:     '#3b82f6',\n    Housing:       '#8b5cf6',\n    Entertainment: '#ec4899',\n    Health:        '#10b981',\n    Education:     '#f59e0b',\n    Other:         '#94a3b8'\n  };\n\n  var transactions = [];\n  var currentType = 'expense';\n  var editId = null;\n  var editType = 'expense';\n\n  // Init\n  (function init() {\n    var stored = localStorage.getItem(STORAGE_KEY);\n    if (stored) {\n      try { transactions = JSON.parse(stored); } catch (e) { transactions = []; }\n    }\n    // Default date to today\n    var today = new Date().toISOString().slice(0, 10);\n    document.getElementById('et-date').value = today;\n    // Default filter to current month\n    document.getElementById('et-filter-month').value = today.slice(0, 7);\n\n    // Listeners\n    document.getElementById('et-filter-month').addEventListener('change', etRender);\n    document.getElementById('et-filter-cat').addEventListener('change', etRender);\n    document.getElementById('et-filter-type').addEventListener('change', etRender);\n\n    etRender();\n  })();\n\n  function etSave() {\n    localStorage.setItem(STORAGE_KEY, JSON.stringify(transactions));\n  }\n\n  function etSetType(type) {\n    currentType = type;\n    document.getElementById('et-btn-income').className = type === 'income' ? 'active-income' : '';\n    document.getElementById('et-btn-expense').className = type === 'expense' ? 'active-expense' : '';\n  }\n\n  function etEditSetType(type) {\n    editType = type;\n    document.getElementById('et-edit-btn-income').className = type === 'income' ? 'active-income' : '';\n    document.getElementById('et-edit-btn-expense').className = type === 'expense' ? 'active-expense' : '';\n  }\n\n  function etAddTransaction() {\n    var date = document.getElementById('et-date').value;\n    var desc = document.getElementById('et-desc').value.trim();\n    var amount = parseFloat(document.getElementById('et-amount').value);\n    var category = document.getElementById('et-category').value;\n\n    if (!date) { alert('Please select a date.'); return; }\n    if (!desc) { alert('Please enter a description.'); return; }\n    if (isNaN(amount) || amount \u003c= 0) { alert('Please enter a valid amount greater than 0.'); return; }\n\n    transactions.push({\n      id: Date.now() + Math.random(),\n      date: date,\n      desc: desc,\n      amount: amount,\n      category: category,\n      type: currentType\n    });\n\n    etSave();\n\n    // Reset form\n    document.getElementById('et-desc').value = '';\n    document.getElementById('et-amount').value = '';\n\n    etRender();\n  }\n\n  function etDeleteTransaction(id) {\n    transactions = transactions.filter(function (t) { return t.id !== id; });\n    etSave();\n    etRender();\n  }\n\n  function etOpenEdit(id) {\n    var t = transactions.find(function (x) { return x.id === id; });\n    if (!t) return;\n    editId = id;\n    document.getElementById('et-edit-date').value = t.date;\n    document.getElementById('et-edit-desc').value = t.desc;\n    document.getElementById('et-edit-amount').value = t.amount;\n    document.getElementById('et-edit-category').value = t.category;\n    etEditSetType(t.type);\n    document.getElementById('et-modal').classList.add('open');\n  }\n\n  function etCloseModal() {\n    document.getElementById('et-modal').classList.remove('open');\n    editId = null;\n  }\n\n  function etSaveEdit() {\n    if (editId === null) return;\n    var date = document.getElementById('et-edit-date').value;\n    var desc = document.getElementById('et-edit-desc').value.trim();\n    var amount = parseFloat(document.getElementById('et-edit-amount').value);\n    var category = document.getElementById('et-edit-category').value;\n\n    if (!date || !desc || isNaN(amount) || amount \u003c= 0) { alert('Please fill in all fields correctly.'); return; }\n\n    transactions = transactions.map(function (t) {\n      if (t.id === editId) {\n        return { id: t.id, date: date, desc: desc, amount: amount, category: category, type: editType };\n      }\n      return t;\n    });\n    etSave();\n    etCloseModal();\n    etRender();\n  }\n\n  function etGetFiltered() {\n    var month = document.getElementById('et-filter-month').value;\n    var cat = document.getElementById('et-filter-cat').value;\n    var type = document.getElementById('et-filter-type').value;\n\n    return transactions.filter(function (t) {\n      if (month \u0026\u0026 t.date.slice(0, 7) !== month) return false;\n      if (cat \u0026\u0026 t.category !== cat) return false;\n      if (type \u0026\u0026 t.type !== type) return false;\n      return true;\n    }).sort(function (a, b) { return b.date.localeCompare(a.date); });\n  }\n\n  function fmt(n) {\n    return '$' + n.toFixed(2).replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',');\n  }\n\n  function etRender() {\n    var filtered = etGetFiltered();\n\n    // Summary (based on filtered month, all types/cats)\n    var month = document.getElementById('et-filter-month').value;\n    var monthTxns = month\n      ? transactions.filter(function (t) { return t.date.slice(0, 7) === month; })\n      : transactions;\n\n    var totalIncome = monthTxns.filter(function (t) { return t.type === 'income'; })\n      .reduce(function (s, t) { return s + t.amount; }, 0);\n    var totalExpense = monthTxns.filter(function (t) { return t.type === 'expense'; })\n      .reduce(function (s, t) { return s + t.amount; }, 0);\n    var balance = totalIncome - totalExpense;\n\n    document.getElementById('et-total-income').textContent = fmt(totalIncome);\n    document.getElementById('et-total-expense').textContent = fmt(totalExpense);\n    document.getElementById('et-balance').textContent = fmt(balance);\n\n    var balCard = document.getElementById('et-balance-card');\n    balCard.className = 'et-summary-card ' + (balance \u003c 0 ? 'et-balance-neg' : 'et-balance-card');\n\n    // Chart \u0026 breakdown (expenses by category in current month filter)\n    var expenseTxns = monthTxns.filter(function (t) { return t.type === 'expense'; });\n    etRenderChart(expenseTxns);\n\n    // Table\n    var tbody = document.getElementById('et-tbody');\n    if (filtered.length === 0) {\n      tbody.innerHTML = '\u003ctr\u003e\u003ctd colspan=\"6\" class=\"et-empty\"\u003eNo transactions found. Add one above!\u003c/td\u003e\u003c/tr\u003e';\n      return;\n    }\n\n    var html = '';\n    filtered.forEach(function (t) {\n      html += '\u003ctr\u003e';\n      html += '\u003ctd\u003e' + t.date + '\u003c/td\u003e';\n      html += '\u003ctd\u003e' + escHtml(t.desc) + '\u003c/td\u003e';\n      html += '\u003ctd\u003e\u003cspan class=\"et-badge\" style=\"background:' + hexBg(COLORS[t.category] || '#94a3b8') + ';color:' + (COLORS[t.category] || '#94a3b8') + ';\"\u003e' + t.category + '\u003c/span\u003e\u003c/td\u003e';\n      html += '\u003ctd\u003e\u003cspan class=\"et-badge ' + (t.type === 'income' ? 'et-badge-income' : 'et-badge-expense') + '\"\u003e' + (t.type === 'income' ? 'Income' : 'Expense') + '\u003c/span\u003e\u003c/td\u003e';\n      html += '\u003ctd style=\"text-align:right;\" class=\"' + (t.type === 'income' ? 'et-amount-income' : 'et-amount-expense') + '\"\u003e' + (t.type === 'income' ? '+' : '-') + fmt(t.amount) + '\u003c/td\u003e';\n      html += '\u003ctd style=\"white-space:nowrap;text-align:right;\"\u003e';\n      html += '\u003cbutton class=\"et-btn et-btn-sm et-btn-edit\" onclick=\"etOpenEdit(' + t.id + ')\" style=\"margin-right:4px;\"\u003eEdit\u003c/button\u003e';\n      html += '\u003cbutton class=\"et-btn et-btn-sm et-btn-delete\" onclick=\"etDeleteTransaction(' + t.id + ')\"\u003eDelete\u003c/button\u003e';\n      html += '\u003c/td\u003e';\n      html += '\u003c/tr\u003e';\n    });\n    tbody.innerHTML = html;\n  }\n\n  function hexBg(hex) {\n    // Return light version of color for badge background\n    var map = {\n      '#f97316': '#fff7ed',\n      '#3b82f6': '#eff6ff',\n      '#8b5cf6': '#f5f3ff',\n      '#ec4899': '#fdf2f8',\n      '#10b981': '#ecfdf5',\n      '#f59e0b': '#fffbeb',\n      '#94a3b8': '#f1f5f9'\n    };\n    return map[hex] || '#f1f5f9';\n  }\n\n  function etRenderChart(expenseTxns) {\n    var section = document.getElementById('et-chart-section');\n    if (expenseTxns.length === 0) { section.style.display = 'none'; return; }\n    section.style.display = '';\n\n    // Aggregate by category\n    var catMap = {};\n    expenseTxns.forEach(function (t) {\n      catMap[t.category] = (catMap[t.category] || 0) + t.amount;\n    });\n    var total = Object.values(catMap).reduce(function (s, v) { return s + v; }, 0);\n    var entries = Object.entries(catMap).sort(function (a, b) { return b[1] - a[1]; });\n\n    // Draw donut\n    var canvas = document.getElementById('et-donut');\n    var ctx = canvas.getContext('2d');\n    var cx = 80, cy = 80, r = 68, innerR = 44;\n    ctx.clearRect(0, 0, 160, 160);\n\n    var startAngle = -Math.PI / 2;\n    entries.forEach(function (entry) {\n      var slice = (entry[1] / total) * 2 * Math.PI;\n      ctx.beginPath();\n      ctx.moveTo(cx, cy);\n      ctx.arc(cx, cy, r, startAngle, startAngle + slice);\n      ctx.closePath();\n      ctx.fillStyle = COLORS[entry[0]] || '#94a3b8';\n      ctx.fill();\n      startAngle += slice;\n    });\n\n    // Inner circle (hole)\n    ctx.beginPath();\n    ctx.arc(cx, cy, innerR, 0, 2 * Math.PI);\n    ctx.fillStyle = '#fff';\n    ctx.fill();\n\n    // Center text\n    ctx.fillStyle = '#1e293b';\n    ctx.font = 'bold 13px system-ui';\n    ctx.textAlign = 'center';\n    ctx.textBaseline = 'middle';\n    ctx.fillText(fmt(total), cx, cy);\n\n    // Legend\n    var legendHtml = '';\n    entries.forEach(function (entry) {\n      var pct = ((entry[1] / total) * 100).toFixed(1);\n      legendHtml += '\u003cdiv class=\"et-legend-item\"\u003e';\n      legendHtml += '\u003cdiv class=\"et-legend-dot\" style=\"background:' + (COLORS[entry[0]] || '#94a3b8') + ';\"\u003e\u003c/div\u003e';\n      legendHtml += '\u003cspan class=\"et-legend-label\"\u003e' + entry[0] + ' \u003cspan style=\"color:#94a3b8;font-size:11px;\"\u003e(' + pct + '%)\u003c/span\u003e\u003c/span\u003e';\n      legendHtml += '\u003cspan class=\"et-legend-val\"\u003e' + fmt(entry[1]) + '\u003c/span\u003e';\n      legendHtml += '\u003c/div\u003e';\n    });\n    document.getElementById('et-legend').innerHTML = legendHtml;\n\n    // Category bar breakdown\n    var breakHtml = '\u003cdiv style=\"font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:0.04em;color:#64748b;margin-bottom:10px;\"\u003eBreakdown by Category\u003c/div\u003e';\n    entries.forEach(function (entry) {\n      var pct = (entry[1] / total) * 100;\n      breakHtml += '\u003cdiv class=\"et-cat-row\"\u003e';\n      breakHtml += '\u003cspan class=\"et-cat-name\"\u003e' + entry[0] + '\u003c/span\u003e';\n      breakHtml += '\u003cdiv class=\"et-cat-bar-bg\"\u003e\u003cdiv class=\"et-cat-bar-fill\" style=\"width:' + pct.toFixed(1) + '%;background:' + (COLORS[entry[0]] || '#94a3b8') + ';\"\u003e\u003c/div\u003e\u003c/div\u003e';\n      breakHtml += '\u003cspan class=\"et-cat-amt\"\u003e' + fmt(entry[1]) + '\u003c/span\u003e';\n      breakHtml += '\u003c/div\u003e';\n    });\n    document.getElementById('et-cat-breakdown').innerHTML = breakHtml;\n  }\n\n  function etExportCSV() {\n    var filtered = etGetFiltered();\n    if (filtered.length === 0) { alert('No transactions to export.'); return; }\n    var rows = [['Date', 'Description', 'Category', 'Type', 'Amount']];\n    filtered.forEach(function (t) {\n      rows.push([t.date, '\"' + t.desc.replace(/\"/g, '\"\"') + '\"', t.category, t.type, t.amount.toFixed(2)]);\n    });\n    var csv = rows.map(function (r) { return r.join(','); }).join('\\n');\n    var blob = new Blob([csv], { type: 'text/csv' });\n    var url = URL.createObjectURL(blob);\n    var a = document.createElement('a');\n    a.href = url;\n    a.download = 'expenses.csv';\n    a.click();\n    URL.revokeObjectURL(url);\n  }\n\n  function escHtml(str) {\n    return String(str)\n      .replace(/\u0026/g, '\u0026amp;')\n      .replace(/\u003c/g, '\u0026lt;')\n      .replace(/\u003e/g, '\u0026gt;')\n      .replace(/\"/g, '\u0026quot;');\n  }\n\n  // Expose to global scope for inline onclick handlers\n  window.etSetType = etSetType;\n  window.etEditSetType = etEditSetType;\n  window.etAddTransaction = etAddTransaction;\n  window.etDeleteTransaction = etDeleteTransaction;\n  window.etOpenEdit = etOpenEdit;\n  window.etCloseModal = etCloseModal;\n  window.etSaveEdit = etSaveEdit;\n  window.etExportCSV = etExportCSV;\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://productivity-works.com/tools/budget-planner/\"\u003eBudget Planner\u003c/a\u003e\n — Plan your monthly budget with the 50/30/20 rule\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://productivity-works.com/tools/split-bill-calculator/\"\u003eSplit Bill Calculator\u003c/a\u003e\n — Split costs fairly among a group\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"related-articles\"\u003eRelated Articles\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://productivity-works.com/posts/best-budgeting-apps-2026/\"\u003e7 Best Budgeting Apps in 2026 (Free \u0026amp; Paid Compared)\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://productivity-works.com/posts/best-budgeting-apps-2026-comparison/\"\u003eBest Budgeting Apps 2026: Full Comparison\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://productivity-works.com/posts/chatgpt-save-money/\"\u003e10 Ways ChatGPT Can Save You $500/Month\u003c/a\u003e\n\u003c/li\u003e\n\u003c/ul\u003e","title":"Expense Tracker"},{"content":" 1. Select Fields Custom column names (optional — overrides defaults) Select fields above to set custom names. 2. Record Count rows (max 1000) 3. Reproducible Seed Use seed Same seed = same data every time. 4. Export Format JSON CSV SQL Table: \u0026#9654; Generate Check All Uncheck All Select fields and click Generate. Preview Output \u0026#8659; Download Copy Format generated JSON output: JSON Formatter Generate placeholder text: Lorem Ipsum Generator Convert your CSV data: CSV to JSON ","permalink":"https://productivity-works.com/tools/fake-data-generator/","summary":"\u003cdiv id=\"fd-app\"\u003e\n\u003cstyle\u003e\n#fd-app *,\n#fd-app *::before,\n#fd-app *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n#fd-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 14px;\n  color: #1e293b;\n  line-height: 1.5;\n}\n\n#fd-app h2 {\n  font-size: 16px;\n  font-weight: 700;\n  color: #0f172a;\n  margin-bottom: 10px;\n}\n\n#fd-app .fd-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 14px;\n  margin-bottom: 14px;\n}\n\n#fd-app .fd-col {\n  flex: 1 1 240px;\n  min-width: 0;\n}\n\n#fd-app .fd-card {\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 16px;\n}\n\n#fd-app .fd-label {\n  display: block;\n  font-size: 12px;\n  font-weight: 600;\n  color: #475569;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  margin-bottom: 6px;\n}\n\n#fd-app .fd-input {\n  width: 100%;\n  padding: 7px 10px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 13px;\n  color: #1e293b;\n  background: #fff;\n  outline: none;\n  transition: border-color 0.15s;\n}\n#fd-app .fd-input:focus { border-color: #2563eb; }\n\n#fd-app .fd-select {\n  width: 100%;\n  padding: 7px 10px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 13px;\n  color: #1e293b;\n  background: #fff;\n  cursor: pointer;\n  outline: none;\n}\n#fd-app .fd-select:focus { border-color: #2563eb; }\n\n#fd-app .fd-fields-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));\n  gap: 8px;\n}\n\n#fd-app .fd-field-item {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 8px 10px;\n  background: #fff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 8px;\n  cursor: pointer;\n  user-select: none;\n  transition: border-color 0.15s, background 0.15s;\n}\n#fd-app .fd-field-item:hover { border-color: #93c5fd; background: #eff6ff; }\n#fd-app .fd-field-item.active { border-color: #2563eb; background: #dbeafe; }\n\n#fd-app .fd-field-item input[type=\"checkbox\"] {\n  width: 15px;\n  height: 15px;\n  accent-color: #2563eb;\n  cursor: pointer;\n  flex-shrink: 0;\n}\n\n#fd-app .fd-field-label {\n  font-size: 13px;\n  font-weight: 500;\n  color: #334155;\n  flex: 1;\n}\n\n#fd-app .fd-field-item.active .fd-field-label { color: #1d4ed8; font-weight: 600; }\n\n#fd-app .fd-field-icon {\n  font-size: 16px;\n  flex-shrink: 0;\n}\n\n#fd-app .fd-custom-names {\n  margin-top: 10px;\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  align-items: center;\n}\n\n#fd-app .fd-custom-name-input {\n  padding: 5px 9px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 12px;\n  width: 120px;\n  color: #1e293b;\n  background: #fff;\n  outline: none;\n}\n#fd-app .fd-custom-name-input:focus { border-color: #2563eb; }\n\n#fd-app .fd-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  align-items: center;\n  margin-bottom: 14px;\n}\n\n#fd-app .fd-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 5px;\n  padding: 8px 16px;\n  border: none;\n  border-radius: 7px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s;\n  white-space: nowrap;\n  line-height: 1;\n}\n#fd-app .fd-btn:active { transform: scale(0.97); }\n#fd-app .fd-btn-primary  { background: #2563eb; color: #fff; }\n#fd-app .fd-btn-primary:hover  { background: #1d4ed8; }\n#fd-app .fd-btn-success  { background: #16a34a; color: #fff; }\n#fd-app .fd-btn-success:hover  { background: #15803d; }\n#fd-app .fd-btn-orange   { background: #d97706; color: #fff; }\n#fd-app .fd-btn-orange:hover   { background: #b45309; }\n#fd-app .fd-btn-purple   { background: #7c3aed; color: #fff; }\n#fd-app .fd-btn-purple:hover   { background: #6d28d9; }\n#fd-app .fd-btn-secondary { background: #e2e8f0; color: #334155; }\n#fd-app .fd-btn-secondary:hover { background: #cbd5e1; }\n#fd-app .fd-btn-sm {\n  padding: 5px 10px;\n  font-size: 12px;\n}\n\n#fd-app .fd-count-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  flex-wrap: wrap;\n}\n\n#fd-app .fd-count-input {\n  width: 90px;\n  padding: 7px 10px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 14px;\n  font-weight: 700;\n  color: #1e293b;\n  background: #fff;\n  outline: none;\n  text-align: center;\n}\n#fd-app .fd-count-input:focus { border-color: #2563eb; }\n\n#fd-app .fd-seed-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  flex-wrap: wrap;\n}\n\n#fd-app .fd-seed-toggle {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  cursor: pointer;\n  font-size: 13px;\n  color: #475569;\n}\n\n#fd-app .fd-seed-input {\n  width: 110px;\n  padding: 5px 9px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 13px;\n  color: #1e293b;\n  background: #fff;\n  outline: none;\n}\n#fd-app .fd-seed-input:focus { border-color: #2563eb; }\n#fd-app .fd-seed-input:disabled { background: #f1f5f9; color: #94a3b8; }\n\n#fd-app .fd-status {\n  font-size: 13px;\n  color: #475569;\n  padding: 6px 12px;\n  background: #f1f5f9;\n  border-radius: 6px;\n}\n\n#fd-app .fd-status.ok   { background: #dcfce7; color: #15803d; }\n#fd-app .fd-status.err  { background: #fee2e2; color: #dc2626; }\n#fd-app .fd-status.info { background: #dbeafe; color: #1d4ed8; }\n\n#fd-app .fd-preview-wrap {\n  overflow-x: auto;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  background: #fff;\n  margin-bottom: 14px;\n}\n\n#fd-app .fd-preview-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 12px;\n  white-space: nowrap;\n}\n\n#fd-app .fd-preview-table th {\n  background: #f1f5f9;\n  padding: 8px 12px;\n  text-align: left;\n  font-weight: 700;\n  color: #475569;\n  border-bottom: 2px solid #e2e8f0;\n  position: sticky;\n  top: 0;\n}\n\n#fd-app .fd-preview-table td {\n  padding: 7px 12px;\n  color: #1e293b;\n  border-bottom: 1px solid #f1f5f9;\n  max-width: 180px;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n#fd-app .fd-preview-table tr:last-child td { border-bottom: none; }\n#fd-app .fd-preview-table tr:hover td { background: #f8fafc; }\n\n#fd-app .fd-output-wrap {\n  background: #0f172a;\n  border-radius: 10px;\n  padding: 16px;\n  margin-bottom: 14px;\n  position: relative;\n  max-height: 360px;\n  overflow-y: auto;\n}\n\n#fd-app .fd-output {\n  font-family: \"Fira Code\", \"Cascadia Code\", \"SF Mono\", Consolas, monospace;\n  font-size: 12px;\n  color: #e2e8f0;\n  white-space: pre;\n  line-height: 1.6;\n}\n\n#fd-app .fd-copy-btn {\n  position: absolute;\n  top: 10px;\n  right: 10px;\n  padding: 5px 12px;\n  background: #334155;\n  color: #cbd5e1;\n  border: none;\n  border-radius: 6px;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n#fd-app .fd-copy-btn:hover { background: #475569; }\n#fd-app .fd-copy-btn.copied { background: #16a34a; color: #fff; }\n\n#fd-app .fd-empty {\n  text-align: center;\n  padding: 40px 20px;\n  color: #94a3b8;\n  font-size: 13px;\n}\n\n#fd-app .fd-divider {\n  border: none;\n  border-top: 1.5px solid #e2e8f0;\n  margin: 16px 0;\n}\n\n#fd-app .fd-format-tabs {\n  display: flex;\n  gap: 6px;\n  margin-bottom: 10px;\n}\n\n#fd-app .fd-tab {\n  padding: 6px 14px;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 6px;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  background: #fff;\n  color: #475569;\n  transition: all 0.15s;\n}\n#fd-app .fd-tab.active { background: #2563eb; color: #fff; border-color: #2563eb; }\n#fd-app .fd-tab:hover:not(.active) { border-color: #93c5fd; color: #1d4ed8; }\n\n#fd-app .fd-sql-table-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin-top: 8px;\n}\n\n#fd-app .fd-sql-table-input {\n  padding: 5px 9px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 13px;\n  color: #1e293b;\n  background: #fff;\n  outline: none;\n  width: 160px;\n}\n#fd-app .fd-sql-table-input:focus { border-color: #2563eb; }\n\n#fd-app .fd-badge {\n  display: inline-block;\n  padding: 2px 8px;\n  background: #dbeafe;\n  color: #1d4ed8;\n  border-radius: 99px;\n  font-size: 11px;\n  font-weight: 700;\n}\n\n@media (max-width: 600px) {\n  #fd-app .fd-fields-grid {\n    grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));\n  }\n  #fd-app .fd-output-wrap { max-height: 240px; }\n}\n\u003c/style\u003e\n\u003c!-- FIELD SELECTION --\u003e\n\u003cdiv class=\"fd-card\" style=\"margin-bottom:14px;\"\u003e\n  \u003ch2\u003e1. Select Fields\u003c/h2\u003e\n  \u003cdiv class=\"fd-fields-grid\" id=\"fdFieldsGrid\"\u003e\u003c/div\u003e\n  \u003chr class=\"fd-divider\"\u003e\n  \u003cdiv style=\"font-size:12px;color:#475569;margin-bottom:6px;font-weight:600;\"\u003eCustom column names (optional — overrides defaults)\u003c/div\u003e\n  \u003cdiv class=\"fd-custom-names\" id=\"fdCustomNames\"\u003e\n    \u003cspan style=\"font-size:12px;color:#94a3b8;\"\u003eSelect fields above to set custom names.\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- CONFIG ROW --\u003e\n\u003cdiv class=\"fd-row\"\u003e\n  \u003cdiv class=\"fd-col fd-card\"\u003e\n    \u003ch2\u003e2. Record Count\u003c/h2\u003e\n    \u003cdiv class=\"fd-count-row\"\u003e\n      \u003cinput type=\"number\" id=\"fdCount\" class=\"fd-count-input\" value=\"10\" min=\"1\" max=\"1000\"\u003e\n      \u003cspan style=\"font-size:13px;color:#475569;\"\u003erows (max 1000)\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cinput type=\"range\" id=\"fdCountSlider\" min=\"1\" max=\"1000\" value=\"10\"\n      style=\"width:100%;margin-top:10px;accent-color:#2563eb;\"\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"fd-col fd-card\"\u003e\n    \u003ch2\u003e3. Reproducible Seed\u003c/h2\u003e\n    \u003cdiv class=\"fd-seed-row\"\u003e\n      \u003clabel class=\"fd-seed-toggle\"\u003e\n        \u003cinput type=\"checkbox\" id=\"fdSeedEnabled\" style=\"accent-color:#2563eb;\"\u003e Use seed\n      \u003c/label\u003e\n      \u003cinput type=\"number\" id=\"fdSeedVal\" class=\"fd-seed-input\" value=\"42\" disabled placeholder=\"e.g. 42\"\u003e\n    \u003c/div\u003e\n    \u003cdiv style=\"font-size:12px;color:#64748b;margin-top:8px;\"\u003eSame seed = same data every time.\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"fd-col fd-card\"\u003e\n    \u003ch2\u003e4. Export Format\u003c/h2\u003e\n    \u003cdiv class=\"fd-format-tabs\" id=\"fdFormatTabs\"\u003e\n      \u003cbutton class=\"fd-tab active\" data-fmt=\"json\"\u003eJSON\u003c/button\u003e\n      \u003cbutton class=\"fd-tab\" data-fmt=\"csv\"\u003eCSV\u003c/button\u003e\n      \u003cbutton class=\"fd-tab\" data-fmt=\"sql\"\u003eSQL\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv id=\"fdSqlTableRow\" class=\"fd-sql-table-row\" style=\"display:none;\"\u003e\n      \u003cspan style=\"font-size:12px;color:#475569;\"\u003eTable:\u003c/span\u003e\n      \u003cinput type=\"text\" id=\"fdSqlTable\" class=\"fd-sql-table-input\" value=\"mock_data\" placeholder=\"table name\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- GENERATE TOOLBAR --\u003e\n\u003cdiv class=\"fd-toolbar\"\u003e\n  \u003cbutton class=\"fd-btn fd-btn-primary\" id=\"fdBtnGenerate\"\u003e\u0026#9654; Generate\u003c/button\u003e\n  \u003cbutton class=\"fd-btn fd-btn-secondary\" id=\"fdBtnSelectAll\"\u003eCheck All\u003c/button\u003e\n  \u003cbutton class=\"fd-btn fd-btn-secondary\" id=\"fdBtnClearAll\"\u003eUncheck All\u003c/button\u003e\n  \u003cdiv id=\"fdStatus\" class=\"fd-status\"\u003eSelect fields and click Generate.\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- PREVIEW TABLE --\u003e\n\u003cdiv id=\"fdPreviewSection\" style=\"display:none;\"\u003e\n  \u003ch2 style=\"margin-bottom:8px;\"\u003ePreview \u003cspan id=\"fdPreviewBadge\" class=\"fd-badge\"\u003e\u003c/span\u003e\u003c/h2\u003e\n  \u003cdiv class=\"fd-preview-wrap\" style=\"max-height:280px;overflow-y:auto;\"\u003e\n    \u003ctable class=\"fd-preview-table\" id=\"fdPreviewTable\"\u003e\n      \u003cthead id=\"fdPreviewHead\"\u003e\u003c/thead\u003e\n      \u003ctbody id=\"fdPreviewBody\"\u003e\u003c/tbody\u003e\n    \u003c/table\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- OUTPUT --\u003e\n\u003cdiv id=\"fdOutputSection\" style=\"display:none;margin-top:14px;\"\u003e\n  \u003cdiv style=\"display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;flex-wrap:wrap;gap:8px;\"\u003e\n    \u003ch2\u003eOutput\u003c/h2\u003e\n    \u003cdiv style=\"display:flex;gap:8px;\"\u003e\n      \u003cbutton class=\"fd-btn fd-btn-success fd-btn-sm\" id=\"fdBtnDownload\"\u003e\u0026#8659; Download\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"fd-output-wrap\"\u003e\n    \u003cbutton class=\"fd-copy-btn\" id=\"fdBtnCopy\"\u003eCopy\u003c/button\u003e\n    \u003cpre class=\"fd-output\" id=\"fdOutput\"\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  // ── FIELD DEFINITIONS ──────────────────────────────────────────────────────\n  var FIELDS = [\n    { id: \"name\",     label: \"Full Name\",    icon: \"\u0026#128100;\" },\n    { id: \"email\",    label: \"Email\",         icon: \"\u0026#9993;\" },\n    { id: \"phone\",    label: \"Phone\",         icon: \"\u0026#128222;\" },\n    { id: \"address\",  label: \"Address\",       icon: \"\u0026#127968;\" },\n    { id: \"company\",  label: \"Company\",       icon: \"\u0026#127970;\" },\n    { id: \"date\",     label: \"Date\",          icon: \"\u0026#128197;\" },\n    { id: \"uuid\",     label: \"UUID\",          icon: \"\u0026#128273;\" },\n    { id: \"number\",   label: \"Number\",        icon: \"\u0026#128290;\" },\n    { id: \"boolean\",  label: \"Boolean\",       icon: \"\u0026#9989;\" },\n    { id: \"lorem\",    label: \"Lorem Text\",    icon: \"\u0026#128218;\" },\n  ];\n\n  var selectedFields = [\"name\", \"email\", \"phone\", \"uuid\"];\n  var customNames = {};\n  var currentFmt = \"json\";\n  var generatedData = [];\n\n  // ── SEEDED RNG ─────────────────────────────────────────────────────────────\n  function mulberry32(seed) {\n    return function() {\n      seed |= 0; seed = seed + 0x6D2B79F5 | 0;\n      var t = Math.imul(seed ^ seed \u003e\u003e\u003e 15, 1 | seed);\n      t = t + Math.imul(t ^ t \u003e\u003e\u003e 7, 61 | t) ^ t;\n      return ((t ^ t \u003e\u003e\u003e 14) \u003e\u003e\u003e 0) / 4294967296;\n    };\n  }\n\n  var rnd = Math.random;\n\n  function rand() { return rnd(); }\n  function randInt(min, max) { return Math.floor(rand() * (max - min + 1)) + min; }\n  function pick(arr) { return arr[Math.floor(rand() * arr.length)]; }\n\n  // ── DATA POOLS ─────────────────────────────────────────────────────────────\n  var FIRST = [\"James\",\"Mary\",\"John\",\"Patricia\",\"Robert\",\"Jennifer\",\"Michael\",\"Linda\",\n    \"William\",\"Barbara\",\"David\",\"Susan\",\"Richard\",\"Jessica\",\"Joseph\",\"Sarah\",\n    \"Thomas\",\"Karen\",\"Charles\",\"Lisa\",\"Christopher\",\"Nancy\",\"Daniel\",\"Betty\",\n    \"Matthew\",\"Margaret\",\"Anthony\",\"Sandra\",\"Mark\",\"Ashley\",\"Donald\",\"Dorothy\",\n    \"Steven\",\"Kimberly\",\"Paul\",\"Emily\",\"Andrew\",\"Donna\",\"Joshua\",\"Michelle\"];\n  var LAST = [\"Smith\",\"Johnson\",\"Williams\",\"Brown\",\"Jones\",\"Garcia\",\"Miller\",\"Davis\",\n    \"Wilson\",\"Taylor\",\"Anderson\",\"Thomas\",\"Jackson\",\"White\",\"Harris\",\"Martin\",\n    \"Thompson\",\"Young\",\"Lee\",\"Perez\",\"Clark\",\"Lewis\",\"Robinson\",\"Walker\",\n    \"Hall\",\"Allen\",\"King\",\"Wright\",\"Scott\",\"Green\",\"Baker\",\"Adams\",\"Nelson\",\n    \"Carter\",\"Mitchell\",\"Roberts\",\"Turner\",\"Campbell\",\"Parker\",\"Evans\"];\n  var DOMAINS = [\"gmail.com\",\"yahoo.com\",\"outlook.com\",\"example.com\",\"icloud.com\",\n    \"proton.me\",\"hotmail.com\",\"company.io\",\"work.org\",\"mail.net\"];\n  var STREETS = [\"Oak St\",\"Maple Ave\",\"Cedar Rd\",\"Pine Blvd\",\"Elm Dr\",\"Washington St\",\n    \"Lincoln Ave\",\"Park Rd\",\"Lake Dr\",\"Hill Blvd\",\"River Rd\",\"Valley Ave\",\n    \"Forest Ln\",\"Sunset Blvd\",\"Sunrise Dr\",\"Main St\",\"Broadway\",\"First Ave\"];\n  var CITIES = [\"New York\",\"Los Angeles\",\"Chicago\",\"Houston\",\"Phoenix\",\"Philadelphia\",\n    \"San Antonio\",\"San Diego\",\"Dallas\",\"San Jose\",\"Austin\",\"Jacksonville\",\n    \"Seattle\",\"Denver\",\"Nashville\",\"Boston\",\"Portland\",\"Las Vegas\",\"Memphis\"];\n  var STATES = [\"NY\",\"CA\",\"IL\",\"TX\",\"AZ\",\"PA\",\"FL\",\"OH\",\"MI\",\"WA\",\"CO\",\"TN\",\"MA\",\"OR\",\"NV\"];\n  var COMPANIES = [\"Acme Corp\",\"Globex\",\"Initech\",\"Umbrella Ltd\",\"Stark Industries\",\n    \"Wayne Enterprises\",\"Oscorp\",\"Vandelay Industries\",\"Dunder Mifflin\",\n    \"Bluth Company\",\"Sterling Cooper\",\"Pied Piper\",\"Hooli\",\"Aviato\",\n    \"Weyland Corp\",\"Blue Sun Corp\",\"Rekall Inc\",\"OmniCorp\",\"Massive Dynamic\"];\n  var LOREM_WORDS = [\"lorem\",\"ipsum\",\"dolor\",\"sit\",\"amet\",\"consectetur\",\"adipiscing\",\n    \"elit\",\"sed\",\"do\",\"eiusmod\",\"tempor\",\"incididunt\",\"ut\",\"labore\",\"et\",\"dolore\",\n    \"magna\",\"aliqua\",\"enim\",\"ad\",\"minim\",\"veniam\",\"quis\",\"nostrud\",\"exercitation\",\n    \"ullamco\",\"laboris\",\"nisi\",\"aliquip\",\"ex\",\"ea\",\"commodo\",\"consequat\",\"duis\",\n    \"aute\",\"irure\",\"in\",\"reprehenderit\",\"voluptate\",\"velit\",\"esse\",\"cillum\",\"fugiat\",\n    \"nulla\",\"pariatur\",\"excepteur\",\"sint\",\"occaecat\",\"cupidatat\",\"non\",\"proident\",\n    \"sunt\",\"culpa\",\"qui\",\"officia\",\"deserunt\",\"mollit\",\"anim\",\"id\",\"est\",\"laborum\"];\n\n  function genFirst() { return pick(FIRST); }\n  function genLast()  { return pick(LAST); }\n  function genName()  { return genFirst() + \" \" + genLast(); }\n  function genEmail() {\n    var f = genFirst().toLowerCase();\n    var l = genLast().toLowerCase();\n    var sep = pick([\".\", \"_\", \"\"]);\n    return f + sep + l + randInt(1, 99) + \"@\" + pick(DOMAINS);\n  }\n  function genPhone() {\n    return \"(\" + randInt(200,999) + \") \" + randInt(200,999) + \"-\" + randInt(1000,9999);\n  }\n  function genAddress() {\n    return randInt(1, 9999) + \" \" + pick(STREETS) + \", \" + pick(CITIES) + \", \" + pick(STATES) + \" \" + String(randInt(10000,99999));\n  }\n  function genCompany() { return pick(COMPANIES); }\n  function genDate() {\n    var y = randInt(1970, 2025);\n    var m = String(randInt(1,12)).padStart(2,\"0\");\n    var d = String(randInt(1,28)).padStart(2,\"0\");\n    return y + \"-\" + m + \"-\" + d;\n  }\n  function genUUID() {\n    function s4() {\n      return Math.floor((1 + rand()) * 0x10000).toString(16).substring(1);\n    }\n    return s4()+s4()+\"-\"+s4()+\"-4\"+s4().substring(1)+\"-\"+\n      ([\"8\",\"9\",\"a\",\"b\"][Math.floor(rand()*4)])+s4().substring(1)+\"-\"+s4()+s4()+s4();\n  }\n  function genNumber() { return randInt(1, 100000); }\n  function genBoolean() { return rand() \u003c 0.5; }\n  function genLorem() {\n    var words = [];\n    var len = randInt(8, 20);\n    for (var i = 0; i \u003c len; i++) words.push(pick(LOREM_WORDS));\n    words[0] = words[0].charAt(0).toUpperCase() + words[0].slice(1);\n    return words.join(\" \") + \".\";\n  }\n\n  function genField(id) {\n    switch(id) {\n      case \"name\":    return genName();\n      case \"email\":   return genEmail();\n      case \"phone\":   return genPhone();\n      case \"address\": return genAddress();\n      case \"company\": return genCompany();\n      case \"date\":    return genDate();\n      case \"uuid\":    return genUUID();\n      case \"number\":  return genNumber();\n      case \"boolean\": return genBoolean();\n      case \"lorem\":   return genLorem();\n      default:        return \"\";\n    }\n  }\n\n  function colName(fid) {\n    return (customNames[fid] \u0026\u0026 customNames[fid].trim()) ? customNames[fid].trim() : fid;\n  }\n\n  // ── BUILD FIELD GRID ───────────────────────────────────────────────────────\n  var grid = document.getElementById(\"fdFieldsGrid\");\n  FIELDS.forEach(function(f) {\n    var item = document.createElement(\"div\");\n    item.className = \"fd-field-item\" + (selectedFields.indexOf(f.id) \u003e= 0 ? \" active\" : \"\");\n    item.dataset.fid = f.id;\n    item.innerHTML =\n      '\u003cinput type=\"checkbox\" id=\"fdCb_'+f.id+'\"' + (selectedFields.indexOf(f.id)\u003e=0?' checked':'')+'\u003e'+\n      '\u003cspan class=\"fd-field-icon\"\u003e'+f.icon+'\u003c/span\u003e'+\n      '\u003cspan class=\"fd-field-label\"\u003e'+f.label+'\u003c/span\u003e';\n    item.addEventListener(\"click\", function(e) {\n      if (e.target.tagName === \"INPUT\") return;\n      var cb = item.querySelector(\"input\");\n      cb.checked = !cb.checked;\n      toggleField(f.id, cb.checked);\n    });\n    item.querySelector(\"input\").addEventListener(\"change\", function(e) {\n      toggleField(f.id, e.target.checked);\n    });\n    grid.appendChild(item);\n  });\n\n  function toggleField(fid, on) {\n    var item = grid.querySelector('[data-fid=\"'+fid+'\"]');\n    if (on) {\n      if (selectedFields.indexOf(fid) \u003c 0) selectedFields.push(fid);\n      item.classList.add(\"active\");\n    } else {\n      selectedFields = selectedFields.filter(function(x){return x!==fid;});\n      item.classList.remove(\"active\");\n      delete customNames[fid];\n    }\n    renderCustomNames();\n  }\n\n  // ── CUSTOM NAMES ───────────────────────────────────────────────────────────\n  function renderCustomNames() {\n    var wrap = document.getElementById(\"fdCustomNames\");\n    wrap.innerHTML = \"\";\n    if (selectedFields.length === 0) {\n      wrap.innerHTML = '\u003cspan style=\"font-size:12px;color:#94a3b8;\"\u003eSelect fields above to set custom names.\u003c/span\u003e';\n      return;\n    }\n    selectedFields.forEach(function(fid) {\n      var f = FIELDS.find(function(x){return x.id===fid;});\n      var row = document.createElement(\"div\");\n      row.style.cssText = \"display:flex;align-items:center;gap:4px;\";\n      row.innerHTML =\n        '\u003cspan style=\"font-size:11px;color:#64748b;font-weight:600;\"\u003e'+f.label+':\u003c/span\u003e'+\n        '\u003cinput type=\"text\" class=\"fd-custom-name-input\" placeholder=\"'+fid+'\" data-fid=\"'+fid+'\" value=\"'+(customNames[fid]||'')+'\"\u003e';\n      row.querySelector(\"input\").addEventListener(\"input\", function(e) {\n        customNames[fid] = e.target.value;\n      });\n      wrap.appendChild(row);\n    });\n  }\n  renderCustomNames();\n\n  // ── COUNT \u0026 SLIDER ─────────────────────────────────────────────────────────\n  var countInput  = document.getElementById(\"fdCount\");\n  var countSlider = document.getElementById(\"fdCountSlider\");\n  countInput.addEventListener(\"input\", function() {\n    var v = Math.max(1, Math.min(1000, parseInt(countInput.value)||1));\n    countSlider.value = v;\n  });\n  countSlider.addEventListener(\"input\", function() {\n    countInput.value = countSlider.value;\n  });\n\n  // ── SEED ───────────────────────────────────────────────────────────────────\n  var seedEnabled = document.getElementById(\"fdSeedEnabled\");\n  var seedVal     = document.getElementById(\"fdSeedVal\");\n  seedEnabled.addEventListener(\"change\", function() {\n    seedVal.disabled = !seedEnabled.checked;\n  });\n\n  // ── FORMAT TABS ────────────────────────────────────────────────────────────\n  document.getElementById(\"fdFormatTabs\").addEventListener(\"click\", function(e) {\n    var tab = e.target.closest(\".fd-tab\");\n    if (!tab) return;\n    currentFmt = tab.dataset.fmt;\n    document.querySelectorAll(\"#fd-app .fd-tab\").forEach(function(t){t.classList.remove(\"active\");});\n    tab.classList.add(\"active\");\n    document.getElementById(\"fdSqlTableRow\").style.display = currentFmt === \"sql\" ? \"flex\" : \"none\";\n    if (generatedData.length \u003e 0) renderOutput();\n  });\n\n  // ── SELECT/CLEAR ALL ───────────────────────────────────────────────────────\n  document.getElementById(\"fdBtnSelectAll\").addEventListener(\"click\", function() {\n    FIELDS.forEach(function(f) { toggleField(f.id, true); });\n    grid.querySelectorAll(\"input[type=checkbox]\").forEach(function(cb){cb.checked=true;});\n    renderCustomNames();\n  });\n  document.getElementById(\"fdBtnClearAll\").addEventListener(\"click\", function() {\n    FIELDS.forEach(function(f) { toggleField(f.id, false); });\n    grid.querySelectorAll(\"input[type=checkbox]\").forEach(function(cb){cb.checked=false;});\n    selectedFields = [];\n    customNames = {};\n    renderCustomNames();\n  });\n\n  // ── GENERATE ───────────────────────────────────────────────────────────────\n  document.getElementById(\"fdBtnGenerate\").addEventListener(\"click\", function() {\n    var status = document.getElementById(\"fdStatus\");\n\n    if (selectedFields.length === 0) {\n      status.className = \"fd-status err\";\n      status.textContent = \"Please select at least one field.\";\n      return;\n    }\n\n    var count = Math.max(1, Math.min(1000, parseInt(countInput.value)||10));\n    countInput.value = count;\n    countSlider.value = count;\n\n    // set RNG\n    if (seedEnabled.checked) {\n      var s = parseInt(seedVal.value) || 42;\n      rnd = mulberry32(s);\n    } else {\n      rnd = Math.random;\n    }\n\n    generatedData = [];\n    for (var i = 0; i \u003c count; i++) {\n      var row = {};\n      selectedFields.forEach(function(fid) {\n        row[colName(fid)] = genField(fid);\n      });\n      generatedData.push(row);\n    }\n\n    status.className = \"fd-status ok\";\n    status.textContent = \"Generated \" + count + \" records with \" + selectedFields.length + \" field(s).\";\n\n    renderPreview();\n    renderOutput();\n\n    document.getElementById(\"fdPreviewSection\").style.display = \"block\";\n    document.getElementById(\"fdOutputSection\").style.display = \"block\";\n  });\n\n  // ── PREVIEW TABLE ──────────────────────────────────────────────────────────\n  function renderPreview() {\n    var head = document.getElementById(\"fdPreviewHead\");\n    var body = document.getElementById(\"fdPreviewBody\");\n    document.getElementById(\"fdPreviewBadge\").textContent = generatedData.length + \" rows\";\n    var cols = selectedFields.map(colName);\n\n    head.innerHTML = \"\u003ctr\u003e\" + cols.map(function(c){return \"\u003cth\u003e\"+escHtml(c)+\"\u003c/th\u003e\";}).join(\"\") + \"\u003c/tr\u003e\";\n    var preview = generatedData.slice(0, 20);\n    body.innerHTML = preview.map(function(row) {\n      return \"\u003ctr\u003e\" + cols.map(function(c){\n        return \"\u003ctd\u003e\" + escHtml(String(row[c])) + \"\u003c/td\u003e\";\n      }).join(\"\") + \"\u003c/tr\u003e\";\n    }).join(\"\");\n  }\n\n  // ── OUTPUT ─────────────────────────────────────────────────────────────────\n  function renderOutput() {\n    var out = document.getElementById(\"fdOutput\");\n    out.textContent = buildOutput();\n  }\n\n  function buildOutput() {\n    if (currentFmt === \"json\") return buildJSON();\n    if (currentFmt === \"csv\")  return buildCSV();\n    if (currentFmt === \"sql\")  return buildSQL();\n    return \"\";\n  }\n\n  function buildJSON() {\n    return JSON.stringify(generatedData, null, 2);\n  }\n\n  function buildCSV() {\n    var cols = selectedFields.map(colName);\n    var lines = [cols.map(csvEsc).join(\",\")];\n    generatedData.forEach(function(row) {\n      lines.push(cols.map(function(c){ return csvEsc(row[c]); }).join(\",\"));\n    });\n    return lines.join(\"\\n\");\n  }\n\n  function buildSQL() {\n    var table = (document.getElementById(\"fdSqlTable\").value.trim() || \"mock_data\");\n    var cols = selectedFields.map(colName);\n    var header = \"INSERT INTO `\" + table + \"` (`\" + cols.join(\"`, `\") + \"`) VALUES\";\n    var rows = generatedData.map(function(row) {\n      var vals = cols.map(function(c) {\n        var v = row[c];\n        if (typeof v === \"boolean\") return v ? \"TRUE\" : \"FALSE\";\n        if (typeof v === \"number\") return v;\n        return \"'\" + String(v).replace(/'/g, \"''\") + \"'\";\n      });\n      return \"  (\" + vals.join(\", \") + \")\";\n    });\n    return header + \"\\n\" + rows.join(\",\\n\") + \";\";\n  }\n\n  function csvEsc(v) {\n    var s = String(v);\n    if (s.includes(\",\") || s.includes('\"') || s.includes(\"\\n\")) {\n      return '\"' + s.replace(/\"/g,'\"\"') + '\"';\n    }\n    return s;\n  }\n\n  function escHtml(s) {\n    return s.replace(/\u0026/g,\"\u0026amp;\").replace(/\u003c/g,\"\u0026lt;\").replace(/\u003e/g,\"\u0026gt;\");\n  }\n\n  // ── COPY ───────────────────────────────────────────────────────────────────\n  document.getElementById(\"fdBtnCopy\").addEventListener(\"click\", function() {\n    var btn = document.getElementById(\"fdBtnCopy\");\n    var text = document.getElementById(\"fdOutput\").textContent;\n    if (navigator.clipboard) {\n      navigator.clipboard.writeText(text).then(function() {\n        btn.textContent = \"Copied!\";\n        btn.classList.add(\"copied\");\n        setTimeout(function(){ btn.textContent=\"Copy\"; btn.classList.remove(\"copied\"); }, 1800);\n      });\n    } else {\n      var ta = document.createElement(\"textarea\");\n      ta.value = text;\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand(\"copy\");\n      document.body.removeChild(ta);\n      btn.textContent = \"Copied!\";\n      btn.classList.add(\"copied\");\n      setTimeout(function(){ btn.textContent=\"Copy\"; btn.classList.remove(\"copied\"); }, 1800);\n    }\n  });\n\n  // ── DOWNLOAD ───────────────────────────────────────────────────────────────\n  document.getElementById(\"fdBtnDownload\").addEventListener(\"click\", function() {\n    var text = buildOutput();\n    var ext  = currentFmt === \"json\" ? \"json\" : currentFmt === \"csv\" ? \"csv\" : \"sql\";\n    var mime = currentFmt === \"json\" ? \"application/json\"\n             : currentFmt === \"csv\"  ? \"text/csv\"\n             : \"text/plain\";\n    var blob = new Blob([text], {type: mime});\n    var url  = URL.createObjectURL(blob);\n    var a    = document.createElement(\"a\");\n    a.href     = url;\n    a.download = \"mock-data.\" + ext;\n    a.click();\n    URL.revokeObjectURL(url);\n  });\n\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003eFormat generated JSON output: \u003ca href=\"https://productivity-works.com/tools/json-formatter/\"\u003eJSON Formatter\u003c/a\u003e\n\u003c/p\u003e","title":"Fake Data Generator - Mock API Data"},{"content":"Check how your favicon looks across browsers, bookmarks, search results, and mobile home screens — all client-side, nothing uploaded to any server.\nRelated tools: Generate favicons → Favicon Generator | Meta tags → Meta Tag Generator Drag \u0026amp; drop your favicon here\nSupports PNG, ICO, SVG, JPG, WebP\nChoose File Upload a favicon above to see previews and size checks. Size Preview Browser \u0026amp; Context Previews Dark mode previews Related tools: Generate favicons → Favicon Generator | Meta tags → Meta Tag Generator ","permalink":"https://productivity-works.com/tools/favicon-checker/","summary":"\u003cp\u003eCheck how your favicon looks across browsers, bookmarks, search results, and mobile home screens — all client-side, nothing uploaded to any server.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eRelated tools:\u003c/strong\u003e Generate favicons → \u003ca href=\"https://productivity-works.com/tools/favicon-generator/\"\u003eFavicon Generator\u003c/a\u003e\n  |  Meta tags → \u003ca href=\"https://productivity-works.com/tools/meta-tag-generator/\"\u003eMeta Tag Generator\u003c/a\u003e\n\u003c/p\u003e\n\u003chr\u003e\n\u003cdiv id=\"fc-app\"\u003e\n\u003cstyle\u003e\n#fc-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #1e293b;\n}\n#fc-app *, #fc-app *::before, #fc-app *::after {\n  box-sizing: border-box;\n}\n#fc-app h2 {\n  font-size: 1.1rem;\n  font-weight: 700;\n  margin: 1.4rem 0 0.6rem;\n  color: #0f172a;\n}\n#fc-drop-zone {\n  border: 2.5px dashed #94a3b8;\n  border-radius: 12px;\n  padding: 36px 24px;\n  text-align: center;\n  cursor: pointer;\n  transition: border-color 0.2s, background 0.2s;\n  background: #f8fafc;\n  position: relative;\n}\n#fc-drop-zone.fc-drag-over {\n  border-color: #3b82f6;\n  background: #eff6ff;\n}\n#fc-drop-zone p {\n  margin: 0 0 10px;\n  color: #64748b;\n  font-size: 0.95rem;\n}\n#fc-drop-zone input[type=\"file\"] {\n  display: none;\n}\n#fc-upload-btn {\n  display: inline-block;\n  padding: 9px 22px;\n  background: #3b82f6;\n  color: #fff;\n  border-radius: 8px;\n  font-size: 0.9rem;\n  font-weight: 600;\n  cursor: pointer;\n  border: none;\n  transition: background 0.18s;\n}\n#fc-upload-btn:hover { background: #2563eb; }\n#fc-file-info {\n  display: none;\n  margin-top: 14px;\n  padding: 12px 16px;\n  background: #f1f5f9;\n  border-radius: 8px;\n  font-size: 0.88rem;\n  color: #475569;\n  text-align: left;\n}\n#fc-file-info span { display: inline-block; margin-right: 18px; }\n#fc-file-info strong { color: #1e293b; }\n#fc-warnings {\n  margin-top: 10px;\n  display: none;\n}\n.fc-warn {\n  display: flex;\n  align-items: flex-start;\n  gap: 8px;\n  padding: 8px 12px;\n  background: #fef3c7;\n  border-left: 3px solid #f59e0b;\n  border-radius: 6px;\n  margin-bottom: 6px;\n  font-size: 0.85rem;\n  color: #92400e;\n}\n.fc-warn-icon { flex-shrink: 0; font-size: 1rem; }\n#fc-size-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));\n  gap: 12px;\n  margin-top: 8px;\n}\n.fc-size-card {\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 12px;\n  text-align: center;\n}\n.fc-size-card canvas {\n  display: block;\n  margin: 0 auto 6px;\n  image-rendering: pixelated;\n  border: 1px solid #e2e8f0;\n  border-radius: 4px;\n}\n.fc-size-label {\n  font-size: 0.8rem;\n  font-weight: 600;\n  color: #475569;\n}\n.fc-size-usage {\n  font-size: 0.72rem;\n  color: #94a3b8;\n  margin-top: 2px;\n}\n.fc-size-ok { color: #16a34a; font-size: 0.75rem; margin-top: 3px; font-weight: 600; }\n.fc-size-warn { color: #dc2626; font-size: 0.75rem; margin-top: 3px; font-weight: 600; }\n\n/* Preview section */\n#fc-previews { margin-top: 4px; }\n#fc-dark-toggle {\n  display: inline-flex;\n  align-items: center;\n  gap: 8px;\n  cursor: pointer;\n  font-size: 0.85rem;\n  color: #475569;\n  user-select: none;\n  margin-bottom: 14px;\n}\n#fc-dark-toggle input { cursor: pointer; }\n\n.fc-preview-block {\n  margin-bottom: 20px;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  overflow: hidden;\n}\n.fc-preview-label {\n  padding: 8px 14px;\n  font-size: 0.82rem;\n  font-weight: 700;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  border-bottom: 1px solid #e2e8f0;\n  background: #f8fafc;\n}\n.fc-preview-inner {\n  padding: 20px 20px;\n}\n.fc-preview-inner.fc-dark {\n  background: #1e293b;\n}\n.fc-preview-inner.fc-light {\n  background: #ffffff;\n}\n\n/* Chrome tab mockup */\n.fc-chrome-tab {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  padding: 6px 14px 6px 10px;\n  background: #fff;\n  border-radius: 8px 8px 0 0;\n  border: 1px solid #d1d5db;\n  border-bottom: none;\n  font-size: 0.82rem;\n  color: #374151;\n  min-width: 140px;\n  max-width: 200px;\n  box-shadow: 0 -1px 3px rgba(0,0,0,0.06);\n}\n.fc-chrome-tab.fc-dark-tab {\n  background: #374151;\n  color: #f9fafb;\n  border-color: #4b5563;\n}\n.fc-chrome-tab img, .fc-chrome-tab canvas {\n  width: 16px; height: 16px;\n  flex-shrink: 0;\n  border-radius: 2px;\n}\n.fc-tab-title {\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n  flex: 1;\n}\n.fc-tab-close {\n  font-size: 0.75rem;\n  color: #9ca3af;\n  flex-shrink: 0;\n}\n.fc-tab-bar {\n  background: #e5e7eb;\n  padding: 6px 8px 0;\n  border-radius: 4px;\n  display: inline-flex;\n}\n.fc-tab-bar.fc-dark { background: #1f2937; }\n\n/* Safari tab */\n.fc-safari-bar {\n  background: #f2f2f7;\n  padding: 10px 12px;\n  border-radius: 4px;\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n.fc-safari-bar.fc-dark { background: #2c2c2e; }\n.fc-safari-urlbar {\n  flex: 1;\n  background: #fff;\n  border-radius: 8px;\n  padding: 5px 10px;\n  font-size: 0.8rem;\n  color: #6b7280;\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n.fc-safari-urlbar.fc-dark { background: #3a3a3c; color: #aeaeb2; }\n.fc-safari-urlbar img, .fc-safari-urlbar canvas {\n  width: 14px; height: 14px;\n  border-radius: 2px;\n  flex-shrink: 0;\n}\n\n/* Firefox tab */\n.fc-firefox-bar {\n  background: #f0f0f4;\n  padding: 8px 10px 0;\n  border-radius: 4px;\n  display: inline-flex;\n}\n.fc-firefox-bar.fc-dark { background: #2b2a33; }\n.fc-firefox-tab {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  padding: 7px 12px;\n  background: #fff;\n  border-radius: 6px 6px 0 0;\n  font-size: 0.82rem;\n  color: #374151;\n  min-width: 140px;\n  box-shadow: 0 -1px 4px rgba(0,0,0,0.08);\n}\n.fc-firefox-tab.fc-dark { background: #42414d; color: #fbfbfe; }\n.fc-firefox-tab img, .fc-firefox-tab canvas {\n  width: 16px; height: 16px;\n  flex-shrink: 0;\n  border-radius: 2px;\n}\n\n/* Bookmarks bar */\n.fc-bookmarks-bar {\n  display: flex;\n  align-items: center;\n  gap: 2px;\n  padding: 4px 10px;\n  background: #f3f4f6;\n  border-radius: 4px;\n  border: 1px solid #e5e7eb;\n  flex-wrap: wrap;\n}\n.fc-bookmarks-bar.fc-dark {\n  background: #374151;\n  border-color: #4b5563;\n}\n.fc-bookmark-item {\n  display: inline-flex;\n  align-items: center;\n  gap: 5px;\n  padding: 3px 8px;\n  border-radius: 4px;\n  font-size: 0.8rem;\n  color: #374151;\n  cursor: default;\n}\n.fc-bookmark-item:hover { background: rgba(0,0,0,0.06); }\n.fc-bookmark-item.fc-dark { color: #d1d5db; }\n.fc-bookmark-item img, .fc-bookmark-item canvas {\n  width: 16px; height: 16px;\n  border-radius: 2px;\n  flex-shrink: 0;\n}\n\n/* Google search result */\n.fc-google-result {\n  padding: 4px 0;\n  max-width: 480px;\n}\n.fc-google-site-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin-bottom: 4px;\n}\n.fc-google-site-row img, .fc-google-site-row canvas {\n  width: 18px; height: 18px;\n  border-radius: 50%;\n  flex-shrink: 0;\n}\n.fc-google-site-name {\n  font-size: 0.82rem;\n  color: #202124;\n  font-weight: 600;\n}\n.fc-google-site-name.fc-dark { color: #bdc1c6; }\n.fc-google-site-url {\n  font-size: 0.78rem;\n  color: #5f6368;\n}\n.fc-google-title {\n  font-size: 1.1rem;\n  color: #1a0dab;\n  font-weight: 400;\n  line-height: 1.3;\n  cursor: pointer;\n}\n.fc-google-title.fc-dark { color: #8ab4f8; }\n.fc-google-desc {\n  font-size: 0.83rem;\n  color: #4d5156;\n  margin-top: 2px;\n}\n.fc-google-desc.fc-dark { color: #9aa0a6; }\n\n/* Mobile home screen */\n.fc-mobile-screen {\n  display: inline-flex;\n  flex-direction: column;\n  gap: 20px;\n  padding: 20px 16px;\n  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n  border-radius: 20px;\n  min-width: 200px;\n}\n.fc-mobile-screen.fc-dark {\n  background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);\n}\n.fc-mobile-row {\n  display: flex;\n  gap: 16px;\n  justify-content: center;\n}\n.fc-app-icon-wrap {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 5px;\n}\n.fc-app-icon {\n  width: 60px;\n  height: 60px;\n  border-radius: 14px;\n  overflow: hidden;\n  box-shadow: 0 2px 8px rgba(0,0,0,0.3);\n  background: #fff;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n.fc-app-icon canvas {\n  width: 60px;\n  height: 60px;\n  border-radius: 14px;\n  display: block;\n}\n.fc-app-icon-name {\n  font-size: 0.7rem;\n  color: #fff;\n  text-align: center;\n  max-width: 60px;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n  text-shadow: 0 1px 3px rgba(0,0,0,0.5);\n}\n\n/* Windows taskbar */\n.fc-taskbar {\n  display: flex;\n  align-items: center;\n  gap: 2px;\n  padding: 6px 8px;\n  background: #1a1a2a;\n  border-radius: 6px;\n}\n.fc-taskbar-item {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 5px 10px;\n  border-radius: 4px;\n  background: rgba(255,255,255,0.08);\n  border-bottom: 2px solid #3b82f6;\n  font-size: 0.8rem;\n  color: #f1f5f9;\n}\n.fc-taskbar-item canvas {\n  width: 20px; height: 20px;\n  flex-shrink: 0;\n}\n\n#fc-placeholder-msg {\n  margin-top: 28px;\n  padding: 24px;\n  background: #f8fafc;\n  border: 1.5px dashed #cbd5e1;\n  border-radius: 10px;\n  text-align: center;\n  color: #94a3b8;\n  font-size: 0.9rem;\n}\n\n#fc-sections { display: none; }\n\u003c/style\u003e\n\u003cdiv id=\"fc-drop-zone\"\u003e\n  \u003cp\u003eDrag \u0026amp; drop your favicon here\u003c/p\u003e","title":"Favicon Checker"},{"content":"Create a favicon for your website in seconds. Choose from text, emoji, or upload your own image — then download ready-to-use PNG files and copy the HTML snippet.\nText / Shape Emoji Upload Image Characters (1–2) Font Size 36 Font Weight Bold Black (900) Regular Shape Circle Rounded Square Background Color Text Color Pick an Emoji Or type/paste: Background Color Shape Circle Rounded Square \u0026#128444; Click or drag \u0026amp; drop an image here\nPNG, JPG, GIF, SVG, WebP\nCrop / Scale 100% Offset X Offset Y Live Preview 64×64 (edit canvas) 32×32 16×16 \u0026#8615; Download 32×32 PNG \u0026#8615; Download 16×16 PNG \u0026#8615; Download 64×64 PNG HTML meta tags — paste into your \u0026lt;head\u0026gt;\nCopy Copied to clipboard!\nRelated: Pick colors for your favicon → Color Picker ","permalink":"https://productivity-works.com/tools/favicon-generator/","summary":"\u003cp\u003eCreate a favicon for your website in seconds. Choose from text, emoji, or upload your own image — then download ready-to-use PNG files and copy the HTML snippet.\u003c/p\u003e\n\u003cdiv id=\"fav-app\"\u003e\n\u003cstyle\u003e\n#fav-app *,\n#fav-app *::before,\n#fav-app *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n#fav-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  max-width: 780px;\n  color: #1e1b2e;\n}\n\n#fav-app .fav-tabs {\n  display: flex;\n  gap: 4px;\n  margin-bottom: 24px;\n  background: #f3f0ff;\n  padding: 4px;\n  border-radius: 10px;\n}\n\n#fav-app .fav-tab {\n  flex: 1;\n  padding: 9px 12px;\n  border: none;\n  background: transparent;\n  border-radius: 7px;\n  font-size: 14px;\n  font-weight: 500;\n  cursor: pointer;\n  color: #6b6b8a;\n  transition: background 0.15s, color 0.15s;\n}\n\n#fav-app .fav-tab.active {\n  background: #7c3aed;\n  color: #fff;\n}\n\n#fav-app .fav-panel { display: none; }\n#fav-app .fav-panel.active { display: block; }\n\n#fav-app .fav-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 16px;\n  margin-bottom: 16px;\n}\n\n@media (max-width: 540px) {\n  #fav-app .fav-grid { grid-template-columns: 1fr; }\n  #fav-app .fav-tabs { flex-direction: column; }\n}\n\n#fav-app label {\n  display: block;\n  font-size: 13px;\n  font-weight: 600;\n  color: #4b4569;\n  margin-bottom: 6px;\n}\n\n#fav-app input[type=\"text\"],\n#fav-app input[type=\"number\"],\n#fav-app select,\n#fav-app textarea {\n  width: 100%;\n  padding: 9px 11px;\n  border: 1.5px solid #d4c9f8;\n  border-radius: 8px;\n  font-size: 15px;\n  background: #faf9ff;\n  color: #1e1b2e;\n  outline: none;\n  transition: border-color 0.15s;\n}\n\n#fav-app input[type=\"text\"]:focus,\n#fav-app input[type=\"number\"]:focus,\n#fav-app select:focus,\n#fav-app textarea:focus {\n  border-color: #7c3aed;\n}\n\n#fav-app input[type=\"color\"] {\n  width: 48px;\n  height: 38px;\n  border: 1.5px solid #d4c9f8;\n  border-radius: 8px;\n  padding: 2px;\n  cursor: pointer;\n  background: #faf9ff;\n}\n\n#fav-app .color-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n\n#fav-app .color-row input[type=\"text\"] {\n  flex: 1;\n  font-family: monospace;\n}\n\n#fav-app .shape-row {\n  display: flex;\n  gap: 8px;\n}\n\n#fav-app .shape-btn {\n  flex: 1;\n  padding: 8px 4px;\n  border: 1.5px solid #d4c9f8;\n  border-radius: 8px;\n  background: #faf9ff;\n  font-size: 13px;\n  cursor: pointer;\n  color: #4b4569;\n  font-weight: 500;\n  transition: border-color 0.15s, background 0.15s, color 0.15s;\n}\n\n#fav-app .shape-btn.active {\n  border-color: #7c3aed;\n  background: #ede9fe;\n  color: #7c3aed;\n}\n\n#fav-app .preview-section {\n  background: #f8f6ff;\n  border: 1.5px solid #e0d8fc;\n  border-radius: 12px;\n  padding: 20px;\n  margin-bottom: 20px;\n  text-align: center;\n}\n\n#fav-app .preview-section h3 {\n  font-size: 13px;\n  font-weight: 600;\n  color: #6b6b8a;\n  margin-bottom: 16px;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n\n#fav-app .preview-canvases {\n  display: flex;\n  align-items: flex-end;\n  justify-content: center;\n  gap: 24px;\n  flex-wrap: wrap;\n}\n\n#fav-app .preview-item {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 6px;\n}\n\n#fav-app .preview-item span {\n  font-size: 12px;\n  color: #9490b2;\n}\n\n#fav-app canvas {\n  image-rendering: pixelated;\n  border-radius: 4px;\n  box-shadow: 0 1px 4px rgba(124,58,237,0.12);\n  background: transparent;\n}\n\n#fav-app .fav-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  padding: 10px 20px;\n  border: none;\n  border-radius: 8px;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: opacity 0.15s, transform 0.1s;\n  text-decoration: none;\n}\n\n#fav-app .fav-btn:active { transform: scale(0.97); }\n\n#fav-app .btn-primary {\n  background: #7c3aed;\n  color: #fff;\n}\n\n#fav-app .btn-secondary {\n  background: #ede9fe;\n  color: #7c3aed;\n}\n\n#fav-app .btn-primary:hover { opacity: 0.88; }\n#fav-app .btn-secondary:hover { opacity: 0.80; }\n\n#fav-app .download-row {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n  margin-bottom: 20px;\n}\n\n#fav-app .meta-section {\n  background: #1e1b2e;\n  border-radius: 10px;\n  padding: 16px;\n  margin-bottom: 8px;\n  position: relative;\n}\n\n#fav-app .meta-section pre {\n  color: #c4b5fd;\n  font-size: 13px;\n  line-height: 1.6;\n  white-space: pre-wrap;\n  word-break: break-all;\n  font-family: 'Menlo', 'Consolas', monospace;\n}\n\n#fav-app .copy-btn {\n  position: absolute;\n  top: 10px;\n  right: 10px;\n  padding: 5px 12px;\n  background: #7c3aed;\n  color: #fff;\n  border: none;\n  border-radius: 6px;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n}\n\n#fav-app .copy-btn:hover { opacity: 0.85; }\n\n#fav-app .upload-zone {\n  border: 2px dashed #c4b5fd;\n  border-radius: 10px;\n  padding: 32px 16px;\n  text-align: center;\n  cursor: pointer;\n  background: #faf9ff;\n  margin-bottom: 16px;\n  transition: background 0.15s, border-color 0.15s;\n}\n\n#fav-app .upload-zone:hover,\n#fav-app .upload-zone.dragover {\n  background: #ede9fe;\n  border-color: #7c3aed;\n}\n\n#fav-app .upload-zone p {\n  font-size: 14px;\n  color: #6b6b8a;\n  margin-top: 8px;\n}\n\n#fav-app .upload-icon {\n  font-size: 36px;\n  line-height: 1;\n}\n\n#fav-app .emoji-grid {\n  display: grid;\n  grid-template-columns: repeat(8, 1fr);\n  gap: 6px;\n  margin-bottom: 12px;\n  max-height: 200px;\n  overflow-y: auto;\n  padding: 4px;\n}\n\n#fav-app .emoji-btn {\n  font-size: 22px;\n  background: #faf9ff;\n  border: 1.5px solid #e0d8fc;\n  border-radius: 7px;\n  padding: 4px;\n  cursor: pointer;\n  text-align: center;\n  line-height: 1.4;\n  transition: background 0.1s, border-color 0.1s;\n}\n\n#fav-app .emoji-btn:hover { background: #ede9fe; border-color: #7c3aed; }\n#fav-app .emoji-btn.active { background: #7c3aed; border-color: #7c3aed; }\n\n#fav-app .emoji-custom {\n  display: flex;\n  gap: 10px;\n  align-items: center;\n  margin-bottom: 16px;\n}\n\n#fav-app .emoji-custom input {\n  font-size: 22px;\n  width: 60px;\n  text-align: center;\n}\n\n#fav-app .section-divider {\n  border: none;\n  border-top: 1.5px solid #e0d8fc;\n  margin: 20px 0;\n}\n\n#fav-app .info-text {\n  font-size: 13px;\n  color: #9490b2;\n  margin-bottom: 14px;\n}\n\n#fav-app .fav-field { margin-bottom: 14px; }\n\n#fav-app .range-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n\n#fav-app .range-row input[type=\"range\"] {\n  flex: 1;\n  accent-color: #7c3aed;\n}\n\n#fav-app .range-val {\n  font-size: 14px;\n  font-weight: 600;\n  color: #7c3aed;\n  min-width: 28px;\n  text-align: right;\n}\n\u003c/style\u003e\n\u003cdiv class=\"fav-tabs\"\u003e\n  \u003cbutton class=\"fav-tab active\" onclick=\"favSwitchTab('text', this)\"\u003eText / Shape\u003c/button\u003e\n  \u003cbutton class=\"fav-tab\" onclick=\"favSwitchTab('emoji', this)\"\u003eEmoji\u003c/button\u003e\n  \u003cbutton class=\"fav-tab\" onclick=\"favSwitchTab('upload', this)\"\u003eUpload Image\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- TEXT TAB --\u003e\n\u003cdiv id=\"fav-panel-text\" class=\"fav-panel active\"\u003e\n  \u003cdiv class=\"fav-grid\"\u003e\n    \u003cdiv\u003e\n      \u003cdiv class=\"fav-field\"\u003e\n        \u003clabel for=\"fav-text\"\u003eCharacters (1–2)\u003c/label\u003e\n        \u003cinput type=\"text\" id=\"fav-text\" maxlength=\"2\" value=\"FV\" oninput=\"favRender()\"\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"fav-field\"\u003e\n        \u003clabel\u003eFont Size\u003c/label\u003e\n        \u003cdiv class=\"range-row\"\u003e\n          \u003cinput type=\"range\" id=\"fav-fontsize\" min=\"14\" max=\"52\" value=\"36\" oninput=\"favRender(); document.getElementById('fav-fontsize-val').textContent=this.value\"\u003e\n          \u003cspan class=\"range-val\" id=\"fav-fontsize-val\"\u003e36\u003c/span\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"fav-field\"\u003e\n        \u003clabel\u003eFont Weight\u003c/label\u003e\n        \u003cselect id=\"fav-fontweight\" onchange=\"favRender()\"\u003e\n          \u003coption value=\"700\" selected\u003eBold\u003c/option\u003e\n          \u003coption value=\"900\"\u003eBlack (900)\u003c/option\u003e\n          \u003coption value=\"400\"\u003eRegular\u003c/option\u003e\n        \u003c/select\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003cdiv class=\"fav-field\"\u003e\n        \u003clabel\u003eShape\u003c/label\u003e\n        \u003cdiv class=\"shape-row\"\u003e\n          \u003cbutton class=\"shape-btn active\" onclick=\"favSetShape('circle', this)\"\u003eCircle\u003c/button\u003e\n          \u003cbutton class=\"shape-btn\" onclick=\"favSetShape('rounded', this)\"\u003eRounded\u003c/button\u003e\n          \u003cbutton class=\"shape-btn\" onclick=\"favSetShape('square', this)\"\u003eSquare\u003c/button\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"fav-field\"\u003e\n        \u003clabel\u003eBackground Color\u003c/label\u003e\n        \u003cdiv class=\"color-row\"\u003e\n          \u003cinput type=\"color\" id=\"fav-bg\" value=\"#7c3aed\" oninput=\"favSyncColor('bg')\"\u003e\n          \u003cinput type=\"text\" id=\"fav-bg-hex\" value=\"#7c3aed\" maxlength=\"7\" oninput=\"favSyncHex('bg')\"\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"fav-field\"\u003e\n        \u003clabel\u003eText Color\u003c/label\u003e\n        \u003cdiv class=\"color-row\"\u003e\n          \u003cinput type=\"color\" id=\"fav-fg\" value=\"#ffffff\" oninput=\"favSyncColor('fg')\"\u003e\n          \u003cinput type=\"text\" id=\"fav-fg-hex\" value=\"#ffffff\" maxlength=\"7\" oninput=\"favSyncHex('fg')\"\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- EMOJI TAB --\u003e\n\u003cdiv id=\"fav-panel-emoji\" class=\"fav-panel\"\u003e\n  \u003cdiv class=\"fav-field\"\u003e\n    \u003clabel\u003ePick an Emoji\u003c/label\u003e\n    \u003cdiv class=\"emoji-grid\" id=\"fav-emoji-grid\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"emoji-custom\"\u003e\n    \u003clabel style=\"white-space:nowrap; margin:0;\"\u003eOr type/paste:\u003c/label\u003e\n    \u003cinput type=\"text\" id=\"fav-emoji-custom\" maxlength=\"4\" placeholder=\"e.g. 🚀\" style=\"width:70px; font-size:22px; text-align:center;\" oninput=\"favSetEmojiCustom(this.value)\"\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"fav-field\"\u003e\n    \u003clabel\u003eBackground Color\u003c/label\u003e\n    \u003cdiv class=\"color-row\"\u003e\n      \u003cinput type=\"color\" id=\"fav-emoji-bg\" value=\"#7c3aed\" oninput=\"favSyncEmojiColor()\"\u003e\n      \u003cinput type=\"text\" id=\"fav-emoji-bg-hex\" value=\"#7c3aed\" maxlength=\"7\" oninput=\"favSyncEmojiHex()\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"fav-field\"\u003e\n    \u003clabel\u003eShape\u003c/label\u003e\n    \u003cdiv class=\"shape-row\"\u003e\n      \u003cbutton class=\"shape-btn active\" id=\"emoji-shape-circle\" onclick=\"favSetEmojiShape('circle', this)\"\u003eCircle\u003c/button\u003e\n      \u003cbutton class=\"shape-btn\" id=\"emoji-shape-rounded\" onclick=\"favSetEmojiShape('rounded', this)\"\u003eRounded\u003c/button\u003e\n      \u003cbutton class=\"shape-btn\" id=\"emoji-shape-square\" onclick=\"favSetEmojiShape('square', this)\"\u003eSquare\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- UPLOAD TAB --\u003e\n\u003cdiv id=\"fav-panel-upload\" class=\"fav-panel\"\u003e\n  \u003cdiv class=\"upload-zone\" id=\"fav-upload-zone\" onclick=\"document.getElementById('fav-file-input').click()\" ondragover=\"favDragOver(event)\" ondragleave=\"favDragLeave(event)\" ondrop=\"favDrop(event)\"\u003e\n    \u003cdiv class=\"upload-icon\"\u003e\u0026#128444;\u003c/div\u003e\n    \u003cp\u003eClick or drag \u0026amp; drop an image here\u003c/p\u003e","title":"Favicon Generator — Free Online Tool"},{"content":" Fibonacci Calculator Nth Term Sequence Is Fibonacci? Golden Ratio Spiral N (1 \u0026ndash; 1000) F(N) Number of Terms (1 \u0026ndash; 100) Copy Enter a number As N increases, F(N)/F(N-1) converges to \u0026phi; (phi) \u0026asymp; 1.6180339887\u0026hellip;\nNF(N)F(N)/F(N-1)Diff from \u0026phi; \u0026phi; = 1.6180339887498948482\u0026hellip; Golden spiral constructed from Fibonacci squares.\nSteps: 10 Related Tools Convert number bases → Number Base Converter Calculate percentages → Percentage Calculator ","permalink":"https://productivity-works.com/tools/fibonacci-calculator/","summary":"\u003cdiv id=\"fib-app\"\u003e\n\u003cstyle\u003e\n#fib-app *, #fib-app *::before, #fib-app *::after { box-sizing: border-box; margin: 0; padding: 0; }\n#fib-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n  max-width: 740px;\n  margin: 0 auto;\n  padding: 12px;\n  color: #1e293b;\n}\n#fib-app h2.fib-title {\n  font-size: 18px;\n  font-weight: 700;\n  color: #0f172a;\n  margin-bottom: 14px;\n  letter-spacing: -0.3px;\n}\n#fib-app .fib-tabs {\n  display: flex;\n  gap: 6px;\n  flex-wrap: wrap;\n  margin-bottom: 16px;\n}\n#fib-app .fib-tab-btn {\n  padding: 7px 16px;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 20px;\n  background: #f8fafc;\n  color: #475569;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#fib-app .fib-tab-btn:hover { background: #f1f5f9; border-color: #cbd5e1; }\n#fib-app .fib-tab-btn.fib-active {\n  background: #7c3aed;\n  border-color: #7c3aed;\n  color: #fff;\n}\n#fib-app .fib-panel { display: none; }\n#fib-app .fib-panel.fib-visible { display: block; }\n#fib-app .fib-card {\n  background: #fff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 18px;\n  margin-bottom: 14px;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.05);\n}\n#fib-app label.fib-label {\n  display: block;\n  font-size: 12px;\n  font-weight: 600;\n  color: #64748b;\n  margin-bottom: 5px;\n  text-transform: uppercase;\n  letter-spacing: 0.4px;\n}\n#fib-app input[type=\"number\"], #fib-app input[type=\"text\"] {\n  width: 100%;\n  padding: 9px 12px;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 8px;\n  font-size: 15px;\n  color: #1e293b;\n  background: #f8fafc;\n  outline: none;\n  transition: border-color 0.15s;\n}\n#fib-app input[type=\"number\"]:focus, #fib-app input[type=\"text\"]:focus {\n  border-color: #7c3aed;\n  background: #fff;\n}\n#fib-app .fib-btn {\n  display: inline-block;\n  padding: 9px 22px;\n  background: #7c3aed;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 14px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.15s;\n  margin-top: 10px;\n}\n#fib-app .fib-btn:hover { background: #6d28d9; }\n#fib-app .fib-btn-sm {\n  display: inline-block;\n  padding: 6px 14px;\n  background: #f1f5f9;\n  color: #475569;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 7px;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#fib-app .fib-btn-sm:hover { background: #e2e8f0; }\n#fib-app .fib-btn-sm.fib-copied { background: #dcfce7; border-color: #86efac; color: #16a34a; }\n#fib-app .fib-result-box {\n  margin-top: 12px;\n  padding: 14px;\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  word-break: break-all;\n}\n#fib-app .fib-result-label {\n  font-size: 11px;\n  font-weight: 700;\n  color: #94a3b8;\n  text-transform: uppercase;\n  letter-spacing: 0.5px;\n  margin-bottom: 5px;\n}\n#fib-app .fib-result-val {\n  font-size: 15px;\n  font-weight: 700;\n  color: #7c3aed;\n  word-break: break-all;\n  line-height: 1.5;\n}\n#fib-app .fib-result-sub {\n  font-size: 12px;\n  color: #64748b;\n  margin-top: 4px;\n}\n#fib-app .fib-seq-wrap {\n  margin-top: 10px;\n  max-height: 180px;\n  overflow-y: auto;\n  padding: 10px 12px;\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  font-size: 13px;\n  color: #334155;\n  line-height: 1.8;\n  word-break: break-all;\n}\n#fib-app .fib-seq-actions {\n  display: flex;\n  gap: 8px;\n  margin-top: 8px;\n  flex-wrap: wrap;\n  align-items: center;\n}\n#fib-app .fib-ratio-table {\n  width: 100%;\n  border-collapse: collapse;\n  margin-top: 10px;\n  font-size: 13px;\n}\n#fib-app .fib-ratio-table th {\n  background: #f1f5f9;\n  padding: 7px 10px;\n  text-align: left;\n  font-size: 11px;\n  font-weight: 700;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.4px;\n  border-bottom: 1.5px solid #e2e8f0;\n}\n#fib-app .fib-ratio-table td {\n  padding: 7px 10px;\n  border-bottom: 1px solid #f1f5f9;\n  color: #334155;\n  vertical-align: top;\n}\n#fib-app .fib-ratio-table tr:last-child td { border-bottom: none; }\n#fib-app .fib-ratio-val { font-weight: 700; color: #7c3aed; font-size: 12px; }\n#fib-app .fib-phi-highlight { color: #d97706; font-weight: 700; }\n#fib-app .fib-check-badge {\n  display: inline-block;\n  padding: 4px 12px;\n  border-radius: 20px;\n  font-size: 13px;\n  font-weight: 700;\n  margin-top: 8px;\n}\n#fib-app .fib-check-yes { background: #dcfce7; color: #16a34a; }\n#fib-app .fib-check-no  { background: #fee2e2; color: #dc2626; }\n#fib-app canvas#fib-canvas {\n  display: block;\n  margin: 12px auto 0;\n  border-radius: 10px;\n  border: 1.5px solid #e2e8f0;\n  max-width: 100%;\n}\n#fib-app .fib-spiral-ctrl {\n  display: flex;\n  gap: 8px;\n  align-items: center;\n  margin-top: 10px;\n  flex-wrap: wrap;\n}\n#fib-app .fib-spiral-ctrl label { font-size: 12px; color: #64748b; font-weight: 600; }\n#fib-app .fib-spiral-ctrl input[type=\"range\"] { flex: 1; min-width: 80px; accent-color: #7c3aed; }\n#fib-app .fib-err {\n  color: #dc2626;\n  font-size: 13px;\n  margin-top: 8px;\n  font-weight: 600;\n}\n#fib-app .fib-info-row {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n  margin-top: 10px;\n}\n#fib-app .fib-info-chip {\n  background: #ede9fe;\n  color: #5b21b6;\n  border-radius: 8px;\n  padding: 5px 12px;\n  font-size: 12px;\n  font-weight: 600;\n}\n@media (max-width: 500px) {\n  #fib-app .fib-ratio-table { font-size: 11px; }\n  #fib-app .fib-ratio-table td, #fib-app .fib-ratio-table th { padding: 5px 6px; }\n}\n\u003c/style\u003e\n\u003ch2 class=\"fib-title\"\u003eFibonacci Calculator\u003c/h2\u003e\n\u003cdiv class=\"fib-tabs\"\u003e\n  \u003cbutton class=\"fib-tab-btn fib-active\" onclick=\"fibSwitchTab('nth',this)\"\u003eNth Term\u003c/button\u003e\n  \u003cbutton class=\"fib-tab-btn\" onclick=\"fibSwitchTab('seq',this)\"\u003eSequence\u003c/button\u003e\n  \u003cbutton class=\"fib-tab-btn\" onclick=\"fibSwitchTab('check',this)\"\u003eIs Fibonacci?\u003c/button\u003e\n  \u003cbutton class=\"fib-tab-btn\" onclick=\"fibSwitchTab('ratio',this)\"\u003eGolden Ratio\u003c/button\u003e\n  \u003cbutton class=\"fib-tab-btn\" onclick=\"fibSwitchTab('spiral',this)\"\u003eSpiral\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Panel: Nth Term --\u003e\n\u003cdiv id=\"fib-panel-nth\" class=\"fib-panel fib-visible\"\u003e\n  \u003cdiv class=\"fib-card\"\u003e\n    \u003clabel class=\"fib-label\"\u003eN (1 \u0026ndash; 1000)\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"fib-nth-input\" min=\"1\" max=\"1000\" value=\"10\" oninput=\"fibCalcNth()\"\u003e\n    \u003cdiv id=\"fib-nth-err\" class=\"fib-err\"\u003e\u003c/div\u003e\n    \u003cdiv id=\"fib-nth-result\" class=\"fib-result-box\" style=\"display:none;\"\u003e\n      \u003cdiv class=\"fib-result-label\"\u003eF(N)\u003c/div\u003e\n      \u003cdiv class=\"fib-result-val\" id=\"fib-nth-val\"\u003e\u003c/div\u003e\n      \u003cdiv class=\"fib-result-sub\" id=\"fib-nth-sub\"\u003e\u003c/div\u003e\n      \u003cdiv class=\"fib-info-row\" id=\"fib-nth-chips\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Panel: Sequence --\u003e\n\u003cdiv id=\"fib-panel-seq\" class=\"fib-panel\"\u003e\n  \u003cdiv class=\"fib-card\"\u003e\n    \u003clabel class=\"fib-label\"\u003eNumber of Terms (1 \u0026ndash; 100)\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"fib-seq-input\" min=\"1\" max=\"100\" value=\"15\" oninput=\"fibGenSeq()\"\u003e\n    \u003cdiv id=\"fib-seq-err\" class=\"fib-err\"\u003e\u003c/div\u003e\n    \u003cdiv id=\"fib-seq-wrap\" class=\"fib-seq-wrap\" style=\"display:none;\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"fib-seq-actions\" id=\"fib-seq-actions\" style=\"display:none;\"\u003e\n      \u003cbutton class=\"fib-btn-sm\" id=\"fib-copy-btn\" onclick=\"fibCopySeq()\"\u003eCopy\u003c/button\u003e\n      \u003cspan id=\"fib-seq-count\" style=\"font-size:12px;color:#94a3b8;\"\u003e\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Panel: Is Fibonacci? --\u003e\n\u003cdiv id=\"fib-panel-check\" class=\"fib-panel\"\u003e\n  \u003cdiv class=\"fib-card\"\u003e\n    \u003clabel class=\"fib-label\"\u003eEnter a number\u003c/label\u003e\n    \u003cinput type=\"text\" id=\"fib-check-input\" placeholder=\"e.g. 89\" oninput=\"fibCheckNum()\"\u003e\n    \u003cdiv id=\"fib-check-err\" class=\"fib-err\"\u003e\u003c/div\u003e\n    \u003cdiv id=\"fib-check-result\" style=\"margin-top:10px;display:none;\"\u003e\n      \u003cdiv id=\"fib-check-badge\"\u003e\u003c/div\u003e\n      \u003cdiv class=\"fib-result-sub\" id=\"fib-check-sub\" style=\"margin-top:6px;\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Panel: Golden Ratio --\u003e\n\u003cdiv id=\"fib-panel-ratio\" class=\"fib-panel\"\u003e\n  \u003cdiv class=\"fib-card\"\u003e\n    \u003cp style=\"font-size:13px;color:#64748b;margin-bottom:10px;\"\u003eAs N increases, F(N)/F(N-1) converges to \u0026phi; (phi) \u0026asymp; 1.6180339887\u0026hellip;\u003c/p\u003e","title":"Fibonacci Calculator"},{"content":" Create Study Stats Manage Add a Card Front (question) Back (answer) + Add Card Bulk import via CSV — one card per line: front,back\nImport CSV No cards yet! Go to Create tab and add some cards first.\n\u0026lt;div class=\u0026quot;fc-study-header\u0026quot;\u0026gt; \u0026lt;span id=\u0026quot;fc-study-counter\u0026quot; style=\u0026quot;font-size:.85rem;color:var(--slate-500);font-weight:600;\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026quot;btn btn-ghost btn-sm\u0026quot; id=\u0026quot;fc-shuffle-btn\u0026quot;\u0026gt;Shuffle\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-progress-bar\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fc-progress-fill\u0026quot; id=\u0026quot;fc-progress-fill\u0026quot; style=\u0026quot;width:0%\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Flip card --\u0026gt; \u0026lt;div class=\u0026quot;fc-flip-scene\u0026quot; id=\u0026quot;fc-flip-scene\u0026quot; title=\u0026quot;Click to flip\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fc-flip-card\u0026quot; id=\u0026quot;fc-flip-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fc-flip-face fc-flip-front\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fc-flip-label\u0026quot;\u0026gt;Question\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-flip-text\u0026quot; id=\u0026quot;fc-flip-front-text\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-flip-hint\u0026quot;\u0026gt;Click to reveal answer\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-flip-face fc-flip-back\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fc-flip-label\u0026quot;\u0026gt;Answer\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-flip-text\u0026quot; id=\u0026quot;fc-flip-back-text\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Answer buttons (hidden until flipped) --\u0026gt; \u0026lt;div class=\u0026quot;fc-study-actions\u0026quot; id=\u0026quot;fc-answer-btns\u0026quot; style=\u0026quot;display:none;\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;btn btn-danger\u0026quot; id=\u0026quot;fc-still-btn\u0026quot;\u0026gt;Still Learning\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;btn btn-success\u0026quot; id=\u0026quot;fc-know-btn\u0026quot;\u0026gt;Know It\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; Session Complete! Here's how you did this round:\n\u0026lt;div class=\u0026quot;fc-summary-grid\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fc-summary-card green\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fc-num\u0026quot; id=\u0026quot;fc-sum-know\u0026quot;\u0026gt;0\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-lbl\u0026quot;\u0026gt;Knew It\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-summary-card red\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fc-num\u0026quot; id=\u0026quot;fc-sum-still\u0026quot;\u0026gt;0\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-lbl\u0026quot;\u0026gt;Still Learning\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-summary-card blue\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fc-num\u0026quot; id=\u0026quot;fc-sum-total\u0026quot;\u0026gt;0\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-lbl\u0026quot;\u0026gt;Reviewed\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-btn-row\u0026quot; style=\u0026quot;justify-content:center;\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;btn btn-primary\u0026quot; id=\u0026quot;fc-restart-btn\u0026quot;\u0026gt;Study Again\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;btn btn-ghost\u0026quot; id=\u0026quot;fc-review-still-btn\u0026quot;\u0026gt;Review Struggling Cards\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; Your Progress 0 Total Cards 0 Mastered 0 Still Learning 0 Current Streak Mastery Progress 0% mastered Reset All Stats\nExport Deck Download all cards as a JSON file to back up or share your deck.\nExport JSON Import Deck Load a previously exported JSON deck. Existing cards will be kept unless you clear first.\nChoose JSON File Clear All Cards Remove every card and reset stats. This cannot be undone.\nClear All Test reading speed → Reading Speed Calculator Focus with Pomodoro → Pomodoro Timer Test typing speed → Typing Speed Test Related Articles How to Use ChatGPT for Studying: The Complete Student Guide 2026 Best AI Tools for Teachers \u0026amp; Educators 2026: Complete Guide ","permalink":"https://productivity-works.com/tools/flashcard-maker/","summary":"\u003cdiv id=\"fc-app\"\u003e\n\u003cstyle\u003e\n/* ── Reset \u0026 base ─────────────────────────────────────────── */\n#fc-app *,\n#fc-app *::before,\n#fc-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\n#fc-app {\n  --slate-50:  #f8fafc;\n  --slate-100: #f1f5f9;\n  --slate-200: #e2e8f0;\n  --slate-300: #cbd5e1;\n  --slate-400: #94a3b8;\n  --slate-500: #64748b;\n  --slate-600: #475569;\n  --slate-700: #334155;\n  --slate-800: #1e293b;\n  --slate-900: #0f172a;\n  --accent:    #3b82f6;\n  --accent-h:  #2563eb;\n  --green:     #22c55e;\n  --red:       #ef4444;\n  --amber:     #f59e0b;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  color: var(--slate-800);\n  background: var(--slate-50);\n  border-radius: 12px;\n  padding: 24px 20px;\n  max-width: 760px;\n  margin: 0 auto;\n}\n\n/* ── Tab nav ──────────────────────────────────────────────── */\n#fc-app .fc-tabs {\n  display: flex;\n  gap: 4px;\n  background: var(--slate-200);\n  border-radius: 10px;\n  padding: 4px;\n  margin-bottom: 24px;\n}\n\n#fc-app .fc-tab {\n  flex: 1;\n  padding: 9px 6px;\n  border: none;\n  background: transparent;\n  border-radius: 7px;\n  font-size: 0.85rem;\n  font-weight: 600;\n  color: var(--slate-500);\n  cursor: pointer;\n  transition: background .18s, color .18s;\n}\n\n#fc-app .fc-tab.active {\n  background: #fff;\n  color: var(--slate-800);\n  box-shadow: 0 1px 4px rgba(0,0,0,.12);\n}\n\n/* ── Panels ───────────────────────────────────────────────── */\n#fc-app .fc-panel { display: none; }\n#fc-app .fc-panel.active { display: block; }\n\n/* ── Section headings ─────────────────────────────────────── */\n#fc-app h2 {\n  font-size: 1.1rem;\n  color: var(--slate-700);\n  margin-bottom: 14px;\n}\n\n/* ── Form elements ────────────────────────────────────────── */\n#fc-app label {\n  display: block;\n  font-size: 0.8rem;\n  font-weight: 600;\n  color: var(--slate-600);\n  margin-bottom: 4px;\n}\n\n#fc-app input[type=\"text\"],\n#fc-app textarea {\n  width: 100%;\n  padding: 9px 12px;\n  border: 1.5px solid var(--slate-300);\n  border-radius: 8px;\n  font-size: 0.95rem;\n  color: var(--slate-800);\n  background: #fff;\n  outline: none;\n  transition: border-color .15s;\n  resize: vertical;\n}\n\n#fc-app input[type=\"text\"]:focus,\n#fc-app textarea:focus {\n  border-color: var(--accent);\n}\n\n#fc-app .fc-row {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 12px;\n  margin-bottom: 12px;\n}\n\n@media (max-width: 500px) {\n  #fc-app .fc-row { grid-template-columns: 1fr; }\n}\n\n/* ── Buttons ──────────────────────────────────────────────── */\n#fc-app .btn {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  gap: 6px;\n  padding: 9px 18px;\n  border: none;\n  border-radius: 8px;\n  font-size: 0.88rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background .15s, transform .1s;\n}\n#fc-app .btn:active { transform: scale(.97); }\n\n#fc-app .btn-primary  { background: var(--accent);  color: #fff; }\n#fc-app .btn-primary:hover { background: var(--accent-h); }\n\n#fc-app .btn-success  { background: var(--green);   color: #fff; }\n#fc-app .btn-success:hover { background: #16a34a; }\n\n#fc-app .btn-danger   { background: var(--red);     color: #fff; }\n#fc-app .btn-danger:hover { background: #dc2626; }\n\n#fc-app .btn-ghost {\n  background: var(--slate-100);\n  color: var(--slate-700);\n  border: 1.5px solid var(--slate-200);\n}\n#fc-app .btn-ghost:hover { background: var(--slate-200); }\n\n#fc-app .btn-sm {\n  padding: 5px 11px;\n  font-size: 0.78rem;\n}\n\n#fc-app .btn-full { width: 100%; }\n\n#fc-app .fc-btn-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-top: 10px;\n}\n\n/* ── Card list ────────────────────────────────────────────── */\n#fc-app .fc-card-list {\n  list-style: none;\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n  margin-top: 20px;\n  max-height: 340px;\n  overflow-y: auto;\n  padding-right: 2px;\n}\n\n#fc-app .fc-card-item {\n  background: #fff;\n  border: 1.5px solid var(--slate-200);\n  border-radius: 9px;\n  padding: 10px 12px;\n  display: flex;\n  align-items: flex-start;\n  gap: 10px;\n}\n\n#fc-app .fc-card-item .fc-card-texts {\n  flex: 1;\n  min-width: 0;\n}\n\n#fc-app .fc-card-item .fc-front {\n  font-weight: 600;\n  font-size: 0.9rem;\n  color: var(--slate-800);\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n#fc-app .fc-card-item .fc-back {\n  font-size: 0.82rem;\n  color: var(--slate-500);\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n#fc-app .fc-card-item .fc-card-actions {\n  display: flex;\n  gap: 6px;\n  flex-shrink: 0;\n}\n\n#fc-app .fc-empty {\n  text-align: center;\n  color: var(--slate-400);\n  font-size: 0.9rem;\n  padding: 24px 0;\n}\n\n/* ── CSV import ───────────────────────────────────────────── */\n#fc-app .fc-csv-box {\n  margin-top: 20px;\n  padding: 16px;\n  background: var(--slate-100);\n  border-radius: 10px;\n  border: 1.5px dashed var(--slate-300);\n}\n\n#fc-app .fc-csv-box p {\n  font-size: 0.8rem;\n  color: var(--slate-500);\n  margin-bottom: 8px;\n}\n\n#fc-app .fc-csv-box code {\n  font-size: 0.78rem;\n  background: var(--slate-200);\n  padding: 1px 5px;\n  border-radius: 4px;\n}\n\n/* ── Flip card ────────────────────────────────────────────── */\n#fc-app .fc-study-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin-bottom: 14px;\n  flex-wrap: wrap;\n  gap: 8px;\n}\n\n#fc-app .fc-progress-bar {\n  width: 100%;\n  height: 6px;\n  background: var(--slate-200);\n  border-radius: 99px;\n  margin-bottom: 20px;\n  overflow: hidden;\n}\n\n#fc-app .fc-progress-fill {\n  height: 100%;\n  background: var(--accent);\n  border-radius: 99px;\n  transition: width .3s ease;\n}\n\n#fc-app .fc-flip-scene {\n  perspective: 1000px;\n  width: 100%;\n  max-width: 560px;\n  margin: 0 auto 24px;\n  cursor: pointer;\n  user-select: none;\n}\n\n#fc-app .fc-flip-card {\n  position: relative;\n  width: 100%;\n  padding-bottom: 56%;\n  transform-style: preserve-3d;\n  transition: transform .5s cubic-bezier(.4,0,.2,1);\n  border-radius: 14px;\n}\n\n#fc-app .fc-flip-card.flipped {\n  transform: rotateY(180deg);\n}\n\n#fc-app .fc-flip-face {\n  position: absolute;\n  inset: 0;\n  backface-visibility: hidden;\n  -webkit-backface-visibility: hidden;\n  border-radius: 14px;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  padding: 28px 24px;\n  text-align: center;\n  box-shadow: 0 4px 20px rgba(0,0,0,.10);\n}\n\n#fc-app .fc-flip-front {\n  background: #fff;\n  border: 2px solid var(--slate-200);\n}\n\n#fc-app .fc-flip-back {\n  background: var(--slate-800);\n  color: #fff;\n  transform: rotateY(180deg);\n}\n\n#fc-app .fc-flip-label {\n  font-size: 0.72rem;\n  font-weight: 700;\n  letter-spacing: .08em;\n  text-transform: uppercase;\n  color: var(--slate-400);\n  margin-bottom: 12px;\n}\n\n#fc-app .fc-flip-back .fc-flip-label {\n  color: var(--slate-400);\n}\n\n#fc-app .fc-flip-text {\n  font-size: 1.25rem;\n  font-weight: 600;\n  line-height: 1.4;\n  color: inherit;\n}\n\n#fc-app .fc-flip-hint {\n  font-size: 0.75rem;\n  color: var(--slate-400);\n  margin-top: 10px;\n}\n\n#fc-app .fc-study-actions {\n  display: flex;\n  gap: 12px;\n  justify-content: center;\n  margin-top: 4px;\n}\n\n#fc-app .fc-study-actions .btn {\n  flex: 1;\n  max-width: 200px;\n  padding: 13px;\n  font-size: 0.95rem;\n}\n\n#fc-app .fc-study-complete {\n  text-align: center;\n  padding: 32px 16px;\n}\n\n#fc-app .fc-study-complete h2 {\n  font-size: 1.6rem;\n  margin-bottom: 8px;\n}\n\n#fc-app .fc-study-complete p {\n  color: var(--slate-500);\n  margin-bottom: 20px;\n}\n\n#fc-app .fc-summary-grid {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 12px;\n  margin-bottom: 20px;\n}\n\n#fc-app .fc-summary-card {\n  background: #fff;\n  border: 1.5px solid var(--slate-200);\n  border-radius: 10px;\n  padding: 14px 8px;\n  text-align: center;\n}\n\n#fc-app .fc-summary-card .fc-num {\n  font-size: 1.9rem;\n  font-weight: 700;\n  line-height: 1;\n}\n\n#fc-app .fc-summary-card .fc-lbl {\n  font-size: 0.75rem;\n  color: var(--slate-500);\n  margin-top: 4px;\n}\n\n#fc-app .fc-summary-card.green .fc-num { color: var(--green); }\n#fc-app .fc-summary-card.red   .fc-num { color: var(--red); }\n#fc-app .fc-summary-card.blue  .fc-num { color: var(--accent); }\n\n/* ── Stats panel ──────────────────────────────────────────── */\n#fc-app .fc-stats-grid {\n  display: grid;\n  grid-template-columns: repeat(2, 1fr);\n  gap: 12px;\n  margin-bottom: 24px;\n}\n\n@media (max-width: 400px) {\n  #fc-app .fc-stats-grid { grid-template-columns: 1fr; }\n}\n\n#fc-app .fc-stat-box {\n  background: #fff;\n  border: 1.5px solid var(--slate-200);\n  border-radius: 10px;\n  padding: 16px;\n  text-align: center;\n}\n\n#fc-app .fc-stat-box .fc-stat-num {\n  font-size: 2.2rem;\n  font-weight: 700;\n  line-height: 1;\n}\n\n#fc-app .fc-stat-box .fc-stat-lbl {\n  font-size: 0.78rem;\n  color: var(--slate-500);\n  margin-top: 4px;\n}\n\n#fc-app .fc-stat-box.green .fc-stat-num { color: var(--green); }\n#fc-app .fc-stat-box.amber .fc-stat-num { color: var(--amber); }\n#fc-app .fc-stat-box.blue  .fc-stat-num { color: var(--accent); }\n#fc-app .fc-stat-box.slate .fc-stat-num { color: var(--slate-600); }\n\n/* ── Manage panel ─────────────────────────────────────────── */\n#fc-app .fc-manage-section {\n  background: #fff;\n  border: 1.5px solid var(--slate-200);\n  border-radius: 10px;\n  padding: 18px;\n  margin-bottom: 16px;\n}\n\n#fc-app .fc-manage-section h3 {\n  font-size: 0.95rem;\n  color: var(--slate-700);\n  margin-bottom: 12px;\n}\n\n#fc-app .fc-manage-section p {\n  font-size: 0.83rem;\n  color: var(--slate-500);\n  margin-bottom: 10px;\n}\n\n/* ── Inline edit ──────────────────────────────────────────── */\n#fc-app .fc-inline-edit {\n  display: none;\n  flex-direction: column;\n  gap: 6px;\n  margin-top: 8px;\n}\n#fc-app .fc-inline-edit.open { display: flex; }\n\n/* ── Toast ────────────────────────────────────────────────── */\n#fc-app .fc-toast {\n  position: fixed;\n  bottom: 28px;\n  left: 50%;\n  transform: translateX(-50%) translateY(20px);\n  background: var(--slate-800);\n  color: #fff;\n  padding: 10px 22px;\n  border-radius: 99px;\n  font-size: 0.85rem;\n  font-weight: 500;\n  opacity: 0;\n  pointer-events: none;\n  transition: opacity .25s, transform .25s;\n  z-index: 9999;\n  white-space: nowrap;\n}\n#fc-app .fc-toast.show {\n  opacity: 1;\n  transform: translateX(-50%) translateY(0);\n}\n\n/* ── No-cards overlay ─────────────────────────────────────── */\n#fc-app .fc-no-cards {\n  text-align: center;\n  padding: 48px 16px;\n  color: var(--slate-400);\n}\n#fc-app .fc-no-cards p { margin-top: 8px; font-size: 0.9rem; }\n\u003c/style\u003e\n\u003c!-- ── Tab Navigation ───────────────────────────────────── --\u003e\n\u003cdiv class=\"fc-tabs\" role=\"tablist\"\u003e\n  \u003cbutton class=\"fc-tab active\" data-tab=\"create\"  role=\"tab\"\u003eCreate\u003c/button\u003e\n  \u003cbutton class=\"fc-tab\"        data-tab=\"study\"   role=\"tab\"\u003eStudy\u003c/button\u003e\n  \u003cbutton class=\"fc-tab\"        data-tab=\"stats\"   role=\"tab\"\u003eStats\u003c/button\u003e\n  \u003cbutton class=\"fc-tab\"        data-tab=\"manage\"  role=\"tab\"\u003eManage\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- ══════════════════════════════════════════════════════════\n     CREATE PANEL\n════════════════════════════════════════════════════════════ --\u003e\n\u003cdiv id=\"fc-panel-create\" class=\"fc-panel active\" role=\"tabpanel\"\u003e\n  \u003ch2\u003eAdd a Card\u003c/h2\u003e\n  \u003cdiv class=\"fc-row\"\u003e\n    \u003cdiv\u003e\n      \u003clabel for=\"fc-front\"\u003eFront (question)\u003c/label\u003e\n      \u003ctextarea id=\"fc-front\" rows=\"3\" placeholder=\"What is the capital of France?\"\u003e\u003c/textarea\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003clabel for=\"fc-back\"\u003eBack (answer)\u003c/label\u003e\n      \u003ctextarea id=\"fc-back\" rows=\"3\" placeholder=\"Paris\"\u003e\u003c/textarea\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"fc-btn-row\"\u003e\n    \u003cbutton class=\"btn btn-primary\" id=\"fc-add-btn\"\u003e+ Add Card\u003c/button\u003e\n  \u003c/div\u003e\n  \u003c!-- Card list --\u003e\n  \u003cul class=\"fc-card-list\" id=\"fc-card-list\"\u003e\n    \u003c!-- populated by JS --\u003e\n  \u003c/ul\u003e\n  \u003c!-- CSV import --\u003e\n  \u003cdiv class=\"fc-csv-box\"\u003e\n    \u003cp\u003eBulk import via CSV — one card per line: \u003ccode\u003efront,back\u003c/code\u003e\u003c/p\u003e","title":"Flashcard Maker"},{"content":" Font Pair Suggester Choose a pairing, adjust controls, and copy your CSS — no API calls, works offline.\nAll Modern Classic Playful Professional Minimal Dark Mode \u0026lt;div class=\u0026quot;fp-control-section\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;fp-label\u0026quot;\u0026gt;Sample Text — Heading\u0026lt;/span\u0026gt; \u0026lt;input class=\u0026quot;fp-input-text\u0026quot; id=\u0026quot;fp-heading-text\u0026quot; type=\u0026quot;text\u0026quot; value=\u0026quot;The quick brown fox\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fp-control-section\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;fp-label\u0026quot;\u0026gt;Sample Text — Body\u0026lt;/span\u0026gt; \u0026lt;input class=\u0026quot;fp-input-text\u0026quot; id=\u0026quot;fp-body-text\u0026quot; type=\u0026quot;text\u0026quot; value=\u0026quot;Good typography improves readability and sets the tone of your content. Pair a strong heading font with a legible body font for best results.\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fp-control-section\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;fp-label\u0026quot;\u0026gt;Heading Size\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;fp-range-row\u0026quot;\u0026gt; \u0026lt;input class=\u0026quot;fp-range\u0026quot; id=\u0026quot;fp-heading-size\u0026quot; type=\u0026quot;range\u0026quot; min=\u0026quot;18\u0026quot; max=\u0026quot;72\u0026quot; value=\u0026quot;40\u0026quot; /\u0026gt; \u0026lt;span class=\u0026quot;fp-range-val\u0026quot; id=\u0026quot;fp-heading-size-val\u0026quot;\u0026gt;40px\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fp-control-section\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;fp-label\u0026quot;\u0026gt;Body Size\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;fp-range-row\u0026quot;\u0026gt; \u0026lt;input class=\u0026quot;fp-range\u0026quot; id=\u0026quot;fp-body-size\u0026quot; type=\u0026quot;range\u0026quot; min=\u0026quot;12\u0026quot; max=\u0026quot;28\u0026quot; value=\u0026quot;16\u0026quot; /\u0026gt; \u0026lt;span class=\u0026quot;fp-range-val\u0026quot; id=\u0026quot;fp-body-size-val\u0026quot;\u0026gt;16px\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fp-control-section\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;fp-label\u0026quot;\u0026gt;Line Height\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;fp-range-row\u0026quot;\u0026gt; \u0026lt;input class=\u0026quot;fp-range\u0026quot; id=\u0026quot;fp-line-height\u0026quot; type=\u0026quot;range\u0026quot; min=\u0026quot;100\u0026quot; max=\u0026quot;220\u0026quot; value=\u0026quot;165\u0026quot; /\u0026gt; \u0026lt;span class=\u0026quot;fp-range-val\u0026quot; id=\u0026quot;fp-line-height-val\u0026quot;\u0026gt;1.65\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fp-control-section\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;fp-label\u0026quot;\u0026gt;Letter Spacing (body)\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;fp-range-row\u0026quot;\u0026gt; \u0026lt;input class=\u0026quot;fp-range\u0026quot; id=\u0026quot;fp-letter-spacing\u0026quot; type=\u0026quot;range\u0026quot; min=\u0026quot;-5\u0026quot; max=\u0026quot;20\u0026quot; value=\u0026quot;0\u0026quot; /\u0026gt; \u0026lt;span class=\u0026quot;fp-range-val\u0026quot; id=\u0026quot;fp-letter-spacing-val\u0026quot;\u0026gt;0em\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fp-control-section\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;fp-label\u0026quot;\u0026gt;Preview Text Color\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;fp-color-row\u0026quot; id=\u0026quot;fp-color-swatches\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;fp-swatch active\u0026quot; data-color=\u0026quot;#1a1a1a\u0026quot; style=\u0026quot;background:#1a1a1a;\u0026quot; title=\u0026quot;Near-black\u0026quot;\u0026gt;\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;fp-swatch\u0026quot; data-color=\u0026quot;#2d3748\u0026quot; style=\u0026quot;background:#2d3748;\u0026quot; title=\u0026quot;Slate\u0026quot;\u0026gt;\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;fp-swatch\u0026quot; data-color=\u0026quot;#1a365d\u0026quot; style=\u0026quot;background:#1a365d;\u0026quot; title=\u0026quot;Navy\u0026quot;\u0026gt;\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;fp-swatch\u0026quot; data-color=\u0026quot;#4a1d2e\u0026quot; style=\u0026quot;background:#4a1d2e;\u0026quot; title=\u0026quot;Burgundy\u0026quot;\u0026gt;\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;fp-swatch\u0026quot; data-color=\u0026quot;#065f46\u0026quot; style=\u0026quot;background:#065f46;\u0026quot; title=\u0026quot;Forest\u0026quot;\u0026gt;\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;fp-swatch\u0026quot; data-color=\u0026quot;#374151\u0026quot; style=\u0026quot;background:#374151;\u0026quot; title=\u0026quot;Cool gray\u0026quot;\u0026gt;\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;fp-swatch\u0026quot; data-color=\u0026quot;#f8f9fa\u0026quot; style=\u0026quot;background:#f8f9fa; border: 1.5px solid #dee2e6;\u0026quot; title=\u0026quot;Near-white\u0026quot;\u0026gt;\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fp-control-section\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;fp-label\u0026quot;\u0026gt;Preview Background\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;fp-color-row\u0026quot; id=\u0026quot;fp-bg-swatches\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;fp-swatch active\u0026quot; data-bg=\u0026quot;#ffffff\u0026quot; style=\u0026quot;background:#ffffff; border:1.5px solid #dee2e6;\u0026quot; title=\u0026quot;White\u0026quot;\u0026gt;\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;fp-swatch\u0026quot; data-bg=\u0026quot;#f8f9fa\u0026quot; style=\u0026quot;background:#f8f9fa; border:1.5px solid #dee2e6;\u0026quot; title=\u0026quot;Light gray\u0026quot;\u0026gt;\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;fp-swatch\u0026quot; data-bg=\u0026quot;#fefce8\u0026quot; style=\u0026quot;background:#fefce8;\u0026quot; title=\u0026quot;Cream\u0026quot;\u0026gt;\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;fp-swatch\u0026quot; data-bg=\u0026quot;#eff6ff\u0026quot; style=\u0026quot;background:#eff6ff;\u0026quot; title=\u0026quot;Blue-tint\u0026quot;\u0026gt;\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;fp-swatch\u0026quot; data-bg=\u0026quot;#0f172a\u0026quot; style=\u0026quot;background:#0f172a;\u0026quot; title=\u0026quot;Dark\u0026quot;\u0026gt;\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;fp-swatch\u0026quot; data-bg=\u0026quot;#1e293b\u0026quot; style=\u0026quot;background:#1e293b;\u0026quot; title=\u0026quot;Slate-dark\u0026quot;\u0026gt;\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Live Preview Georgia + Trebuchet MS The quick brown fox Good typography improves readability and sets the tone of your content. Pair a strong heading font with a legible body font for best results. CSS Output Copy CSS Related Tools CSS Variables Generator CSS Text Gradient Generator Reading Time Calculator ","permalink":"https://productivity-works.com/tools/font-pair-suggester/","summary":"\u003cdiv id=\"fp-app\"\u003e\n\u003cstyle\u003e\n/* ── Scoped styles: all selectors prefixed with #fp-app ── */\n#fp-app *,\n#fp-app *::before,\n#fp-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\n#fp-app {\n  --fp-bg: #ffffff;\n  --fp-surface: #f8f9fa;\n  --fp-border: #dee2e6;\n  --fp-text: #212529;\n  --fp-text-muted: #6c757d;\n  --fp-accent: #4f46e5;\n  --fp-accent-hover: #4338ca;\n  --fp-preview-bg: #ffffff;\n  --fp-preview-text: #1a1a1a;\n  --fp-radius: 8px;\n  --fp-shadow: 0 1px 3px rgba(0,0,0,.12);\n  font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n  color: var(--fp-text);\n  background: var(--fp-bg);\n  padding: 1.5rem 1rem;\n  max-width: 900px;\n  margin: 0 auto;\n  line-height: 1.6;\n}\n\n#fp-app.fp-dark {\n  --fp-bg: #0f172a;\n  --fp-surface: #1e293b;\n  --fp-border: #334155;\n  --fp-text: #e2e8f0;\n  --fp-text-muted: #94a3b8;\n  --fp-accent: #818cf8;\n  --fp-accent-hover: #a5b4fc;\n  --fp-preview-bg: #1e293b;\n  --fp-preview-text: #f1f5f9;\n}\n\n/* Header */\n#fp-app .fp-header {\n  text-align: center;\n  margin-bottom: 2rem;\n}\n#fp-app .fp-header h1 {\n  font-size: clamp(1.4rem, 4vw, 2rem);\n  font-weight: 700;\n  color: var(--fp-text);\n  margin-bottom: .4rem;\n}\n#fp-app .fp-header p {\n  color: var(--fp-text-muted);\n  font-size: .95rem;\n}\n\n/* Top bar: style presets + dark toggle */\n#fp-app .fp-topbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: .5rem;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 1.25rem;\n}\n#fp-app .fp-preset-group {\n  display: flex;\n  flex-wrap: wrap;\n  gap: .4rem;\n}\n#fp-app .fp-preset-btn {\n  padding: .35rem .85rem;\n  border: 1.5px solid var(--fp-border);\n  border-radius: 999px;\n  background: var(--fp-surface);\n  color: var(--fp-text);\n  font-size: .85rem;\n  cursor: pointer;\n  transition: all .18s;\n}\n#fp-app .fp-preset-btn:hover,\n#fp-app .fp-preset-btn.active {\n  background: var(--fp-accent);\n  border-color: var(--fp-accent);\n  color: #fff;\n}\n#fp-app .fp-dark-toggle {\n  padding: .35rem .85rem;\n  border: 1.5px solid var(--fp-border);\n  border-radius: 999px;\n  background: var(--fp-surface);\n  color: var(--fp-text);\n  font-size: .85rem;\n  cursor: pointer;\n  transition: all .18s;\n  white-space: nowrap;\n}\n#fp-app .fp-dark-toggle:hover {\n  border-color: var(--fp-accent);\n  color: var(--fp-accent);\n}\n\n/* Main layout */\n#fp-app .fp-layout {\n  display: grid;\n  grid-template-columns: 280px 1fr;\n  gap: 1.25rem;\n  align-items: start;\n}\n@media (max-width: 680px) {\n  #fp-app .fp-layout {\n    grid-template-columns: 1fr;\n  }\n}\n\n/* Controls panel */\n#fp-app .fp-controls {\n  background: var(--fp-surface);\n  border: 1px solid var(--fp-border);\n  border-radius: var(--fp-radius);\n  padding: 1.1rem;\n  display: flex;\n  flex-direction: column;\n  gap: 1rem;\n}\n#fp-app .fp-control-section {\n  display: flex;\n  flex-direction: column;\n  gap: .5rem;\n}\n#fp-app .fp-label {\n  font-size: .78rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: .06em;\n  color: var(--fp-text-muted);\n}\n#fp-app .fp-select,\n#fp-app .fp-input-text {\n  width: 100%;\n  padding: .45rem .7rem;\n  border: 1px solid var(--fp-border);\n  border-radius: 6px;\n  background: var(--fp-bg);\n  color: var(--fp-text);\n  font-size: .9rem;\n  transition: border-color .15s;\n}\n#fp-app .fp-select:focus,\n#fp-app .fp-input-text:focus {\n  outline: none;\n  border-color: var(--fp-accent);\n}\n\n/* Range controls */\n#fp-app .fp-range-row {\n  display: flex;\n  align-items: center;\n  gap: .6rem;\n}\n#fp-app .fp-range {\n  flex: 1;\n  accent-color: var(--fp-accent);\n  cursor: pointer;\n}\n#fp-app .fp-range-val {\n  font-size: .82rem;\n  color: var(--fp-text-muted);\n  min-width: 38px;\n  text-align: right;\n}\n\n/* Color swatches */\n#fp-app .fp-color-row {\n  display: flex;\n  gap: .5rem;\n  flex-wrap: wrap;\n}\n#fp-app .fp-swatch {\n  width: 28px;\n  height: 28px;\n  border-radius: 50%;\n  border: 2px solid transparent;\n  cursor: pointer;\n  transition: transform .15s, border-color .15s;\n}\n#fp-app .fp-swatch:hover { transform: scale(1.15); }\n#fp-app .fp-swatch.active { border-color: var(--fp-accent); }\n\n/* Preview panel */\n#fp-app .fp-preview-panel {\n  border: 1px solid var(--fp-border);\n  border-radius: var(--fp-radius);\n  overflow: hidden;\n}\n#fp-app .fp-preview-bar {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  padding: .6rem 1rem;\n  background: var(--fp-surface);\n  border-bottom: 1px solid var(--fp-border);\n  gap: .5rem;\n  flex-wrap: wrap;\n}\n#fp-app .fp-preview-label {\n  font-size: .8rem;\n  font-weight: 600;\n  color: var(--fp-text-muted);\n  text-transform: uppercase;\n  letter-spacing: .06em;\n}\n#fp-app .fp-pair-name {\n  font-size: .85rem;\n  color: var(--fp-accent);\n  font-weight: 600;\n}\n#fp-app .fp-preview-area {\n  background: var(--fp-preview-bg);\n  color: var(--fp-preview-text);\n  padding: 2rem 2rem 1.5rem;\n  min-height: 260px;\n  transition: background .2s, color .2s;\n}\n#fp-app .fp-preview-heading {\n  margin-bottom: .6rem;\n  line-height: 1.2;\n  word-break: break-word;\n}\n#fp-app .fp-preview-body {\n  line-height: inherit;\n  word-break: break-word;\n}\n\n/* Editable sample text */\n#fp-app [contenteditable] {\n  outline: none;\n  border-bottom: 1px dashed transparent;\n  transition: border-color .15s;\n}\n#fp-app [contenteditable]:hover,\n#fp-app [contenteditable]:focus {\n  border-bottom-color: var(--fp-accent);\n}\n\n/* CSS output */\n#fp-app .fp-css-section {\n  margin-top: 1.25rem;\n  background: var(--fp-surface);\n  border: 1px solid var(--fp-border);\n  border-radius: var(--fp-radius);\n  overflow: hidden;\n}\n#fp-app .fp-css-bar {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  padding: .6rem 1rem;\n  background: var(--fp-surface);\n  border-bottom: 1px solid var(--fp-border);\n}\n#fp-app .fp-css-title {\n  font-size: .8rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: .07em;\n  color: var(--fp-text-muted);\n}\n#fp-app .fp-copy-btn {\n  padding: .3rem .8rem;\n  background: var(--fp-accent);\n  color: #fff;\n  border: none;\n  border-radius: 6px;\n  font-size: .82rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background .18s;\n}\n#fp-app .fp-copy-btn:hover { background: var(--fp-accent-hover); }\n#fp-app .fp-copy-btn.copied {\n  background: #16a34a;\n}\n#fp-app .fp-css-output {\n  padding: 1rem;\n  font-family: \"Courier New\", Courier, monospace;\n  font-size: .8rem;\n  line-height: 1.7;\n  color: var(--fp-text);\n  white-space: pre-wrap;\n  word-break: break-all;\n  overflow-x: auto;\n}\n\n/* Pair grid */\n#fp-app .fp-pair-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));\n  gap: .6rem;\n  margin-top: 1.25rem;\n}\n#fp-app .fp-pair-card {\n  background: var(--fp-surface);\n  border: 1.5px solid var(--fp-border);\n  border-radius: var(--fp-radius);\n  padding: .75rem .9rem;\n  cursor: pointer;\n  transition: border-color .18s, transform .18s;\n}\n#fp-app .fp-pair-card:hover { border-color: var(--fp-accent); transform: translateY(-2px); }\n#fp-app .fp-pair-card.active { border-color: var(--fp-accent); background: color-mix(in srgb, var(--fp-accent) 8%, var(--fp-surface)); }\n#fp-app .fp-card-heading {\n  font-size: 1.05rem;\n  font-weight: 700;\n  line-height: 1.2;\n  margin-bottom: .2rem;\n  color: var(--fp-text);\n}\n#fp-app .fp-card-body {\n  font-size: .78rem;\n  color: var(--fp-text-muted);\n}\n#fp-app .fp-card-tag {\n  display: inline-block;\n  font-size: .68rem;\n  padding: .1rem .45rem;\n  border-radius: 999px;\n  background: color-mix(in srgb, var(--fp-accent) 12%, transparent);\n  color: var(--fp-accent);\n  margin-top: .35rem;\n  font-weight: 600;\n}\n\n/* Related links */\n#fp-app .fp-related {\n  margin-top: 2rem;\n  padding-top: 1.25rem;\n  border-top: 1px solid var(--fp-border);\n}\n#fp-app .fp-related-title {\n  font-size: .85rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: .07em;\n  color: var(--fp-text-muted);\n  margin-bottom: .75rem;\n}\n#fp-app .fp-related-links {\n  display: flex;\n  flex-wrap: wrap;\n  gap: .6rem;\n}\n#fp-app .fp-related-links a {\n  color: var(--fp-accent);\n  text-decoration: none;\n  font-size: .9rem;\n  border: 1px solid var(--fp-border);\n  padding: .3rem .8rem;\n  border-radius: 6px;\n  transition: border-color .15s, background .15s;\n}\n#fp-app .fp-related-links a:hover {\n  background: color-mix(in srgb, var(--fp-accent) 8%, transparent);\n  border-color: var(--fp-accent);\n}\n\u003c/style\u003e\n\u003cdiv class=\"fp-header\"\u003e\n  \u003ch1\u003eFont Pair Suggester\u003c/h1\u003e\n  \u003cp\u003eChoose a pairing, adjust controls, and copy your CSS — no API calls, works offline.\u003c/p\u003e","title":"Font Pair Suggester — Typography Tool"},{"content":" Font Pairing Tool 12 curated system-font pairings — live preview, adjust typography, copy CSS\nFont Pairings \u0026lt;!-- Info strip --\u0026gt; \u0026lt;div class=\u0026quot;fp-info-strip\u0026quot; id=\u0026quot;fp-info-strip\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;fp-info-item\u0026quot;\u0026gt;\u0026lt;strong\u0026gt;Heading:\u0026lt;/strong\u0026gt; \u0026lt;span id=\u0026quot;fp-info-heading\u0026quot;\u0026gt;—\u0026lt;/span\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;fp-info-item\u0026quot;\u0026gt;\u0026lt;strong\u0026gt;Body:\u0026lt;/strong\u0026gt; \u0026lt;span id=\u0026quot;fp-info-body\u0026quot;\u0026gt;—\u0026lt;/span\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;fp-info-item\u0026quot;\u0026gt;\u0026lt;strong\u0026gt;Style:\u0026lt;/strong\u0026gt; \u0026lt;span id=\u0026quot;fp-info-style\u0026quot;\u0026gt;—\u0026lt;/span\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Controls --\u0026gt; \u0026lt;div class=\u0026quot;fp-card\u0026quot; style=\u0026quot;margin-bottom:16px;\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Customize\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;fp-controls\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fp-control-group\u0026quot; style=\u0026quot;grid-column:1/-1;\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Sample Text\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;fp-sample-text\u0026quot; value=\u0026quot;The quick brown fox jumps over the lazy dog\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fp-control-group\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Heading Size \u0026lt;span class=\u0026quot;fp-val\u0026quot; id=\u0026quot;fp-heading-size-val\u0026quot;\u0026gt;2.4rem\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;fp-heading-size\u0026quot; min=\u0026quot;1.2\u0026quot; max=\u0026quot;4.5\u0026quot; step=\u0026quot;0.1\u0026quot; value=\u0026quot;2.4\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fp-control-group\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Body Size \u0026lt;span class=\u0026quot;fp-val\u0026quot; id=\u0026quot;fp-body-size-val\u0026quot;\u0026gt;1rem\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;fp-body-size\u0026quot; min=\u0026quot;0.75\u0026quot; max=\u0026quot;1.5\u0026quot; step=\u0026quot;0.05\u0026quot; value=\u0026quot;1\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fp-control-group\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Line Height \u0026lt;span class=\u0026quot;fp-val\u0026quot; id=\u0026quot;fp-line-height-val\u0026quot;\u0026gt;1.6\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;fp-line-height\u0026quot; min=\u0026quot;1.0\u0026quot; max=\u0026quot;2.5\u0026quot; step=\u0026quot;0.05\u0026quot; value=\u0026quot;1.6\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fp-control-group\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Letter Spacing \u0026lt;span class=\u0026quot;fp-val\u0026quot; id=\u0026quot;fp-letter-spacing-val\u0026quot;\u0026gt;0em\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;fp-letter-spacing\u0026quot; min=\u0026quot;-0.05\u0026quot; max=\u0026quot;0.2\u0026quot; step=\u0026quot;0.005\u0026quot; value=\u0026quot;0\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fp-control-group\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Heading Color\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;fp-heading-color\u0026quot; value=\u0026quot;#1a1a2e\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fp-control-group\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Body Color\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;fp-body-color\u0026quot; value=\u0026quot;#374151\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fp-section-title\u0026quot;\u0026gt;Preview Mode\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fp-mode-tabs\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;fp-mode-tab active\u0026quot; data-mode=\u0026quot;blog\u0026quot;\u0026gt;Blog Post\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;fp-mode-tab\u0026quot; data-mode=\u0026quot;hero\u0026quot;\u0026gt;Hero Section\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;fp-mode-tab\u0026quot; data-mode=\u0026quot;card\u0026quot;\u0026gt;Card Layout\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Preview --\u0026gt; \u0026lt;div class=\u0026quot;fp-preview-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fp-preview-label\u0026quot;\u0026gt;Live Preview\u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;fp-preview\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- CSS Output --\u0026gt; \u0026lt;div class=\u0026quot;fp-card\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;CSS Code\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;fp-css-box\u0026quot;\u0026gt; \u0026lt;pre id=\u0026quot;fp-css-output\u0026quot;\u0026gt;\u0026lt;/pre\u0026gt; \u0026lt;button class=\u0026quot;fp-copy-btn\u0026quot; id=\u0026quot;fp-copy-btn\u0026quot;\u0026gt;Copy CSS\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Related Tools Convert font sizes → Font Size Converter Generate color palettes → Color Palette Generator ","permalink":"https://productivity-works.com/tools/font-pairing-tool/","summary":"\u003cdiv id=\"fp-app\"\u003e\n\u003cstyle\u003e\n#fp-app *,\n#fp-app *::before,\n#fp-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\n#fp-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  color: #1a1a2e;\n  line-height: 1.6;\n  max-width: 960px;\n  margin: 0 auto;\n  padding: 0 16px;\n}\n\n#fp-app .fp-hero {\n  background: linear-gradient(135deg, #7c3aed 0%, #2563eb 100%);\n  color: #fff;\n  border-radius: 14px;\n  padding: 36px 28px 28px;\n  margin-bottom: 24px;\n  text-align: center;\n}\n\n#fp-app .fp-hero h2 {\n  font-size: 1.85rem;\n  font-weight: 800;\n  margin-bottom: 8px;\n  letter-spacing: -0.02em;\n}\n\n#fp-app .fp-hero p {\n  font-size: 1rem;\n  opacity: 0.9;\n}\n\n#fp-app .fp-layout {\n  display: grid;\n  grid-template-columns: 300px 1fr;\n  gap: 20px;\n  align-items: start;\n}\n\n@media (max-width: 700px) {\n  #fp-app .fp-layout {\n    grid-template-columns: 1fr;\n  }\n}\n\n#fp-app .fp-card {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 20px;\n}\n\n#fp-app .fp-card h3 {\n  font-size: 0.8rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #64748b;\n  margin-bottom: 14px;\n}\n\n/* Pairing list */\n#fp-app .fp-pairing-list {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n}\n\n#fp-app .fp-pairing-btn {\n  display: block;\n  width: 100%;\n  text-align: left;\n  padding: 12px 14px;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 9px;\n  background: #fff;\n  cursor: pointer;\n  transition: border-color 0.15s, background 0.15s;\n}\n\n#fp-app .fp-pairing-btn:hover {\n  border-color: #7c3aed;\n  background: #faf5ff;\n}\n\n#fp-app .fp-pairing-btn.active {\n  border-color: #7c3aed;\n  background: #f5f3ff;\n}\n\n#fp-app .fp-pairing-btn .fp-btn-heading {\n  font-size: 0.95rem;\n  font-weight: 700;\n  color: #1e1b4b;\n  line-height: 1.2;\n}\n\n#fp-app .fp-pairing-btn .fp-btn-body {\n  font-size: 0.78rem;\n  color: #64748b;\n  margin-top: 2px;\n}\n\n#fp-app .fp-pairing-btn .fp-btn-tag {\n  font-size: 0.68rem;\n  padding: 2px 7px;\n  border-radius: 20px;\n  background: #ede9fe;\n  color: #6d28d9;\n  display: inline-block;\n  margin-top: 5px;\n  font-weight: 600;\n}\n\n/* Controls */\n#fp-app .fp-controls {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 12px;\n  margin-bottom: 16px;\n}\n\n@media (max-width: 480px) {\n  #fp-app .fp-controls {\n    grid-template-columns: 1fr;\n  }\n}\n\n#fp-app .fp-control-group label {\n  display: block;\n  font-size: 0.75rem;\n  font-weight: 600;\n  color: #475569;\n  margin-bottom: 5px;\n}\n\n#fp-app .fp-control-group input[type=\"range\"] {\n  width: 100%;\n  accent-color: #7c3aed;\n  cursor: pointer;\n}\n\n#fp-app .fp-control-group input[type=\"text\"] {\n  width: 100%;\n  padding: 7px 10px;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 7px;\n  font-size: 0.85rem;\n  outline: none;\n  transition: border-color 0.15s;\n}\n\n#fp-app .fp-control-group input[type=\"text\"]:focus {\n  border-color: #7c3aed;\n}\n\n#fp-app .fp-control-group input[type=\"color\"] {\n  width: 100%;\n  height: 36px;\n  padding: 2px 4px;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 7px;\n  cursor: pointer;\n  background: #fff;\n}\n\n#fp-app .fp-val {\n  font-size: 0.72rem;\n  color: #7c3aed;\n  font-weight: 700;\n  margin-left: 6px;\n}\n\n/* Mode tabs */\n#fp-app .fp-mode-tabs {\n  display: flex;\n  gap: 6px;\n  margin-bottom: 16px;\n  flex-wrap: wrap;\n}\n\n#fp-app .fp-mode-tab {\n  padding: 6px 14px;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 20px;\n  background: #fff;\n  font-size: 0.8rem;\n  font-weight: 600;\n  color: #64748b;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n\n#fp-app .fp-mode-tab:hover {\n  border-color: #7c3aed;\n  color: #7c3aed;\n}\n\n#fp-app .fp-mode-tab.active {\n  background: #7c3aed;\n  border-color: #7c3aed;\n  color: #fff;\n}\n\n/* Preview area */\n#fp-app .fp-preview-wrap {\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  overflow: hidden;\n  margin-bottom: 16px;\n}\n\n#fp-app .fp-preview-label {\n  font-size: 0.72rem;\n  font-weight: 600;\n  color: #94a3b8;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  padding: 8px 14px;\n  background: #f8fafc;\n  border-bottom: 1px solid #e2e8f0;\n}\n\n#fp-app #fp-preview {\n  padding: 28px 24px;\n  min-height: 220px;\n  background: #fff;\n  transition: background 0.2s;\n}\n\n/* Preview mode: hero */\n#fp-app #fp-preview.mode-hero {\n  background: linear-gradient(135deg, #1e1b4b 0%, #312e81 100%);\n  padding: 48px 36px;\n}\n\n/* Preview mode: card */\n#fp-app #fp-preview.mode-card {\n  background: #f1f5f9;\n  display: flex;\n  gap: 16px;\n  flex-wrap: wrap;\n  padding: 20px;\n}\n\n#fp-app .fp-preview-card {\n  flex: 1;\n  min-width: 180px;\n  background: #fff;\n  border-radius: 10px;\n  padding: 18px;\n  box-shadow: 0 1px 6px rgba(0,0,0,0.08);\n}\n\n#fp-app .fp-preview-card .fp-card-tag {\n  font-size: 0.7rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #7c3aed;\n  margin-bottom: 8px;\n}\n\n/* CSS output */\n#fp-app .fp-css-box {\n  background: #0f172a;\n  border-radius: 10px;\n  padding: 16px;\n  position: relative;\n}\n\n#fp-app .fp-css-box pre {\n  font-family: \"Consolas\", \"Courier New\", Courier, monospace;\n  font-size: 0.78rem;\n  color: #e2e8f0;\n  white-space: pre-wrap;\n  word-break: break-all;\n  line-height: 1.7;\n}\n\n#fp-app .fp-copy-btn {\n  position: absolute;\n  top: 10px;\n  right: 10px;\n  padding: 5px 12px;\n  background: #7c3aed;\n  color: #fff;\n  border: none;\n  border-radius: 6px;\n  font-size: 0.75rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n\n#fp-app .fp-copy-btn:hover {\n  background: #6d28d9;\n}\n\n#fp-app .fp-copy-btn.copied {\n  background: #16a34a;\n}\n\n#fp-app .fp-info-strip {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n  padding: 12px 14px;\n  background: #f5f3ff;\n  border: 1.5px solid #ddd6fe;\n  border-radius: 9px;\n  margin-bottom: 16px;\n}\n\n#fp-app .fp-info-item {\n  font-size: 0.78rem;\n  color: #5b21b6;\n}\n\n#fp-app .fp-info-item strong {\n  font-weight: 700;\n}\n\n#fp-app .fp-section-title {\n  font-size: 0.8rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  color: #64748b;\n  margin-bottom: 10px;\n}\n\u003c/style\u003e\n\u003cdiv class=\"fp-hero\"\u003e\n  \u003ch2\u003eFont Pairing Tool\u003c/h2\u003e\n  \u003cp\u003e12 curated system-font pairings — live preview, adjust typography, copy CSS\u003c/p\u003e","title":"Font Pairing Tool — Find Perfect Heading \u0026 Body Font Combinations"},{"content":" Font Size Converter Convert between px, rem, em, pt, %, and vw instantly. Set your base font size and see live results.\nBase Font Size Root font size (px): Browser default is 16px. Change to match your project. Unit Converter — type in any field \u0026lt;div class=\u0026quot;fs-unit-block\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-label\u0026quot;\u0026gt;px — Pixels\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-input-row\u0026quot;\u0026gt; \u0026lt;input class=\u0026quot;fs-unit-input\u0026quot; id=\u0026quot;fs-px\u0026quot; type=\u0026quot;number\u0026quot; min=\u0026quot;0\u0026quot; step=\u0026quot;0.5\u0026quot; placeholder=\u0026quot;16\u0026quot; /\u0026gt; \u0026lt;button class=\u0026quot;fs-copy-btn\u0026quot; data-copy=\u0026quot;fs-px\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-desc\u0026quot;\u0026gt;Absolute screen pixels\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-block\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-label\u0026quot;\u0026gt;rem — Root EM\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-input-row\u0026quot;\u0026gt; \u0026lt;input class=\u0026quot;fs-unit-input\u0026quot; id=\u0026quot;fs-rem\u0026quot; type=\u0026quot;number\u0026quot; min=\u0026quot;0\u0026quot; step=\u0026quot;0.0625\u0026quot; placeholder=\u0026quot;1\u0026quot; /\u0026gt; \u0026lt;button class=\u0026quot;fs-copy-btn\u0026quot; data-copy=\u0026quot;fs-rem\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-desc\u0026quot;\u0026gt;Relative to root element font size\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-block\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-label\u0026quot;\u0026gt;em — Element EM\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-input-row\u0026quot;\u0026gt; \u0026lt;input class=\u0026quot;fs-unit-input\u0026quot; id=\u0026quot;fs-em\u0026quot; type=\u0026quot;number\u0026quot; min=\u0026quot;0\u0026quot; step=\u0026quot;0.0625\u0026quot; placeholder=\u0026quot;1\u0026quot; /\u0026gt; \u0026lt;button class=\u0026quot;fs-copy-btn\u0026quot; data-copy=\u0026quot;fs-em\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-desc\u0026quot;\u0026gt;Relative to parent element font size\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-block\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-label\u0026quot;\u0026gt;pt — Points\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-input-row\u0026quot;\u0026gt; \u0026lt;input class=\u0026quot;fs-unit-input\u0026quot; id=\u0026quot;fs-pt\u0026quot; type=\u0026quot;number\u0026quot; min=\u0026quot;0\u0026quot; step=\u0026quot;0.5\u0026quot; placeholder=\u0026quot;12\u0026quot; /\u0026gt; \u0026lt;button class=\u0026quot;fs-copy-btn\u0026quot; data-copy=\u0026quot;fs-pt\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-desc\u0026quot;\u0026gt;Print unit: 1pt = 1/72 inch = 1.333px\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-block\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-label\u0026quot;\u0026gt;% — Percent\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-input-row\u0026quot;\u0026gt; \u0026lt;input class=\u0026quot;fs-unit-input\u0026quot; id=\u0026quot;fs-pct\u0026quot; type=\u0026quot;number\u0026quot; min=\u0026quot;0\u0026quot; step=\u0026quot;1\u0026quot; placeholder=\u0026quot;100\u0026quot; /\u0026gt; \u0026lt;button class=\u0026quot;fs-copy-btn\u0026quot; data-copy=\u0026quot;fs-pct\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-desc\u0026quot;\u0026gt;% of parent/root font size\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-block\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-label\u0026quot;\u0026gt;vw — Viewport Width\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-input-row\u0026quot;\u0026gt; \u0026lt;input class=\u0026quot;fs-unit-input\u0026quot; id=\u0026quot;fs-vw\u0026quot; type=\u0026quot;number\u0026quot; min=\u0026quot;0\u0026quot; step=\u0026quot;0.01\u0026quot; placeholder=\u0026quot;1\u0026quot; /\u0026gt; \u0026lt;button class=\u0026quot;fs-copy-btn\u0026quot; data-copy=\u0026quot;fs-vw\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fs-unit-desc\u0026quot;\u0026gt;1vw = 1% of viewport width (assumes 1440px)\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Live Preview The quick brown fox jumps over the lazy dog. Rendering at 16px\nConversion Table — Common Sizes px rem em pt % vw (1440px) Calculated with base = 16px. Change base above to update.\nConversion Formulas px → rem rem = px ÷ base e.g. 24px ÷ 16 = 1.5rem rem → px px = rem × base e.g. 1.5rem × 16 = 24px px → em em = px ÷ parent-px Same as rem when parent = root px → pt pt = px × 0.75 e.g. 16px × 0.75 = 12pt px → % % = (px ÷ base) × 100 e.g. (16 ÷ 16) × 100 = 100% px → vw vw = (px ÷ vp) × 100 vp = viewport width (default 1440px) Related Tools Typography Scale Generator CSS Variables Generator ","permalink":"https://productivity-works.com/tools/font-size-converter/","summary":"\u003cdiv id=\"fs-app\"\u003e\n\u003cstyle\u003e\n#fs-app *,\n#fs-app *::before,\n#fs-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\n#fs-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  color: #1a1a2e;\n  line-height: 1.6;\n  max-width: 860px;\n  margin: 0 auto;\n  padding: 0 16px;\n}\n\n/* Hero */\n#fs-app .fs-hero {\n  background: linear-gradient(135deg, #0ea5e9 0%, #6366f1 100%);\n  color: #fff;\n  border-radius: 14px;\n  padding: 36px 28px 28px;\n  margin-bottom: 24px;\n  text-align: center;\n}\n\n#fs-app .fs-hero h2 {\n  font-size: 1.75rem;\n  font-weight: 800;\n  margin-bottom: 8px;\n  letter-spacing: -0.02em;\n}\n\n#fs-app .fs-hero p {\n  font-size: 1rem;\n  opacity: 0.9;\n}\n\n/* Cards */\n#fs-app .fs-card {\n  background: #ffffff;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 24px;\n  margin-bottom: 20px;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.05);\n}\n\n#fs-app .fs-section-title {\n  font-size: 0.78rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #0ea5e9;\n  margin-bottom: 16px;\n}\n\n/* Base font size row */\n#fs-app .fs-base-row {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  flex-wrap: wrap;\n  margin-bottom: 0;\n}\n\n#fs-app .fs-base-row label {\n  font-size: 0.875rem;\n  font-weight: 600;\n  color: #374151;\n  white-space: nowrap;\n}\n\n#fs-app .fs-base-input {\n  width: 80px;\n  padding: 7px 10px;\n  border: 1px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 0.95rem;\n  color: #1a1a2e;\n  background: #f8fafc;\n  transition: border-color 0.15s;\n}\n\n#fs-app .fs-base-input:focus {\n  outline: none;\n  border-color: #0ea5e9;\n  background: #fff;\n}\n\n#fs-app .fs-base-note {\n  font-size: 0.8rem;\n  color: #64748b;\n}\n\n/* Converter grid */\n#fs-app .fs-converter-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n  gap: 14px;\n}\n\n#fs-app .fs-unit-block {\n  background: #f8fafc;\n  border: 2px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 16px;\n  transition: border-color 0.15s, box-shadow 0.15s;\n}\n\n#fs-app .fs-unit-block:focus-within {\n  border-color: #0ea5e9;\n  box-shadow: 0 0 0 3px rgba(14,165,233,0.12);\n}\n\n#fs-app .fs-unit-label {\n  font-size: 0.75rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  color: #64748b;\n  margin-bottom: 8px;\n}\n\n#fs-app .fs-unit-input-row {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n\n#fs-app .fs-unit-input {\n  flex: 1;\n  min-width: 0;\n  padding: 8px 10px;\n  border: 1px solid #cbd5e1;\n  border-radius: 7px;\n  font-size: 1.05rem;\n  font-weight: 600;\n  color: #1a1a2e;\n  background: #fff;\n  transition: border-color 0.15s;\n}\n\n#fs-app .fs-unit-input:focus {\n  outline: none;\n  border-color: #0ea5e9;\n}\n\n#fs-app .fs-copy-btn {\n  flex-shrink: 0;\n  padding: 7px 10px;\n  background: #e0f2fe;\n  border: none;\n  border-radius: 7px;\n  color: #0369a1;\n  font-size: 0.78rem;\n  font-weight: 700;\n  cursor: pointer;\n  white-space: nowrap;\n  transition: background 0.15s, color 0.15s;\n}\n\n#fs-app .fs-copy-btn:hover {\n  background: #0ea5e9;\n  color: #fff;\n}\n\n#fs-app .fs-copy-btn.copied {\n  background: #22c55e;\n  color: #fff;\n}\n\n#fs-app .fs-unit-desc {\n  font-size: 0.72rem;\n  color: #94a3b8;\n  margin-top: 6px;\n}\n\n/* Live Preview */\n#fs-app .fs-preview-box {\n  border: 1px dashed #cbd5e1;\n  border-radius: 8px;\n  padding: 20px 18px;\n  min-height: 80px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  background: #f8fafc;\n  overflow: hidden;\n}\n\n#fs-app .fs-preview-text {\n  font-weight: 600;\n  color: #1a1a2e;\n  transition: font-size 0.2s;\n  word-break: break-word;\n  text-align: center;\n  line-height: 1.3;\n}\n\n#fs-app .fs-preview-meta {\n  font-size: 0.8rem;\n  color: #64748b;\n  margin-top: 10px;\n  text-align: center;\n}\n\n/* Conversion Table */\n#fs-app .fs-table-wrap {\n  overflow-x: auto;\n  -webkit-overflow-scrolling: touch;\n}\n\n#fs-app .fs-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.875rem;\n  min-width: 560px;\n}\n\n#fs-app .fs-table thead tr {\n  background: #0ea5e9;\n  color: #fff;\n}\n\n#fs-app .fs-table thead th {\n  padding: 10px 12px;\n  font-weight: 700;\n  text-align: right;\n  letter-spacing: 0.03em;\n}\n\n#fs-app .fs-table thead th:first-child {\n  text-align: center;\n  border-radius: 8px 0 0 0;\n}\n\n#fs-app .fs-table thead th:last-child {\n  border-radius: 0 8px 0 0;\n}\n\n#fs-app .fs-table tbody tr:nth-child(odd) {\n  background: #f8fafc;\n}\n\n#fs-app .fs-table tbody tr:hover {\n  background: #e0f2fe;\n}\n\n#fs-app .fs-table tbody td {\n  padding: 9px 12px;\n  border-bottom: 1px solid #f1f5f9;\n  text-align: right;\n  font-variant-numeric: tabular-nums;\n  color: #374151;\n}\n\n#fs-app .fs-table tbody td:first-child {\n  text-align: center;\n  font-weight: 700;\n  color: #0ea5e9;\n}\n\n#fs-app .fs-table-note {\n  font-size: 0.75rem;\n  color: #94a3b8;\n  margin-top: 8px;\n}\n\n/* Formulas */\n#fs-app .fs-formula-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));\n  gap: 12px;\n}\n\n#fs-app .fs-formula-item {\n  background: #f8fafc;\n  border-left: 3px solid #0ea5e9;\n  border-radius: 0 8px 8px 0;\n  padding: 12px 14px;\n}\n\n#fs-app .fs-formula-unit {\n  font-size: 0.72rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  color: #0ea5e9;\n  margin-bottom: 4px;\n}\n\n#fs-app .fs-formula-eq {\n  font-family: \"SF Mono\", \"Fira Code\", \"Consolas\", monospace;\n  font-size: 0.82rem;\n  color: #1e293b;\n  margin-bottom: 3px;\n}\n\n#fs-app .fs-formula-note {\n  font-size: 0.72rem;\n  color: #64748b;\n}\n\n/* Related Tools */\n#fs-app .fs-crosslinks {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  margin-top: 4px;\n}\n\n#fs-app .fs-crosslinks a {\n  color: #0ea5e9;\n  text-decoration: none;\n  font-size: 0.85rem;\n  font-weight: 600;\n  padding: 5px 12px;\n  border: 1.5px solid #bae6fd;\n  border-radius: 20px;\n  transition: background 0.15s, color 0.15s, border-color 0.15s;\n}\n\n#fs-app .fs-crosslinks a:hover {\n  background: #0ea5e9;\n  color: #fff;\n  border-color: #0ea5e9;\n}\n\n/* Responsive */\n@media (max-width: 600px) {\n  #fs-app .fs-hero { padding: 28px 18px 22px; }\n  #fs-app .fs-hero h2 { font-size: 1.4rem; }\n  #fs-app .fs-card { padding: 18px 14px; }\n  #fs-app .fs-converter-grid { grid-template-columns: 1fr 1fr; }\n}\n\n@media (max-width: 400px) {\n  #fs-app .fs-converter-grid { grid-template-columns: 1fr; }\n}\n\u003c/style\u003e\n\u003c!-- Hero --\u003e\n\u003cdiv class=\"fs-hero\"\u003e\n  \u003ch2\u003eFont Size Converter\u003c/h2\u003e\n  \u003cp\u003eConvert between px, rem, em, pt, %, and vw instantly. Set your base font size and see live results.\u003c/p\u003e","title":"Font Size Converter - px, rem, em, pt, %"},{"content":" Trip Calculator Compare Vehicles Annual Estimator Trip Fuel Cost \u0026lt;!-- Unit toggle --\u0026gt; \u0026lt;div class=\u0026quot;fc-toggles\u0026quot;\u0026gt; \u0026lt;label class=\u0026quot;fc-toggle-label\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;fc-imperial\u0026quot; onchange=\u0026quot;fcCalcTrip()\u0026quot;\u0026gt; Imperial (miles / MPG / gallon) \u0026lt;/label\u0026gt; \u0026lt;label class=\u0026quot;fc-toggle-label\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;fc-roundtrip\u0026quot; onchange=\u0026quot;fcCalcTrip()\u0026quot;\u0026gt; Round Trip \u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-grid\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fc-field\u0026quot;\u0026gt; \u0026lt;label id=\u0026quot;fc-dist-lbl\u0026quot;\u0026gt;Distance (km)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;fc-distance\u0026quot; value=\u0026quot;100\u0026quot; min=\u0026quot;0\u0026quot; step=\u0026quot;1\u0026quot; oninput=\u0026quot;fcCalcTrip()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-field\u0026quot;\u0026gt; \u0026lt;label id=\u0026quot;fc-eff-lbl\u0026quot;\u0026gt;Fuel Efficiency (L/100km)\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;fc-eff-mode\u0026quot; onchange=\u0026quot;fcCalcTrip()\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;l100km\u0026quot;\u0026gt;L / 100 km\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;kml\u0026quot;\u0026gt;km / L\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;mpg\u0026quot;\u0026gt;MPG\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-field\u0026quot;\u0026gt; \u0026lt;label id=\u0026quot;fc-eff-val-lbl\u0026quot;\u0026gt;Efficiency Value\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;fc-efficiency\u0026quot; value=\u0026quot;8\u0026quot; min=\u0026quot;0.1\u0026quot; step=\u0026quot;0.1\u0026quot; oninput=\u0026quot;fcCalcTrip()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-field\u0026quot;\u0026gt; \u0026lt;label id=\u0026quot;fc-price-lbl\u0026quot;\u0026gt;Fuel Price (per liter)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;fc-price\u0026quot; value=\u0026quot;1.60\u0026quot; min=\u0026quot;0.01\u0026quot; step=\u0026quot;0.01\u0026quot; oninput=\u0026quot;fcCalcTrip()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;fc-btn\u0026quot; onclick=\u0026quot;fcCalcTrip()\u0026quot;\u0026gt;Calculate\u0026lt;/button\u0026gt; \u0026lt;div class=\u0026quot;fc-result-bar\u0026quot; id=\u0026quot;fc-trip-results\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fc-result-item\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fc-result-val\u0026quot; id=\u0026quot;fc-r-fuel\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-result-lbl\u0026quot;\u0026gt;Fuel Needed\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-result-item\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fc-result-val\u0026quot; id=\u0026quot;fc-r-cost\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-result-lbl\u0026quot;\u0026gt;Total Cost\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-result-item\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fc-result-val\u0026quot; id=\u0026quot;fc-r-dist\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-result-lbl\u0026quot;\u0026gt;Distance\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-result-item\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fc-result-val\u0026quot; id=\u0026quot;fc-r-perkm\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-result-lbl\u0026quot; id=\u0026quot;fc-r-perkm-lbl\u0026quot;\u0026gt;Cost / km\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-tip\u0026quot; id=\u0026quot;fc-trip-tip\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; Compare Two Vehicles \u0026lt;div class=\u0026quot;fc-grid\u0026quot; style=\u0026quot;margin-bottom:1rem;\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fc-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Distance (km)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;fc-cmp-dist\u0026quot; value=\u0026quot;200\u0026quot; min=\u0026quot;1\u0026quot; step=\u0026quot;1\u0026quot; oninput=\u0026quot;fcCalcCompare()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Fuel Price (per liter)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;fc-cmp-price\u0026quot; value=\u0026quot;1.60\u0026quot; min=\u0026quot;0.01\u0026quot; step=\u0026quot;0.01\u0026quot; oninput=\u0026quot;fcCalcCompare()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-compare-grid\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fc-vehicle-card\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Vehicle A\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;fc-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Name\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;fc-va-name\u0026quot; value=\u0026quot;Current Car\u0026quot; oninput=\u0026quot;fcCalcCompare()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Fuel Mode\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;fc-va-mode\u0026quot; onchange=\u0026quot;fcCalcCompare()\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;l100km\u0026quot;\u0026gt;L / 100 km\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;kml\u0026quot;\u0026gt;km / L\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;mpg\u0026quot;\u0026gt;MPG\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Efficiency Value\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;fc-va-eff\u0026quot; value=\u0026quot;9\u0026quot; min=\u0026quot;0.1\u0026quot; step=\u0026quot;0.1\u0026quot; oninput=\u0026quot;fcCalcCompare()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-vehicle-card\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Vehicle B\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;fc-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Name\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;fc-vb-name\u0026quot; value=\u0026quot;New Car\u0026quot; oninput=\u0026quot;fcCalcCompare()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Fuel Mode\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;fc-vb-mode\u0026quot; onchange=\u0026quot;fcCalcCompare()\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;l100km\u0026quot;\u0026gt;L / 100 km\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;kml\u0026quot;\u0026gt;km / L\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;mpg\u0026quot;\u0026gt;MPG\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Efficiency Value\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;fc-vb-eff\u0026quot; value=\u0026quot;6\u0026quot; min=\u0026quot;0.1\u0026quot; step=\u0026quot;0.1\u0026quot; oninput=\u0026quot;fcCalcCompare()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;fc-cmp-output\u0026quot;\u0026gt; \u0026lt;table class=\u0026quot;fc-cmp-table\u0026quot; id=\u0026quot;fc-cmp-table\u0026quot;\u0026gt; \u0026lt;thead\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;th\u0026gt;Metric\u0026lt;/th\u0026gt; \u0026lt;th id=\u0026quot;fc-cmp-th-a\u0026quot;\u0026gt;Vehicle A\u0026lt;/th\u0026gt; \u0026lt;th id=\u0026quot;fc-cmp-th-b\u0026quot;\u0026gt;Vehicle B\u0026lt;/th\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;/thead\u0026gt; \u0026lt;tbody id=\u0026quot;fc-cmp-tbody\u0026quot;\u0026gt;\u0026lt;/tbody\u0026gt; \u0026lt;/table\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-chart-wrap\u0026quot; style=\u0026quot;margin-top:1rem;\u0026quot;\u0026gt; \u0026lt;canvas id=\u0026quot;fc-cmp-chart\u0026quot; height=\u0026quot;240\u0026quot;\u0026gt;\u0026lt;/canvas\u0026gt; \u0026lt;/div\u0026gt; Annual Fuel Cost Estimator \u0026lt;div class=\u0026quot;fc-grid\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fc-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Daily Commute (km one-way)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;fc-ann-commute\u0026quot; value=\u0026quot;25\u0026quot; min=\u0026quot;0\u0026quot; step=\u0026quot;1\u0026quot; oninput=\u0026quot;fcCalcAnnual()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Work Days / Week\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;fc-ann-workdays\u0026quot; value=\u0026quot;5\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;7\u0026quot; step=\u0026quot;1\u0026quot; oninput=\u0026quot;fcCalcAnnual()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Fuel Mode\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;fc-ann-mode\u0026quot; onchange=\u0026quot;fcCalcAnnual()\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;l100km\u0026quot;\u0026gt;L / 100 km\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;kml\u0026quot;\u0026gt;km / L\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;mpg\u0026quot;\u0026gt;MPG\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Efficiency Value\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;fc-ann-eff\u0026quot; value=\u0026quot;8\u0026quot; min=\u0026quot;0.1\u0026quot; step=\u0026quot;0.1\u0026quot; oninput=\u0026quot;fcCalcAnnual()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Fuel Price (per liter)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;fc-ann-price\u0026quot; value=\u0026quot;1.60\u0026quot; min=\u0026quot;0.01\u0026quot; step=\u0026quot;0.01\u0026quot; oninput=\u0026quot;fcCalcAnnual()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Extra km / week (errands, etc.)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;fc-ann-extra\u0026quot; value=\u0026quot;50\u0026quot; min=\u0026quot;0\u0026quot; step=\u0026quot;10\u0026quot; oninput=\u0026quot;fcCalcAnnual()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-annual-grid\u0026quot; id=\u0026quot;fc-ann-stats\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;fc-annual-stat\u0026quot;\u0026gt;\u0026lt;div class=\u0026quot;val\u0026quot; id=\u0026quot;fc-an-weekly\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt;\u0026lt;div class=\u0026quot;lbl\u0026quot;\u0026gt;Weekly\u0026lt;/div\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-annual-stat\u0026quot;\u0026gt;\u0026lt;div class=\u0026quot;val\u0026quot; id=\u0026quot;fc-an-monthly\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt;\u0026lt;div class=\u0026quot;lbl\u0026quot;\u0026gt;Monthly\u0026lt;/div\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-annual-stat\u0026quot;\u0026gt;\u0026lt;div class=\u0026quot;val\u0026quot; id=\u0026quot;fc-an-yearly\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt;\u0026lt;div class=\u0026quot;lbl\u0026quot;\u0026gt;Yearly\u0026lt;/div\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-annual-stat\u0026quot;\u0026gt;\u0026lt;div class=\u0026quot;val\u0026quot; id=\u0026quot;fc-an-liters\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt;\u0026lt;div class=\u0026quot;lbl\u0026quot;\u0026gt;Liters/Year\u0026lt;/div\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-annual-stat\u0026quot;\u0026gt;\u0026lt;div class=\u0026quot;val\u0026quot; id=\u0026quot;fc-an-km\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt;\u0026lt;div class=\u0026quot;lbl\u0026quot;\u0026gt;km/Year\u0026lt;/div\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-annual-stat\u0026quot;\u0026gt;\u0026lt;div class=\u0026quot;val\u0026quot; id=\u0026quot;fc-an-perday\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt;\u0026lt;div class=\u0026quot;lbl\u0026quot;\u0026gt;Daily Cost\u0026lt;/div\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;fc-chart-wrap\u0026quot;\u0026gt; \u0026lt;canvas id=\u0026quot;fc-ann-chart\u0026quot; height=\u0026quot;240\u0026quot;\u0026gt;\u0026lt;/canvas\u0026gt; \u0026lt;/div\u0026gt; \u0026#8594;Track your monthly spending Budget Planner \u0026#8594;Estimate your home electricity costs Electricity Cost Calculator Related Tools Carbon Footprint Calculator Cooking Unit Converter Electricity Cost Calculator ","permalink":"https://productivity-works.com/tools/fuel-cost-calculator/","summary":"\u003cdiv id=\"fc-app\"\u003e\n\u003cstyle\u003e\n#fc-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #e2e8f0;\n}\n#fc-app * { box-sizing: border-box; }\n\u003cp\u003e#fc-app h2 {\nfont-size: 1.2rem;\nfont-weight: 700;\ncolor: #f1f5f9;\nmargin: 0 0 1rem 0;\npadding-bottom: 0.5rem;\nborder-bottom: 2px solid #334155;\n}\n#fc-app h3 {\nfont-size: 0.95rem;\nfont-weight: 600;\ncolor: #cbd5e1;\nmargin: 0 0 0.75rem 0;\n}\u003c/p\u003e\n\u003cp\u003e/* Tabs */\n#fc-app .fc-tabs {\ndisplay: flex;\ngap: 0.5rem;\nmargin-bottom: 1.5rem;\nflex-wrap: wrap;\n}\n#fc-app .fc-tab {\npadding: 0.5rem 1.25rem;\nborder-radius: 6px;\nborder: 1px solid #334155;\nbackground: #1e293b;\ncolor: #94a3b8;\ncursor: pointer;\nfont-size: 0.875rem;\nfont-weight: 500;\ntransition: all 0.15s ease;\n}\n#fc-app .fc-tab:hover { border-color: #475569; color: #cbd5e1; }\n#fc-app .fc-tab.active { background: #10b981; border-color: #10b981; color: #fff; }\u003c/p\u003e","title":"Fuel Cost Calculator"},{"content":"Select a scenario to get the exact git command — with flag explanations and a one-click copy. No account needed.\n\u0026#9881; Select a scenario from the left panel to see the git command and explanation.\n\u0026#9889; Common Workflows Related Tools Build cron expressions → Cron Expression Generator Generate changelogs → Changelog Generator Build .htaccess → .htaccess Generator ","permalink":"https://productivity-works.com/tools/git-command-generator/","summary":"\u003cp\u003eSelect a scenario to get the exact git command — with flag explanations and a one-click copy. No account needed.\u003c/p\u003e\n\u003cdiv id=\"git-app\"\u003e\n\u003cstyle\u003e\n  #git-app *,\n  #git-app *::before,\n  #git-app *::after {\n    box-sizing: border-box;\n  }\n  #git-app {\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n    font-size: 15px;\n    color: #1a1a2e;\n    margin: 0 auto;\n    max-width: 960px;\n  }\n\u003cp\u003e/* Search */\n#git-app .git-search-wrap {\nmargin-bottom: 20px;\n}\n#git-app #git-search {\nwidth: 100%;\npadding: 11px 16px;\nborder: 2px solid #e2e8f0;\nborder-radius: 10px;\nfont-size: 1rem;\nfont-family: inherit;\nbackground: #fff;\ncolor: #1a1a2e;\noutline: none;\ntransition: border-color 0.18s;\n}\n#git-app #git-search:focus {\nborder-color: #f05033;\n}\n#git-app .git-search-count {\nmargin-top: 6px;\nfont-size: 0.82rem;\ncolor: #718096;\n}\u003c/p\u003e","title":"Git Command Generator — Quick Reference"},{"content":"Build a professional GitHub profile README in minutes. Choose a template, fill in your details, pick your skills, and copy the generated Markdown straight into your GitHub profile repo.\nRelated tools: Markdown Preview • Git Command Generator Template: Minimal Developer Creative Identity Your Name Title / Role GitHub Username (for stats) Header Style Wave Gradient Banner Typing Badge Plain About Me Short Bio Currently Working On Currently Learning Fun Fact Skills \u0026amp; Tech Social Links GitHub URL Twitter / X URL LinkedIn URL Dev.to URL Personal Website Email Address GitHub Stats Cards GitHub Stats Card Streak Stats Top Languages Card Stats Theme Default Dark Radical Merko Gruvbox Tokyo Night One Dark Cobalt Synthwave High Contrast Generated README.md Copy README.md Copied! How to Use Your New README Create a special repo on GitHub named exactly the same as your username (e.g. github.com/janedoe/janedoe). Initialize it with a README.md file. Paste the generated content above into README.md. Commit and push — GitHub will display it automatically on your profile page. The stats cards pull live data from github-readme-stats and streak-stats . No API key needed — just replace yourusername with your real GitHub handle.\nRelated tools: Markdown Preview • Git Command Generator ","permalink":"https://productivity-works.com/tools/github-readme-generator/","summary":"\u003cp\u003eBuild a professional GitHub profile README in minutes. Choose a template, fill in your details, pick your skills, and copy the generated Markdown straight into your GitHub profile repo.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eRelated tools:\u003c/strong\u003e \u003ca href=\"https://productivity-works.com/tools/markdown-preview/\"\u003eMarkdown Preview\u003c/a\u003e\n • \u003ca href=\"https://productivity-works.com/tools/git-command-generator/\"\u003eGit Command Generator\u003c/a\u003e\n\u003c/p\u003e\n\u003chr\u003e\n\u003cdiv id=\"gh-app\"\u003e\n\u003cstyle\u003e\n/* ── Reset \u0026 base ── */\n#gh-app *{box-sizing:border-box;margin:0;padding:0}\n#gh-app{font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;color:#24292f;--accent:#0969da;--border:#d0d7de;--bg:#f6f8fa;--radius:6px;--pad:16px}\n\n/* ── Layout ── */\n#gh-app .gh-wrap{display:grid;grid-template-columns:1fr 1fr;gap:24px;margin-top:16px}\n@media(max-width:820px){#gh-app .gh-wrap{grid-template-columns:1fr}}\n\n/* ── Sections ── */\n#gh-app .gh-panel{background:#fff;border:1px solid var(--border);border-radius:var(--radius);padding:var(--pad)}\n#gh-app h2.gh-title{font-size:1rem;font-weight:600;margin-bottom:12px;display:flex;align-items:center;gap:6px}\n#gh-app h2.gh-title svg{flex-shrink:0}\n\n/* ── Controls toolbar ── */\n#gh-app .gh-toolbar{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:20px;align-items:center}\n#gh-app .gh-preset-btn{padding:6px 14px;border:1px solid var(--border);background:#fff;border-radius:20px;cursor:pointer;font-size:.85rem;transition:all .15s}\n#gh-app .gh-preset-btn.active,#gh-app .gh-preset-btn:hover{background:var(--accent);color:#fff;border-color:var(--accent)}\n#gh-app .gh-toolbar label{font-size:.85rem;font-weight:500;margin-left:8px}\n\n/* ── Form fields ── */\n#gh-app .gh-field{margin-bottom:14px}\n#gh-app .gh-field label{display:block;font-size:.82rem;font-weight:600;color:#57606a;margin-bottom:4px}\n#gh-app .gh-field input,#gh-app .gh-field textarea,#gh-app .gh-field select{width:100%;padding:7px 10px;border:1px solid var(--border);border-radius:var(--radius);font-size:.9rem;font-family:inherit;background:#fff;transition:border .15s}\n#gh-app .gh-field input:focus,#gh-app .gh-field textarea:focus,#gh-app .gh-field select:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px rgba(9,105,218,.15)}\n#gh-app .gh-field textarea{resize:vertical;min-height:64px}\n\n/* ── Skills grid ── */\n#gh-app .gh-skills-grid{display:flex;flex-wrap:wrap;gap:6px;margin-top:4px}\n#gh-app .gh-skill-tag{padding:4px 10px;border:1px solid var(--border);border-radius:20px;font-size:.78rem;cursor:pointer;user-select:none;transition:all .15s;background:#fff}\n#gh-app .gh-skill-tag.selected{background:var(--accent);color:#fff;border-color:var(--accent)}\n\n/* ── Header style picker ── */\n#gh-app .gh-style-grid{display:flex;gap:8px;flex-wrap:wrap;margin-top:4px}\n#gh-app .gh-style-opt{padding:5px 12px;border:1px solid var(--border);border-radius:var(--radius);cursor:pointer;font-size:.82rem;background:#fff;transition:all .15s}\n#gh-app .gh-style-opt.active{background:var(--accent);color:#fff;border-color:var(--accent)}\n\n/* ── Stats toggle ── */\n#gh-app .gh-checks{display:flex;flex-direction:column;gap:8px;margin-top:4px}\n#gh-app .gh-check-row{display:flex;align-items:center;gap:8px;font-size:.88rem;cursor:pointer}\n#gh-app .gh-check-row input[type=checkbox]{width:16px;height:16px;accent-color:var(--accent);cursor:pointer}\n\n/* ── Preview ── */\n#gh-app .gh-preview{background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);padding:var(--pad);font-family:\"SFMono-Regular\",Consolas,monospace;font-size:.78rem;line-height:1.6;white-space:pre-wrap;word-break:break-all;max-height:620px;overflow-y:auto;color:#1f2328}\n\n/* ── Copy button ── */\n#gh-app .gh-copy-wrap{margin-top:10px;display:flex;gap:8px}\n#gh-app .gh-copy-btn{padding:9px 20px;background:var(--accent);color:#fff;border:none;border-radius:var(--radius);cursor:pointer;font-size:.9rem;font-weight:600;transition:background .15s;flex:1}\n#gh-app .gh-copy-btn:hover{background:#0757ba}\n#gh-app .gh-copy-btn:active{background:#044289}\n#gh-app .gh-copy-msg{font-size:.82rem;color:#1a7f37;display:none;align-items:center;gap:4px;font-weight:600}\n#gh-app .gh-copy-msg.show{display:flex}\n\n/* ── Section separator ── */\n#gh-app .gh-sep{border:none;border-top:1px solid var(--border);margin:14px 0}\n\u003c/style\u003e\n\u003c!-- Toolbar: presets + header style --\u003e\n\u003cdiv class=\"gh-toolbar\"\u003e\n  \u003cspan style=\"font-size:.85rem;font-weight:600;color:#57606a\"\u003eTemplate:\u003c/span\u003e\n  \u003cbutton class=\"gh-preset-btn\" onclick=\"ghApplyPreset('minimal')\"\u003eMinimal\u003c/button\u003e\n  \u003cbutton class=\"gh-preset-btn active\" onclick=\"ghApplyPreset('developer')\"\u003eDeveloper\u003c/button\u003e\n  \u003cbutton class=\"gh-preset-btn\" onclick=\"ghApplyPreset('creative')\"\u003eCreative\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"gh-wrap\"\u003e\n\u003c!-- LEFT: Form --\u003e\n\u003cdiv\u003e\n  \u003c!-- Identity --\u003e\n  \u003cdiv class=\"gh-panel\" style=\"margin-bottom:16px\"\u003e\n    \u003ch2 class=\"gh-title\"\u003e\n      \u003csvg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\"\u003e\u003ccircle cx=\"8\" cy=\"5\" r=\"3\" stroke=\"#57606a\" stroke-width=\"1.5\"/\u003e\u003cpath d=\"M2 14c0-3.314 2.686-6 6-6s6 2.686 6 6\" stroke=\"#57606a\" stroke-width=\"1.5\" stroke-linecap=\"round\"/\u003e\u003c/svg\u003e\n      Identity\n    \u003c/h2\u003e\n    \u003cdiv class=\"gh-field\"\u003e\u003clabel\u003eYour Name\u003c/label\u003e\u003cinput id=\"gh-name\" type=\"text\" placeholder=\"Jane Doe\" oninput=\"ghGenerate()\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"gh-field\"\u003e\u003clabel\u003eTitle / Role\u003c/label\u003e\u003cinput id=\"gh-title-input\" type=\"text\" placeholder=\"Full-Stack Developer | Open Source Enthusiast\" oninput=\"ghGenerate()\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"gh-field\"\u003e\u003clabel\u003eGitHub Username (for stats)\u003c/label\u003e\u003cinput id=\"gh-user\" type=\"text\" placeholder=\"janedoe\" oninput=\"ghGenerate()\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"gh-field\"\u003e\n      \u003clabel\u003eHeader Style\u003c/label\u003e\n      \u003cdiv class=\"gh-style-grid\"\u003e\n        \u003cdiv class=\"gh-style-opt active\" onclick=\"ghSetStyle('wave')\"\u003eWave\u003c/div\u003e\n        \u003cdiv class=\"gh-style-opt\" onclick=\"ghSetStyle('gradient')\"\u003eGradient Banner\u003c/div\u003e\n        \u003cdiv class=\"gh-style-opt\" onclick=\"ghSetStyle('typing')\"\u003eTyping Badge\u003c/div\u003e\n        \u003cdiv class=\"gh-style-opt\" onclick=\"ghSetStyle('plain')\"\u003ePlain\u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- About --\u003e\n  \u003cdiv class=\"gh-panel\" style=\"margin-bottom:16px\"\u003e\n    \u003ch2 class=\"gh-title\"\u003e\n      \u003csvg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\"\u003e\u003crect x=\"2\" y=\"2\" width=\"12\" height=\"12\" rx=\"2\" stroke=\"#57606a\" stroke-width=\"1.5\"/\u003e\u003cpath d=\"M5 8h6M5 5.5h6M5 10.5h4\" stroke=\"#57606a\" stroke-width=\"1.5\" stroke-linecap=\"round\"/\u003e\u003c/svg\u003e\n      About Me\n    \u003c/h2\u003e\n    \u003cdiv class=\"gh-field\"\u003e\u003clabel\u003eShort Bio\u003c/label\u003e\u003ctextarea id=\"gh-bio\" placeholder=\"Passionate developer who loves building things for the web...\" oninput=\"ghGenerate()\"\u003e\u003c/textarea\u003e\u003c/div\u003e\n    \u003cdiv class=\"gh-field\"\u003e\u003clabel\u003eCurrently Working On\u003c/label\u003e\u003cinput id=\"gh-working\" type=\"text\" placeholder=\"An open-source CLI tool for devs\" oninput=\"ghGenerate()\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"gh-field\"\u003e\u003clabel\u003eCurrently Learning\u003c/label\u003e\u003cinput id=\"gh-learning\" type=\"text\" placeholder=\"Rust, WebAssembly, GraphQL\" oninput=\"ghGenerate()\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"gh-field\"\u003e\u003clabel\u003eFun Fact\u003c/label\u003e\u003cinput id=\"gh-fun\" type=\"text\" placeholder=\"I debug with rubber ducks\" oninput=\"ghGenerate()\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Skills --\u003e\n  \u003cdiv class=\"gh-panel\" style=\"margin-bottom:16px\"\u003e\n    \u003ch2 class=\"gh-title\"\u003e\n      \u003csvg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\"\u003e\u003cpath d=\"M5 4L2 8l3 4M11 4l3 4-3 4M8 2l-1.5 12\" stroke=\"#57606a\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/\u003e\u003c/svg\u003e\n      Skills \u0026amp; Tech\n    \u003c/h2\u003e\n    \u003cdiv class=\"gh-skills-grid\" id=\"gh-skills-grid\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Social Links --\u003e\n  \u003cdiv class=\"gh-panel\" style=\"margin-bottom:16px\"\u003e\n    \u003ch2 class=\"gh-title\"\u003e\n      \u003csvg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\"\u003e\u003ccircle cx=\"12\" cy=\"4\" r=\"2\" stroke=\"#57606a\" stroke-width=\"1.5\"/\u003e\u003ccircle cx=\"4\" cy=\"8\" r=\"2\" stroke=\"#57606a\" stroke-width=\"1.5\"/\u003e\u003ccircle cx=\"12\" cy=\"12\" r=\"2\" stroke=\"#57606a\" stroke-width=\"1.5\"/\u003e\u003cpath d=\"M6 7l4-2M6 9l4 2\" stroke=\"#57606a\" stroke-width=\"1.5\" stroke-linecap=\"round\"/\u003e\u003c/svg\u003e\n      Social Links\n    \u003c/h2\u003e\n    \u003cdiv class=\"gh-field\"\u003e\u003clabel\u003eGitHub URL\u003c/label\u003e\u003cinput id=\"gh-social-github\" type=\"text\" placeholder=\"https://github.com/janedoe\" oninput=\"ghGenerate()\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"gh-field\"\u003e\u003clabel\u003eTwitter / X URL\u003c/label\u003e\u003cinput id=\"gh-social-twitter\" type=\"text\" placeholder=\"https://twitter.com/janedoe\" oninput=\"ghGenerate()\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"gh-field\"\u003e\u003clabel\u003eLinkedIn URL\u003c/label\u003e\u003cinput id=\"gh-social-linkedin\" type=\"text\" placeholder=\"https://linkedin.com/in/janedoe\" oninput=\"ghGenerate()\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"gh-field\"\u003e\u003clabel\u003eDev.to URL\u003c/label\u003e\u003cinput id=\"gh-social-devto\" type=\"text\" placeholder=\"https://dev.to/janedoe\" oninput=\"ghGenerate()\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"gh-field\"\u003e\u003clabel\u003ePersonal Website\u003c/label\u003e\u003cinput id=\"gh-social-web\" type=\"text\" placeholder=\"https://janedoe.dev\" oninput=\"ghGenerate()\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"gh-field\"\u003e\u003clabel\u003eEmail Address\u003c/label\u003e\u003cinput id=\"gh-social-email\" type=\"text\" placeholder=\"jane@example.com\" oninput=\"ghGenerate()\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Stats --\u003e\n  \u003cdiv class=\"gh-panel\"\u003e\n    \u003ch2 class=\"gh-title\"\u003e\n      \u003csvg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\"\u003e\u003cpath d=\"M2 12l3-4 3 2 3-5 3 3\" stroke=\"#57606a\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/\u003e\u003c/svg\u003e\n      GitHub Stats Cards\n    \u003c/h2\u003e\n    \u003cdiv class=\"gh-checks\"\u003e\n      \u003clabel class=\"gh-check-row\"\u003e\u003cinput type=\"checkbox\" id=\"gh-stat-main\" checked onchange=\"ghGenerate()\"\u003e GitHub Stats Card\u003c/label\u003e\n      \u003clabel class=\"gh-check-row\"\u003e\u003cinput type=\"checkbox\" id=\"gh-stat-streak\" checked onchange=\"ghGenerate()\"\u003e Streak Stats\u003c/label\u003e\n      \u003clabel class=\"gh-check-row\"\u003e\u003cinput type=\"checkbox\" id=\"gh-stat-langs\" checked onchange=\"ghGenerate()\"\u003e Top Languages Card\u003c/label\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"gh-field\" style=\"margin-top:12px\"\u003e\n      \u003clabel\u003eStats Theme\u003c/label\u003e\n      \u003cselect id=\"gh-stat-theme\" onchange=\"ghGenerate()\"\u003e\n        \u003coption value=\"default\"\u003eDefault\u003c/option\u003e\n        \u003coption value=\"dark\"\u003eDark\u003c/option\u003e\n        \u003coption value=\"radical\"\u003eRadical\u003c/option\u003e\n        \u003coption value=\"merko\"\u003eMerko\u003c/option\u003e\n        \u003coption value=\"gruvbox\"\u003eGruvbox\u003c/option\u003e\n        \u003coption value=\"tokyonight\"\u003eTokyo Night\u003c/option\u003e\n        \u003coption value=\"onedark\"\u003eOne Dark\u003c/option\u003e\n        \u003coption value=\"cobalt\"\u003eCobalt\u003c/option\u003e\n        \u003coption value=\"synthwave\"\u003eSynthwave\u003c/option\u003e\n        \u003coption value=\"highcontrast\"\u003eHigh Contrast\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- RIGHT: Preview --\u003e\n\u003cdiv\u003e\n  \u003cdiv class=\"gh-panel\" style=\"position:sticky;top:80px\"\u003e\n    \u003ch2 class=\"gh-title\"\u003e\n      \u003csvg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\"\u003e\u003cpath d=\"M1 4h14M1 8h10M1 12h12\" stroke=\"#57606a\" stroke-width=\"1.5\" stroke-linecap=\"round\"/\u003e\u003c/svg\u003e\n      Generated README.md\n    \u003c/h2\u003e\n    \u003cpre class=\"gh-preview\" id=\"gh-preview\"\u003e\u003c!-- generated --\u003e\u003c/pre\u003e\n    \u003cdiv class=\"gh-copy-wrap\"\u003e\n      \u003cbutton class=\"gh-copy-btn\" onclick=\"ghCopy()\"\u003eCopy README.md\u003c/button\u003e\n      \u003cspan class=\"gh-copy-msg\" id=\"gh-copy-msg\"\u003eCopied!\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003c!-- .gh-wrap --\u003e\n\u003cscript\u003e\n(function(){\n\"use strict\";\n\n/* ── Skills catalog ── */\nvar SKILLS = [\n  {id:\"js\",   label:\"JavaScript\", color:\"F7DF1E\", logo:\"javascript\",     logoColor:\"black\"},\n  {id:\"ts\",   label:\"TypeScript\", color:\"3178C6\", logo:\"typescript\",      logoColor:\"white\"},\n  {id:\"py\",   label:\"Python\",     color:\"3776AB\", logo:\"python\",          logoColor:\"white\"},\n  {id:\"go\",   label:\"Go\",         color:\"00ADD8\", logo:\"go\",              logoColor:\"white\"},\n  {id:\"rust\", label:\"Rust\",       color:\"000000\", logo:\"rust\",            logoColor:\"white\"},\n  {id:\"java\", label:\"Java\",       color:\"ED8B00\", logo:\"openjdk\",         logoColor:\"white\"},\n  {id:\"cpp\",  label:\"C++\",        color:\"00599C\", logo:\"cplusplus\",       logoColor:\"white\"},\n  {id:\"cs\",   label:\"C#\",         color:\"512BD4\", logo:\"csharp\",          logoColor:\"white\"},\n  {id:\"rb\",   label:\"Ruby\",       color:\"CC342D\", logo:\"ruby\",            logoColor:\"white\"},\n  {id:\"php\",  label:\"PHP\",        color:\"777BB4\", logo:\"php\",             logoColor:\"white\"},\n  {id:\"swift\",label:\"Swift\",      color:\"FA7343\", logo:\"swift\",           logoColor:\"white\"},\n  {id:\"kt\",   label:\"Kotlin\",     color:\"7F52FF\", logo:\"kotlin\",          logoColor:\"white\"},\n  {id:\"react\",label:\"React\",      color:\"20232A\", logo:\"react\",           logoColor:\"61DAFB\"},\n  {id:\"vue\",  label:\"Vue.js\",     color:\"4FC08D\", logo:\"vuedotjs\",        logoColor:\"white\"},\n  {id:\"ng\",   label:\"Angular\",    color:\"DD0031\", logo:\"angular\",         logoColor:\"white\"},\n  {id:\"svelte\",label:\"Svelte\",    color:\"FF3E00\", logo:\"svelte\",          logoColor:\"white\"},\n  {id:\"next\", label:\"Next.js\",    color:\"000000\", logo:\"nextdotjs\",       logoColor:\"white\"},\n  {id:\"nuxt\", label:\"Nuxt.js\",    color:\"00DC82\", logo:\"nuxtdotjs\",       logoColor:\"white\"},\n  {id:\"node\", label:\"Node.js\",    color:\"339933\", logo:\"nodedotjs\",       logoColor:\"white\"},\n  {id:\"deno\", label:\"Deno\",       color:\"000000\", logo:\"deno\",            logoColor:\"white\"},\n  {id:\"express\",label:\"Express\",  color:\"000000\", logo:\"express\",         logoColor:\"white\"},\n  {id:\"fastapi\",label:\"FastAPI\",  color:\"009688\", logo:\"fastapi\",         logoColor:\"white\"},\n  {id:\"django\",label:\"Django\",    color:\"092E20\", logo:\"django\",          logoColor:\"white\"},\n  {id:\"rails\",label:\"Rails\",      color:\"CC0000\", logo:\"rubyonrails\",     logoColor:\"white\"},\n  {id:\"laravel\",label:\"Laravel\",  color:\"FF2D20\", logo:\"laravel\",         logoColor:\"white\"},\n  {id:\"docker\",label:\"Docker\",    color:\"2496ED\", logo:\"docker\",          logoColor:\"white\"},\n  {id:\"k8s\",  label:\"Kubernetes\", color:\"326CE5\", logo:\"kubernetes\",      logoColor:\"white\"},\n  {id:\"aws\",  label:\"AWS\",        color:\"FF9900\", logo:\"amazonaws\",       logoColor:\"white\"},\n  {id:\"gcp\",  label:\"GCP\",        color:\"4285F4\", logo:\"googlecloud\",     logoColor:\"white\"},\n  {id:\"azure\",label:\"Azure\",      color:\"0078D4\", logo:\"microsoftazure\",  logoColor:\"white\"},\n  {id:\"git\",  label:\"Git\",        color:\"F05032\", logo:\"git\",             logoColor:\"white\"},\n  {id:\"linux\",label:\"Linux\",      color:\"FCC624\", logo:\"linux\",           logoColor:\"black\"},\n  {id:\"pg\",   label:\"PostgreSQL\", color:\"4169E1\", logo:\"postgresql\",      logoColor:\"white\"},\n  {id:\"mysql\",label:\"MySQL\",      color:\"4479A1\", logo:\"mysql\",           logoColor:\"white\"},\n  {id:\"mongo\",label:\"MongoDB\",    color:\"47A248\", logo:\"mongodb\",         logoColor:\"white\"},\n  {id:\"redis\",label:\"Redis\",      color:\"DC382D\", logo:\"redis\",           logoColor:\"white\"},\n  {id:\"graphql\",label:\"GraphQL\",  color:\"E10098\", logo:\"graphql\",         logoColor:\"white\"},\n  {id:\"figma\",label:\"Figma\",      color:\"F24E1E\", logo:\"figma\",           logoColor:\"white\"},\n];\n\nvar selected = {js:true, ts:true, react:true, node:true, docker:true, git:true};\nvar headerStyle = \"wave\";\nvar currentPreset = \"developer\";\n\n/* ── Build skill grid ── */\nvar grid = document.getElementById(\"gh-skills-grid\");\nSKILLS.forEach(function(s){\n  var el = document.createElement(\"div\");\n  el.className = \"gh-skill-tag\" + (selected[s.id] ? \" selected\" : \"\");\n  el.textContent = s.label;\n  el.dataset.id = s.id;\n  el.addEventListener(\"click\", function(){\n    selected[s.id] = !selected[s.id];\n    el.classList.toggle(\"selected\", selected[s.id]);\n    ghGenerate();\n  });\n  grid.appendChild(el);\n});\n\n/* ── Header style picker ── */\nwindow.ghSetStyle = function(style){\n  headerStyle = style;\n  document.querySelectorAll(\"#gh-app .gh-style-opt\").forEach(function(el){\n    el.classList.toggle(\"active\", el.textContent.toLowerCase().replace(\" \",\"\") === style || el.textContent === \"Wave\" \u0026\u0026 style === \"wave\" || el.textContent === \"Gradient Banner\" \u0026\u0026 style === \"gradient\" || el.textContent === \"Typing Badge\" \u0026\u0026 style === \"typing\" || el.textContent === \"Plain\" \u0026\u0026 style === \"plain\");\n  });\n  ghGenerate();\n};\n\n/* ── Presets ── */\nvar PRESETS = {\n  minimal: {\n    name:\"Your Name\", titleInput:\"Software Developer\", user:\"yourusername\", bio:\"Building things one commit at a time.\",\n    working:\"\", learning:\"\", fun:\"\",\n    skills:{js:true, py:true, git:true, linux:true},\n    statMain:true, statStreak:false, statLangs:false, theme:\"default\", headerStyle:\"plain\",\n    github:\"\", twitter:\"\", linkedin:\"\", devto:\"\", web:\"\", email:\"\"\n  },\n  developer: {\n    name:\"Your Name\", titleInput:\"Full-Stack Developer | Open Source Enthusiast\", user:\"yourusername\",\n    bio:\"Passionate developer who loves building scalable web applications and contributing to open source.\",\n    working:\"An open-source developer toolkit\", learning:\"Rust and WebAssembly\", fun:\"I debug with rubber ducks\",\n    skills:{js:true, ts:true, react:true, node:true, docker:true, git:true, pg:true, py:true},\n    statMain:true, statStreak:true, statLangs:true, theme:\"tokyonight\", headerStyle:\"wave\",\n    github:\"\", twitter:\"\", linkedin:\"\", devto:\"\", web:\"\", email:\"\"\n  },\n  creative: {\n    name:\"Your Name\", titleInput:\"Creative Developer \u0026 Designer\", user:\"yourusername\",\n    bio:\"I craft beautiful digital experiences at the intersection of design and engineering.\",\n    working:\"A design system library\", learning:\"Three.js and WebGL\", fun:\"I ship at 2am\",\n    skills:{js:true, ts:true, react:true, svelte:true, figma:true, node:true, git:true},\n    statMain:true, statStreak:true, statLangs:true, theme:\"radical\", headerStyle:\"gradient\",\n    github:\"\", twitter:\"\", linkedin:\"\", devto:\"\", web:\"\", email:\"\"\n  }\n};\n\nwindow.ghApplyPreset = function(name){\n  currentPreset = name;\n  var p = PRESETS[name];\n  document.getElementById(\"gh-name\").value = p.name;\n  document.getElementById(\"gh-title-input\").value = p.titleInput;\n  document.getElementById(\"gh-user\").value = p.user;\n  document.getElementById(\"gh-bio\").value = p.bio;\n  document.getElementById(\"gh-working\").value = p.working;\n  document.getElementById(\"gh-learning\").value = p.learning;\n  document.getElementById(\"gh-fun\").value = p.fun;\n  document.getElementById(\"gh-stat-main\").checked = p.statMain;\n  document.getElementById(\"gh-stat-streak\").checked = p.statStreak;\n  document.getElementById(\"gh-stat-langs\").checked = p.statLangs;\n  document.getElementById(\"gh-stat-theme\").value = p.theme;\n  selected = Object.assign({}, p.skills);\n  document.querySelectorAll(\"#gh-skills-grid .gh-skill-tag\").forEach(function(el){\n    el.classList.toggle(\"selected\", !!selected[el.dataset.id]);\n  });\n  document.querySelectorAll(\"#gh-app .gh-preset-btn\").forEach(function(el){\n    el.classList.toggle(\"active\", el.textContent.toLowerCase() === name);\n  });\n  ghSetStyle(p.headerStyle);\n  ghGenerate();\n};\n\n/* ── Badge URL helper ── */\nfunction skillBadge(s){\n  return \"![\" + s.label + \"](https://img.shields.io/badge/\" + encodeURIComponent(s.label) + \"-\" + s.color + \"?style=for-the-badge\u0026logo=\" + s.logo + \"\u0026logoColor=\" + s.logoColor + \")\";\n}\n\n/* ── Social badge helper ── */\nfunction socialBadge(label, color, logo, url){\n  if(!url) return \"\";\n  return \"[![\" + label + \"](https://img.shields.io/badge/\" + label + \"-\" + color + \"?style=for-the-badge\u0026logo=\" + logo + \"\u0026logoColor=white)](\" + url + \")\";\n}\n\n/* ── Generate markdown ── */\nwindow.ghGenerate = function(){\n  var name       = document.getElementById(\"gh-name\").value.trim() || \"Your Name\";\n  var title      = document.getElementById(\"gh-title-input\").value.trim() || \"Developer\";\n  var user       = document.getElementById(\"gh-user\").value.trim() || \"yourusername\";\n  var bio        = document.getElementById(\"gh-bio\").value.trim();\n  var working    = document.getElementById(\"gh-working\").value.trim();\n  var learning   = document.getElementById(\"gh-learning\").value.trim();\n  var fun        = document.getElementById(\"gh-fun\").value.trim();\n  var theme      = document.getElementById(\"gh-stat-theme\").value;\n  var statMain   = document.getElementById(\"gh-stat-main\").checked;\n  var statStreak = document.getElementById(\"gh-stat-streak\").checked;\n  var statLangs  = document.getElementById(\"gh-stat-langs\").checked;\n\n  var socialGithub  = document.getElementById(\"gh-social-github\").value.trim();\n  var socialTwitter = document.getElementById(\"gh-social-twitter\").value.trim();\n  var socialLinkedin= document.getElementById(\"gh-social-linkedin\").value.trim();\n  var socialDevto   = document.getElementById(\"gh-social-devto\").value.trim();\n  var socialWeb     = document.getElementById(\"gh-social-web\").value.trim();\n  var socialEmail   = document.getElementById(\"gh-social-email\").value.trim();\n\n  var lines = [];\n\n  /* Header */\n  if(headerStyle === \"wave\"){\n    lines.push('\u003cp align=\"center\"\u003e');\n    lines.push('  \u003cimg src=\"https://capsule-render.vercel.app/api?type=waving\u0026color=gradient\u0026height=200\u0026section=header\u0026text=' + encodeURIComponent(name) + '\u0026fontSize=50\u0026fontAlignY=35\u0026desc=' + encodeURIComponent(title) + '\u0026descAlignY=55\u0026descAlign=50\u0026animation=fadeIn\" /\u003e');\n    lines.push(\"\u003c/p\u003e","title":"GitHub Profile README Generator"},{"content":"Generate frosted-glass CSS effects in seconds. Adjust blur, transparency, border radius, and more — then copy the ready-to-use CSS code.\nPresets\nFrost Ocean Sunset Midnight Forest \u0026lt;hr class=\u0026quot;gm-section-divider\u0026quot;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;p class=\u0026quot;gm-panel-title\u0026quot;\u0026gt;Glass Effect\u0026lt;/p\u0026gt; \u0026lt;div class=\u0026quot;gm-field\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;gm-label\u0026quot;\u0026gt;Blur Amount \u0026lt;span id=\u0026quot;gm-blur-val\u0026quot;\u0026gt;12px\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;gm-blur\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;30\u0026quot; value=\u0026quot;12\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;gm-field\u0026quot; style=\u0026quot;margin-top:12px;\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;gm-label\u0026quot;\u0026gt;Transparency \u0026lt;span id=\u0026quot;gm-alpha-val\u0026quot;\u0026gt;0.15\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;gm-alpha\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;100\u0026quot; value=\u0026quot;15\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;gm-field\u0026quot; style=\u0026quot;margin-top:12px;\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;gm-label\u0026quot;\u0026gt;Border Radius \u0026lt;span id=\u0026quot;gm-radius-val\u0026quot;\u0026gt;16px\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;gm-radius\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;48\u0026quot; value=\u0026quot;16\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;gm-field\u0026quot; style=\u0026quot;margin-top:12px;\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;gm-label\u0026quot;\u0026gt;Border Width \u0026lt;span id=\u0026quot;gm-bw-val\u0026quot;\u0026gt;1px\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;gm-bw\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;6\u0026quot; value=\u0026quot;1\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;gm-field\u0026quot; style=\u0026quot;margin-top:12px;\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;gm-label\u0026quot;\u0026gt;Border Opacity \u0026lt;span id=\u0026quot;gm-bopacity-val\u0026quot;\u0026gt;0.30\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;gm-bopacity\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;100\u0026quot; value=\u0026quot;30\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;hr class=\u0026quot;gm-section-divider\u0026quot;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;p class=\u0026quot;gm-panel-title\u0026quot;\u0026gt;Colors\u0026lt;/p\u0026gt; \u0026lt;div class=\u0026quot;gm-color-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;gm-color-label\u0026quot;\u0026gt;Card base color\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;gm-card-color\u0026quot; value=\u0026quot;#ffffff\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;gm-color-row\u0026quot; style=\u0026quot;margin-top:10px;\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;gm-color-label\u0026quot;\u0026gt;Text color\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;gm-text-color\u0026quot; value=\u0026quot;#ffffff\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;hr class=\u0026quot;gm-section-divider\u0026quot;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;p class=\u0026quot;gm-panel-title\u0026quot;\u0026gt;Background Gradient\u0026lt;/p\u0026gt; \u0026lt;div class=\u0026quot;gm-color-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;gm-color-label\u0026quot;\u0026gt;Color 1\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;gm-bg1\u0026quot; value=\u0026quot;#6366f1\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;gm-color-row\u0026quot; style=\u0026quot;margin-top:10px;\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;gm-color-label\u0026quot;\u0026gt;Color 2\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;gm-bg2\u0026quot; value=\u0026quot;#8b5cf6\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;hr class=\u0026quot;gm-section-divider\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;gm-toggle-row\u0026quot;\u0026gt; \u0026lt;label class=\u0026quot;gm-toggle\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;gm-dark-toggle\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;gm-toggle-slider\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/label\u0026gt; \u0026lt;span class=\u0026quot;gm-toggle-text\u0026quot;\u0026gt;Dark preview background\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; backdrop-filter is supported in Chrome 76+, Firefox 103+, Safari 9+, Edge 17+. Prefix: -webkit-backdrop-filter for older Safari. \u0026lt;!-- Preview --\u0026gt; \u0026lt;div class=\u0026quot;gm-preview-area\u0026quot; id=\u0026quot;gm-preview-area\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;gm-backdrop-shapes\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;gm-shape gm-shape-1\u0026quot; id=\u0026quot;gm-shape1\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;gm-shape gm-shape-2\u0026quot; id=\u0026quot;gm-shape2\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;gm-shape gm-shape-3\u0026quot; id=\u0026quot;gm-shape3\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;gm-glass-card\u0026quot; id=\u0026quot;gm-glass-card\u0026quot;\u0026gt; \u0026lt;p class=\u0026quot;gm-card-inner-title\u0026quot; id=\u0026quot;gm-card-title\u0026quot;\u0026gt;Glass Card\u0026lt;/p\u0026gt; \u0026lt;p class=\u0026quot;gm-card-inner-sub\u0026quot; id=\u0026quot;gm-card-sub\u0026quot;\u0026gt;Frosted glass effect\u0026lt;/p\u0026gt; \u0026lt;span class=\u0026quot;gm-card-btn\u0026quot; id=\u0026quot;gm-card-btn\u0026quot;\u0026gt;Learn more\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Code --\u0026gt; \u0026lt;div class=\u0026quot;gm-code-block\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;gm-code-header\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;gm-code-label\u0026quot;\u0026gt;CSS\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026quot;gm-copy-btn\u0026quot; id=\u0026quot;gm-copy-btn\u0026quot; onclick=\u0026quot;gmCopyCSS()\u0026quot;\u0026gt;Copy CSS\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;pre id=\u0026quot;gm-css-output\u0026quot;\u0026gt;\u0026lt;/pre\u0026gt; \u0026lt;/div\u0026gt; Generate box shadows → Box Shadow Generator Create gradients → CSS Gradient Generator Build buttons → CSS Button Generator ","permalink":"https://productivity-works.com/tools/glassmorphism-generator/","summary":"\u003cp\u003eGenerate frosted-glass CSS effects in seconds. Adjust blur, transparency, border radius, and more — then copy the ready-to-use CSS code.\u003c/p\u003e\n\u003cdiv id=\"gm-app\"\u003e\n\u003cstyle\u003e\n  #gm-app *,\n  #gm-app *::before,\n  #gm-app *::after {\n    box-sizing: border-box;\n  }\n  #gm-app {\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n    font-size: 14px;\n    color: #1e293b;\n    line-height: 1.5;\n  }\n  #gm-app .gm-layout {\n    display: grid;\n    grid-template-columns: 320px 1fr;\n    gap: 24px;\n    align-items: start;\n    margin-top: 20px;\n  }\n  @media (max-width: 700px) {\n    #gm-app .gm-layout {\n      grid-template-columns: 1fr;\n    }\n  }\n\u003cp\u003e/* Controls Panel */\n#gm-app .gm-panel {\nbackground: #f8fafc;\nborder: 1px solid #e2e8f0;\nborder-radius: 12px;\npadding: 20px;\ndisplay: flex;\nflex-direction: column;\ngap: 16px;\n}\n#gm-app .gm-panel-title {\nfont-size: 13px;\nfont-weight: 700;\ntext-transform: uppercase;\nletter-spacing: 0.06em;\ncolor: #64748b;\nmargin: 0 0 2px 0;\n}\n#gm-app .gm-section-divider {\nborder: none;\nborder-top: 1px solid #e2e8f0;\nmargin: 0;\n}\n#gm-app .gm-field {\ndisplay: flex;\nflex-direction: column;\ngap: 6px;\n}\n#gm-app .gm-label {\nfont-size: 13px;\nfont-weight: 600;\ncolor: #374151;\ndisplay: flex;\njustify-content: space-between;\nalign-items: center;\n}\n#gm-app .gm-label span {\nfont-weight: 400;\ncolor: #6366f1;\nfont-size: 12px;\nfont-family: monospace;\nbackground: #eef2ff;\npadding: 1px 6px;\nborder-radius: 4px;\n}\n#gm-app input[type=\u0026ldquo;range\u0026rdquo;] {\nwidth: 100%;\nheight: 4px;\naccent-color: #6366f1;\ncursor: pointer;\n}\n#gm-app input[type=\u0026ldquo;color\u0026rdquo;] {\nwidth: 36px;\nheight: 30px;\nborder: 1px solid #cbd5e1;\nborder-radius: 6px;\ncursor: pointer;\npadding: 2px;\nbackground: #fff;\n}\n#gm-app .gm-color-row {\ndisplay: flex;\nalign-items: center;\ngap: 10px;\n}\n#gm-app .gm-color-label {\nfont-size: 12px;\ncolor: #64748b;\nflex: 1;\n}\u003c/p\u003e","title":"Glassmorphism Generator"},{"content":" Scale: 4.0 5.0 Weighted Semester GPA — / 4.0 scale Total Credits 0 credit hours Quality Points 0 grade × credits Course Name Credits Grade + Add Course\nGrade Scale Reference Reset All\nCalculate percentages \u0026rarr; Percentage Calculator\nAdvanced math \u0026rarr; Scientific Calculator ","permalink":"https://productivity-works.com/tools/gpa-calculator/","summary":"\u003cdiv id=\"gp-app\"\u003e\n\u003cstyle\u003e\n#gp-app * { box-sizing: border-box; margin: 0; padding: 0; }\n#gp-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n  max-width: 680px;\n  margin: 0 auto;\n  padding: 16px;\n  color: #1e293b;\n}\n\n/* Controls bar */\n#gp-app .gp-controls {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  align-items: center;\n  margin-bottom: 18px;\n}\n#gp-app .gp-scale-group,\n#gp-app .gp-weighted-group {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  background: #f1f5f9;\n  border-radius: 8px;\n  padding: 8px 14px;\n}\n#gp-app .gp-controls label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #475569;\n}\n#gp-app .gp-scale-btn {\n  padding: 5px 14px;\n  border-radius: 6px;\n  border: 1.5px solid #cbd5e1;\n  background: #fff;\n  color: #475569;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#gp-app .gp-scale-btn.active {\n  background: #2563eb;\n  border-color: #2563eb;\n  color: #fff;\n}\n#gp-app .gp-scale-btn:hover:not(.active) {\n  border-color: #93c5fd;\n  color: #2563eb;\n}\n#gp-app .gp-toggle {\n  position: relative;\n  width: 40px;\n  height: 22px;\n  cursor: pointer;\n}\n#gp-app .gp-toggle input { opacity: 0; width: 0; height: 0; position: absolute; }\n#gp-app .gp-toggle-track {\n  position: absolute;\n  inset: 0;\n  border-radius: 22px;\n  background: #cbd5e1;\n  transition: background 0.2s;\n}\n#gp-app .gp-toggle input:checked + .gp-toggle-track { background: #2563eb; }\n#gp-app .gp-toggle-thumb {\n  position: absolute;\n  top: 3px;\n  left: 3px;\n  width: 16px;\n  height: 16px;\n  border-radius: 50%;\n  background: #fff;\n  transition: left 0.2s;\n  box-shadow: 0 1px 3px rgba(0,0,0,0.2);\n}\n#gp-app .gp-toggle input:checked ~ .gp-toggle-thumb { left: 21px; }\n\n/* Course table */\n#gp-app .gp-table-wrap {\n  background: #fff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  overflow: hidden;\n  margin-bottom: 16px;\n}\n#gp-app .gp-thead {\n  display: grid;\n  grid-template-columns: 1fr 90px 110px 44px;\n  background: #f8fafc;\n  border-bottom: 1.5px solid #e2e8f0;\n  padding: 10px 14px;\n  gap: 8px;\n}\n#gp-app .gp-thead span {\n  font-size: 12px;\n  font-weight: 700;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n}\n#gp-app .gp-course-list { list-style: none; }\n#gp-app .gp-course-row {\n  display: grid;\n  grid-template-columns: 1fr 90px 110px 44px;\n  gap: 8px;\n  padding: 10px 14px;\n  border-bottom: 1px solid #f1f5f9;\n  align-items: center;\n  transition: background 0.1s;\n}\n#gp-app .gp-course-row:last-child { border-bottom: none; }\n#gp-app .gp-course-row:hover { background: #fafbff; }\n#gp-app .gp-input {\n  width: 100%;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 6px;\n  padding: 6px 10px;\n  font-size: 14px;\n  color: #1e293b;\n  background: #fff;\n  transition: border-color 0.15s;\n  -webkit-appearance: none;\n}\n#gp-app .gp-input:focus {\n  outline: none;\n  border-color: #3b82f6;\n  box-shadow: 0 0 0 3px rgba(59,130,246,0.12);\n}\n#gp-app select.gp-input { cursor: pointer; }\n#gp-app .gp-remove-btn {\n  width: 28px;\n  height: 28px;\n  border: none;\n  border-radius: 6px;\n  background: #fee2e2;\n  color: #dc2626;\n  font-size: 16px;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  transition: background 0.15s;\n  line-height: 1;\n  justify-self: center;\n}\n#gp-app .gp-remove-btn:hover { background: #fecaca; }\n\n/* Add course button */\n#gp-app .gp-add-btn {\n  width: 100%;\n  padding: 10px;\n  border: 1.5px dashed #93c5fd;\n  border-radius: 8px;\n  background: #eff6ff;\n  color: #2563eb;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n  margin-bottom: 20px;\n}\n#gp-app .gp-add-btn:hover {\n  background: #dbeafe;\n  border-color: #3b82f6;\n}\n\n/* Results panel */\n#gp-app .gp-results {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 12px;\n  margin-bottom: 20px;\n}\n#gp-app .gp-result-card {\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 16px 14px;\n  text-align: center;\n}\n#gp-app .gp-result-card.highlight {\n  background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);\n  border-color: #93c5fd;\n}\n#gp-app .gp-result-label {\n  font-size: 11px;\n  font-weight: 700;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  margin-bottom: 6px;\n}\n#gp-app .gp-result-value {\n  font-size: 28px;\n  font-weight: 700;\n  color: #1e293b;\n  line-height: 1.1;\n}\n#gp-app .gp-result-card.highlight .gp-result-value { color: #1d4ed8; }\n#gp-app .gp-result-sub {\n  font-size: 12px;\n  color: #94a3b8;\n  margin-top: 4px;\n}\n\n/* Grade scale reference */\n#gp-app .gp-scale-ref {\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 14px 16px;\n  margin-bottom: 20px;\n}\n#gp-app .gp-scale-ref h3 {\n  font-size: 12px;\n  font-weight: 700;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  margin-bottom: 10px;\n}\n#gp-app .gp-scale-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));\n  gap: 6px;\n}\n#gp-app .gp-scale-item {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 6px;\n  padding: 5px 8px;\n  text-align: center;\n  font-size: 12px;\n}\n#gp-app .gp-scale-item .si-grade { font-weight: 700; color: #1e293b; }\n#gp-app .gp-scale-item .si-points { color: #2563eb; font-weight: 600; }\n\n/* Reset btn */\n#gp-app .gp-reset-btn {\n  display: block;\n  margin: 0 auto 20px;\n  padding: 9px 24px;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 8px;\n  background: #fff;\n  color: #64748b;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#gp-app .gp-reset-btn:hover { border-color: #94a3b8; color: #334155; }\n\n/* Cross-links */\n#gp-app .gp-crosslinks {\n  margin-top: 20px;\n  font-size: 13px;\n  color: #6c757d;\n  line-height: 1.8;\n}\n#gp-app .gp-crosslinks a { color: #2563eb; text-decoration: none; }\n#gp-app .gp-crosslinks a:hover { text-decoration: underline; }\n\n/* Mobile */\n@media (max-width: 520px) {\n  #gp-app .gp-results { grid-template-columns: 1fr 1fr; }\n  #gp-app .gp-result-card:first-child { grid-column: span 2; }\n  #gp-app .gp-thead,\n  #gp-app .gp-course-row { grid-template-columns: 1fr 72px 96px 36px; gap: 6px; padding: 8px 10px; }\n  #gp-app .gp-result-value { font-size: 22px; }\n}\n\u003c/style\u003e\n\u003c!-- Controls --\u003e\n\u003cdiv class=\"gp-controls\"\u003e\n  \u003cdiv class=\"gp-scale-group\"\u003e\n    \u003clabel\u003eScale:\u003c/label\u003e\n    \u003cbutton class=\"gp-scale-btn active\" id=\"gp-btn-40\" onclick=\"gpSetScale(4.0)\"\u003e4.0\u003c/button\u003e\n    \u003cbutton class=\"gp-scale-btn\" id=\"gp-btn-50\" onclick=\"gpSetScale(5.0)\"\u003e5.0\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"gp-weighted-group\"\u003e\n    \u003clabel for=\"gp-weighted-toggle\"\u003eWeighted\u003c/label\u003e\n    \u003clabel class=\"gp-toggle\"\u003e\n      \u003cinput type=\"checkbox\" id=\"gp-weighted-toggle\" onchange=\"gpRecalc()\"\u003e\n      \u003cspan class=\"gp-toggle-track\"\u003e\u003c/span\u003e\n      \u003cspan class=\"gp-toggle-thumb\"\u003e\u003c/span\u003e\n    \u003c/label\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Results --\u003e\n\u003cdiv class=\"gp-results\"\u003e\n  \u003cdiv class=\"gp-result-card highlight\"\u003e\n    \u003cdiv class=\"gp-result-label\"\u003eSemester GPA\u003c/div\u003e\n    \u003cdiv class=\"gp-result-value\" id=\"gp-sem-gpa\"\u003e—\u003c/div\u003e\n    \u003cdiv class=\"gp-result-sub\" id=\"gp-scale-label\"\u003e/ 4.0 scale\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"gp-result-card\"\u003e\n    \u003cdiv class=\"gp-result-label\"\u003eTotal Credits\u003c/div\u003e\n    \u003cdiv class=\"gp-result-value\" id=\"gp-total-credits\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"gp-result-sub\"\u003ecredit hours\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"gp-result-card\"\u003e\n    \u003cdiv class=\"gp-result-label\"\u003eQuality Points\u003c/div\u003e\n    \u003cdiv class=\"gp-result-value\" id=\"gp-quality-pts\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"gp-result-sub\"\u003egrade × credits\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Course table --\u003e\n\u003cdiv class=\"gp-table-wrap\"\u003e\n  \u003cdiv class=\"gp-thead\"\u003e\n    \u003cspan\u003eCourse Name\u003c/span\u003e\n    \u003cspan\u003eCredits\u003c/span\u003e\n    \u003cspan\u003eGrade\u003c/span\u003e\n    \u003cspan\u003e\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cul class=\"gp-course-list\" id=\"gp-course-list\"\u003e\u003c/ul\u003e\n\u003c/div\u003e\n\u003cp\u003e\u003cbutton class=\"gp-add-btn\" onclick=\"gpAddCourse()\"\u003e+ Add Course\u003c/button\u003e\u003c/p\u003e","title":"GPA Calculator"},{"content":" Grade Calculator Calculate weighted averages, letter grades, GPA, and find out what you need on your final exam.\n📋 Assignments \u0026amp; Grades Assignment Name Score Max Score Weight (%) Grade % + Add Assignment Reset All Total: 0% 📊 Current Grade Summary Weighted Average — out of 100% Letter Grade — GPA (4.0 scale) — standard 4.0 scale Points Scored — score / max score 🎯 \"What Do I Need on My Final?\" Calculator Final Exam Weight in Course (%) % of total grade Desired Final Grade A+ (97%) A (93%) A- (90%) B+ (87%) B (83%) B- (80%) C+ (77%) C (73%) C- (70%) D+ (67%) D (60%) Related Free Tools GPA Calculator — Convert letter grades to GPA across multiple courses Percentage Calculator — Quick percent change, of, and off calculations Study Timer (Pomodoro) — Focus sessions with automatic break reminders Unit Converter — Length, weight, temperature, and more ","permalink":"https://productivity-works.com/tools/grade-calculator/","summary":"\u003cdiv id=\"gc-app\"\u003e\n\u003cstyle\u003e\n#gc-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  padding: 16px;\n  color: #1a1a2e;\n  box-sizing: border-box;\n}\n#gc-app *, #gc-app *::before, #gc-app *::after { box-sizing: inherit; }\n#gc-app h2 {\n  font-size: 1.4rem;\n  font-weight: 700;\n  margin: 0 0 4px;\n  color: #1a1a2e;\n}\n#gc-app .gc-subtitle {\n  font-size: 0.9rem;\n  color: #666;\n  margin: 0 0 20px;\n}\n#gc-app .gc-card {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 20px;\n  margin-bottom: 16px;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.05);\n}\n#gc-app .gc-card-title {\n  font-size: 1rem;\n  font-weight: 600;\n  margin: 0 0 14px;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n#gc-app .gc-card-title span.icon {\n  font-size: 1.1rem;\n}\n#gc-app table.gc-table {\n  width: 100%;\n  border-collapse: collapse;\n}\n#gc-app table.gc-table th {\n  text-align: left;\n  font-size: 0.75rem;\n  font-weight: 600;\n  color: #888;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  padding: 0 6px 8px;\n  border-bottom: 2px solid #f0f0f0;\n}\n#gc-app table.gc-table td {\n  padding: 6px 6px;\n  vertical-align: middle;\n}\n#gc-app table.gc-table td input {\n  width: 100%;\n  padding: 7px 9px;\n  border: 1px solid #dde1e7;\n  border-radius: 7px;\n  font-size: 0.9rem;\n  color: #1a1a2e;\n  background: #fafbfc;\n  transition: border-color 0.15s;\n  outline: none;\n}\n#gc-app table.gc-table td input:focus {\n  border-color: #6366f1;\n  background: #fff;\n}\n#gc-app table.gc-table td.gc-pct {\n  font-size: 0.85rem;\n  color: #6366f1;\n  font-weight: 600;\n  text-align: right;\n  white-space: nowrap;\n  padding-right: 4px;\n}\n#gc-app table.gc-table td.gc-grade-cell {\n  text-align: center;\n  font-size: 0.9rem;\n  font-weight: 700;\n}\n#gc-app .gc-btn-remove {\n  background: none;\n  border: none;\n  cursor: pointer;\n  color: #cbd5e1;\n  font-size: 1.1rem;\n  padding: 4px 6px;\n  border-radius: 5px;\n  line-height: 1;\n  transition: color 0.15s, background 0.15s;\n}\n#gc-app .gc-btn-remove:hover { color: #ef4444; background: #fef2f2; }\n#gc-app .gc-add-row {\n  margin-top: 12px;\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  flex-wrap: wrap;\n}\n#gc-app .gc-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  padding: 8px 16px;\n  border-radius: 8px;\n  font-size: 0.875rem;\n  font-weight: 600;\n  cursor: pointer;\n  border: none;\n  transition: background 0.15s, transform 0.1s;\n}\n#gc-app .gc-btn:active { transform: scale(0.97); }\n#gc-app .gc-btn-primary {\n  background: #6366f1;\n  color: #fff;\n}\n#gc-app .gc-btn-primary:hover { background: #4f46e5; }\n#gc-app .gc-btn-secondary {\n  background: #f1f5f9;\n  color: #475569;\n  border: 1px solid #e2e8f0;\n}\n#gc-app .gc-btn-secondary:hover { background: #e2e8f0; }\n#gc-app .gc-weight-bar-wrap {\n  margin-top: 10px;\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n#gc-app .gc-weight-bar-bg {\n  flex: 1;\n  height: 8px;\n  background: #f0f0f0;\n  border-radius: 99px;\n  overflow: hidden;\n}\n#gc-app .gc-weight-bar-fill {\n  height: 100%;\n  border-radius: 99px;\n  transition: width 0.3s, background 0.3s;\n}\n#gc-app .gc-weight-label {\n  font-size: 0.82rem;\n  font-weight: 600;\n  min-width: 80px;\n  text-align: right;\n}\n#gc-app .gc-summary-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));\n  gap: 12px;\n  margin-bottom: 0;\n}\n#gc-app .gc-stat {\n  background: #f8faff;\n  border: 1px solid #e8edf5;\n  border-radius: 10px;\n  padding: 14px 16px;\n  text-align: center;\n}\n#gc-app .gc-stat-label {\n  font-size: 0.72rem;\n  font-weight: 600;\n  color: #888;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 6px;\n}\n#gc-app .gc-stat-value {\n  font-size: 1.7rem;\n  font-weight: 800;\n  line-height: 1.1;\n}\n#gc-app .gc-stat-sub {\n  font-size: 0.78rem;\n  color: #888;\n  margin-top: 3px;\n}\n#gc-app .gc-grade-badge {\n  display: inline-block;\n  padding: 2px 9px;\n  border-radius: 99px;\n  font-size: 0.8rem;\n  font-weight: 700;\n}\n#gc-app .grade-Aplus  { color: #16a34a; background: #dcfce7; }\n#gc-app .grade-A      { color: #16a34a; background: #dcfce7; }\n#gc-app .grade-Aminus { color: #16a34a; background: #dcfce7; }\n#gc-app .grade-Bplus  { color: #2563eb; background: #dbeafe; }\n#gc-app .grade-B      { color: #2563eb; background: #dbeafe; }\n#gc-app .grade-Bminus { color: #2563eb; background: #dbeafe; }\n#gc-app .grade-Cplus  { color: #d97706; background: #fef3c7; }\n#gc-app .grade-C      { color: #d97706; background: #fef3c7; }\n#gc-app .grade-Cminus { color: #d97706; background: #fef3c7; }\n#gc-app .grade-Dplus  { color: #ea580c; background: #ffedd5; }\n#gc-app .grade-D      { color: #ea580c; background: #ffedd5; }\n#gc-app .grade-F      { color: #dc2626; background: #fee2e2; }\n#gc-app .grade-NA     { color: #94a3b8; background: #f1f5f9; }\n#gc-app .gc-final-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n  gap: 12px;\n}\n#gc-app .gc-final-input-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin-top: 10px;\n  flex-wrap: wrap;\n}\n#gc-app .gc-final-input-row label {\n  font-size: 0.875rem;\n  color: #475569;\n  white-space: nowrap;\n}\n#gc-app .gc-final-input-row input, #gc-app .gc-final-input-row select {\n  padding: 7px 10px;\n  border: 1px solid #dde1e7;\n  border-radius: 7px;\n  font-size: 0.9rem;\n  width: 90px;\n  background: #fafbfc;\n  outline: none;\n  color: #1a1a2e;\n}\n#gc-app .gc-final-input-row input:focus, #gc-app .gc-final-input-row select:focus {\n  border-color: #6366f1;\n  background: #fff;\n}\n#gc-app .gc-final-result {\n  margin-top: 14px;\n  padding: 14px 16px;\n  background: #f0f4ff;\n  border-radius: 10px;\n  border-left: 4px solid #6366f1;\n  font-size: 0.95rem;\n  color: #1a1a2e;\n  line-height: 1.5;\n}\n#gc-app .gc-final-result strong { color: #4f46e5; }\n#gc-app .gc-alert {\n  padding: 10px 14px;\n  border-radius: 8px;\n  font-size: 0.85rem;\n  margin-top: 10px;\n}\n#gc-app .gc-alert-warn { background: #fef3c7; color: #92400e; border: 1px solid #fde68a; }\n#gc-app .gc-alert-ok   { background: #dcfce7; color: #166534; border: 1px solid #bbf7d0; }\n#gc-app .gc-alert-err  { background: #fee2e2; color: #991b1b; border: 1px solid #fecaca; }\n#gc-app .gc-gpa-row {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  margin-top: 8px;\n  flex-wrap: wrap;\n}\n#gc-app .gc-gpa-scale {\n  display: flex;\n  gap: 4px;\n  flex-wrap: wrap;\n  margin-top: 10px;\n}\n#gc-app .gc-gpa-chip {\n  font-size: 0.72rem;\n  padding: 3px 8px;\n  border-radius: 99px;\n  background: #f1f5f9;\n  color: #475569;\n  border: 1px solid #e2e8f0;\n}\n#gc-app .gc-gpa-chip.active {\n  background: #6366f1;\n  color: #fff;\n  border-color: #6366f1;\n}\n#gc-app .gc-related {\n  margin-top: 6px;\n  padding: 14px 18px;\n  background: #f8faff;\n  border-radius: 10px;\n  border: 1px solid #e2e8f0;\n}\n#gc-app .gc-related h3 { font-size: 0.9rem; font-weight: 700; margin: 0 0 8px; color: #475569; }\n#gc-app .gc-related ul { margin: 0; padding-left: 18px; }\n#gc-app .gc-related li { font-size: 0.875rem; margin-bottom: 4px; }\n#gc-app .gc-related a { color: #6366f1; text-decoration: none; }\n#gc-app .gc-related a:hover { text-decoration: underline; }\n#gc-app .gc-empty-row td {\n  text-align: center;\n  color: #aaa;\n  font-size: 0.85rem;\n  padding: 20px;\n}\n@media (max-width: 600px) {\n  #gc-app table.gc-table th:nth-child(5),\n  #gc-app table.gc-table td:nth-child(5) { display: none; }\n}\n\u003c/style\u003e\n\u003ch2\u003eGrade Calculator\u003c/h2\u003e\n\u003cp class=\"gc-subtitle\"\u003eCalculate weighted averages, letter grades, GPA, and find out what you need on your final exam.\u003c/p\u003e","title":"Grade Calculator - Weighted Average \u0026 Final Grade"},{"content":" All Pink Red Orange Yellow Green Teal Blue Purple Dark Pastel Warm Multi No gradients found. Try a different search. Customize Gradient \u0026#x2715; Type Linear Radial Conic Angle 135° Color 1 at % Color 2 at % Copy CSS Click anywhere to close Copied to clipboard! Browse 100+ handpicked CSS gradient presets. Click a card to open the customizer and tweak colors, angle, and gradient type. Double-click a swatch for a fullscreen preview. Hit Copy CSS to grab the code instantly.\nRelated tools: CSS Gradient Generator | Color Palette Generator ","permalink":"https://productivity-works.com/tools/gradient-gallery/","summary":"\u003cdiv id=\"gg-app\"\u003e\n\u003cstyle\u003e\n#gg-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 1100px;\n  margin: 0 auto;\n  padding: 0 0 56px;\n  color: #1e293b;\n}\n#gg-app * { box-sizing: border-box; }\n\n/* Controls */\n#gg-controls {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  margin-bottom: 20px;\n  align-items: center;\n}\n#gg-search {\n  flex: 1;\n  min-width: 200px;\n  padding: 10px 16px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 10px;\n  font-size: 14px;\n  outline: none;\n  transition: border-color .2s;\n  background: #fff;\n}\n#gg-search:focus { border-color: #6366f1; }\n#gg-filters {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 6px;\n}\n.gg-filter {\n  padding: 7px 16px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 20px;\n  background: #fff;\n  font-size: 13px;\n  cursor: pointer;\n  transition: all .15s;\n  white-space: nowrap;\n}\n.gg-filter:hover { border-color: #6366f1; color: #6366f1; }\n.gg-filter.active { background: #6366f1; border-color: #6366f1; color: #fff; }\n\n/* Count */\n#gg-count {\n  font-size: 13px;\n  color: #64748b;\n  margin-bottom: 16px;\n}\n\n/* Grid */\n#gg-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));\n  gap: 16px;\n}\n.gg-card {\n  border-radius: 14px;\n  overflow: hidden;\n  box-shadow: 0 2px 10px rgba(0,0,0,0.10);\n  cursor: pointer;\n  transition: transform .18s, box-shadow .18s;\n  background: #fff;\n}\n.gg-card:hover {\n  transform: translateY(-4px);\n  box-shadow: 0 8px 28px rgba(0,0,0,0.18);\n}\n.gg-swatch {\n  width: 100%;\n  height: 130px;\n  transition: background .3s;\n}\n.gg-info {\n  padding: 10px 12px 12px;\n}\n.gg-name {\n  font-size: 13px;\n  font-weight: 600;\n  color: #1e293b;\n  margin-bottom: 4px;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n.gg-css-preview {\n  font-size: 10px;\n  color: #64748b;\n  font-family: 'SFMono-Regular', Consolas, monospace;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  margin-bottom: 8px;\n}\n.gg-copy-btn {\n  width: 100%;\n  padding: 7px;\n  border: none;\n  border-radius: 8px;\n  background: #f1f5f9;\n  color: #374151;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background .15s, color .15s;\n}\n.gg-copy-btn:hover { background: #6366f1; color: #fff; }\n.gg-copy-btn.copied { background: #22c55e; color: #fff; }\n\n/* No results */\n#gg-empty {\n  grid-column: 1/-1;\n  text-align: center;\n  padding: 60px 20px;\n  color: #94a3b8;\n  font-size: 15px;\n  display: none;\n}\n\n/* Customizer panel */\n#gg-customizer {\n  position: fixed;\n  bottom: 0; right: 0;\n  width: 340px;\n  background: #fff;\n  border-top-left-radius: 18px;\n  border-top: 2px solid #e2e8f0;\n  border-left: 2px solid #e2e8f0;\n  box-shadow: -4px -4px 24px rgba(0,0,0,0.12);\n  padding: 20px;\n  z-index: 999;\n  display: none;\n  transition: transform .25s;\n}\n#gg-customizer.open { display: block; }\n#gg-cust-title {\n  font-size: 14px;\n  font-weight: 700;\n  color: #1e293b;\n  margin-bottom: 14px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n#gg-cust-close {\n  background: none;\n  border: none;\n  font-size: 20px;\n  cursor: pointer;\n  color: #64748b;\n  line-height: 1;\n}\n#gg-cust-preview {\n  width: 100%;\n  height: 80px;\n  border-radius: 10px;\n  margin-bottom: 14px;\n  transition: background .2s;\n}\n.gg-cust-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-bottom: 10px;\n  font-size: 13px;\n  color: #374151;\n}\n.gg-cust-row label { min-width: 60px; font-weight: 600; }\n.gg-cust-row input[type=\"color\"] {\n  width: 40px; height: 32px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 6px;\n  cursor: pointer;\n  padding: 2px;\n  background: none;\n}\n.gg-cust-row input[type=\"range\"] {\n  flex: 1;\n  accent-color: #6366f1;\n}\n.gg-cust-row select {\n  flex: 1;\n  padding: 6px 10px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 13px;\n  outline: none;\n}\n#gg-cust-code {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 8px;\n  padding: 10px;\n  font-size: 11px;\n  font-family: 'SFMono-Regular', Consolas, monospace;\n  color: #374151;\n  word-break: break-all;\n  margin-bottom: 10px;\n  min-height: 52px;\n}\n#gg-cust-copy {\n  width: 100%;\n  padding: 9px;\n  border: none;\n  border-radius: 9px;\n  background: #6366f1;\n  color: #fff;\n  font-size: 13px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background .15s;\n}\n#gg-cust-copy:hover { background: #4f46e5; }\n#gg-cust-copy.copied { background: #22c55e; }\n\n/* Fullscreen */\n#gg-fullscreen {\n  display: none;\n  position: fixed;\n  inset: 0;\n  z-index: 9999;\n  align-items: center;\n  justify-content: center;\n  flex-direction: column;\n  cursor: pointer;\n}\n#gg-fullscreen.open { display: flex; }\n#gg-fs-label {\n  position: absolute;\n  bottom: 30px;\n  background: rgba(0,0,0,0.5);\n  color: #fff;\n  padding: 8px 20px;\n  border-radius: 20px;\n  font-size: 13px;\n  pointer-events: none;\n}\n\n/* Toast */\n#gg-toast {\n  position: fixed;\n  bottom: 32px;\n  left: 50%;\n  transform: translateX(-50%) translateY(60px);\n  background: #1e293b;\n  color: #fff;\n  padding: 10px 22px;\n  border-radius: 24px;\n  font-size: 13px;\n  font-weight: 600;\n  z-index: 10000;\n  opacity: 0;\n  transition: opacity .25s, transform .25s;\n  pointer-events: none;\n}\n#gg-toast.show {\n  opacity: 1;\n  transform: translateX(-50%) translateY(0);\n}\n\n@media (max-width: 600px) {\n  #gg-customizer { width: 100%; border-radius: 18px 18px 0 0; border-left: none; }\n  #gg-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }\n  .gg-swatch { height: 100px; }\n}\n\u003c/style\u003e\n\u003c!-- Controls --\u003e\n\u003cdiv id=\"gg-controls\"\u003e\n  \u003cinput type=\"text\" id=\"gg-search\" placeholder=\"Search gradients...\" aria-label=\"Search gradients\"\u003e\n  \u003cdiv id=\"gg-filters\"\u003e\n    \u003cbutton class=\"gg-filter active\" data-family=\"all\"\u003eAll\u003c/button\u003e\n    \u003cbutton class=\"gg-filter\" data-family=\"pink\"\u003ePink\u003c/button\u003e\n    \u003cbutton class=\"gg-filter\" data-family=\"red\"\u003eRed\u003c/button\u003e\n    \u003cbutton class=\"gg-filter\" data-family=\"orange\"\u003eOrange\u003c/button\u003e\n    \u003cbutton class=\"gg-filter\" data-family=\"yellow\"\u003eYellow\u003c/button\u003e\n    \u003cbutton class=\"gg-filter\" data-family=\"green\"\u003eGreen\u003c/button\u003e\n    \u003cbutton class=\"gg-filter\" data-family=\"teal\"\u003eTeal\u003c/button\u003e\n    \u003cbutton class=\"gg-filter\" data-family=\"blue\"\u003eBlue\u003c/button\u003e\n    \u003cbutton class=\"gg-filter\" data-family=\"purple\"\u003ePurple\u003c/button\u003e\n    \u003cbutton class=\"gg-filter\" data-family=\"dark\"\u003eDark\u003c/button\u003e\n    \u003cbutton class=\"gg-filter\" data-family=\"pastel\"\u003ePastel\u003c/button\u003e\n    \u003cbutton class=\"gg-filter\" data-family=\"warm\"\u003eWarm\u003c/button\u003e\n    \u003cbutton class=\"gg-filter\" data-family=\"multi\"\u003eMulti\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"gg-count\"\u003e\u003c/div\u003e\n\u003c!-- Grid --\u003e\n\u003cdiv id=\"gg-grid\"\u003e\n  \u003cdiv id=\"gg-empty\"\u003eNo gradients found. Try a different search.\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Customizer --\u003e\n\u003cdiv id=\"gg-customizer\"\u003e\n  \u003cdiv id=\"gg-cust-title\"\u003e\n    \u003cspan\u003eCustomize Gradient\u003c/span\u003e\n    \u003cbutton id=\"gg-cust-close\" title=\"Close\"\u003e\u0026#x2715;\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv id=\"gg-cust-preview\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"gg-cust-row\"\u003e\n    \u003clabel\u003eType\u003c/label\u003e\n    \u003cselect id=\"gg-cust-type\"\u003e\n      \u003coption value=\"linear\"\u003eLinear\u003c/option\u003e\n      \u003coption value=\"radial\"\u003eRadial\u003c/option\u003e\n      \u003coption value=\"conic\"\u003eConic\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"gg-cust-row\" id=\"gg-angle-row\"\u003e\n    \u003clabel\u003eAngle\u003c/label\u003e\n    \u003cinput type=\"range\" id=\"gg-cust-angle\" min=\"0\" max=\"360\" value=\"135\"\u003e\n    \u003cspan id=\"gg-angle-val\"\u003e135°\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"gg-cust-row\"\u003e\n    \u003clabel\u003eColor 1\u003c/label\u003e\n    \u003cinput type=\"color\" id=\"gg-cust-c1\" value=\"#667eea\"\u003e\n    \u003cspan id=\"gg-c1-pct-wrap\"\u003eat \u003cinput type=\"number\" id=\"gg-c1-pct\" value=\"0\" min=\"0\" max=\"100\" style=\"width:48px;border:1.5px solid #cbd5e1;border-radius:6px;padding:4px;font-size:12px;outline:none\"\u003e%\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"gg-cust-row\"\u003e\n    \u003clabel\u003eColor 2\u003c/label\u003e\n    \u003cinput type=\"color\" id=\"gg-cust-c2\" value=\"#764ba2\"\u003e\n    \u003cspan id=\"gg-c2-pct-wrap\"\u003eat \u003cinput type=\"number\" id=\"gg-c2-pct\" value=\"100\" min=\"0\" max=\"100\" style=\"width:48px;border:1.5px solid #cbd5e1;border-radius:6px;padding:4px;font-size:12px;outline:none\"\u003e%\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv id=\"gg-cust-code\"\u003e\u003c/div\u003e\n  \u003cbutton id=\"gg-cust-copy\"\u003eCopy CSS\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Fullscreen overlay --\u003e\n\u003cdiv id=\"gg-fullscreen\" title=\"Click to close\"\u003e\n  \u003cdiv id=\"gg-fs-label\"\u003eClick anywhere to close\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Toast --\u003e\n\u003cdiv id=\"gg-toast\"\u003eCopied to clipboard!\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  'use strict';\n\n  const GRADIENTS = [\n    // Pink\n    { name: 'Candy Bliss', css: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)', family: ['pink', 'red'] },\n    { name: 'Rose Quartz', css: 'linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%)', family: ['pink', 'pastel', 'warm'] },\n    { name: 'Pink Lemonade', css: 'linear-gradient(135deg, #f8acff 0%, #696eff 100%)', family: ['pink', 'purple'] },\n    { name: 'Flamingo', css: 'linear-gradient(135deg, #f7797d 0%, #FBD786 50%, #C6FFDD 100%)', family: ['pink', 'multi'] },\n    { name: 'Cherry Blossom', css: 'linear-gradient(135deg, #FFB7B2 0%, #FFDAC1 100%)', family: ['pink', 'pastel'] },\n    { name: 'Hot Coral', css: 'linear-gradient(135deg, #FF6CAB 0%, #7366FF 100%)', family: ['pink', 'purple'] },\n    { name: 'Blush Peach', css: 'linear-gradient(135deg, #fbc2eb 0%, #a6c1ee 100%)', family: ['pink', 'blue', 'pastel'] },\n    { name: 'Rose Gold', css: 'linear-gradient(135deg, #f3c4cc 0%, #d4a5a5 50%, #b07d7d 100%)', family: ['pink', 'warm'] },\n    { name: 'Cotton Candy', css: 'linear-gradient(135deg, #FDCBF1 0%, #E6DEE9 100%)', family: ['pink', 'pastel'] },\n\n    // Red\n    { name: 'Crimson Tide', css: 'linear-gradient(135deg, #C33764 0%, #1D2671 100%)', family: ['red', 'dark', 'blue'] },\n    { name: 'Scarlet Ember', css: 'linear-gradient(135deg, #ff416c 0%, #ff4b2b 100%)', family: ['red', 'orange'] },\n    { name: 'Bloodmoon', css: 'linear-gradient(135deg, #8B0000 0%, #FF4500 100%)', family: ['red', 'dark'] },\n    { name: 'Red Velvet', css: 'linear-gradient(135deg, #c0392b 0%, #8e44ad 100%)', family: ['red', 'purple'] },\n    { name: 'Lava Flow', css: 'linear-gradient(135deg, #ff0000 0%, #ff7300 50%, #ffcc00 100%)', family: ['red', 'orange', 'yellow', 'multi'] },\n    { name: 'Berry Jam', css: 'linear-gradient(135deg, #9b2335 0%, #c0392b 50%, #e74c3c 100%)', family: ['red'] },\n    { name: 'Firework', css: 'linear-gradient(135deg, #f12711 0%, #f5af19 100%)', family: ['red', 'orange'] },\n    { name: 'Cherry Pop', css: 'linear-gradient(135deg, #eb3349 0%, #f45c43 100%)', family: ['red', 'orange'] },\n\n    // Orange\n    { name: 'Sunrise', css: 'linear-gradient(135deg, #f83600 0%, #f9d423 100%)', family: ['orange', 'yellow', 'warm'] },\n    { name: 'Desert Sun', css: 'linear-gradient(135deg, #FFD200 0%, #F7971E 100%)', family: ['orange', 'yellow', 'warm'] },\n    { name: 'Autumn Maple', css: 'linear-gradient(135deg, #d4531a 0%, #f0a500 100%)', family: ['orange', 'warm'] },\n    { name: 'Mango Tango', css: 'linear-gradient(135deg, #FF8008 0%, #FFC837 100%)', family: ['orange', 'yellow'] },\n    { name: 'Ember Glow', css: 'linear-gradient(135deg, #e14fad 0%, #f9d423 100%)', family: ['orange', 'pink', 'warm'] },\n    { name: 'Creamsicle', css: 'linear-gradient(135deg, #FFB347 0%, #FFCC33 100%)', family: ['orange', 'yellow', 'pastel'] },\n    { name: 'Tangerine Dream', css: 'linear-gradient(135deg, #FF6B35 0%, #F7C59F 100%)', family: ['orange', 'pastel'] },\n    { name: 'Bronze Age', css: 'linear-gradient(135deg, #a0522d 0%, #d2691e 50%, #f4a460 100%)', family: ['orange', 'warm'] },\n\n    // Yellow\n    { name: 'Golden Hour', css: 'linear-gradient(135deg, #f6d365 0%, #fda085 100%)', family: ['yellow', 'orange', 'warm'] },\n    { name: 'Lemon Zest', css: 'linear-gradient(135deg, #FFE000 0%, #799F0C 100%)', family: ['yellow', 'green'] },\n    { name: 'Honeycomb', css: 'linear-gradient(135deg, #f7971e 0%, #ffd200 100%)', family: ['yellow', 'orange'] },\n    { name: 'Sunflower', css: 'linear-gradient(135deg, #DAA520 0%, #FFD700 50%, #FFFACD 100%)', family: ['yellow', 'pastel'] },\n    { name: 'Canary', css: 'linear-gradient(135deg, #FDFC47 0%, #24FE41 100%)', family: ['yellow', 'green'] },\n    { name: 'Butter Scotch', css: 'linear-gradient(135deg, #f7e98e 0%, #e8b86d 100%)', family: ['yellow', 'pastel', 'warm'] },\n    { name: 'Neon Yellow', css: 'linear-gradient(135deg, #ccff00 0%, #ffef00 100%)', family: ['yellow', 'green'] },\n\n    // Green\n    { name: 'Emerald Isle', css: 'linear-gradient(135deg, #0f9b58 0%, #00bf8f 100%)', family: ['green', 'teal'] },\n    { name: 'Forest Floor', css: 'linear-gradient(135deg, #134e5e 0%, #71b280 100%)', family: ['green', 'teal', 'dark'] },\n    { name: 'Sage Meadow', css: 'linear-gradient(135deg, #a8e063 0%, #56ab2f 100%)', family: ['green'] },\n    { name: 'Lime Soda', css: 'linear-gradient(135deg, #96fbc4 0%, #f9f586 100%)', family: ['green', 'yellow', 'pastel'] },\n    { name: 'Mint Chip', css: 'linear-gradient(135deg, #b2fefa 0%, #0ed2f7 100%)', family: ['green', 'teal', 'pastel'] },\n    { name: 'Jungle Mist', css: 'linear-gradient(135deg, #4ca1af 0%, #c4e0e5 100%)', family: ['green', 'teal', 'pastel'] },\n    { name: 'Matcha Latte', css: 'linear-gradient(135deg, #C6D68F 0%, #F0E68C 100%)', family: ['green', 'yellow', 'pastel'] },\n    { name: 'Neon Lime', css: 'linear-gradient(135deg, #39FF14 0%, #00bfff 100%)', family: ['green', 'blue'] },\n    { name: 'Rainforest', css: 'linear-gradient(135deg, #1a3c34 0%, #2d7a5f 50%, #4caf7d 100%)', family: ['green', 'dark'] },\n    { name: 'Kiwi Smash', css: 'linear-gradient(135deg, #93f9b9 0%, #1d976c 100%)', family: ['green', 'teal'] },\n\n    // Teal\n    { name: 'Ocean Breeze', css: 'linear-gradient(135deg, #00b4db 0%, #0083b0 100%)', family: ['teal', 'blue'] },\n    { name: 'Aquamarine', css: 'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)', family: ['teal', 'green'] },\n    { name: 'Lagoon', css: 'linear-gradient(135deg, #43CBFF 0%, #9708CC 100%)', family: ['teal', 'purple'] },\n    { name: 'Seafoam', css: 'linear-gradient(135deg, #E0F7FA 0%, #80DEEA 50%, #00ACC1 100%)', family: ['teal', 'pastel'] },\n    { name: 'Deep Sea', css: 'linear-gradient(135deg, #001B2E 0%, #004E7C 50%, #0099AA 100%)', family: ['teal', 'dark', 'blue'] },\n    { name: 'Turquoise', css: 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)', family: ['teal', 'green'] },\n    { name: 'Aqua Splash', css: 'linear-gradient(135deg, #13547a 0%, #80d0c7 100%)', family: ['teal', 'blue'] },\n    { name: 'Arctic Mist', css: 'linear-gradient(135deg, #DFFFCD 0%, #90F9C4 50%, #39F3BB 100%)', family: ['teal', 'green', 'pastel'] },\n\n    // Blue\n    { name: 'Royal Blue', css: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', family: ['blue', 'purple'] },\n    { name: 'Blueberry', css: 'linear-gradient(135deg, #1e3c72 0%, #2a5298 100%)', family: ['blue', 'dark'] },\n    { name: 'Sky Dream', css: 'linear-gradient(135deg, #89f7fe 0%, #66a6ff 100%)', family: ['blue', 'pastel'] },\n    { name: 'Sapphire', css: 'linear-gradient(135deg, #0F2027 0%, #203A43 50%, #2C5364 100%)', family: ['blue', 'dark'] },\n    { name: 'Midnight', css: 'linear-gradient(135deg, #2c3e50 0%, #4ca1af 100%)', family: ['blue', 'dark', 'teal'] },\n    { name: 'Cobalt', css: 'linear-gradient(135deg, #004bff 0%, #007cff 100%)', family: ['blue'] },\n    { name: 'Electric Blue', css: 'linear-gradient(135deg, #0072ff 0%, #00c6ff 100%)', family: ['blue', 'teal'] },\n    { name: 'Denim', css: 'linear-gradient(135deg, #1e3a5f 0%, #2980b9 100%)', family: ['blue', 'dark'] },\n    { name: 'Arctic Aurora', css: 'linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%)', family: ['blue', 'dark'] },\n    { name: 'Baby Blue', css: 'linear-gradient(135deg, #c2e9fb 0%, #a1c4fd 100%)', family: ['blue', 'pastel'] },\n    { name: 'Ice Cap', css: 'linear-gradient(135deg, #E8F4FD 0%, #AED6F1 100%)', family: ['blue', 'pastel'] },\n    { name: 'Neptune', css: 'linear-gradient(135deg, #24C6DC 0%, #514A9D 100%)', family: ['blue', 'purple'] },\n\n    // Purple\n    { name: 'Amethyst', css: 'linear-gradient(135deg, #9D50BB 0%, #6E48AA 100%)', family: ['purple'] },\n    { name: 'Grape Ape', css: 'linear-gradient(135deg, #4776E6 0%, #8E54E9 100%)', family: ['purple', 'blue'] },\n    { name: 'Violet Dusk', css: 'linear-gradient(135deg, #a18cd1 0%, #fbc2eb 100%)', family: ['purple', 'pink', 'pastel'] },\n    { name: 'Cosmic', css: 'linear-gradient(135deg, #020024 0%, #090979 50%, #00d4ff 100%)', family: ['purple', 'blue', 'dark'] },\n    { name: 'Lavender', css: 'linear-gradient(135deg, #e9d5ff 0%, #a855f7 100%)', family: ['purple', 'pastel'] },\n    { name: 'Galaxy', css: 'linear-gradient(135deg, #2c1654 0%, #6a0572 50%, #d63031 100%)', family: ['purple', 'dark', 'red'] },\n    { name: 'Plum', css: 'linear-gradient(135deg, #4B0082 0%, #8B00FF 100%)', family: ['purple', 'dark'] },\n    { name: 'Lilac Sky', css: 'linear-gradient(135deg, #c3cfe2 0%, #f5f7fa 100%)', family: ['purple', 'pastel'] },\n    { name: 'Ultraviolet', css: 'linear-gradient(135deg, #5f0a87 0%, #a4508b 100%)', family: ['purple'] },\n    { name: 'Twilight', css: 'linear-gradient(135deg, #0F0C29 0%, #302B63 50%, #24243e 100%)', family: ['purple', 'dark'] },\n\n    // Dark\n    { name: 'Obsidian', css: 'linear-gradient(135deg, #1a1a2e 0%, #16213e 100%)', family: ['dark'] },\n    { name: 'Carbon', css: 'linear-gradient(135deg, #1c1c1c 0%, #3d3d3d 100%)', family: ['dark'] },\n    { name: 'Gunmetal', css: 'linear-gradient(135deg, #29323c 0%, #485563 100%)', family: ['dark'] },\n    { name: 'Shadow', css: 'linear-gradient(135deg, #000000 0%, #434343 100%)', family: ['dark'] },\n    { name: 'Slate', css: 'linear-gradient(135deg, #0f2027 0%, #203a43 100%)', family: ['dark', 'blue'] },\n    { name: 'Noir', css: 'linear-gradient(135deg, #000000 0%, #7b7b7b 100%)', family: ['dark'] },\n    { name: 'Ash', css: 'linear-gradient(135deg, #606c88 0%, #3f4c6b 100%)', family: ['dark', 'blue'] },\n    { name: 'Charcoal', css: 'linear-gradient(135deg, #333333 0%, #dd1818 100%)', family: ['dark', 'red'] },\n    { name: 'Storm', css: 'linear-gradient(135deg, #373B44 0%, #4286f4 100%)', family: ['dark', 'blue'] },\n\n    // Pastel\n    { name: 'Pastel Rainbow', css: 'linear-gradient(135deg, #FFDEE9 0%, #B5FFFC 100%)', family: ['pastel', 'multi'] },\n    { name: 'Soft Peach', css: 'linear-gradient(135deg, #FFE0B2 0%, #FFCCBC 100%)', family: ['pastel', 'orange', 'warm'] },\n    { name: 'Dreamy', css: 'linear-gradient(135deg, #e0c3fc 0%, #8ec5fc 100%)', family: ['pastel', 'purple', 'blue'] },\n    { name: 'Cloud Nine', css: 'linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%)', family: ['pastel', 'blue'] },\n    { name: 'Sorbet', css: 'linear-gradient(135deg, #fccb90 0%, #d57eeb 100%)', family: ['pastel', 'pink', 'orange'] },\n    { name: 'Melon', css: 'linear-gradient(135deg, #fddb92 0%, #d1fdff 100%)', family: ['pastel', 'yellow'] },\n    { name: 'Powder Blue', css: 'linear-gradient(135deg, #dfe9f3 0%, #ffffff 100%)', family: ['pastel', 'blue'] },\n    { name: 'Bubblegum', css: 'linear-gradient(135deg, #FFC8DD 0%, #BDE0FE 100%)', family: ['pastel', 'pink', 'blue'] },\n    { name: 'Ice Cream', css: 'linear-gradient(135deg, #ffc3a0 0%, #FFAFBD 100%)', family: ['pastel', 'pink', 'orange'] },\n    { name: 'Soft Mint', css: 'linear-gradient(135deg, #B5EAD7 0%, #C7CEEA 100%)', family: ['pastel', 'green', 'purple'] },\n\n    // Warm\n    { name: 'Mojave', css: 'linear-gradient(135deg, #f9a825 0%, #e65100 100%)', family: ['warm', 'orange'] },\n    { name: 'Caramel', css: 'linear-gradient(135deg, #C6A35D 0%, #8B5E3C 100%)', family: ['warm', 'orange'] },\n    { name: 'Terracotta', css: 'linear-gradient(135deg, #C46A39 0%, #7C3C21 100%)', family: ['warm', 'red', 'orange'] },\n    { name: 'Cinnamon', css: 'linear-gradient(135deg, #D2691E 0%, #F4A460 100%)', family: ['warm', 'orange'] },\n    { name: 'Peach Fuzz', css: 'linear-gradient(135deg, #FFBE98 0%, #FFD3BA 100%)', family: ['warm', 'orange', 'pastel'] },\n\n    // Multi\n    { name: 'Prism', css: 'linear-gradient(135deg, #ff0000, #ff7700, #ffff00, #00ff00, #0000ff, #8b00ff)', family: ['multi'] },\n    { name: 'Northern Lights', css: 'linear-gradient(135deg, #43e97b 0%, #38f9d7 33%, #4facfe 66%, #a18cd1 100%)', family: ['multi', 'green', 'blue', 'purple'] },\n    { name: 'Carnival', css: 'linear-gradient(135deg, #FF3CAC 0%, #784BA0 50%, #2B86C5 100%)', family: ['multi', 'pink', 'purple', 'blue'] },\n    { name: 'Holographic', css: 'linear-gradient(135deg, #a8edea 0%, #fed6e3 33%, #ffecd2 66%, #a8edea 100%)', family: ['multi', 'pastel'] },\n    { name: 'Sunset Vibes', css: 'linear-gradient(135deg, #FDBB2D 0%, #3A1C71 100%)', family: ['multi', 'yellow', 'purple'] },\n    { name: 'Opal', css: 'linear-gradient(135deg, #c3cfe2 0%, #f5f7fa 25%, #e0c3fc 75%, #8ec5fc 100%)', family: ['multi', 'pastel'] },\n    { name: 'Vaporwave', css: 'linear-gradient(135deg, #ff71ce 0%, #01cdfe 50%, #05ffa1 100%)', family: ['multi', 'pink', 'teal', 'green'] },\n    { name: 'Aurora', css: 'linear-gradient(135deg, #00c6ff 0%, #0072ff 33%, #a100ff 66%, #ff00ff 100%)', family: ['multi', 'blue', 'purple'] },\n    { name: 'Tie Dye', css: 'linear-gradient(135deg, #FF61D2 0%, #FE9090 25%, #FFED86 50%, #83FF85 75%, #92FFFC 100%)', family: ['multi', 'pastel'] },\n    { name: 'Oil Slick', css: 'linear-gradient(135deg, #202020 0%, #1e3a5f 25%, #4a0e8f 50%, #8b0000 75%, #202020 100%)', family: ['multi', 'dark'] },\n\n    // Additional variety\n    { name: 'Nebula', css: 'radial-gradient(ellipse at top, #1a1a3e 0%, #6b21a8 50%, #1a1a3e 100%)', family: ['purple', 'dark'] },\n    { name: 'Sunburst', css: 'radial-gradient(circle at center, #FFD700 0%, #FF8C00 50%, #FF4500 100%)', family: ['yellow', 'orange', 'warm'] },\n    { name: 'Moonrise', css: 'radial-gradient(ellipse at bottom, #1b2735 0%, #090a0f 100%)', family: ['dark', 'blue'] },\n    { name: 'Halo', css: 'radial-gradient(circle at center, #ffffff 0%, #e8f4fd 40%, #aed6f1 100%)', family: ['blue', 'pastel'] },\n    { name: 'Neon Glow', css: 'radial-gradient(circle at center, #39ff14 0%, #001a00 100%)', family: ['green', 'dark'] },\n\n    { name: 'Conic Wheel', css: 'conic-gradient(from 0deg, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000)', family: ['multi'] },\n    { name: 'Conic Pastel', css: 'conic-gradient(from 45deg, #fbc2eb, #a6c1ee, #ffecd2, #d4fc79, #fbc2eb)', family: ['multi', 'pastel'] },\n    { name: 'Pie Slices', css: 'conic-gradient(#e74c3c 0deg 90deg, #3498db 90deg 180deg, #2ecc71 180deg 270deg, #f39c12 270deg 360deg)', family: ['multi'] },\n  ];\n\n  let currentFamily = 'all';\n  let currentSearch = '';\n  let custGradient = null;\n\n  const grid = document.getElementById('gg-grid');\n  const empty = document.getElementById('gg-empty');\n  const countEl = document.getElementById('gg-count');\n  const searchEl = document.getElementById('gg-search');\n  const customizer = document.getElementById('gg-customizer');\n  const custPreview = document.getElementById('gg-cust-preview');\n  const custType = document.getElementById('gg-cust-type');\n  const custAngle = document.getElementById('gg-cust-angle');\n  const angleVal = document.getElementById('gg-angle-val');\n  const angleRow = document.getElementById('gg-angle-row');\n  const custC1 = document.getElementById('gg-cust-c1');\n  const custC2 = document.getElementById('gg-cust-c2');\n  const c1Pct = document.getElementById('gg-c1-pct');\n  const c2Pct = document.getElementById('gg-c2-pct');\n  const custCode = document.getElementById('gg-cust-code');\n  const custCopy = document.getElementById('gg-cust-copy');\n  const fullscreen = document.getElementById('gg-fullscreen');\n  const toast = document.getElementById('gg-toast');\n\n  function showToast(msg) {\n    toast.textContent = msg;\n    toast.classList.add('show');\n    setTimeout(() =\u003e toast.classList.remove('show'), 2000);\n  }\n\n  function copyText(text) {\n    if (navigator.clipboard) {\n      navigator.clipboard.writeText(text).catch(() =\u003e fallbackCopy(text));\n    } else {\n      fallbackCopy(text);\n    }\n  }\n\n  function fallbackCopy(text) {\n    const ta = document.createElement('textarea');\n    ta.value = text;\n    ta.style.position = 'fixed';\n    ta.style.opacity = '0';\n    document.body.appendChild(ta);\n    ta.select();\n    document.execCommand('copy');\n    document.body.removeChild(ta);\n  }\n\n  function matchesFilter(g) {\n    if (currentFamily !== 'all' \u0026\u0026 !g.family.includes(currentFamily)) return false;\n    if (currentSearch) {\n      const q = currentSearch.toLowerCase();\n      if (!g.name.toLowerCase().includes(q) \u0026\u0026 !g.css.toLowerCase().includes(q) \u0026\u0026 !g.family.join(' ').includes(q)) return false;\n    }\n    return true;\n  }\n\n  function buildGrid() {\n    const cards = grid.querySelectorAll('.gg-card');\n    cards.forEach(c =\u003e c.remove());\n\n    const filtered = GRADIENTS.filter(matchesFilter);\n    countEl.textContent = filtered.length + ' gradients';\n\n    if (filtered.length === 0) {\n      empty.style.display = 'block';\n      return;\n    }\n    empty.style.display = 'none';\n\n    filtered.forEach((g, i) =\u003e {\n      const card = document.createElement('div');\n      card.className = 'gg-card';\n      card.innerHTML =\n        '\u003cdiv class=\"gg-swatch\" style=\"background:' + g.css + '\"\u003e\u003c/div\u003e' +\n        '\u003cdiv class=\"gg-info\"\u003e' +\n          '\u003cdiv class=\"gg-name\"\u003e' + escHtml(g.name) + '\u003c/div\u003e' +\n          '\u003cdiv class=\"gg-css-preview\"\u003e' + escHtml(g.css) + '\u003c/div\u003e' +\n          '\u003cbutton class=\"gg-copy-btn\"\u003eCopy CSS\u003c/button\u003e' +\n        '\u003c/div\u003e';\n\n      const swatch = card.querySelector('.gg-swatch');\n      const btn = card.querySelector('.gg-copy-btn');\n\n      swatch.addEventListener('click', () =\u003e openCustomizer(g));\n      card.querySelector('.gg-name').addEventListener('click', () =\u003e openCustomizer(g));\n\n      btn.addEventListener('click', (e) =\u003e {\n        e.stopPropagation();\n        const css = 'background: ' + g.css + ';';\n        copyText(css);\n        btn.textContent = 'Copied!';\n        btn.classList.add('copied');\n        showToast('CSS copied!');\n        setTimeout(() =\u003e {\n          btn.textContent = 'Copy CSS';\n          btn.classList.remove('copied');\n        }, 1800);\n      });\n\n      swatch.addEventListener('dblclick', (e) =\u003e {\n        e.stopPropagation();\n        openFullscreen(g.css);\n      });\n\n      grid.appendChild(card);\n    });\n  }\n\n  function escHtml(s) {\n    return s.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n  }\n\n  // Customizer\n  function openCustomizer(g) {\n    custGradient = g;\n    // Parse existing gradient type\n    if (g.css.startsWith('radial')) {\n      custType.value = 'radial';\n    } else if (g.css.startsWith('conic')) {\n      custType.value = 'conic';\n    } else {\n      custType.value = 'linear';\n    }\n\n    // Try to extract colors from css\n    const colorMatch = g.css.match(/#[0-9a-fA-F]{3,6}/g);\n    if (colorMatch \u0026\u0026 colorMatch.length \u003e= 2) {\n      custC1.value = normalizeHex(colorMatch[0]);\n      custC2.value = normalizeHex(colorMatch[colorMatch.length - 1]);\n    }\n    c1Pct.value = 0;\n    c2Pct.value = 100;\n\n    const angleMatch = g.css.match(/(\\d+)deg/);\n    if (angleMatch) {\n      custAngle.value = parseInt(angleMatch[1]);\n      angleVal.textContent = custAngle.value + '°';\n    }\n\n    updateAngleRow();\n    updateCustPreview();\n    customizer.classList.add('open');\n  }\n\n  function normalizeHex(hex) {\n    if (hex.length === 4) {\n      return '#' + hex[1]+hex[1]+hex[2]+hex[2]+hex[3]+hex[3];\n    }\n    return hex;\n  }\n\n  function updateAngleRow() {\n    angleRow.style.display = custType.value === 'linear' ? 'flex' : 'none';\n  }\n\n  function buildCustCSS() {\n    const c1 = custC1.value, c2 = custC2.value;\n    const p1 = c1Pct.value, p2 = c2Pct.value;\n    const angle = custAngle.value;\n    const type = custType.value;\n    if (type === 'radial') {\n      return 'radial-gradient(ellipse at center, ' + c1 + ' ' + p1 + '%, ' + c2 + ' ' + p2 + '%)';\n    } else if (type === 'conic') {\n      return 'conic-gradient(from ' + angle + 'deg, ' + c1 + ' 0%, ' + c2 + ' 100%)';\n    } else {\n      return 'linear-gradient(' + angle + 'deg, ' + c1 + ' ' + p1 + '%, ' + c2 + ' ' + p2 + '%)';\n    }\n  }\n\n  function updateCustPreview() {\n    const css = buildCustCSS();\n    custPreview.style.background = css;\n    custCode.textContent = 'background: ' + css + ';';\n  }\n\n  custType.addEventListener('change', () =\u003e { updateAngleRow(); updateCustPreview(); });\n  custAngle.addEventListener('input', () =\u003e { angleVal.textContent = custAngle.value + '°'; updateCustPreview(); });\n  custC1.addEventListener('input', updateCustPreview);\n  custC2.addEventListener('input', updateCustPreview);\n  c1Pct.addEventListener('input', updateCustPreview);\n  c2Pct.addEventListener('input', updateCustPreview);\n\n  custCopy.addEventListener('click', () =\u003e {\n    const css = custCode.textContent;\n    copyText(css);\n    custCopy.textContent = 'Copied!';\n    custCopy.classList.add('copied');\n    showToast('CSS copied!');\n    setTimeout(() =\u003e {\n      custCopy.textContent = 'Copy CSS';\n      custCopy.classList.remove('copied');\n    }, 1800);\n  });\n\n  document.getElementById('gg-cust-close').addEventListener('click', () =\u003e {\n    customizer.classList.remove('open');\n  });\n\n  // Fullscreen\n  function openFullscreen(css) {\n    fullscreen.style.background = css;\n    fullscreen.classList.add('open');\n  }\n\n  fullscreen.addEventListener('click', () =\u003e {\n    fullscreen.classList.remove('open');\n  });\n\n  // Search\n  searchEl.addEventListener('input', () =\u003e {\n    currentSearch = searchEl.value.trim();\n    buildGrid();\n  });\n\n  // Filters\n  document.querySelectorAll('.gg-filter').forEach(btn =\u003e {\n    btn.addEventListener('click', () =\u003e {\n      document.querySelectorAll('.gg-filter').forEach(b =\u003e b.classList.remove('active'));\n      btn.classList.add('active');\n      currentFamily = btn.dataset.family;\n      buildGrid();\n    });\n  });\n\n  // Init\n  buildGrid();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003cp\u003eBrowse 100+ handpicked CSS gradient presets. Click a card to open the customizer and tweak colors, angle, and gradient type. Double-click a swatch for a fullscreen preview. Hit \u003cstrong\u003eCopy CSS\u003c/strong\u003e to grab the code instantly.\u003c/p\u003e","title":"Gradient Gallery - 100+ CSS Gradients"},{"content":" Today Weekly Grid Heatmap Stats Manage Weekly Grid Monthly Heatmap Stats Add New Habit Habit name Color Frequency Daily Weekdays Custom days Select days Su Mo Tu We Th Fr Sa Add Habit My Habits Data Export JSON Import JSON Import Cancel Edit Habit Habit name Color Frequency Daily Weekdays Custom days Select days Su Mo Tu We Th Fr Sa Delete Cancel Save Focus with Pomodoro → Pomodoro Timer Study with flashcards → Flashcard Maker Plan your budget → 50/30/20 Budget Calculator ","permalink":"https://productivity-works.com/tools/habit-tracker/","summary":"\u003cdiv id=\"ht-app\"\u003e\n\u003cstyle\u003e\n/* ── Reset \u0026 base ── */\n#ht-app *,#ht-app *::before,#ht-app *::after{box-sizing:border-box;margin:0;padding:0}\n#ht-app{\n  font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;\n  font-size:15px;line-height:1.5;\n  color:#e2e8f0;\n  background:#0f172a;\n  border-radius:12px;\n  padding:24px;\n  max-width:900px;\n  margin:0 auto;\n}\n\u003cp\u003e/* ── Typography ── */\n#ht-app h2{font-size:1.35rem;font-weight:700;color:#f1f5f9;margin-bottom:16px}\n#ht-app h3{font-size:1.05rem;font-weight:600;color:#cbd5e1;margin-bottom:10px}\u003c/p\u003e\n\u003cp\u003e/* ── Tabs ── */\n#ht-app .ht-tabs{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:24px;border-bottom:1px solid #1e293b;padding-bottom:12px}\n#ht-app .ht-tab{\npadding:7px 16px;border-radius:8px;border:none;cursor:pointer;\nfont-size:0.875rem;font-weight:600;\nbackground:#1e293b;color:#94a3b8;\ntransition:background 0.15s,color 0.15s;\n}\n#ht-app .ht-tab:hover{background:#334155;color:#e2e8f0}\n#ht-app .ht-tab.active{background:#6366f1;color:#fff}\u003c/p\u003e\n\u003cp\u003e/* ── Panels ── */\n#ht-app .ht-panel{display:none}\n#ht-app .ht-panel.active{display:block}\u003c/p\u003e\n\u003cp\u003e/* ── Add-habit form ── */\n#ht-app .ht-form{background:#1e293b;border-radius:10px;padding:20px;margin-bottom:24px}\n#ht-app .ht-form-row{display:flex;flex-wrap:wrap;gap:12px;align-items:flex-end}\n#ht-app .ht-field{display:flex;flex-direction:column;gap:5px;flex:1;min-width:140px}\n#ht-app .ht-field label{font-size:0.78rem;font-weight:600;color:#94a3b8;text-transform:uppercase;letter-spacing:.04em}\n#ht-app .ht-field input[type=text],\n#ht-app .ht-field select{\nbackground:#0f172a;border:1px solid #334155;border-radius:7px;\ncolor:#e2e8f0;padding:8px 10px;font-size:0.9rem;outline:none;\ntransition:border-color 0.15s;\n}\n#ht-app .ht-field input[type=text]:focus,\n#ht-app .ht-field select:focus{border-color:#6366f1}\n#ht-app .ht-field input[type=color]{\nwidth:44px;height:36px;padding:2px;border-radius:7px;\nborder:1px solid #334155;background:#0f172a;cursor:pointer;\n}\n#ht-app .ht-days-picker{display:flex;gap:6px;flex-wrap:wrap;margin-top:6px}\n#ht-app .ht-day-btn{\nwidth:34px;height:34px;border-radius:50%;border:1px solid #334155;\nbackground:#0f172a;color:#94a3b8;font-size:0.78rem;font-weight:600;\ncursor:pointer;transition:all 0.15s;\n}\n#ht-app .ht-day-btn.sel{background:#6366f1;border-color:#6366f1;color:#fff}\n#ht-app .ht-btn{\npadding:8px 20px;border-radius:8px;border:none;cursor:pointer;\nfont-size:0.9rem;font-weight:600;transition:opacity 0.15s;\n}\n#ht-app .ht-btn:hover{opacity:0.85}\n#ht-app .ht-btn-primary{background:#6366f1;color:#fff}\n#ht-app .ht-btn-sm{padding:5px 12px;font-size:0.8rem;border-radius:6px}\n#ht-app .ht-btn-danger{background:#ef4444;color:#fff}\n#ht-app .ht-btn-outline{background:transparent;border:1px solid #334155;color:#94a3b8}\n#ht-app .ht-btn-outline:hover{border-color:#6366f1;color:#e2e8f0}\n#ht-app .ht-btn-success{background:#22c55e;color:#fff}\u003c/p\u003e","title":"Habit Tracker"},{"content":" Text Input File Hash Compare Hashes Generate All Hashes Clear Uppercase Live update Computing… Copy All AlgorithmHash ValueInfo \u0026#128196; Drop a file here, or click to browse All file types \u0026bull; Processed entirely in your browser \u0026bull; Never uploaded \u0026#128196; | Hashing file… Copy All AlgorithmHash ValueInfo Paste two hash strings to verify they are identical. Useful for confirming file integrity after download, or checking password digests match. Hash A Hash B Clear What Is a Cryptographic Hash? A hash function takes any input — text, a file, a password — and produces a fixed-length hexadecimal string called a digest. The same input always yields the same digest, but even a single character change flips roughly half the output bits (the avalanche effect). Hashes are one-way: you cannot reverse a digest to recover the original data.\nAlgorithm Output Status Common Use MD5 128 bits / 32 hex Deprecated for security Checksums, non-critical verification SHA-1 160 bits / 40 hex Deprecated (SHAttered 2017) Legacy systems, Git internals SHA-256 256 bits / 64 hex Current standard TLS certs, Bitcoin, code signing SHA-512 512 bits / 128 hex Current standard High-security digests, 64-bit optimised How to Use This Tool Text Input — type or paste any string. With Live update checked, all four hashes refresh automatically as you type. Toggle Uppercase to switch hex case. Use Copy All to export all hashes as labelled text, or copy them individually.\nFile Hash — drag and drop any file onto the upload zone (or click to browse). The file is read using the browser\u0026rsquo;s FileReader API and hashed locally — it is never sent to a server. Ideal for verifying download integrity against a published checksum.\nCompare Hashes — paste two digest strings to instantly determine if they match. The tool reports the exact character position of the first difference when they do not.\nPrivacy Notice All hashing runs locally in your browser. SHA-1, SHA-256, and SHA-512 use the native Web Crypto API (SubtleCrypto). MD5 is implemented in pure JavaScript. No data leaves your device.\nEncode or decode text with Base64, URL, HTML, and more → Universal Encoder/Decoder Decode JWT tokens and inspect headers and claims → JWT Decoder ","permalink":"https://productivity-works.com/tools/hash-generator/","summary":"\u003cdiv id=\"hg-app\"\u003e\n\u003cstyle\u003e\n#hg-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 800px;\n  margin: 0 auto;\n  color: #1e293b;\n}\n#hg-app *, #hg-app *::before, #hg-app *::after {\n  box-sizing: border-box;\n}\n/* ---- Tab bar ---- */\n#hg-app .hg-tab-bar {\n  display: flex;\n  gap: 0;\n  border-bottom: 2px solid #e2e8f0;\n  margin-bottom: 20px;\n}\n#hg-app .hg-tab {\n  padding: 9px 20px;\n  font-size: 13px;\n  font-weight: 600;\n  color: #64748b;\n  cursor: pointer;\n  border: none;\n  background: none;\n  border-bottom: 2.5px solid transparent;\n  margin-bottom: -2px;\n  transition: color 0.15s, border-color 0.15s;\n  white-space: nowrap;\n}\n#hg-app .hg-tab.active { color: #6366f1; border-bottom-color: #6366f1; }\n#hg-app .hg-tab:hover:not(.active) { color: #334155; }\n/* ---- Panels ---- */\n#hg-app .hg-panel { display: none; }\n#hg-app .hg-panel.active { display: block; }\n/* ---- Card ---- */\n#hg-app .hg-card {\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 18px 20px;\n  margin-bottom: 16px;\n}\n/* ---- Textarea ---- */\n#hg-app .hg-textarea {\n  width: 100%;\n  min-height: 110px;\n  padding: 11px 13px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 14px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", monospace;\n  resize: vertical;\n  background: #fff;\n  color: #1e293b;\n  transition: border-color 0.2s, box-shadow 0.2s;\n}\n#hg-app .hg-textarea:focus {\n  outline: none;\n  border-color: #6366f1;\n  box-shadow: 0 0 0 3px rgba(99,102,241,0.13);\n}\n/* ---- Controls row ---- */\n#hg-app .hg-row {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  gap: 10px;\n  margin-top: 12px;\n}\n/* ---- Buttons ---- */\n#hg-app .hg-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 5px;\n  padding: 8px 16px;\n  border-radius: 7px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  border: none;\n  transition: background 0.18s, transform 0.1s;\n  user-select: none;\n  line-height: 1;\n}\n#hg-app .hg-btn:active { transform: scale(0.97); }\n#hg-app .hg-btn-primary   { background: #6366f1; color: #fff; }\n#hg-app .hg-btn-primary:hover { background: #4f46e5; }\n#hg-app .hg-btn-secondary { background: #e2e8f0; color: #374151; }\n#hg-app .hg-btn-secondary:hover { background: #cbd5e1; }\n/* ---- Toggle label ---- */\n#hg-app .hg-toggle {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 13px;\n  color: #475569;\n  cursor: pointer;\n  user-select: none;\n}\n#hg-app .hg-toggle input[type=checkbox] {\n  width: 15px; height: 15px;\n  accent-color: #6366f1;\n  cursor: pointer;\n}\n/* ---- Progress ---- */\n#hg-app .hg-progress {\n  display: none;\n  align-items: center;\n  gap: 10px;\n  margin-top: 10px;\n  font-size: 12px;\n  color: #6366f1;\n}\n#hg-app .hg-progress.visible { display: flex; }\n#hg-app .hg-progress-track {\n  flex: 1; height: 5px;\n  background: #e0e7ff;\n  border-radius: 3px;\n  overflow: hidden;\n}\n#hg-app .hg-progress-fill {\n  height: 100%;\n  background: #6366f1;\n  border-radius: 3px;\n  width: 0%;\n  transition: width 0.15s;\n}\n/* ---- Status bar ---- */\n#hg-app .hg-status {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 9px 14px;\n  border-radius: 8px;\n  font-size: 13px;\n  margin-bottom: 12px;\n}\n#hg-app .hg-status.ok   { background:#f0fdf4; border:1px solid #bbf7d0; color:#166534; }\n#hg-app .hg-status.err  { background:#fef2f2; border:1px solid #fecaca; color:#991b1b; }\n#hg-app .hg-status.hidden { display: none; }\n/* ---- Hash result table ---- */\n#hg-app .hg-results-header {\n  display: flex;\n  justify-content: flex-end;\n  margin-bottom: 8px;\n}\n#hg-app .hg-table-wrap {\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  overflow: hidden;\n}\n#hg-app .hg-table {\n  width: 100%;\n  border-collapse: collapse;\n}\n#hg-app .hg-table th {\n  text-align: left;\n  padding: 8px 12px;\n  background: #f1f5f9;\n  font-size: 11px;\n  font-weight: 700;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  border-bottom: 1.5px solid #e2e8f0;\n}\n#hg-app .hg-table td {\n  padding: 11px 12px;\n  border-bottom: 1px solid #f1f5f9;\n  vertical-align: middle;\n  font-size: 13px;\n}\n#hg-app .hg-table tr:last-child td { border-bottom: none; }\n#hg-app .hg-table tr:hover td { background: #f8fafc; }\n/* ---- Algorithm badge ---- */\n#hg-app .hg-badge {\n  display: inline-block;\n  padding: 3px 9px;\n  border-radius: 5px;\n  font-size: 11px;\n  font-weight: 700;\n  white-space: nowrap;\n  letter-spacing: 0.04em;\n}\n#hg-app .hg-badge-md5    { background:#fef3c7; color:#92400e; }\n#hg-app .hg-badge-sha1   { background:#ede9fe; color:#5b21b6; }\n#hg-app .hg-badge-sha256 { background:#d1fae5; color:#065f46; }\n#hg-app .hg-badge-sha512 { background:#dbeafe; color:#1e40af; }\n/* ---- Hash value cell ---- */\n#hg-app .hg-hash-val {\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  font-size: 12px;\n  color: #334155;\n  word-break: break-all;\n  max-width: 420px;\n}\n#hg-app .hg-hash-meta {\n  font-size: 11px;\n  color: #94a3b8;\n  white-space: nowrap;\n}\n/* ---- Copy button (inline) ---- */\n#hg-app .hg-copy {\n  display: inline-flex;\n  align-items: center;\n  gap: 4px;\n  padding: 4px 10px;\n  border-radius: 5px;\n  font-size: 11px;\n  font-weight: 600;\n  cursor: pointer;\n  border: 1.5px solid #e2e8f0;\n  background: #fff;\n  color: #475569;\n  transition: all 0.15s;\n  white-space: nowrap;\n}\n#hg-app .hg-copy:hover   { border-color:#6366f1; color:#6366f1; background:#eef2ff; }\n#hg-app .hg-copy.copied  { border-color:#10b981; color:#10b981; background:#ecfdf5; }\n/* ---- File drop zone ---- */\n#hg-app .hg-dropzone {\n  border: 2.5px dashed #c7d2fe;\n  border-radius: 10px;\n  padding: 32px 20px;\n  text-align: center;\n  cursor: pointer;\n  transition: all 0.2s;\n  background: #fafbff;\n  color: #6366f1;\n  font-size: 14px;\n  font-weight: 500;\n}\n#hg-app .hg-dropzone:hover,\n#hg-app .hg-dropzone.over { border-color:#6366f1; background:#eef2ff; }\n#hg-app .hg-dropzone .hg-drop-icon { font-size: 34px; display: block; margin-bottom: 8px; }\n#hg-app .hg-file-info {\n  display: none;\n  align-items: center;\n  gap: 10px;\n  padding: 9px 14px;\n  background: #eff6ff;\n  border: 1px solid #bfdbfe;\n  border-radius: 8px;\n  font-size: 13px;\n  color: #1e40af;\n  margin-top: 10px;\n}\n/* ---- Compare ---- */\n#hg-app .hg-compare-label {\n  display: block;\n  font-size: 11px;\n  font-weight: 700;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 5px;\n}\n#hg-app .hg-compare-ta {\n  width: 100%;\n  padding: 9px 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  font-size: 12px;\n  min-height: 58px;\n  resize: vertical;\n  background: #fff;\n  color: #1e293b;\n  transition: border-color 0.2s;\n}\n#hg-app .hg-compare-ta:focus { outline:none; border-color:#6366f1; }\n#hg-app .hg-compare-result {\n  padding: 10px 14px;\n  border-radius: 8px;\n  font-size: 13px;\n  font-weight: 600;\n  margin-top: 12px;\n  display: none;\n}\n#hg-app .hg-compare-result.match    { background:#d1fae5; color:#065f46; border:1px solid #6ee7b7; }\n#hg-app .hg-compare-result.no-match { background:#fee2e2; color:#991b1b; border:1px solid #fca5a5; }\n/* ---- Toast ---- */\n.hg-toast {\n  position: fixed;\n  bottom: 24px; right: 24px;\n  background: #6366f1; color: #fff;\n  padding: 10px 18px;\n  border-radius: 8px;\n  font-size: 13px;\n  font-weight: 600;\n  z-index: 9999;\n  box-shadow: 0 4px 16px rgba(0,0,0,0.18);\n  transition: opacity 0.3s;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n}\n@media (max-width: 580px) {\n  #hg-app .hg-hash-val  { max-width: 160px; font-size: 10px; }\n  #hg-app .hg-hash-meta { display: none; }\n  #hg-app .hg-table th:nth-child(3),\n  #hg-app .hg-table td:nth-child(3) { display: none; }\n}\n\u003c/style\u003e\n\u003c!-- TAB BAR --\u003e\n\u003cdiv class=\"hg-tab-bar\"\u003e\n  \u003cbutton class=\"hg-tab active\" onclick=\"hgTab('text',this)\"\u003eText Input\u003c/button\u003e\n  \u003cbutton class=\"hg-tab\" onclick=\"hgTab('file',this)\"\u003eFile Hash\u003c/button\u003e\n  \u003cbutton class=\"hg-tab\" onclick=\"hgTab('compare',this)\"\u003eCompare Hashes\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- ===== TEXT PANEL ===== --\u003e\n\u003cdiv id=\"hg-panel-text\" class=\"hg-panel active\"\u003e\n  \u003cdiv class=\"hg-card\"\u003e\n    \u003ctextarea id=\"hg-text\" class=\"hg-textarea\" placeholder=\"Type or paste text here — hashes generate as you type…\" oninput=\"hgLive()\"\u003e\u003c/textarea\u003e\n    \u003cdiv class=\"hg-row\"\u003e\n      \u003cbutton class=\"hg-btn hg-btn-primary\" onclick=\"hgGenAll()\"\u003eGenerate All Hashes\u003c/button\u003e\n      \u003cbutton class=\"hg-btn hg-btn-secondary\" onclick=\"hgClearText()\"\u003eClear\u003c/button\u003e\n      \u003clabel class=\"hg-toggle\"\u003e\u003cinput type=\"checkbox\" id=\"hg-uc\" onchange=\"hgApplyCase()\"\u003e Uppercase\u003c/label\u003e\n      \u003clabel class=\"hg-toggle\"\u003e\u003cinput type=\"checkbox\" id=\"hg-live\" checked\u003e Live update\u003c/label\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"hg-progress\" id=\"hg-prog\"\u003e\n      \u003cspan id=\"hg-prog-lbl\"\u003eComputing…\u003c/span\u003e\n      \u003cdiv class=\"hg-progress-track\"\u003e\u003cdiv class=\"hg-progress-fill\" id=\"hg-prog-fill\"\u003e\u003c/div\u003e\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"hg-status hidden\" id=\"hg-status\"\u003e\u003c/div\u003e\n  \u003cdiv id=\"hg-text-results\" style=\"display:none\"\u003e\n    \u003cdiv class=\"hg-results-header\"\u003e\n      \u003cbutton class=\"hg-btn hg-btn-secondary\" style=\"font-size:12px;padding:5px 13px\" onclick=\"hgCopyAllText()\"\u003eCopy All\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"hg-table-wrap\"\u003e\n      \u003ctable class=\"hg-table\"\u003e\n        \u003cthead\u003e\u003ctr\u003e\u003cth\u003eAlgorithm\u003c/th\u003e\u003cth\u003eHash Value\u003c/th\u003e\u003cth\u003eInfo\u003c/th\u003e\u003cth\u003e\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e\n        \u003ctbody id=\"hg-text-tbody\"\u003e\u003c/tbody\u003e\n      \u003c/table\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ===== FILE PANEL ===== --\u003e\n\u003cdiv id=\"hg-panel-file\" class=\"hg-panel\"\u003e\n  \u003cdiv class=\"hg-card\"\u003e\n    \u003cdiv class=\"hg-dropzone\" id=\"hg-dropzone\"\n      onclick=\"document.getElementById('hg-file-inp').click()\"\n      ondragover=\"hgDragOver(event)\" ondragleave=\"hgDragLeave(event)\" ondrop=\"hgDrop(event)\"\u003e\n      \u003cspan class=\"hg-drop-icon\"\u003e\u0026#128196;\u003c/span\u003e\n      \u003cdiv\u003eDrop a file here, or \u003cstrong\u003eclick to browse\u003c/strong\u003e\u003c/div\u003e\n      \u003cdiv style=\"font-size:12px;color:#94a3b8;margin-top:6px;\"\u003eAll file types \u0026bull; Processed entirely in your browser \u0026bull; Never uploaded\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cinput type=\"file\" id=\"hg-file-inp\" style=\"display:none\" onchange=\"hgFileSelected(this.files[0])\"\u003e\n    \u003cdiv class=\"hg-file-info\" id=\"hg-finfo\"\u003e\n      \u003cspan style=\"font-size:18px\"\u003e\u0026#128196;\u003c/span\u003e\n      \u003cstrong id=\"hg-fname\"\u003e\u003c/strong\u003e\n      \u003cspan style=\"color:#93c5fd\"\u003e|\u003c/span\u003e\n      \u003cspan id=\"hg-fsize\" style=\"color:#3b82f6\"\u003e\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"hg-progress\" id=\"hg-fprog\" style=\"margin-top:12px\"\u003e\n      \u003cspan id=\"hg-fprog-lbl\"\u003eHashing file…\u003c/span\u003e\n      \u003cdiv class=\"hg-progress-track\"\u003e\u003cdiv class=\"hg-progress-fill\" id=\"hg-fprog-fill\"\u003e\u003c/div\u003e\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"hg-status hidden\" id=\"hg-fstatus\"\u003e\u003c/div\u003e\n  \u003cdiv id=\"hg-file-results\" style=\"display:none\"\u003e\n    \u003cdiv class=\"hg-results-header\"\u003e\n      \u003cbutton class=\"hg-btn hg-btn-secondary\" style=\"font-size:12px;padding:5px 13px\" onclick=\"hgCopyAllFile()\"\u003eCopy All\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"hg-table-wrap\"\u003e\n      \u003ctable class=\"hg-table\"\u003e\n        \u003cthead\u003e\u003ctr\u003e\u003cth\u003eAlgorithm\u003c/th\u003e\u003cth\u003eHash Value\u003c/th\u003e\u003cth\u003eInfo\u003c/th\u003e\u003cth\u003e\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e\n        \u003ctbody id=\"hg-file-tbody\"\u003e\u003c/tbody\u003e\n      \u003c/table\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ===== COMPARE PANEL ===== --\u003e\n\u003cdiv id=\"hg-panel-compare\" class=\"hg-panel\"\u003e\n  \u003cdiv class=\"hg-card\"\u003e\n    \u003cp style=\"margin:0 0 14px;font-size:14px;color:#475569;line-height:1.6;\"\u003e\n      Paste two hash strings to verify they are identical. Useful for confirming file integrity after download, or checking password digests match.\n    \u003c/p\u003e","title":"Hash Generator"},{"content":" \u0026#10024; Beautify \u0026#9889; Minify \u0026#10005; Clear Indent: 2 Spaces 4 Spaces Tab Wrap long lines View: Side-by-side Stacked 0Chars 0Lines –Out Chars –Out Lines –Tags \u0026#8635; Paste HTML on the left and click Beautify or Minify. Input HTML 0 chars 1 Output – // Output will appear here \u0026#128203; Copy Copied to clipboard! Convert HTML → HTML to Markdown Format XML → XML Formatter Related Articles Best Online Coding Bootcamps 2026: Honest Comparison by Career Goal ","permalink":"https://productivity-works.com/tools/html-beautifier/","summary":"\u003cdiv id=\"hb-app\"\u003e\n\u003cstyle\u003e\n#hb-app *,\n#hb-app *::before,\n#hb-app *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n#hb-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 14px;\n  color: #1e293b;\n  line-height: 1.5;\n}\n\n#hb-app .hb-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  align-items: center;\n  margin-bottom: 12px;\n}\n\n#hb-app .hb-toolbar-group {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  flex-wrap: wrap;\n}\n\n#hb-app .hb-toolbar-sep {\n  width: 1px;\n  height: 28px;\n  background: #e2e8f0;\n  margin: 0 2px;\n}\n\n#hb-app .hb-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 5px;\n  padding: 7px 14px;\n  border: none;\n  border-radius: 6px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s;\n  white-space: nowrap;\n  line-height: 1;\n}\n#hb-app .hb-btn:active { transform: scale(0.97); }\n#hb-app .hb-btn-primary   { background: #2563eb; color: #fff; }\n#hb-app .hb-btn-primary:hover   { background: #1d4ed8; }\n#hb-app .hb-btn-secondary { background: #f1f5f9; color: #334155; border: 1px solid #cbd5e1; }\n#hb-app .hb-btn-secondary:hover { background: #e2e8f0; }\n#hb-app .hb-btn-success   { background: #16a34a; color: #fff; }\n#hb-app .hb-btn-success:hover   { background: #15803d; }\n#hb-app .hb-btn-orange    { background: #d97706; color: #fff; }\n#hb-app .hb-btn-orange:hover    { background: #b45309; }\n#hb-app .hb-btn-danger    { background: #dc2626; color: #fff; }\n#hb-app .hb-btn-danger:hover    { background: #b91c1c; }\n\n#hb-app .hb-select {\n  padding: 7px 10px;\n  border: 1px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 13px;\n  background: #fff;\n  color: #334155;\n  cursor: pointer;\n}\n\n#hb-app .hb-label {\n  font-size: 12px;\n  font-weight: 700;\n  color: #64748b;\n  white-space: nowrap;\n}\n\n#hb-app .hb-toggle-wrap {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  cursor: pointer;\n  user-select: none;\n}\n#hb-app .hb-toggle-wrap input[type=\"checkbox\"] {\n  width: 16px;\n  height: 16px;\n  cursor: pointer;\n  accent-color: #2563eb;\n}\n\n/* View toggle */\n#hb-app .hb-view-toggle {\n  display: flex;\n  gap: 0;\n  border: 1px solid #cbd5e1;\n  border-radius: 6px;\n  overflow: hidden;\n}\n#hb-app .hb-view-btn {\n  padding: 6px 12px;\n  border: none;\n  background: #f8fafc;\n  color: #64748b;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s;\n}\n#hb-app .hb-view-btn.active {\n  background: #2563eb;\n  color: #fff;\n}\n#hb-app .hb-view-btn:not(.active):hover { background: #e2e8f0; }\n\n/* Panels */\n#hb-app .hb-panels {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 12px;\n  margin-bottom: 12px;\n}\n#hb-app .hb-panels.stacked {\n  grid-template-columns: 1fr;\n}\n@media (max-width: 700px) {\n  #hb-app .hb-panels { grid-template-columns: 1fr; }\n  #hb-app .hb-toolbar-sep { display: none; }\n}\n\n#hb-app .hb-panel {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n}\n\n#hb-app .hb-panel-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n\n#hb-app .hb-panel-label {\n  font-size: 12px;\n  font-weight: 700;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n\n#hb-app .hb-count {\n  font-size: 11px;\n  color: #94a3b8;\n  font-weight: 500;\n}\n\n/* Editor wrapper */\n#hb-app .hb-editor-wrap {\n  position: relative;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  overflow: hidden;\n  background: #fafafa;\n}\n\n#hb-app .hb-with-lines {\n  display: flex;\n}\n\n#hb-app .hb-line-nums {\n  width: 36px;\n  min-width: 36px;\n  background: #f1f5f9;\n  color: #94a3b8;\n  font-family: \"JetBrains Mono\", \"Fira Code\", \"Cascadia Code\", monospace;\n  font-size: 12px;\n  line-height: 1.6;\n  padding: 10px 0;\n  text-align: right;\n  padding-right: 6px;\n  user-select: none;\n  overflow: hidden;\n  border-right: 1px solid #e2e8f0;\n}\n#hb-app .hb-line-nums span { display: block; }\n\n#hb-app .hb-textarea {\n  width: 100%;\n  min-height: 300px;\n  padding: 10px 12px;\n  border: none;\n  background: transparent;\n  font-family: \"JetBrains Mono\", \"Fira Code\", \"Cascadia Code\", monospace;\n  font-size: 13px;\n  line-height: 1.6;\n  color: #1e293b;\n  resize: vertical;\n  outline: none;\n}\n\n/* Output / syntax highlighted */\n#hb-app .hb-output-wrap {\n  position: relative;\n  min-height: 300px;\n  overflow: auto;\n  background: #0f172a;\n  border-radius: 6px;\n  padding: 10px 12px;\n}\n\n#hb-app .hb-output-pre {\n  font-family: \"JetBrains Mono\", \"Fira Code\", \"Cascadia Code\", monospace;\n  font-size: 13px;\n  line-height: 1.6;\n  white-space: pre;\n  color: #e2e8f0;\n  margin: 0;\n}\n\n/* Syntax colors */\n#hb-app .hl-tag    { color: #7dd3fc; }   /* tag names */\n#hb-app .hl-attr   { color: #fbbf24; }   /* attribute names */\n#hb-app .hl-val    { color: #86efac; }   /* attribute values */\n#hb-app .hl-cmt    { color: #64748b; font-style: italic; } /* comments */\n#hb-app .hl-entity { color: #f9a8d4; }   /* entities */\n#hb-app .hl-doctype{ color: #a78bfa; }   /* doctype */\n#hb-app .hl-punct  { color: #94a3b8; }   /* \u003c \u003e = / */\n#hb-app .hl-text   { color: #e2e8f0; }\n\n/* Status bar */\n#hb-app .hb-status {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 9px 14px;\n  border-radius: 8px;\n  font-size: 13px;\n  font-weight: 600;\n  min-height: 40px;\n  margin-bottom: 10px;\n}\n#hb-app .hb-status.hb-idle  { background: #f8fafc; color: #64748b; border: 1px solid #e2e8f0; }\n#hb-app .hb-status.hb-ok    { background: #f0fdf4; color: #16a34a; border: 1px solid #bbf7d0; }\n#hb-app .hb-status.hb-err   { background: #fef2f2; color: #dc2626; border: 1px solid #fecaca; }\n\n#hb-app .hb-stats {\n  display: flex;\n  gap: 16px;\n  flex-wrap: wrap;\n  margin-bottom: 12px;\n}\n#hb-app .hb-stat-item {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 8px;\n  padding: 8px 16px;\n  min-width: 80px;\n}\n#hb-app .hb-stat-val {\n  font-size: 20px;\n  font-weight: 700;\n  color: #2563eb;\n  line-height: 1.2;\n}\n#hb-app .hb-stat-lbl {\n  font-size: 11px;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-top: 2px;\n}\n\n/* Copy toast */\n#hb-app .hb-toast {\n  position: fixed;\n  bottom: 28px;\n  left: 50%;\n  transform: translateX(-50%) translateY(20px);\n  background: #1e293b;\n  color: #fff;\n  padding: 10px 22px;\n  border-radius: 8px;\n  font-size: 13px;\n  font-weight: 600;\n  opacity: 0;\n  pointer-events: none;\n  transition: opacity 0.2s, transform 0.2s;\n  z-index: 9999;\n  white-space: nowrap;\n}\n#hb-app .hb-toast.show {\n  opacity: 1;\n  transform: translateX(-50%) translateY(0);\n}\n\n/* Copy btn inside output */\n#hb-app .hb-copy-overlay {\n  position: absolute;\n  top: 8px;\n  right: 8px;\n}\n#hb-app .hb-copy-overlay .hb-btn {\n  font-size: 12px;\n  padding: 5px 11px;\n  opacity: 0.85;\n}\n#hb-app .hb-copy-overlay .hb-btn:hover { opacity: 1; }\n\u003c/style\u003e\n\u003c!-- Toolbar --\u003e\n\u003cdiv class=\"hb-toolbar\"\u003e\n  \u003cdiv class=\"hb-toolbar-group\"\u003e\n    \u003cbutton class=\"hb-btn hb-btn-primary\" id=\"hb-beautify-btn\"\u003e\u0026#10024; Beautify\u003c/button\u003e\n    \u003cbutton class=\"hb-btn hb-btn-orange\"  id=\"hb-minify-btn\"\u003e\u0026#9889; Minify\u003c/button\u003e\n    \u003cbutton class=\"hb-btn hb-btn-danger\"  id=\"hb-clear-btn\"\u003e\u0026#10005; Clear\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"hb-toolbar-sep\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"hb-toolbar-group\"\u003e\n    \u003cspan class=\"hb-label\"\u003eIndent:\u003c/span\u003e\n    \u003cselect class=\"hb-select\" id=\"hb-indent-sel\"\u003e\n      \u003coption value=\"2\"\u003e2 Spaces\u003c/option\u003e\n      \u003coption value=\"4\" selected\u003e4 Spaces\u003c/option\u003e\n      \u003coption value=\"tab\"\u003eTab\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"hb-toolbar-sep\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"hb-toolbar-group\"\u003e\n    \u003clabel class=\"hb-toggle-wrap\"\u003e\n      \u003cinput type=\"checkbox\" id=\"hb-wrap-chk\"\u003e\n      \u003cspan class=\"hb-label\"\u003eWrap long lines\u003c/span\u003e\n    \u003c/label\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"hb-toolbar-sep\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"hb-toolbar-group\"\u003e\n    \u003cspan class=\"hb-label\"\u003eView:\u003c/span\u003e\n    \u003cdiv class=\"hb-view-toggle\"\u003e\n      \u003cbutton class=\"hb-view-btn active\" id=\"hb-view-side\"\u003eSide-by-side\u003c/button\u003e\n      \u003cbutton class=\"hb-view-btn\" id=\"hb-view-stack\"\u003eStacked\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Stats --\u003e\n\u003cdiv class=\"hb-stats\" id=\"hb-stats-bar\"\u003e\n  \u003cdiv class=\"hb-stat-item\"\u003e\u003cspan class=\"hb-stat-val\" id=\"hb-stat-chars\"\u003e0\u003c/span\u003e\u003cspan class=\"hb-stat-lbl\"\u003eChars\u003c/span\u003e\u003c/div\u003e\n  \u003cdiv class=\"hb-stat-item\"\u003e\u003cspan class=\"hb-stat-val\" id=\"hb-stat-lines\"\u003e0\u003c/span\u003e\u003cspan class=\"hb-stat-lbl\"\u003eLines\u003c/span\u003e\u003c/div\u003e\n  \u003cdiv class=\"hb-stat-item\"\u003e\u003cspan class=\"hb-stat-val\" id=\"hb-stat-out-chars\"\u003e–\u003c/span\u003e\u003cspan class=\"hb-stat-lbl\"\u003eOut Chars\u003c/span\u003e\u003c/div\u003e\n  \u003cdiv class=\"hb-stat-item\"\u003e\u003cspan class=\"hb-stat-val\" id=\"hb-stat-out-lines\"\u003e–\u003c/span\u003e\u003cspan class=\"hb-stat-lbl\"\u003eOut Lines\u003c/span\u003e\u003c/div\u003e\n  \u003cdiv class=\"hb-stat-item\"\u003e\u003cspan class=\"hb-stat-val\" id=\"hb-stat-tags\"\u003e–\u003c/span\u003e\u003cspan class=\"hb-stat-lbl\"\u003eTags\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Status --\u003e\n\u003cdiv class=\"hb-status hb-idle\" id=\"hb-status\"\u003e\n  \u0026#8635; Paste HTML on the left and click Beautify or Minify.\n\u003c/div\u003e\n\u003c!-- Panels --\u003e\n\u003cdiv class=\"hb-panels\" id=\"hb-panels\"\u003e\n  \u003c!-- Input --\u003e\n  \u003cdiv class=\"hb-panel\"\u003e\n    \u003cdiv class=\"hb-panel-header\"\u003e\n      \u003cspan class=\"hb-panel-label\"\u003eInput HTML\u003c/span\u003e\n      \u003cspan class=\"hb-count\" id=\"hb-in-count\"\u003e0 chars\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"hb-editor-wrap\"\u003e\n      \u003cdiv class=\"hb-with-lines\"\u003e\n        \u003cdiv class=\"hb-line-nums\" id=\"hb-in-lines\"\u003e\u003cspan\u003e1\u003c/span\u003e\u003c/div\u003e\n        \u003ctextarea class=\"hb-textarea\" id=\"hb-input\" placeholder=\"Paste your HTML here…\" spellcheck=\"false\" autocomplete=\"off\" autocorrect=\"off\" autocapitalize=\"off\"\u003e\u003c/textarea\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Output --\u003e\n  \u003cdiv class=\"hb-panel\"\u003e\n    \u003cdiv class=\"hb-panel-header\"\u003e\n      \u003cspan class=\"hb-panel-label\"\u003eOutput\u003c/span\u003e\n      \u003cspan class=\"hb-count\" id=\"hb-out-count\"\u003e–\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"hb-editor-wrap\" style=\"background:#0f172a;\"\u003e\n      \u003cdiv class=\"hb-output-wrap\" id=\"hb-output-wrap\"\u003e\n        \u003cpre class=\"hb-output-pre\" id=\"hb-output\"\u003e\u003cspan style=\"color:#475569;\"\u003e// Output will appear here\u003c/span\u003e\u003c/pre\u003e\n        \u003cdiv class=\"hb-copy-overlay\"\u003e\n          \u003cbutton class=\"hb-btn hb-btn-success\" id=\"hb-copy-btn\"\u003e\u0026#128203; Copy\u003c/button\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Toast --\u003e\n\u003cdiv class=\"hb-toast\" id=\"hb-toast\"\u003eCopied to clipboard!\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n  'use strict';\n\n  var input      = document.getElementById('hb-input');\n  var output     = document.getElementById('hb-output');\n  var outWrap    = document.getElementById('hb-output-wrap');\n  var statusEl   = document.getElementById('hb-status');\n  var inCount    = document.getElementById('hb-in-count');\n  var outCount   = document.getElementById('hb-out-count');\n  var inLines    = document.getElementById('hb-in-lines');\n  var indentSel  = document.getElementById('hb-indent-sel');\n  var wrapChk    = document.getElementById('hb-wrap-chk');\n  var panels     = document.getElementById('hb-panels');\n  var toast      = document.getElementById('hb-toast');\n\n  var statChars    = document.getElementById('hb-stat-chars');\n  var statLines    = document.getElementById('hb-stat-lines');\n  var statOutChars = document.getElementById('hb-stat-out-chars');\n  var statOutLines = document.getElementById('hb-stat-out-lines');\n  var statTags     = document.getElementById('hb-stat-tags');\n\n  var lastOutput = '';\n  var toastTimer = null;\n\n  /* ── Indent helper ── */\n  function getIndent() {\n    var v = indentSel.value;\n    if (v === 'tab') return '\\t';\n    return ' '.repeat(parseInt(v, 10));\n  }\n\n  /* ── Syntax highlighter ── */\n  function escape(s) {\n    return s.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;').replace(/\"/g,'\u0026quot;');\n  }\n\n  function highlight(code) {\n    // We process token by token using a simple state machine\n    var result = '';\n    var i = 0;\n    var len = code.length;\n\n    while (i \u003c len) {\n      // Comment\n      if (code.slice(i, i+4) === '\u003c!--') {\n        var end = code.indexOf('--\u003e', i+4);\n        if (end === -1) end = len - 3;\n        var cmt = code.slice(i, end+3);\n        result += '\u003cspan class=\"hl-cmt\"\u003e' + escape(cmt) + '\u003c/span\u003e';\n        i = end + 3;\n        continue;\n      }\n      // Doctype\n      if (code.slice(i, i+2) === '\u003c!' \u0026\u0026 code.slice(i,i+9).toLowerCase() !== '\u003c!--') {\n        var end2 = code.indexOf('\u003e', i);\n        if (end2 === -1) end2 = len - 1;\n        result += '\u003cspan class=\"hl-doctype\"\u003e' + escape(code.slice(i, end2+1)) + '\u003c/span\u003e';\n        i = end2 + 1;\n        continue;\n      }\n      // Tag\n      if (code[i] === '\u003c') {\n        var end3 = code.indexOf('\u003e', i);\n        if (end3 === -1) { result += escape(code.slice(i)); i = len; continue; }\n        var tagContent = code.slice(i, end3+1);\n        result += highlightTag(tagContent);\n        i = end3 + 1;\n        continue;\n      }\n      // Text node\n      var next = code.indexOf('\u003c', i);\n      if (next === -1) next = len;\n      var txt = code.slice(i, next);\n      // highlight entities\n      result += '\u003cspan class=\"hl-text\"\u003e' + escape(txt).replace(/\u0026amp;[a-zA-Z0-9#]+;/g, function(m){ return '\u003cspan class=\"hl-entity\"\u003e'+m+'\u003c/span\u003e'; }) + '\u003c/span\u003e';\n      i = next;\n    }\n    return result;\n  }\n\n  function highlightTag(tag) {\n    // tag = e.g. \u003cdiv class=\"foo\" id=\"bar\"\u003e or \u003c/div\u003e or \u003cbr/\u003e\n    var out = '';\n    var inner = tag.slice(1, tag.endsWith('/\u003e') ? -2 : (tag.endsWith('\u003e') ? -1 : tag.length));\n    var isClose = inner.startsWith('/');\n    if (isClose) inner = inner.slice(1);\n\n    // tag name\n    var nameMatch = inner.match(/^([a-zA-Z0-9:\\-_]+)([\\s\\S]*)?$/);\n    if (!nameMatch) return '\u003cspan class=\"hl-punct\"\u003e' + escape(tag) + '\u003c/span\u003e';\n\n    var tagName = nameMatch[1];\n    var rest = nameMatch[2] || '';\n\n    out += '\u003cspan class=\"hl-punct\"\u003e\u0026lt;\u003c/span\u003e';\n    if (isClose) out += '\u003cspan class=\"hl-punct\"\u003e/\u003c/span\u003e';\n    out += '\u003cspan class=\"hl-tag\"\u003e' + escape(tagName) + '\u003c/span\u003e';\n\n    // Parse attributes\n    var attrReg = /\\s+([a-zA-Z0-9\\-_:@.]+)(?:\\s*=\\s*(?:\"([^\"]*?)\"|'([^']*?)'|([^\\s\u003e]+)))?/g;\n    var m;\n    var lastIdx = 0;\n    while ((m = attrReg.exec(rest)) !== null) {\n      // whitespace before attr\n      var ws = rest.slice(lastIdx, m.index + (m[0].match(/^\\s+/) || [''])[0].length);\n      out += escape(ws.replace(/[^ \\t\\n\\r]/g, ''));\n\n      var spaceLen = (m[0].match(/^\\s+/) || [''])[0].length;\n      out += escape(rest.slice(m.index, m.index + spaceLen));\n\n      out += '\u003cspan class=\"hl-attr\"\u003e' + escape(m[1]) + '\u003c/span\u003e';\n      if (m[2] !== undefined || m[3] !== undefined || m[4] !== undefined) {\n        var val = m[2] !== undefined ? '\"'+m[2]+'\"' : (m[3] !== undefined ? \"'\"+m[3]+\"'\" : m[4]);\n        var q = m[2] !== undefined ? '\"' : (m[3] !== undefined ? \"'\" : '');\n        out += '\u003cspan class=\"hl-punct\"\u003e=\u003c/span\u003e';\n        out += '\u003cspan class=\"hl-val\"\u003e' + escape(val) + '\u003c/span\u003e';\n      }\n      lastIdx = m.index + m[0].length;\n    }\n    // remaining (spaces, self-close slash)\n    var rem = rest.slice(lastIdx);\n    if (rem) out += escape(rem);\n\n    if (tag.endsWith('/\u003e')) out += '\u003cspan class=\"hl-punct\"\u003e/\u0026gt;\u003c/span\u003e';\n    else out += '\u003cspan class=\"hl-punct\"\u003e\u0026gt;\u003c/span\u003e';\n\n    return out;\n  }\n\n  /* ── HTML Beautifier ── */\n  function beautifyHTML(html, indent) {\n    var INLINE = 'a abbr acronym b bdo big br cite code dfn em i img input kbd label map object output q samp select small span strong sub sup textarea time tt var button'.split(' ');\n    var VOID   = 'area base br col embed hr img input link meta param source track wbr'.split(' ');\n\n    var lines = [];\n    var level = 0;\n    // Tokenize\n    var tokens = [];\n    var re = /(\u003c!--[\\s\\S]*?--\u003e|\u003c!DOCTYPE[^\u003e]*\u003e|\u003c\\/[a-zA-Z0-9:\\-]+\\s*\u003e|\u003c[a-zA-Z0-9:\\-]+(?:\\s[^\u003e]*?)?\\s*\\/?\u003e|[^\u003c]+)/gi;\n    var m;\n    while ((m = re.exec(html)) !== null) {\n      var tok = m[0];\n      if (tok.trim()) tokens.push(tok);\n    }\n\n    tokens.forEach(function(tok) {\n      var trimmed = tok.trim();\n      if (!trimmed) return;\n\n      // Comment or doctype\n      if (trimmed.startsWith('\u003c!--') || trimmed.startsWith('\u003c!')) {\n        lines.push(indent.repeat(level) + trimmed);\n        return;\n      }\n      // Closing tag\n      if (trimmed.startsWith('\u003c/')) {\n        level = Math.max(0, level - 1);\n        lines.push(indent.repeat(level) + trimmed);\n        return;\n      }\n      // Opening tag\n      if (trimmed.startsWith('\u003c')) {\n        var nameM = trimmed.match(/^\u003c([a-zA-Z0-9:\\-]+)/i);\n        var name = nameM ? nameM[1].toLowerCase() : '';\n        var isVoid = VOID.indexOf(name) !== -1;\n        var isSelfClose = trimmed.endsWith('/\u003e');\n        var isInline = INLINE.indexOf(name) !== -1;\n\n        lines.push(indent.repeat(level) + trimmed);\n\n        if (!isVoid \u0026\u0026 !isSelfClose) {\n          level++;\n        }\n        return;\n      }\n      // Text node\n      lines.push(indent.repeat(level) + trimmed);\n    });\n\n    return lines.join('\\n');\n  }\n\n  /* ── HTML Minifier ── */\n  function minifyHTML(html) {\n    return html\n      .replace(/\u003c!--[\\s\\S]*?--\u003e/g, '')           // remove comments\n      .replace(/\\s+/g, ' ')                        // collapse whitespace\n      .replace(/\u003e\\s+\u003c/g, '\u003e\u003c')                     // remove space between tags\n      .replace(/\\s*=\\s*/g, '=')                    // remove spaces around =\n      .replace(/\"\\s+\u003e/g, '\"\u003e')                     // clean closing\n      .trim();\n  }\n\n  /* ── Count tags ── */\n  function countTags(html) {\n    var m = html.match(/\u003c[a-zA-Z][a-zA-Z0-9:\\-]*/g);\n    return m ? m.length : 0;\n  }\n\n  /* ── Update line numbers ── */\n  function updateLineNums() {\n    var val = input.value;\n    var count = (val.match(/\\n/g) || []).length + 1;\n    var html = '';\n    for (var i = 1; i \u003c= count; i++) html += '\u003cspan\u003e' + i + '\u003c/span\u003e';\n    inLines.innerHTML = html;\n    // sync scroll\n    inLines.scrollTop = input.scrollTop;\n  }\n\n  /* ── Update input stats ── */\n  function updateInputStats() {\n    var val = input.value;\n    var chars = val.length;\n    var lines = val ? val.split('\\n').length : 0;\n    inCount.textContent = chars + ' chars';\n    statChars.textContent = chars.toLocaleString();\n    statLines.textContent = lines.toLocaleString();\n    updateLineNums();\n  }\n\n  /* ── Set status ── */\n  function setStatus(type, msg) {\n    statusEl.className = 'hb-status hb-' + type;\n    statusEl.innerHTML = msg;\n  }\n\n  /* ── Render output ── */\n  function renderOutput(text, mode) {\n    lastOutput = text;\n    var chars = text.length;\n    var lines = text ? text.split('\\n').length : 0;\n    outCount.textContent = chars + ' chars';\n    statOutChars.textContent = chars.toLocaleString();\n    statOutLines.textContent = lines.toLocaleString();\n    statTags.textContent = countTags(text).toLocaleString();\n\n    var hl = highlight(text);\n    output.innerHTML = hl;\n\n    if (wrapChk.checked) {\n      outWrap.style.whiteSpace = 'pre-wrap';\n      output.style.whiteSpace = 'pre-wrap';\n    } else {\n      outWrap.style.whiteSpace = '';\n      output.style.whiteSpace = 'pre';\n    }\n\n    var label = mode === 'minify' ? 'Minified' : 'Beautified';\n    setStatus('ok', '\u0026#10003; ' + label + ' \u0026mdash; ' + chars.toLocaleString() + ' chars, ' + lines.toLocaleString() + ' lines');\n  }\n\n  /* ── Beautify handler ── */\n  document.getElementById('hb-beautify-btn').addEventListener('click', function() {\n    var val = input.value.trim();\n    if (!val) { setStatus('err', '\u0026#9888; Please paste some HTML first.'); return; }\n    var indent = getIndent();\n    try {\n      var result = beautifyHTML(val, indent);\n      renderOutput(result, 'beautify');\n    } catch(e) {\n      setStatus('err', '\u0026#9888; Error: ' + e.message);\n    }\n  });\n\n  /* ── Minify handler ── */\n  document.getElementById('hb-minify-btn').addEventListener('click', function() {\n    var val = input.value.trim();\n    if (!val) { setStatus('err', '\u0026#9888; Please paste some HTML first.'); return; }\n    try {\n      var result = minifyHTML(val);\n      renderOutput(result, 'minify');\n    } catch(e) {\n      setStatus('err', '\u0026#9888; Error: ' + e.message);\n    }\n  });\n\n  /* ── Clear handler ── */\n  document.getElementById('hb-clear-btn').addEventListener('click', function() {\n    input.value = '';\n    output.innerHTML = '\u003cspan style=\"color:#475569;\"\u003e// Output will appear here\u003c/span\u003e';\n    lastOutput = '';\n    outCount.textContent = '–';\n    statOutChars.textContent = '–';\n    statOutLines.textContent = '–';\n    statTags.textContent = '–';\n    setStatus('idle', '\u0026#8635; Paste HTML on the left and click Beautify or Minify.');\n    updateInputStats();\n  });\n\n  /* ── Copy handler ── */\n  document.getElementById('hb-copy-btn').addEventListener('click', function() {\n    if (!lastOutput) return;\n    var el = this;\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(lastOutput).then(showToast);\n    } else {\n      var ta = document.createElement('textarea');\n      ta.value = lastOutput;\n      ta.style.position = 'fixed';\n      ta.style.opacity = '0';\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      showToast();\n    }\n  });\n\n  function showToast() {\n    toast.classList.add('show');\n    clearTimeout(toastTimer);\n    toastTimer = setTimeout(function(){ toast.classList.remove('show'); }, 2000);\n  }\n\n  /* ── Wrap toggle ── */\n  wrapChk.addEventListener('change', function() {\n    if (lastOutput) {\n      if (this.checked) {\n        outWrap.style.whiteSpace = 'pre-wrap';\n        output.style.whiteSpace = 'pre-wrap';\n      } else {\n        outWrap.style.whiteSpace = '';\n        output.style.whiteSpace = 'pre';\n      }\n    }\n  });\n\n  /* ── View toggle ── */\n  document.getElementById('hb-view-side').addEventListener('click', function() {\n    panels.classList.remove('stacked');\n    this.classList.add('active');\n    document.getElementById('hb-view-stack').classList.remove('active');\n  });\n  document.getElementById('hb-view-stack').addEventListener('click', function() {\n    panels.classList.add('stacked');\n    this.classList.add('active');\n    document.getElementById('hb-view-side').classList.remove('active');\n  });\n\n  /* ── Input events ── */\n  input.addEventListener('input', updateInputStats);\n  input.addEventListener('scroll', function() { inLines.scrollTop = this.scrollTop; });\n  input.addEventListener('keydown', function(e) {\n    if (e.key === 'Tab') {\n      e.preventDefault();\n      var start = this.selectionStart;\n      var end = this.selectionEnd;\n      var indent = getIndent();\n      this.value = this.value.slice(0, start) + indent + this.value.slice(end);\n      this.selectionStart = this.selectionEnd = start + indent.length;\n      updateInputStats();\n    }\n  });\n\n  /* ── Init ── */\n  updateInputStats();\n\n  /* ── Sample HTML ── */\n  var sample = '\u003c!DOCTYPE html\u003e\\n\u003chtml lang=\"en\"\u003e\\n\u003chead\u003e\\n\u003cmeta charset=\"UTF-8\"\u003e\\n\u003ctitle\u003eSample Page\u003c/title\u003e\\n\u003clink rel=\"stylesheet\" href=\"style.css\"\u003e\\n\u003c/head\u003e\\n\u003cbody\u003e\\n\u003cheader\u003e\\n\u003ch1\u003eHello World\u003c/h1\u003e\\n\u003cnav\u003e\\n\u003ca href=\"/\"\u003eHome\u003c/a\u003e\\n\u003ca href=\"/about\"\u003eAbout\u003c/a\u003e\\n\u003c/nav\u003e\\n\u003c/header\u003e\\n\u003cmain\u003e\\n\u003cp\u003eThis is a \u003cstrong\u003esample\u003c/strong\u003e HTML document for the \u003cem\u003ebeautifier\u003c/em\u003e.\u003c/p\u003e","title":"HTML Beautifier \u0026 Formatter"},{"content":" Plain Text / Raw HTML ","permalink":"https://productivity-works.com/tools/html-entity-converter/","summary":"\u003cstyle\u003e\n#hec-app *,#hec-app *::before,#hec-app *::after{box-sizing:border-box;margin:0;padding:0}\n#hec-app{font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;max-width:900px;margin:0 auto;padding:16px;color:#1a1a2e}\n#hec-app h2{font-size:1.1rem;font-weight:600;margin-bottom:12px;color:#333}\n#hec-app .hec-panels{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:12px}\n@media(max-width:600px){#hec-app .hec-panels{grid-template-columns:1fr}}\n#hec-app .hec-panel label{display:block;font-size:.85rem;font-weight:600;color:#555;margin-bottom:6px}\n#hec-app textarea{width:100%;height:200px;padding:10px;border:1.5px solid #d0d5e8;border-radius:8px;font-size:.9rem;font-family:monospace;resize:vertical;background:#fafbff;transition:border-color .2s}\n#hec-app textarea:focus{outline:none;border-color:#4f6bed}\n#hec-app .hec-controls{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;margin-bottom:20px}\n#hec-app button{padding:9px 20px;border:none;border-radius:7px;font-size:.9rem;font-weight:600;cursor:pointer;transition:background .2s,transform .1s}\n#hec-app button:active{transform:scale(.97)}\n#hec-app .btn-encode{background:#4f6bed;color:#fff}\n#hec-app .btn-encode:hover{background:#3a55d4}\n#hec-app .btn-decode{background:#22a67a;color:#fff}\n#hec-app .btn-decode:hover{background:#198a64}\n#hec-app .btn-copy-raw{background:#f0f2ff;color:#4f6bed;border:1.5px solid #c8cffa}\n#hec-app .btn-copy-raw:hover{background:#e0e4ff}\n#hec-app .btn-copy-enc{background:#f0f2ff;color:#22a67a;border:1.5px solid #b2e6d4}\n#hec-app .btn-copy-enc:hover{background:#e0f7f0}\n#hec-app .btn-clear{background:#f5f5f5;color:#666;border:1.5px solid #ddd}\n#hec-app .btn-clear:hover{background:#ebebeb}\n#hec-app .hec-toast{position:fixed;bottom:24px;right:24px;background:#333;color:#fff;padding:9px 18px;border-radius:8px;font-size:.85rem;opacity:0;pointer-events:none;transition:opacity .3s;z-index:9999}\n#hec-app .hec-toast.show{opacity:1}\n#hec-app .hec-ref{margin-top:8px}\n#hec-app .hec-ref h3{font-size:1rem;font-weight:600;color:#333;margin-bottom:10px}\n#hec-app .hec-ref table{width:100%;border-collapse:collapse;font-size:.85rem}\n#hec-app .hec-ref th{background:#f0f2ff;color:#4f6bed;padding:8px 10px;text-align:left;border-bottom:2px solid #d0d5e8}\n#hec-app .hec-ref td{padding:7px 10px;border-bottom:1px solid #eee;font-family:monospace}\n#hec-app .hec-ref tr:hover td{background:#fafbff}\n#hec-app .hec-ref td:first-child{font-size:1.1rem;text-align:center;font-family:inherit}\n#hec-app .hec-click-cell{cursor:pointer;color:#4f6bed;text-decoration:underline dotted}\n#hec-app .hec-click-cell:hover{color:#22a67a}\n\u003c/style\u003e\n\u003cdiv id=\"hec-app\"\u003e\n\u003cdiv class=\"hec-panels\"\u003e\n  \u003cdiv class=\"hec-panel\"\u003e\n    \u003clabel for=\"hec-raw\"\u003ePlain Text / Raw HTML\u003c/label\u003e\n    \u003ctextarea id=\"hec-raw\" placeholder=\"Type or paste text here...\u0026#10;Example: \u003cHello \u0026 \"World\"\u003e\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"hec-panel\"\u003e\n    \u003clabel for=\"hec-enc\"\u003eHTML Entities\u003c/label\u003e\n    \u003ctextarea id=\"hec-enc\" placeholder=\"Encoded output appears here...\u0026#10;Example: \u0026lt;Hello \u0026amp; \u0026quot;World\u0026quot;\u0026gt;\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"hec-controls\"\u003e\n  \u003cbutton class=\"btn-encode\" onclick=\"hecEncode()\"\u003eEncode \u0026rarr;\u003c/button\u003e\n  \u003cbutton class=\"btn-decode\" onclick=\"hecDecode()\"\u003e\u0026larr; Decode\u003c/button\u003e\n  \u003cbutton class=\"btn-copy-raw\" onclick=\"hecCopy('raw')\"\u003eCopy Raw\u003c/button\u003e\n  \u003cbutton class=\"btn-copy-enc\" onclick=\"hecCopy('enc')\"\u003eCopy Encoded\u003c/button\u003e\n  \u003cbutton class=\"btn-clear\" onclick=\"hecClear()\"\u003eClear All\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"hec-ref\"\u003e\n  \u003ch3\u003eCommon HTML Entities Reference\u003c/h3\u003e\n  \u003ctable\u003e\n    \u003cthead\u003e\n      \u003ctr\u003e\n        \u003cth\u003eChar\u003c/th\u003e\n        \u003cth\u003eNamed Entity\u003c/th\u003e\n        \u003cth\u003eNumeric Code\u003c/th\u003e\n        \u003cth\u003eHex Code\u003c/th\u003e\n        \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody id=\"hec-ref-body\"\u003e\u003c/tbody\u003e\n  \u003c/table\u003e\n  \u003cp style=\"font-size:.78rem;color:#888;margin-top:8px\"\u003eClick any entity code to copy it to clipboard.\u003c/p\u003e","title":"HTML Entity Converter - Encode \u0026 Decode HTML"},{"content":"Encode special characters to HTML entities or decode HTML entities back to plain text — instantly in your browser. No data is sent to any server.\nEncode Decode Input (plain text) \u0026#8646; Output (HTML entities) Copy Output Clear Copied! Common HTML Entity Reference Character Named Entity Numeric (Dec) Numeric (Hex) Description No matching entities found. Related Tools Encode URLs for safe use in links and APIs → URL Encoder Convert text between camelCase, snake_case, kebab-case, and more → Case Converter Remove extra whitespace from HTML or code → Whitespace Remover ","permalink":"https://productivity-works.com/tools/html-entity-encoder/","summary":"\u003cp\u003eEncode special characters to HTML entities or decode HTML entities back to plain text — instantly in your browser. No data is sent to any server.\u003c/p\u003e\n\u003cdiv id=\"html-enc-app\"\u003e\n\u003cstyle\u003e\n#html-enc-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #1e293b;\n}\n#html-enc-app *,\n#html-enc-app *::before,\n#html-enc-app *::after {\n  box-sizing: border-box;\n}\n#html-enc-app h2 {\n  font-size: 1.1rem;\n  font-weight: 700;\n  color: #475569;\n  margin: 1.5rem 0 0.5rem;\n  border-left: 4px solid #f97316;\n  padding-left: 0.6rem;\n}\n#html-enc-app .enc-mode-bar {\n  display: flex;\n  gap: 0.5rem;\n  margin-bottom: 1rem;\n  flex-wrap: wrap;\n}\n#html-enc-app .enc-mode-btn {\n  padding: 0.45rem 1.2rem;\n  border: 2px solid #475569;\n  border-radius: 6px;\n  background: #fff;\n  color: #475569;\n  font-size: 0.95rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s;\n}\n#html-enc-app .enc-mode-btn.active {\n  background: #f97316;\n  border-color: #f97316;\n  color: #fff;\n}\n#html-enc-app .enc-mode-btn:hover:not(.active) {\n  background: #f1f5f9;\n}\n#html-enc-app .enc-textarea-row {\n  display: grid;\n  grid-template-columns: 1fr auto 1fr;\n  gap: 0.75rem;\n  align-items: start;\n}\n@media (max-width: 600px) {\n  #html-enc-app .enc-textarea-row {\n    grid-template-columns: 1fr;\n  }\n  #html-enc-app .enc-swap-col {\n    display: flex;\n    justify-content: center;\n  }\n}\n#html-enc-app .enc-col label {\n  display: block;\n  font-weight: 600;\n  color: #475569;\n  margin-bottom: 0.3rem;\n  font-size: 0.92rem;\n}\n#html-enc-app textarea {\n  width: 100%;\n  min-height: 180px;\n  padding: 0.75rem;\n  border: 2px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 0.93rem;\n  font-family: 'Courier New', monospace;\n  resize: vertical;\n  outline: none;\n  transition: border-color 0.15s;\n  color: #1e293b;\n  background: #f8fafc;\n}\n#html-enc-app textarea:focus {\n  border-color: #f97316;\n  background: #fff;\n}\n#html-enc-app textarea[readonly] {\n  background: #f1f5f9;\n  color: #475569;\n  cursor: default;\n}\n#html-enc-app .enc-swap-col {\n  display: flex;\n  align-items: center;\n  padding-top: 1.6rem;\n}\n#html-enc-app .enc-swap-btn {\n  background: #fff;\n  border: 2px solid #475569;\n  border-radius: 50%;\n  width: 40px;\n  height: 40px;\n  cursor: pointer;\n  font-size: 1.1rem;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: #475569;\n  transition: background 0.15s, color 0.15s;\n  flex-shrink: 0;\n}\n#html-enc-app .enc-swap-btn:hover {\n  background: #f97316;\n  border-color: #f97316;\n  color: #fff;\n}\n#html-enc-app .enc-actions {\n  display: flex;\n  gap: 0.5rem;\n  margin-top: 0.6rem;\n  flex-wrap: wrap;\n}\n#html-enc-app .enc-btn {\n  padding: 0.4rem 1rem;\n  border-radius: 6px;\n  font-size: 0.88rem;\n  font-weight: 600;\n  cursor: pointer;\n  border: 2px solid #f97316;\n  background: #f97316;\n  color: #fff;\n  transition: opacity 0.15s;\n}\n#html-enc-app .enc-btn:hover {\n  opacity: 0.85;\n}\n#html-enc-app .enc-btn.secondary {\n  background: #fff;\n  color: #475569;\n  border-color: #475569;\n}\n#html-enc-app .enc-btn.secondary:hover {\n  background: #f1f5f9;\n  opacity: 1;\n}\n#html-enc-app .enc-copy-msg {\n  font-size: 0.82rem;\n  color: #16a34a;\n  align-self: center;\n  display: none;\n}\n#html-enc-app .enc-copy-msg.visible {\n  display: inline;\n}\n/* Entity table */\n#html-enc-app .enc-search-row {\n  display: flex;\n  gap: 0.5rem;\n  margin-bottom: 0.75rem;\n  align-items: center;\n}\n#html-enc-app .enc-search {\n  flex: 1;\n  padding: 0.45rem 0.75rem;\n  border: 2px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 0.92rem;\n  outline: none;\n  transition: border-color 0.15s;\n}\n#html-enc-app .enc-search:focus {\n  border-color: #f97316;\n}\n#html-enc-app .enc-table-wrap {\n  overflow-x: auto;\n  border-radius: 8px;\n  border: 1px solid #e2e8f0;\n}\n#html-enc-app table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.9rem;\n}\n#html-enc-app thead th {\n  background: #475569;\n  color: #fff;\n  padding: 0.5rem 0.75rem;\n  text-align: left;\n  font-weight: 600;\n  white-space: nowrap;\n}\n#html-enc-app tbody tr:nth-child(even) {\n  background: #f8fafc;\n}\n#html-enc-app tbody tr:hover {\n  background: #fff7ed;\n}\n#html-enc-app td {\n  padding: 0.45rem 0.75rem;\n  border-top: 1px solid #e2e8f0;\n  font-family: 'Courier New', monospace;\n  vertical-align: middle;\n}\n#html-enc-app td.render {\n  font-family: system-ui, sans-serif;\n  font-size: 1.1rem;\n  text-align: center;\n}\n#html-enc-app .enc-use-btn {\n  padding: 0.25rem 0.65rem;\n  font-size: 0.8rem;\n  border: 1px solid #f97316;\n  background: #fff;\n  color: #f97316;\n  border-radius: 5px;\n  cursor: pointer;\n  font-weight: 600;\n  transition: background 0.12s, color 0.12s;\n}\n#html-enc-app .enc-use-btn:hover {\n  background: #f97316;\n  color: #fff;\n}\n#html-enc-app .enc-no-results {\n  padding: 1rem;\n  text-align: center;\n  color: #94a3b8;\n  font-style: italic;\n  display: none;\n}\n\u003c/style\u003e\n\u003cdiv class=\"enc-mode-bar\"\u003e\n  \u003cbutton class=\"enc-mode-btn active\" id=\"enc-btn-encode\" onclick=\"encSetMode('encode')\"\u003eEncode\u003c/button\u003e\n  \u003cbutton class=\"enc-mode-btn\" id=\"enc-btn-decode\" onclick=\"encSetMode('decode')\"\u003eDecode\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"enc-textarea-row\"\u003e\n  \u003cdiv class=\"enc-col\"\u003e\n    \u003clabel for=\"enc-input\" id=\"enc-input-label\"\u003eInput (plain text)\u003c/label\u003e\n    \u003ctextarea id=\"enc-input\" placeholder=\"Paste or type text here...\" oninput=\"encProcess()\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"enc-swap-col\"\u003e\n    \u003cbutton class=\"enc-swap-btn\" title=\"Swap input and output\" onclick=\"encSwap()\"\u003e\u0026#8646;\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"enc-col\"\u003e\n    \u003clabel for=\"enc-output\" id=\"enc-output-label\"\u003eOutput (HTML entities)\u003c/label\u003e\n    \u003ctextarea id=\"enc-output\" readonly placeholder=\"Converted result appears here...\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"enc-actions\"\u003e\n  \u003cbutton class=\"enc-btn\" onclick=\"encCopy()\"\u003eCopy Output\u003c/button\u003e\n  \u003cbutton class=\"enc-btn secondary\" onclick=\"encClear()\"\u003eClear\u003c/button\u003e\n  \u003cspan class=\"enc-copy-msg\" id=\"enc-copy-msg\"\u003eCopied!\u003c/span\u003e\n\u003c/div\u003e\n\u003ch2\u003eCommon HTML Entity Reference\u003c/h2\u003e\n\u003cdiv class=\"enc-search-row\"\u003e\n  \u003cinput class=\"enc-search\" id=\"enc-search\" type=\"text\" placeholder=\"Search by name, entity, or character...\" oninput=\"encFilterTable()\"\u003e\n\u003c/div\u003e\n\u003cdiv class=\"enc-table-wrap\"\u003e\n  \u003ctable id=\"enc-table\"\u003e\n    \u003cthead\u003e\n      \u003ctr\u003e\n        \u003cth\u003eCharacter\u003c/th\u003e\n        \u003cth\u003eNamed Entity\u003c/th\u003e\n        \u003cth\u003eNumeric (Dec)\u003c/th\u003e\n        \u003cth\u003eNumeric (Hex)\u003c/th\u003e\n        \u003cth\u003eDescription\u003c/th\u003e\n        \u003cth\u003e\u003c/th\u003e\n      \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody id=\"enc-tbody\"\u003e\u003c/tbody\u003e\n  \u003c/table\u003e\n  \u003cdiv class=\"enc-no-results\" id=\"enc-no-results\"\u003eNo matching entities found.\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var encMode = 'encode';\n\n  var ENTITIES = [\n    ['\u0026', '\u0026amp;', '\u0026#38;', '\u0026#x26;', 'Ampersand'],\n    ['\u003c', '\u0026lt;', '\u0026#60;', '\u0026#x3C;', 'Less-than sign'],\n    ['\u003e', '\u0026gt;', '\u0026#62;', '\u0026#x3E;', 'Greater-than sign'],\n    ['\"', '\u0026quot;', '\u0026#34;', '\u0026#x22;', 'Quotation mark'],\n    [\"'\", '\u0026apos;', '\u0026#39;', '\u0026#x27;', 'Apostrophe'],\n    [' ', '\u0026nbsp;', '\u0026#160;', '\u0026#xA0;', 'Non-breaking space'],\n    ['©', '\u0026copy;', '\u0026#169;', '\u0026#xA9;', 'Copyright sign'],\n    ['®', '\u0026reg;', '\u0026#174;', '\u0026#xAE;', 'Registered sign'],\n    ['™', '\u0026trade;', '\u0026#8482;', '\u0026#x2122;', 'Trade mark sign'],\n    ['€', '\u0026euro;', '\u0026#8364;', '\u0026#x20AC;', 'Euro sign'],\n    ['£', '\u0026pound;', '\u0026#163;', '\u0026#xA3;', 'Pound sign'],\n    ['¥', '\u0026yen;', '\u0026#165;', '\u0026#xA5;', 'Yen sign'],\n    ['¢', '\u0026cent;', '\u0026#162;', '\u0026#xA2;', 'Cent sign'],\n    ['§', '\u0026sect;', '\u0026#167;', '\u0026#xA7;', 'Section sign'],\n    ['¶', '\u0026para;', '\u0026#182;', '\u0026#xB6;', 'Pilcrow sign'],\n    ['·', '\u0026middot;', '\u0026#183;', '\u0026#xB7;', 'Middle dot'],\n    ['–', '\u0026ndash;', '\u0026#8211;', '\u0026#x2013;', 'En dash'],\n    ['—', '\u0026mdash;', '\u0026#8212;', '\u0026#x2014;', 'Em dash'],\n    ['\\u2018', '\u0026lsquo;', '\u0026#8216;', '\u0026#x2018;', 'Left single quotation'],\n    ['\\u2019', '\u0026rsquo;', '\u0026#8217;', '\u0026#x2019;', 'Right single quotation'],\n    ['\\u201C', '\u0026ldquo;', '\u0026#8220;', '\u0026#x201C;', 'Left double quotation'],\n    ['\\u201D', '\u0026rdquo;', '\u0026#8221;', '\u0026#x201D;', 'Right double quotation'],\n    ['…', '\u0026hellip;', '\u0026#8230;', '\u0026#x2026;', 'Horizontal ellipsis'],\n    ['•', '\u0026bull;', '\u0026#8226;', '\u0026#x2022;', 'Bullet'],\n    ['★', '', '\u0026#9733;', '\u0026#x2605;', 'Black star'],\n    ['♠', '\u0026spades;', '\u0026#9824;', '\u0026#x2660;', 'Black spade suit'],\n    ['♣', '\u0026clubs;', '\u0026#9827;', '\u0026#x2663;', 'Black club suit'],\n    ['♥', '\u0026hearts;', '\u0026#9829;', '\u0026#x2665;', 'Black heart suit'],\n    ['♦', '\u0026diams;', '\u0026#9830;', '\u0026#x2666;', 'Black diamond suit'],\n    ['÷', '\u0026divide;', '\u0026#247;', '\u0026#xF7;', 'Division sign'],\n    ['×', '\u0026times;', '\u0026#215;', '\u0026#xD7;', 'Multiplication sign'],\n    ['±', '\u0026plusmn;', '\u0026#177;', '\u0026#xB1;', 'Plus-minus sign'],\n    ['¼', '\u0026frac14;', '\u0026#188;', '\u0026#xBC;', 'Vulgar fraction one quarter'],\n    ['½', '\u0026frac12;', '\u0026#189;', '\u0026#xBD;', 'Vulgar fraction one half'],\n    ['¾', '\u0026frac34;', '\u0026#190;', '\u0026#xBE;', 'Vulgar fraction three quarters'],\n    ['°', '\u0026deg;', '\u0026#176;', '\u0026#xB0;', 'Degree sign'],\n    ['µ', '\u0026micro;', '\u0026#181;', '\u0026#xB5;', 'Micro sign'],\n    ['∞', '\u0026infin;', '\u0026#8734;', '\u0026#x221E;', 'Infinity'],\n    ['←', '\u0026larr;', '\u0026#8592;', '\u0026#x2190;', 'Leftwards arrow'],\n    ['→', '\u0026rarr;', '\u0026#8594;', '\u0026#x2192;', 'Rightwards arrow'],\n    ['↑', '\u0026uarr;', '\u0026#8593;', '\u0026#x2191;', 'Upwards arrow'],\n    ['↓', '\u0026darr;', '\u0026#8595;', '\u0026#x2193;', 'Downwards arrow'],\n  ];\n\n  function buildTable() {\n    var tbody = document.getElementById('enc-tbody');\n    var html = '';\n    for (var i = 0; i \u003c ENTITIES.length; i++) {\n      var e = ENTITIES[i];\n      html += '\u003ctr data-search=\"' + escAttr((e[0] + ' ' + e[1] + ' ' + e[2] + ' ' + e[3] + ' ' + e[4]).toLowerCase()) + '\"\u003e';\n      html += '\u003ctd class=\"render\"\u003e' + escHtml(e[0]) + '\u003c/td\u003e';\n      html += '\u003ctd\u003e' + (e[1] ? escHtml(e[1]) : '\u003cspan style=\"color:#94a3b8\"\u003e—\u003c/span\u003e') + '\u003c/td\u003e';\n      html += '\u003ctd\u003e' + escHtml(e[2]) + '\u003c/td\u003e';\n      html += '\u003ctd\u003e' + escHtml(e[3]) + '\u003c/td\u003e';\n      html += '\u003ctd style=\"font-family:system-ui,sans-serif;font-size:0.88rem\"\u003e' + escHtml(e[4]) + '\u003c/td\u003e';\n      html += '\u003ctd\u003e\u003cbutton class=\"enc-use-btn\" onclick=\"encUseEntity(' + i + ')\"\u003eInsert\u003c/button\u003e\u003c/td\u003e';\n      html += '\u003c/tr\u003e';\n    }\n    tbody.innerHTML = html;\n  }\n\n  function escHtml(str) {\n    return str.replace(/\u0026/g, '\u0026amp;').replace(/\u003c/g, '\u0026lt;').replace(/\u003e/g, '\u0026gt;').replace(/\"/g, '\u0026quot;');\n  }\n\n  function escAttr(str) {\n    return str.replace(/\"/g, '\u0026quot;');\n  }\n\n  window.encSetMode = function(mode) {\n    encMode = mode;\n    document.getElementById('enc-btn-encode').classList.toggle('active', mode === 'encode');\n    document.getElementById('enc-btn-decode').classList.toggle('active', mode === 'decode');\n    document.getElementById('enc-input-label').textContent = mode === 'encode' ? 'Input (plain text)' : 'Input (HTML entities)';\n    document.getElementById('enc-output-label').textContent = mode === 'encode' ? 'Output (HTML entities)' : 'Output (plain text)';\n    document.getElementById('enc-input').placeholder = mode === 'encode' ? 'Paste or type text here...' : 'Paste HTML with entities here...';\n    document.getElementById('enc-output').placeholder = mode === 'encode' ? 'HTML entities appear here...' : 'Decoded text appears here...';\n    encProcess();\n  };\n\n  window.encProcess = function() {\n    var input = document.getElementById('enc-input').value;\n    var output = '';\n    if (encMode === 'encode') {\n      output = encodeEntities(input);\n    } else {\n      output = decodeEntities(input);\n    }\n    document.getElementById('enc-output').value = output;\n  };\n\n  function encodeEntities(str) {\n    var result = '';\n    for (var i = 0; i \u003c str.length; i++) {\n      var cp = str.codePointAt(i);\n      var ch = str[i];\n      if (cp \u003e 0xFFFF) i++; // surrogate pair\n      if (cp === 38) { result += '\u0026amp;'; }\n      else if (cp === 60) { result += '\u0026lt;'; }\n      else if (cp === 62) { result += '\u0026gt;'; }\n      else if (cp === 34) { result += '\u0026quot;'; }\n      else if (cp === 39) { result += '\u0026apos;'; }\n      else if (cp \u003e 127) { result += '\u0026#' + cp + ';'; }\n      else { result += ch; }\n    }\n    return result;\n  }\n\n  function decodeEntities(str) {\n    var ta = document.createElement('textarea');\n    ta.innerHTML = str;\n    return ta.value;\n  }\n\n  window.encSwap = function() {\n    var inp = document.getElementById('enc-input');\n    var out = document.getElementById('enc-output');\n    var tmp = inp.value;\n    inp.value = out.value;\n    out.removeAttribute('readonly');\n    out.value = tmp;\n    out.setAttribute('readonly', '');\n    encProcess();\n  };\n\n  window.encCopy = function() {\n    var out = document.getElementById('enc-output').value;\n    if (!out) return;\n    if (navigator.clipboard) {\n      navigator.clipboard.writeText(out).then(showCopied);\n    } else {\n      var ta = document.createElement('textarea');\n      ta.value = out;\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      showCopied();\n    }\n  };\n\n  function showCopied() {\n    var msg = document.getElementById('enc-copy-msg');\n    msg.classList.add('visible');\n    setTimeout(function() { msg.classList.remove('visible'); }, 2000);\n  }\n\n  window.encClear = function() {\n    document.getElementById('enc-input').value = '';\n    document.getElementById('enc-output').value = '';\n  };\n\n  window.encFilterTable = function() {\n    var q = document.getElementById('enc-search').value.toLowerCase();\n    var rows = document.querySelectorAll('#enc-tbody tr');\n    var any = false;\n    rows.forEach(function(row) {\n      var match = !q || row.dataset.search.indexOf(q) !== -1;\n      row.style.display = match ? '' : 'none';\n      if (match) any = true;\n    });\n    document.getElementById('enc-no-results').style.display = any ? 'none' : 'block';\n  };\n\n  window.encUseEntity = function(idx) {\n    var entity = ENTITIES[idx][1] || ENTITIES[idx][2];\n    var inp = document.getElementById('enc-input');\n    var start = inp.selectionStart;\n    var end = inp.selectionEnd;\n    inp.value = inp.value.slice(0, start) + entity + inp.value.slice(end);\n    inp.setSelectionRange(start + entity.length, start + entity.length);\n    inp.focus();\n    encProcess();\n  };\n\n  buildTable();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003eEncode URLs for safe use in links and APIs → \u003ca href=\"https://productivity-works.com/tools/url-encoder/\"\u003eURL Encoder\u003c/a\u003e\n\u003c/p\u003e","title":"HTML Entity Encoder / Decoder"},{"content":"Build HTML tables visually — no coding required. Click any cell to edit, add or remove rows and columns, choose styles, import CSV data, and copy the finished HTML with one click.\nEdit Grid + Add Row − Remove Last Row + Add Column − Remove Last Column Clear All Header Row Borders Striped Rows Hover Effect Size: Normal Compact Spacious CSS Preset: Plain HTML Bootstrap-style Tailwind-style Merge Cells Merge Selected Cells Unmerge Click a cell to select, Shift+click to extend selection Import CSV / TSV Import CSV Import TSV Live Preview Export HTML Code CSV Markdown Copy Download HTML Download CSV Download .md Copied! Related Tools Generate markdown tables → Markdown Table Generator Convert JSON to CSV → JSON to CSV Converter Format SQL queries → SQL Formatter ","permalink":"https://productivity-works.com/tools/html-table-generator/","summary":"\u003cp\u003eBuild HTML tables visually — no coding required. Click any cell to edit, add or remove rows and columns, choose styles, import CSV data, and copy the finished HTML with one click.\u003c/p\u003e\n\u003cdiv id=\"ht-app\"\u003e\n\u003cstyle\u003e\n/* ── Scope: all styles are prefixed with #ht-app ── */\n#ht-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  font-size: 14px;\n  color: #1a1a1a;\n  max-width: 100%;\n}\n#ht-app * { box-sizing: border-box; }\n\n/* Toolbar */\n#ht-app .ht-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 6px;\n  margin-bottom: 12px;\n}\n#ht-app .ht-btn {\n  padding: 6px 12px;\n  border: 1px solid #d1d5db;\n  border-radius: 6px;\n  background: #fff;\n  cursor: pointer;\n  font-size: 13px;\n  transition: background 0.15s, border-color 0.15s;\n  white-space: nowrap;\n}\n#ht-app .ht-btn:hover { background: #f3f4f6; border-color: #9ca3af; }\n#ht-app .ht-btn.ht-btn-primary {\n  background: #2563eb; color: #fff; border-color: #2563eb;\n}\n#ht-app .ht-btn.ht-btn-primary:hover { background: #1d4ed8; border-color: #1d4ed8; }\n#ht-app .ht-btn.ht-btn-danger {\n  background: #fff; color: #dc2626; border-color: #fca5a5;\n}\n#ht-app .ht-btn.ht-btn-danger:hover { background: #fef2f2; }\n#ht-app .ht-btn.ht-active {\n  background: #eff6ff; border-color: #2563eb; color: #2563eb;\n}\n\n/* Section labels */\n#ht-app .ht-section {\n  margin-bottom: 12px;\n}\n#ht-app .ht-section-title {\n  font-size: 11px;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  color: #6b7280;\n  margin-bottom: 6px;\n}\n\n/* Options row */\n#ht-app .ht-options {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  align-items: center;\n  margin-bottom: 12px;\n  padding: 10px 12px;\n  background: #f9fafb;\n  border: 1px solid #e5e7eb;\n  border-radius: 8px;\n}\n#ht-app .ht-option-group {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 13px;\n}\n#ht-app .ht-option-group label { cursor: pointer; user-select: none; }\n#ht-app .ht-select {\n  padding: 4px 8px;\n  border: 1px solid #d1d5db;\n  border-radius: 5px;\n  background: #fff;\n  font-size: 13px;\n  cursor: pointer;\n}\n\n/* Spreadsheet grid */\n#ht-app .ht-grid-wrap {\n  overflow-x: auto;\n  margin-bottom: 14px;\n  border: 1px solid #d1d5db;\n  border-radius: 8px;\n}\n#ht-app .ht-grid {\n  border-collapse: collapse;\n  min-width: 100%;\n}\n#ht-app .ht-grid th,\n#ht-app .ht-grid td {\n  border: 1px solid #d1d5db;\n  padding: 0;\n  min-width: 80px;\n  max-width: 240px;\n  position: relative;\n}\n#ht-app .ht-grid .ht-row-ctrl th,\n#ht-app .ht-grid .ht-row-ctrl td {\n  background: #f3f4f6;\n  text-align: center;\n  font-size: 11px;\n  color: #6b7280;\n  padding: 2px 4px;\n  cursor: pointer;\n  min-width: 28px;\n  width: 28px;\n  white-space: nowrap;\n}\n#ht-app .ht-grid .ht-row-ctrl td:hover { background: #fef2f2; color: #dc2626; }\n#ht-app .ht-grid .ht-col-ctrl td {\n  background: #f3f4f6;\n  text-align: center;\n  font-size: 11px;\n  color: #6b7280;\n  padding: 4px 2px;\n  cursor: pointer;\n  white-space: nowrap;\n}\n#ht-app .ht-grid .ht-col-ctrl td:hover { background: #fef2f2; color: #dc2626; }\n\n#ht-app .ht-cell-input {\n  width: 100%;\n  min-height: 32px;\n  padding: 6px 8px;\n  border: none;\n  outline: none;\n  background: transparent;\n  font-size: 13px;\n  font-family: inherit;\n  resize: none;\n  display: block;\n}\n#ht-app .ht-cell-input:focus {\n  background: #eff6ff;\n  outline: 2px solid #2563eb;\n  outline-offset: -2px;\n  border-radius: 2px;\n}\n#ht-app .ht-header-cell .ht-cell-input {\n  font-weight: 600;\n  background: #f0f9ff;\n}\n#ht-app .ht-header-cell { background: #e0f2fe; }\n\n/* Merge controls */\n#ht-app .ht-cell-selected {\n  outline: 2px solid #f59e0b !important;\n  outline-offset: -2px;\n}\n\n/* Import area */\n#ht-app .ht-import-area {\n  width: 100%;\n  height: 80px;\n  padding: 8px;\n  border: 1px solid #d1d5db;\n  border-radius: 6px;\n  font-size: 12px;\n  font-family: monospace;\n  resize: vertical;\n  background: #fff;\n}\n#ht-app .ht-import-area:focus { outline: 2px solid #2563eb; border-color: transparent; }\n\n/* Preview area */\n#ht-app .ht-preview-wrap {\n  overflow-x: auto;\n  border: 1px solid #d1d5db;\n  border-radius: 8px;\n  padding: 16px;\n  background: #fff;\n  margin-bottom: 14px;\n}\n/* Preview table styles injected by JS */\n\n/* Output code */\n#ht-app .ht-code-wrap {\n  position: relative;\n  margin-bottom: 8px;\n}\n#ht-app .ht-code-output {\n  width: 100%;\n  height: 160px;\n  padding: 10px;\n  font-family: 'Courier New', monospace;\n  font-size: 12px;\n  border: 1px solid #d1d5db;\n  border-radius: 6px;\n  background: #1e1e2e;\n  color: #cdd6f4;\n  resize: vertical;\n}\n#ht-app .ht-copy-btn {\n  position: absolute;\n  top: 8px;\n  right: 8px;\n  padding: 4px 10px;\n  font-size: 12px;\n  background: #374151;\n  color: #f3f4f6;\n  border: 1px solid #4b5563;\n  border-radius: 4px;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n#ht-app .ht-copy-btn:hover { background: #4b5563; }\n#ht-app .ht-copy-btn.ht-copied { background: #16a34a; border-color: #16a34a; }\n\n/* Tabs */\n#ht-app .ht-tabs {\n  display: flex;\n  gap: 0;\n  border-bottom: 2px solid #e5e7eb;\n  margin-bottom: 10px;\n}\n#ht-app .ht-tab {\n  padding: 7px 16px;\n  font-size: 13px;\n  cursor: pointer;\n  border: none;\n  background: none;\n  color: #6b7280;\n  border-bottom: 2px solid transparent;\n  margin-bottom: -2px;\n  transition: color 0.15s;\n}\n#ht-app .ht-tab:hover { color: #111; }\n#ht-app .ht-tab.ht-tab-active { color: #2563eb; border-bottom-color: #2563eb; font-weight: 600; }\n\n/* Toast */\n#ht-app .ht-toast {\n  display: none;\n  position: fixed;\n  bottom: 24px;\n  left: 50%;\n  transform: translateX(-50%);\n  background: #111827;\n  color: #f9fafb;\n  padding: 10px 20px;\n  border-radius: 8px;\n  font-size: 13px;\n  z-index: 9999;\n  box-shadow: 0 4px 12px rgba(0,0,0,0.3);\n  pointer-events: none;\n}\n#ht-app .ht-toast.ht-show { display: block; }\n\n/* Divider */\n#ht-app .ht-divider {\n  border: none;\n  border-top: 1px solid #e5e7eb;\n  margin: 14px 0;\n}\n\n/* Responsive */\n@media (max-width: 600px) {\n  #ht-app .ht-btn { padding: 5px 9px; font-size: 12px; }\n  #ht-app .ht-options { gap: 8px; }\n  #ht-app .ht-cell-input { font-size: 12px; min-height: 28px; padding: 4px 6px; }\n}\n\u003c/style\u003e\n\u003c!-- ── Toolbar ── --\u003e\n\u003cdiv class=\"ht-section\"\u003e\n  \u003cdiv class=\"ht-section-title\"\u003eEdit Grid\u003c/div\u003e\n  \u003cdiv class=\"ht-toolbar\"\u003e\n    \u003cbutton class=\"ht-btn\" id=\"ht-add-row\"\u003e+ Add Row\u003c/button\u003e\n    \u003cbutton class=\"ht-btn ht-btn-danger\" id=\"ht-del-row\"\u003e− Remove Last Row\u003c/button\u003e\n    \u003cbutton class=\"ht-btn\" id=\"ht-add-col\"\u003e+ Add Column\u003c/button\u003e\n    \u003cbutton class=\"ht-btn ht-btn-danger\" id=\"ht-del-col\"\u003e− Remove Last Column\u003c/button\u003e\n    \u003cbutton class=\"ht-btn\" id=\"ht-clear-all\"\u003eClear All\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ── Options ── --\u003e\n\u003cdiv class=\"ht-options\"\u003e\n  \u003cdiv class=\"ht-option-group\"\u003e\n    \u003cinput type=\"checkbox\" id=\"ht-opt-header\" checked\u003e\n    \u003clabel for=\"ht-opt-header\"\u003eHeader Row\u003c/label\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ht-option-group\"\u003e\n    \u003cinput type=\"checkbox\" id=\"ht-opt-border\" checked\u003e\n    \u003clabel for=\"ht-opt-border\"\u003eBorders\u003c/label\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ht-option-group\"\u003e\n    \u003cinput type=\"checkbox\" id=\"ht-opt-striped\"\u003e\n    \u003clabel for=\"ht-opt-striped\"\u003eStriped Rows\u003c/label\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ht-option-group\"\u003e\n    \u003cinput type=\"checkbox\" id=\"ht-opt-hover\"\u003e\n    \u003clabel for=\"ht-opt-hover\"\u003eHover Effect\u003c/label\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ht-option-group\"\u003e\n    \u003clabel for=\"ht-opt-size\"\u003eSize:\u003c/label\u003e\n    \u003cselect class=\"ht-select\" id=\"ht-opt-size\"\u003e\n      \u003coption value=\"normal\"\u003eNormal\u003c/option\u003e\n      \u003coption value=\"compact\"\u003eCompact\u003c/option\u003e\n      \u003coption value=\"spacious\"\u003eSpacious\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ht-option-group\"\u003e\n    \u003clabel for=\"ht-opt-preset\"\u003eCSS Preset:\u003c/label\u003e\n    \u003cselect class=\"ht-select\" id=\"ht-opt-preset\"\u003e\n      \u003coption value=\"plain\"\u003ePlain HTML\u003c/option\u003e\n      \u003coption value=\"bootstrap\"\u003eBootstrap-style\u003c/option\u003e\n      \u003coption value=\"tailwind\"\u003eTailwind-style\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ── Grid ── --\u003e\n\u003cdiv class=\"ht-grid-wrap\"\u003e\n  \u003ctable class=\"ht-grid\" id=\"ht-grid\"\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c!-- ── Merge cells ── --\u003e\n\u003cdiv class=\"ht-section\"\u003e\n  \u003cdiv class=\"ht-section-title\"\u003eMerge Cells\u003c/div\u003e\n  \u003cdiv class=\"ht-toolbar\"\u003e\n    \u003cbutton class=\"ht-btn\" id=\"ht-merge-sel\"\u003eMerge Selected Cells\u003c/button\u003e\n    \u003cbutton class=\"ht-btn\" id=\"ht-unmerge-sel\"\u003eUnmerge\u003c/button\u003e\n    \u003cspan style=\"font-size:12px;color:#6b7280;align-self:center;\"\u003eClick a cell to select, Shift+click to extend selection\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003chr class=\"ht-divider\"\u003e\n\u003c!-- ── Import ── --\u003e\n\u003cdiv class=\"ht-section\"\u003e\n  \u003cdiv class=\"ht-section-title\"\u003eImport CSV / TSV\u003c/div\u003e\n  \u003ctextarea class=\"ht-import-area\" id=\"ht-import-txt\" placeholder=\"Paste CSV or TSV data here, then click Import…\u0026#10;name,age,city\u0026#10;Alice,30,New York\u0026#10;Bob,25,London\"\u003e\u003c/textarea\u003e\n  \u003cdiv class=\"ht-toolbar\" style=\"margin-top:6px;\"\u003e\n    \u003cbutton class=\"ht-btn ht-btn-primary\" id=\"ht-import-csv\"\u003eImport CSV\u003c/button\u003e\n    \u003cbutton class=\"ht-btn\" id=\"ht-import-tsv\"\u003eImport TSV\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003chr class=\"ht-divider\"\u003e\n\u003c!-- ── Preview ── --\u003e\n\u003cdiv class=\"ht-section\"\u003e\n  \u003cdiv class=\"ht-section-title\"\u003eLive Preview\u003c/div\u003e\n  \u003cdiv class=\"ht-preview-wrap\" id=\"ht-preview-wrap\"\u003e\n    \u003ctable id=\"ht-preview\"\u003e\u003c/table\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ── Export ── --\u003e\n\u003cdiv class=\"ht-section\"\u003e\n  \u003cdiv class=\"ht-section-title\"\u003eExport\u003c/div\u003e\n  \u003cdiv class=\"ht-tabs\"\u003e\n    \u003cbutton class=\"ht-tab ht-tab-active\" data-tab=\"html\"\u003eHTML Code\u003c/button\u003e\n    \u003cbutton class=\"ht-tab\" data-tab=\"csv\"\u003eCSV\u003c/button\u003e\n    \u003cbutton class=\"ht-tab\" data-tab=\"md\"\u003eMarkdown\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ht-code-wrap\"\u003e\n    \u003ctextarea class=\"ht-code-output\" id=\"ht-code-output\" readonly\u003e\u003c/textarea\u003e\n    \u003cbutton class=\"ht-copy-btn\" id=\"ht-copy-btn\"\u003eCopy\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ht-toolbar\"\u003e\n    \u003cbutton class=\"ht-btn ht-btn-primary\" id=\"ht-dl-html\"\u003eDownload HTML\u003c/button\u003e\n    \u003cbutton class=\"ht-btn\" id=\"ht-dl-csv\"\u003eDownload CSV\u003c/button\u003e\n    \u003cbutton class=\"ht-btn\" id=\"ht-dl-md\"\u003eDownload .md\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ht-toast\" id=\"ht-toast\"\u003eCopied!\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  'use strict';\n\n  /* ── State ── */\n  const DEFAULT_ROWS = 4;\n  const DEFAULT_COLS = 4;\n\n  let data = [];          // data[r][c] = { text, colspan, rowspan, hidden }\n  let merges = [];        // { r, c, rowspan, colspan }\n  let selection = [];     // [{r,c}]\n  let activeTab = 'html';\n\n  /* ── DOM refs ── */\n  const grid        = document.getElementById('ht-grid');\n  const preview     = document.getElementById('ht-preview');\n  const codeOutput  = document.getElementById('ht-code-output');\n  const toast       = document.getElementById('ht-toast');\n  const importTxt   = document.getElementById('ht-import-txt');\n\n  const optHeader   = document.getElementById('ht-opt-header');\n  const optBorder   = document.getElementById('ht-opt-border');\n  const optStriped  = document.getElementById('ht-opt-striped');\n  const optHover    = document.getElementById('ht-opt-hover');\n  const optSize     = document.getElementById('ht-opt-size');\n  const optPreset   = document.getElementById('ht-opt-preset');\n\n  /* ── Init data ── */\n  function makeCell(text) { return { text: text || '', colspan: 1, rowspan: 1, hidden: false }; }\n\n  function initData(rows, cols) {\n    data = [];\n    merges = [];\n    selection = [];\n    for (let r = 0; r \u003c rows; r++) {\n      data[r] = [];\n      for (let c = 0; c \u003c cols; c++) {\n        data[r][c] = makeCell('');\n      }\n    }\n  }\n\n  function rows() { return data.length; }\n  function cols() { return data[0] ? data[0].length : 0; }\n\n  /* ── Render editable grid ── */\n  function renderGrid() {\n    grid.innerHTML = '';\n\n    /* Column control row */\n    const ctrlRow = grid.insertRow();\n    ctrlRow.className = 'ht-col-ctrl';\n    const cornerTd = ctrlRow.insertCell();\n    cornerTd.style.width = '28px';\n    for (let c = 0; c \u003c cols(); c++) {\n      const td = ctrlRow.insertCell();\n      td.textContent = '✕ Col ' + (c + 1);\n      td.dataset.col = c;\n      td.addEventListener('click', function () { removeCol(+this.dataset.col); });\n    }\n\n    /* Data rows */\n    for (let r = 0; r \u003c rows(); r++) {\n      const tr = grid.insertRow();\n      /* Row control */\n      const rowCtrl = document.createElement('th');\n      rowCtrl.className = 'ht-row-ctrl-cell';\n      rowCtrl.textContent = '✕ ' + (r + 1);\n      rowCtrl.dataset.row = r;\n      rowCtrl.style.cssText = 'background:#f3f4f6;text-align:center;font-size:11px;color:#6b7280;padding:2px 4px;cursor:pointer;min-width:28px;width:28px;';\n      rowCtrl.addEventListener('click', function () { removeRow(+this.dataset.row); });\n      tr.appendChild(rowCtrl);\n\n      for (let c = 0; c \u003c cols(); c++) {\n        const cell = data[r][c];\n        if (cell.hidden) continue;\n\n        const isHeader = optHeader.checked \u0026\u0026 r === 0;\n        const td = document.createElement(isHeader ? 'th' : 'td');\n        if (isHeader) td.className = 'ht-header-cell';\n        if (cell.colspan \u003e 1) td.colSpan = cell.colspan;\n        if (cell.rowspan \u003e 1) td.rowSpan = cell.rowspan;\n\n        /* Selection highlight */\n        if (selection.some(s =\u003e s.r === r \u0026\u0026 s.c === c)) {\n          td.classList.add('ht-cell-selected');\n        }\n\n        const inp = document.createElement('textarea');\n        inp.className = 'ht-cell-input';\n        inp.value = cell.text;\n        inp.rows = 1;\n        inp.dataset.r = r;\n        inp.dataset.c = c;\n        inp.setAttribute('aria-label', 'Cell R' + (r+1) + 'C' + (c+1));\n\n        inp.addEventListener('input', function () {\n          data[this.dataset.r][this.dataset.c].text = this.value;\n          updateOutputs();\n        });\n        inp.addEventListener('click', function (e) {\n          const rr = +this.dataset.r, cc = +this.dataset.c;\n          if (e.shiftKey \u0026\u0026 selection.length \u003e 0) {\n            const last = selection[selection.length - 1];\n            const r0 = Math.min(last.r, rr), r1 = Math.max(last.r, rr);\n            const c0 = Math.min(last.c, cc), c1 = Math.max(last.c, cc);\n            selection = [];\n            for (let ri = r0; ri \u003c= r1; ri++) for (let ci = c0; ci \u003c= c1; ci++) selection.push({r:ri,c:ci});\n          } else {\n            selection = [{r: rr, c: cc}];\n          }\n          renderGrid();\n        });\n\n        td.appendChild(inp);\n        tr.appendChild(td);\n      }\n    }\n  }\n\n  /* ── Row / Col manipulation ── */\n  function addRow() {\n    const row = [];\n    for (let c = 0; c \u003c cols(); c++) row.push(makeCell(''));\n    data.push(row);\n    renderGrid(); updateOutputs();\n  }\n  function removeRow(r) {\n    if (rows() \u003c= 1) return;\n    data.splice(r, 1);\n    /* fix merges */\n    merges = merges.filter(m =\u003e m.r !== r).map(m =\u003e ({ ...m, r: m.r \u003e r ? m.r - 1 : m.r }));\n    selection = [];\n    renderGrid(); updateOutputs();\n  }\n  function addCol() {\n    for (let r = 0; r \u003c rows(); r++) data[r].push(makeCell(''));\n    renderGrid(); updateOutputs();\n  }\n  function removeCol(c) {\n    if (cols() \u003c= 1) return;\n    for (let r = 0; r \u003c rows(); r++) data[r].splice(c, 1);\n    merges = merges.filter(m =\u003e m.c !== c).map(m =\u003e ({ ...m, c: m.c \u003e c ? m.c - 1 : m.c }));\n    selection = [];\n    renderGrid(); updateOutputs();\n  }\n\n  /* ── Merge / Unmerge ── */\n  function mergeCells() {\n    if (selection.length \u003c 2) return;\n    const rs = selection.map(s =\u003e s.r), cs = selection.map(s =\u003e s.c);\n    const r0 = Math.min(...rs), r1 = Math.max(...rs);\n    const c0 = Math.min(...cs), c1 = Math.max(...cs);\n    const colspan = c1 - c0 + 1, rowspan = r1 - r0 + 1;\n    /* collect text */\n    let combined = '';\n    for (let r = r0; r \u003c= r1; r++) for (let c = c0; c \u003c= c1; c++) {\n      if (data[r][c].text) combined += (combined ? ' ' : '') + data[r][c].text;\n    }\n    /* reset all selected cells */\n    for (let r = r0; r \u003c= r1; r++) for (let c = c0; c \u003c= c1; c++) {\n      data[r][c] = makeCell('');\n      data[r][c].hidden = !(r === r0 \u0026\u0026 c === c0);\n    }\n    data[r0][c0].text = combined;\n    data[r0][c0].colspan = colspan;\n    data[r0][c0].rowspan = rowspan;\n    data[r0][c0].hidden = false;\n    selection = [];\n    renderGrid(); updateOutputs();\n  }\n\n  function unmergeCells() {\n    if (selection.length === 0) return;\n    selection.forEach(({ r, c }) =\u003e {\n      const cell = data[r][c];\n      const rs = cell.rowspan, cs2 = cell.colspan;\n      for (let ri = r; ri \u003c r + rs; ri++) for (let ci = c; ci \u003c c + cs2; ci++) {\n        data[ri][ci] = makeCell(ri === r \u0026\u0026 ci === c ? cell.text : '');\n      }\n    });\n    selection = [];\n    renderGrid(); updateOutputs();\n  }\n\n  /* ── Build preview HTML ── */\n  function buildTableClasses() {\n    const preset = optPreset.value;\n    const size   = optSize.value;\n    const cls = [];\n    if (preset === 'bootstrap') {\n      cls.push('table');\n      if (optBorder.checked)  cls.push('table-bordered');\n      if (optStriped.checked) cls.push('table-striped');\n      if (optHover.checked)   cls.push('table-hover');\n      if (size === 'compact') cls.push('table-sm');\n    } else if (preset === 'tailwind') {\n      cls.push('tw-table');\n    } else {\n      cls.push('ht-plain-table');\n    }\n    return cls.join(' ');\n  }\n\n  function buildInlineStyle() {\n    /* For plain / tailwind: inject minimal inline styles */\n    const preset = optPreset.value;\n    if (preset === 'bootstrap') return '';\n    const parts = [];\n    parts.push('border-collapse:collapse;width:100%');\n    return parts.join(';');\n  }\n\n  function buildPreviewStyle() {\n    const preset = optPreset.value;\n    let css = '#ht-preview-wrap .ht-plain-table td, #ht-preview-wrap .ht-plain-table th { padding:' + (optSize.value === 'compact' ? '4px 8px' : optSize.value === 'spacious' ? '14px 20px' : '8px 12px') + '; }';\n    if (optBorder.checked \u0026\u0026 preset !== 'bootstrap') {\n      css += ' #ht-preview-wrap table td, #ht-preview-wrap table th { border:1px solid #d1d5db; }';\n    } else if (preset !== 'bootstrap') {\n      css += ' #ht-preview-wrap table td, #ht-preview-wrap table th { border:none; }';\n    }\n    if (optStriped.checked \u0026\u0026 preset !== 'bootstrap') {\n      css += ' #ht-preview-wrap table tbody tr:nth-child(even) { background:#f9fafb; }';\n    }\n    if (optHover.checked \u0026\u0026 preset !== 'bootstrap') {\n      css += ' #ht-preview-wrap table tbody tr:hover { background:#eff6ff; }';\n    }\n    if (optHeader.checked) {\n      css += ' #ht-preview-wrap table thead th { background:#e0f2fe; font-weight:600; }';\n      if (preset === 'bootstrap') css += ' #ht-preview-wrap table.table thead th { background:#e0f2fe; }';\n    }\n    return css;\n  }\n\n  function renderPreview() {\n    /* remove old style tag */\n    const old = document.getElementById('ht-preview-style');\n    if (old) old.remove();\n    const styleEl = document.createElement('style');\n    styleEl.id = 'ht-preview-style';\n    styleEl.textContent = buildPreviewStyle();\n    document.getElementById('ht-preview-wrap').appendChild(styleEl);\n\n    preview.innerHTML = '';\n    preview.className = buildTableClasses();\n    preview.setAttribute('style', buildInlineStyle());\n\n    const hasHeader = optHeader.checked;\n    if (hasHeader) {\n      const thead = document.createElement('thead');\n      const tr = document.createElement('tr');\n      for (let c = 0; c \u003c cols(); c++) {\n        const cell = data[0][c];\n        if (cell.hidden) continue;\n        const th = document.createElement('th');\n        th.textContent = cell.text;\n        if (cell.colspan \u003e 1) th.colSpan = cell.colspan;\n        if (cell.rowspan \u003e 1) th.rowSpan = cell.rowspan;\n        tr.appendChild(th);\n      }\n      thead.appendChild(tr);\n      preview.appendChild(thead);\n    }\n\n    const tbody = document.createElement('tbody');\n    const startRow = hasHeader ? 1 : 0;\n    for (let r = startRow; r \u003c rows(); r++) {\n      const tr = document.createElement('tr');\n      for (let c = 0; c \u003c cols(); c++) {\n        const cell = data[r][c];\n        if (cell.hidden) continue;\n        const td = document.createElement('td');\n        td.textContent = cell.text;\n        if (cell.colspan \u003e 1) td.colSpan = cell.colspan;\n        if (cell.rowspan \u003e 1) td.rowSpan = cell.rowspan;\n        tr.appendChild(td);\n      }\n      tbody.appendChild(tr);\n    }\n    preview.appendChild(tbody);\n  }\n\n  /* ── Generate outputs ── */\n  function generateHTML() {\n    const preset   = optPreset.value;\n    const hasHeader= optHeader.checked;\n    const tableClass = buildTableClasses();\n    const tableStyle = buildInlineStyle();\n\n    let styleBlock = '';\n    if (preset !== 'bootstrap') {\n      const sizeMap = { compact: '4px 8px', normal: '8px 12px', spacious: '14px 20px' };\n      const pad = sizeMap[optSize.value] || '8px 12px';\n      let css = 'table { border-collapse: collapse; width: 100%; }\\n';\n      css += 'th, td { padding: ' + pad + '; }\\n';\n      if (optBorder.checked) css += 'th, td { border: 1px solid #d1d5db; }\\n';\n      if (optHeader.checked) css += 'thead th { background: #e0f2fe; font-weight: 600; }\\n';\n      if (optStriped.checked) css += 'tbody tr:nth-child(even) { background: #f9fafb; }\\n';\n      if (optHover.checked)   css += 'tbody tr:hover { background: #eff6ff; }\\n';\n      styleBlock = '\u003cstyle\u003e\\n' + css + '\u003c/style\u003e\\n';\n\u003cpre\u003e\u003ccode\u003e}\n\nlet html = styleBlock + '\u0026lt;table';\nif (tableClass) html += ' class=\u0026quot;' + tableClass + '\u0026quot;';\nif (tableStyle \u0026amp;\u0026amp; preset === 'tailwind') html += ' style=\u0026quot;' + tableStyle + '\u0026quot;';\nhtml += '\u0026gt;\\n';\n\nif (hasHeader) {\n  html += '  \u0026lt;thead\u0026gt;\\n    \u0026lt;tr\u0026gt;\\n';\n  for (let c = 0; c \u0026lt; cols(); c++) {\n    const cell = data[0][c];\n    if (cell.hidden) continue;\n    html += '      \u0026lt;th';\n    if (cell.colspan \u0026gt; 1) html += ' colspan=\u0026quot;' + cell.colspan + '\u0026quot;';\n    if (cell.rowspan \u0026gt; 1) html += ' rowspan=\u0026quot;' + cell.rowspan + '\u0026quot;';\n    html += '\u0026gt;' + escapeHtml(cell.text) + '\u0026lt;/th\u0026gt;\\n';\n  }\n  html += '    \u0026lt;/tr\u0026gt;\\n  \u0026lt;/thead\u0026gt;\\n';\n}\n\nhtml += '  \u0026lt;tbody\u0026gt;\\n';\nconst startRow = hasHeader ? 1 : 0;\nfor (let r = startRow; r \u0026lt; rows(); r++) {\n  html += '    \u0026lt;tr\u0026gt;\\n';\n  for (let c = 0; c \u0026lt; cols(); c++) {\n    const cell = data[r][c];\n    if (cell.hidden) continue;\n    html += '      \u0026lt;td';\n    if (cell.colspan \u0026gt; 1) html += ' colspan=\u0026quot;' + cell.colspan + '\u0026quot;';\n    if (cell.rowspan \u0026gt; 1) html += ' rowspan=\u0026quot;' + cell.rowspan + '\u0026quot;';\n    html += '\u0026gt;' + escapeHtml(cell.text) + '\u0026lt;/td\u0026gt;\\n';\n  }\n  html += '    \u0026lt;/tr\u0026gt;\\n';\n}\nhtml += '  \u0026lt;/tbody\u0026gt;\\n\u0026lt;/table\u0026gt;';\nreturn html;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e}\u003c/p\u003e","title":"HTML Table Generator — Free Online Tool"},{"content":"Paste your HTML in the left panel and get clean Markdown output instantly — no sign-up, no server, everything runs in your browser.\nNeed to preview your output? Try the Markdown Preview tool. Need to build a table from scratch? Use the Markdown Table Generator .\n\u0026#128196; Load Sample HTML \u0026#10005; Clear GitHub Flavored Markdown Reference-style links HTML Input0 chars Markdown Output \u0026#128203; Copy Markdown Related Tools Html Beautifier Html Entity Converter Html Entity Encoder ","permalink":"https://productivity-works.com/tools/html-to-markdown/","summary":"\u003cp\u003ePaste your HTML in the left panel and get clean Markdown output instantly — no sign-up, no server, everything runs in your browser.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eNeed to preview your output? Try the \u003ca href=\"https://productivity-works.com/tools/markdown-preview/\"\u003eMarkdown Preview\u003c/a\u003e\n tool.\nNeed to build a table from scratch? Use the \u003ca href=\"https://productivity-works.com/tools/markdown-table-generator/\"\u003eMarkdown Table Generator\u003c/a\u003e\n.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cdiv id=\"hm-app\"\u003e\n\u003cstyle\u003e\n#hm-app *,#hm-app *::before,#hm-app *::after{box-sizing:border-box;margin:0;padding:0}\n#hm-app{font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;font-size:14px;color:#1e293b;line-height:1.5}\n#hm-app .hm-toolbar{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:12px;padding:10px 14px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px}\n#hm-app .hm-btn{display:inline-flex;align-items:center;gap:5px;padding:6px 14px;border:1px solid #cbd5e1;border-radius:6px;background:#fff;color:#334155;font-size:13px;font-weight:500;cursor:pointer;transition:background .15s,border-color .15s}\n#hm-app .hm-btn:hover{background:#f1f5f9;border-color:#94a3b8}\n#hm-app .hm-btn.hm-btn-primary{background:#2563eb;border-color:#2563eb;color:#fff}\n#hm-app .hm-btn.hm-btn-primary:hover{background:#1d4ed8;border-color:#1d4ed8}\n#hm-app .hm-btn.hm-btn-success{background:#16a34a;border-color:#16a34a;color:#fff}\n#hm-app .hm-btn.hm-btn-success:hover{background:#15803d;border-color:#15803d}\n#hm-app .hm-toggles{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-left:auto}\n#hm-app .hm-toggle-label{display:flex;align-items:center;gap:5px;font-size:12px;color:#64748b;cursor:pointer;user-select:none}\n#hm-app .hm-toggle-label input[type=checkbox]{width:14px;height:14px;cursor:pointer;accent-color:#2563eb}\n#hm-app .hm-panels{display:grid;grid-template-columns:1fr 1fr;gap:12px}\n@media(max-width:680px){#hm-app .hm-panels{grid-template-columns:1fr}}\n#hm-app .hm-panel{display:flex;flex-direction:column;gap:6px}\n#hm-app .hm-panel-header{display:flex;justify-content:space-between;align-items:center;font-size:12px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:.04em}\n#hm-app textarea,#hm-app .hm-output{width:100%;min-height:360px;padding:12px;border:1px solid #e2e8f0;border-radius:8px;font-family:\"SFMono-Regular\",Consolas,\"Liberation Mono\",Menlo,monospace;font-size:12.5px;line-height:1.6;resize:vertical;background:#fff;color:#1e293b;transition:border-color .15s}\n#hm-app textarea:focus{outline:none;border-color:#2563eb;box-shadow:0 0 0 3px rgba(37,99,235,.1)}\n#hm-app .hm-output{white-space:pre-wrap;word-break:break-word;background:#f8fafc;overflow:auto;cursor:text}\n#hm-app .hm-status{font-size:11px;color:#94a3b8;text-align:right;margin-top:4px}\n#hm-app .hm-copy-notice{font-size:12px;color:#16a34a;font-weight:600}\n\u003c/style\u003e\n\u003cdiv class=\"hm-toolbar\"\u003e\n  \u003cbutton class=\"hm-btn hm-btn-primary\" id=\"hm-sample-btn\"\u003e\u0026#128196; Load Sample HTML\u003c/button\u003e\n  \u003cbutton class=\"hm-btn\" id=\"hm-clear-btn\"\u003e\u0026#10005; Clear\u003c/button\u003e\n  \u003cdiv class=\"hm-toggles\"\u003e\n    \u003clabel class=\"hm-toggle-label\"\u003e\u003cinput type=\"checkbox\" id=\"hm-gfm\" checked\u003e GitHub Flavored Markdown\u003c/label\u003e\n    \u003clabel class=\"hm-toggle-label\"\u003e\u003cinput type=\"checkbox\" id=\"hm-reflinks\"\u003e Reference-style links\u003c/label\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"hm-panels\"\u003e\n  \u003cdiv class=\"hm-panel\"\u003e\n    \u003cdiv class=\"hm-panel-header\"\u003e\u003cspan\u003eHTML Input\u003c/span\u003e\u003cspan id=\"hm-char-count\"\u003e0 chars\u003c/span\u003e\u003c/div\u003e\n    \u003ctextarea id=\"hm-input\" placeholder=\"Paste your HTML here…\" spellcheck=\"false\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"hm-panel\"\u003e\n    \u003cdiv class=\"hm-panel-header\"\u003e\n      \u003cspan\u003eMarkdown Output\u003c/span\u003e\n      \u003cspan id=\"hm-copy-status\"\u003e\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"hm-output\" id=\"hm-output\" aria-live=\"polite\"\u003e\u003c/div\u003e\n    \u003cdiv style=\"display:flex;justify-content:flex-end;margin-top:6px\"\u003e\n      \u003cbutton class=\"hm-btn hm-btn-success\" id=\"hm-copy-btn\"\u003e\u0026#128203; Copy Markdown\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"hm-status\" id=\"hm-line-count\"\u003e\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  'use strict';\n\n  var input   = document.getElementById('hm-input');\n  var output  = document.getElementById('hm-output');\n  var copyBtn = document.getElementById('hm-copy-btn');\n  var copyStatus = document.getElementById('hm-copy-status');\n  var charCount  = document.getElementById('hm-char-count');\n  var lineCount  = document.getElementById('hm-line-count');\n  var gfmToggle  = document.getElementById('hm-gfm');\n  var refToggle  = document.getElementById('hm-reflinks');\n  var sampleBtn  = document.getElementById('hm-sample-btn');\n  var clearBtn   = document.getElementById('hm-clear-btn');\n\n  var SAMPLE_HTML = [\n    '\u003ch1\u003eWelcome to HTML→Markdown\u003c/h1\u003e',\n    '\u003cp\u003eThis converter handles \u003cstrong\u003ebold\u003c/strong\u003e, \u003cem\u003eitalic\u003c/em\u003e, and \u003ca href=\"https://example.com\"\u003elinks\u003c/a\u003e.\u003c/p\u003e","title":"HTML to Markdown Converter"},{"content":"Browse 50+ HTTP headers with descriptions and examples, build a security header set visually, parse raw HTTP headers instantly, and generate ready-to-use Apache .htaccess and Nginx config snippets.\nReference Security Builder Header Parser Browse and search 50+ HTTP headers grouped by category. Click any header to see its description, common values, and an example. No headers match your search. Select and configure security headers below, then copy the generated snippet for Apache (.htaccess) or Nginx. \u0026lt;!-- CSP --\u0026gt; \u0026lt;div class=\u0026quot;hh-builder-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;hh-builder-sec-head\u0026quot;\u0026gt; Content-Security-Policy \u0026lt;span class=\u0026quot;hh-builder-sec-desc\u0026quot;\u0026gt;— Controls allowed resource origins\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-b-csp\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;hh-b-csp\u0026quot;\u0026gt;Enable CSP header\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-field-group\u0026quot; id=\u0026quot;hh-b-csp-fields\u0026quot; style=\u0026quot;display:none;\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;hh-field-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;hh-field-label\u0026quot;\u0026gt;default-src\u0026lt;/span\u0026gt; \u0026lt;select class=\u0026quot;hh-select\u0026quot; id=\u0026quot;hh-csp-default\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;'self'\u0026quot;\u0026gt;'self'\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;'self' 'unsafe-inline'\u0026quot;\u0026gt;'self' 'unsafe-inline'\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;'none'\u0026quot;\u0026gt;'none'\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;*\u0026quot;\u0026gt;* (any)\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-field-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;hh-field-label\u0026quot;\u0026gt;script-src\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;hh-input\u0026quot; id=\u0026quot;hh-csp-script\u0026quot; placeholder=\u0026quot;'self' (leave blank to inherit)\u0026quot; oninput=\u0026quot;hhBuild()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-field-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;hh-field-label\u0026quot;\u0026gt;style-src\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;hh-input\u0026quot; id=\u0026quot;hh-csp-style\u0026quot; placeholder=\u0026quot;'self' (leave blank to inherit)\u0026quot; oninput=\u0026quot;hhBuild()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-field-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;hh-field-label\u0026quot;\u0026gt;img-src\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;hh-input\u0026quot; id=\u0026quot;hh-csp-img\u0026quot; placeholder=\u0026quot;'self' data: (leave blank to inherit)\u0026quot; oninput=\u0026quot;hhBuild()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-field-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;hh-field-label\u0026quot;\u0026gt;frame-src\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;hh-input\u0026quot; id=\u0026quot;hh-csp-frame\u0026quot; placeholder=\u0026quot;'none' (leave blank to inherit)\u0026quot; oninput=\u0026quot;hhBuild()\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- HSTS --\u0026gt; \u0026lt;div class=\u0026quot;hh-builder-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;hh-builder-sec-head\u0026quot;\u0026gt;Strict-Transport-Security \u0026lt;span class=\u0026quot;hh-builder-sec-desc\u0026quot;\u0026gt;— HTTPS enforcement\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-b-hsts\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; checked\u0026gt; \u0026lt;label for=\u0026quot;hh-b-hsts\u0026quot;\u0026gt;Enable HSTS\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-field-group\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;hh-field-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;hh-field-label\u0026quot;\u0026gt;max-age\u0026lt;/span\u0026gt; \u0026lt;select class=\u0026quot;hh-select\u0026quot; id=\u0026quot;hh-hsts-age\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; style=\u0026quot;max-width:220px;\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;2592000\u0026quot;\u0026gt;30 days (2592000)\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;15552000\u0026quot;\u0026gt;6 months (15552000)\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;31536000\u0026quot; selected\u0026gt;1 year (31536000)\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot; style=\u0026quot;padding:0;border:none;\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-hsts-sub\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; checked\u0026gt; \u0026lt;label for=\u0026quot;hh-hsts-sub\u0026quot;\u0026gt;includeSubDomains\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot; style=\u0026quot;padding:0;border:none;\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-hsts-pre\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;hh-hsts-pre\u0026quot;\u0026gt;preload\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- X-Frame-Options --\u0026gt; \u0026lt;div class=\u0026quot;hh-builder-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;hh-builder-sec-head\u0026quot;\u0026gt;X-Frame-Options \u0026lt;span class=\u0026quot;hh-builder-sec-desc\u0026quot;\u0026gt;— Clickjacking protection\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-b-xframe\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; checked\u0026gt; \u0026lt;label for=\u0026quot;hh-b-xframe\u0026quot;\u0026gt;Enable X-Frame-Options\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-field-group\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;hh-field-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;hh-field-label\u0026quot;\u0026gt;Value\u0026lt;/span\u0026gt; \u0026lt;select class=\u0026quot;hh-select\u0026quot; id=\u0026quot;hh-xframe-val\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; style=\u0026quot;max-width:220px;\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;SAMEORIGIN\u0026quot; selected\u0026gt;SAMEORIGIN\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;DENY\u0026quot;\u0026gt;DENY\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- X-Content-Type-Options --\u0026gt; \u0026lt;div class=\u0026quot;hh-builder-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;hh-builder-sec-head\u0026quot;\u0026gt;X-Content-Type-Options \u0026lt;span class=\u0026quot;hh-builder-sec-desc\u0026quot;\u0026gt;— MIME sniffing prevention\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-b-xcto\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; checked\u0026gt; \u0026lt;label for=\u0026quot;hh-b-xcto\u0026quot;\u0026gt;Enable (always nosniff)\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Referrer-Policy --\u0026gt; \u0026lt;div class=\u0026quot;hh-builder-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;hh-builder-sec-head\u0026quot;\u0026gt;Referrer-Policy \u0026lt;span class=\u0026quot;hh-builder-sec-desc\u0026quot;\u0026gt;— Controls referrer info sent\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-b-rp\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; checked\u0026gt; \u0026lt;label for=\u0026quot;hh-b-rp\u0026quot;\u0026gt;Enable Referrer-Policy\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-field-group\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;hh-field-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;hh-field-label\u0026quot;\u0026gt;Policy\u0026lt;/span\u0026gt; \u0026lt;select class=\u0026quot;hh-select\u0026quot; id=\u0026quot;hh-rp-val\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;no-referrer\u0026quot;\u0026gt;no-referrer\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;no-referrer-when-downgrade\u0026quot;\u0026gt;no-referrer-when-downgrade\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;strict-origin-when-cross-origin\u0026quot; selected\u0026gt;strict-origin-when-cross-origin\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;same-origin\u0026quot;\u0026gt;same-origin\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;origin\u0026quot;\u0026gt;origin\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Permissions-Policy --\u0026gt; \u0026lt;div class=\u0026quot;hh-builder-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;hh-builder-sec-head\u0026quot;\u0026gt;Permissions-Policy \u0026lt;span class=\u0026quot;hh-builder-sec-desc\u0026quot;\u0026gt;— Browser feature control\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-b-pp\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;hh-b-pp\u0026quot;\u0026gt;Enable Permissions-Policy\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-field-group\u0026quot; id=\u0026quot;hh-b-pp-fields\u0026quot; style=\u0026quot;display:none;\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot; style=\u0026quot;padding:0;border:none;\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-pp-cam\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; checked\u0026gt; \u0026lt;label for=\u0026quot;hh-pp-cam\u0026quot;\u0026gt;camera=()\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot; style=\u0026quot;padding:0;border:none;\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-pp-mic\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; checked\u0026gt; \u0026lt;label for=\u0026quot;hh-pp-mic\u0026quot;\u0026gt;microphone=()\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot; style=\u0026quot;padding:0;border:none;\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-pp-geo\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; checked\u0026gt; \u0026lt;label for=\u0026quot;hh-pp-geo\u0026quot;\u0026gt;geolocation=()\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot; style=\u0026quot;padding:0;border:none;\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-pp-pay\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;hh-pp-pay\u0026quot;\u0026gt;payment=()\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot; style=\u0026quot;padding:0;border:none;\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-pp-usb\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;hh-pp-usb\u0026quot;\u0026gt;usb=()\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt;\u0026lt;!-- /.hh-builder-left --\u0026gt; \u0026lt;div class=\u0026quot;hh-builder-right\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;hh-out-box\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;hh-out-topbar\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;hh-out-label\u0026quot;\u0026gt;Generated Config\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;hh-out-actions\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;hh-format-btns\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;hh-format-btn hh-fmt-active\u0026quot; id=\u0026quot;hh-fmt-apache\u0026quot; onclick=\u0026quot;hhSetFmt('apache')\u0026quot;\u0026gt;Apache\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;hh-format-btn\u0026quot; id=\u0026quot;hh-fmt-nginx\u0026quot; onclick=\u0026quot;hhSetFmt('nginx')\u0026quot;\u0026gt;Nginx\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;hh-copy-btn\u0026quot; id=\u0026quot;hh-build-copy\u0026quot; onclick=\u0026quot;hhCopyBuild()\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;pre class=\u0026quot;hh-out-code\u0026quot; id=\u0026quot;hh-build-out\u0026quot;\u0026gt;\u0026lt;/pre\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Paste raw HTTP response headers below and click Parse to get a formatted, explained breakdown of each header. ","permalink":"https://productivity-works.com/tools/http-header-analyzer/","summary":"\u003cp\u003eBrowse 50+ HTTP headers with descriptions and examples, build a security header set visually, parse raw HTTP headers instantly, and generate ready-to-use Apache \u003ccode\u003e.htaccess\u003c/code\u003e and Nginx config snippets.\u003c/p\u003e\n\u003cdiv id=\"hh-app\"\u003e\n\u003cstyle\u003e\n#hh-app *, #hh-app *::before, #hh-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#hh-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n  font-size: 15px;\n  color: #1a1a2e;\n  line-height: 1.6;\n  max-width: 960px;\n  margin: 0 auto;\n}\n\n/* ── Tabs ── */\n#hh-app .hh-tabs {\n  display: flex;\n  gap: 0;\n  border-bottom: 2px solid #e2e8f0;\n  margin-bottom: 24px;\n  flex-wrap: wrap;\n}\n#hh-app .hh-tab {\n  padding: 10px 20px;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  border: none;\n  background: none;\n  color: #64748b;\n  border-bottom: 2px solid transparent;\n  margin-bottom: -2px;\n  transition: color 0.15s, border-color 0.15s;\n  white-space: nowrap;\n}\n#hh-app .hh-tab:hover { color: #3a56d4; }\n#hh-app .hh-tab.hh-active { color: #3a56d4; border-bottom-color: #3a56d4; }\n#hh-app .hh-panel { display: none; }\n#hh-app .hh-panel.hh-active { display: block; }\n\n/* ── Search / filter bar ── */\n#hh-app .hh-search-row {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n  margin-bottom: 18px;\n}\n#hh-app .hh-search {\n  flex: 1 1 240px;\n  padding: 9px 14px;\n  border: 2px solid #d1d5db;\n  border-radius: 8px;\n  font-size: 14px;\n  outline: none;\n  transition: border-color 0.2s;\n  color: #1a1a2e;\n}\n#hh-app .hh-search:focus { border-color: #3a56d4; }\n#hh-app .hh-cat-btns {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 6px;\n}\n#hh-app .hh-cat-btn {\n  padding: 6px 13px;\n  border-radius: 20px;\n  border: 1px solid #cbd5e1;\n  background: #f8fafc;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  color: #475569;\n  transition: background 0.15s, border-color 0.15s, color 0.15s;\n}\n#hh-app .hh-cat-btn:hover { background: #e8f0fe; border-color: #3a56d4; color: #3a56d4; }\n#hh-app .hh-cat-btn.hh-cat-active { background: #3a56d4; border-color: #3a56d4; color: #fff; }\n\n/* ── Header cards ── */\n#hh-app .hh-cards { display: flex; flex-direction: column; gap: 10px; }\n#hh-app .hh-card {\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n  overflow: hidden;\n}\n#hh-app .hh-card-head {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  padding: 11px 16px;\n  background: #f8fafc;\n  cursor: pointer;\n  user-select: none;\n}\n#hh-app .hh-card-head:hover { background: #f0f4ff; }\n#hh-app .hh-card-name {\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  font-size: 13px;\n  font-weight: 700;\n  color: #1e3a8a;\n  flex: 1;\n}\n#hh-app .hh-badge {\n  font-size: 10px;\n  font-weight: 700;\n  padding: 2px 7px;\n  border-radius: 10px;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  white-space: nowrap;\n  flex-shrink: 0;\n}\n#hh-app .hh-badge-req  { background: #dbeafe; color: #1e40af; }\n#hh-app .hh-badge-res  { background: #dcfce7; color: #166534; }\n#hh-app .hh-badge-both { background: #fef3c7; color: #92400e; }\n#hh-app .hh-badge-cat  { background: #f3e8ff; color: #6b21a8; }\n#hh-app .hh-card-chevron { font-size: 10px; color: #94a3b8; flex-shrink: 0; }\n#hh-app .hh-card-body {\n  padding: 14px 16px;\n  border-top: 1px solid #e2e8f0;\n  background: #fff;\n  display: none;\n}\n#hh-app .hh-card-body.hh-open { display: block; }\n#hh-app .hh-card-desc { font-size: 13px; color: #334155; margin-bottom: 10px; }\n#hh-app .hh-card-meta {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 14px;\n  margin-bottom: 10px;\n}\n#hh-app .hh-meta-item { font-size: 12px; color: #64748b; }\n#hh-app .hh-meta-item strong { color: #475569; }\n#hh-app .hh-example-block {\n  background: #0f172a;\n  border-radius: 7px;\n  padding: 10px 14px;\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  font-size: 12px;\n  color: #e2e8f0;\n  white-space: pre-wrap;\n  word-break: break-all;\n}\n#hh-app .hh-example-label {\n  font-size: 11px;\n  color: #94a3b8;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  margin-bottom: 5px;\n  font-weight: 600;\n}\n#hh-app .hh-no-results {\n  text-align: center;\n  padding: 40px 20px;\n  color: #94a3b8;\n  font-size: 14px;\n}\n\n/* ── Security Builder ── */\n#hh-app .hh-builder-layout {\n  display: flex;\n  gap: 20px;\n  flex-wrap: wrap;\n}\n#hh-app .hh-builder-left { flex: 1 1 440px; min-width: 0; }\n#hh-app .hh-builder-right { flex: 1 1 340px; min-width: 0; }\n#hh-app .hh-builder-section {\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n  margin-bottom: 14px;\n  overflow: hidden;\n}\n#hh-app .hh-builder-sec-head {\n  padding: 12px 16px;\n  background: #f8fafc;\n  font-weight: 700;\n  font-size: 13px;\n  color: #1e3a8a;\n  border-bottom: 1px solid #e2e8f0;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n#hh-app .hh-builder-sec-desc {\n  font-size: 11px;\n  color: #64748b;\n  font-weight: 400;\n  margin-left: 4px;\n}\n#hh-app .hh-field-group { padding: 14px 16px; display: flex; flex-direction: column; gap: 10px; }\n#hh-app .hh-field-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }\n#hh-app .hh-field-label { font-size: 12px; color: #475569; min-width: 100px; flex-shrink: 0; }\n#hh-app .hh-input, #hh-app .hh-select {\n  flex: 1;\n  min-width: 120px;\n  padding: 7px 10px;\n  border: 1px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 13px;\n  color: #1a1a2e;\n  background: #fff;\n  outline: none;\n  transition: border-color 0.15s;\n}\n#hh-app .hh-input:focus, #hh-app .hh-select:focus {\n  border-color: #3a56d4;\n  box-shadow: 0 0 0 3px rgba(58,86,212,0.1);\n}\n#hh-app .hh-enable-row {\n  display: flex; align-items: center; gap: 8px;\n  font-size: 13px; color: #334155;\n  padding: 8px 16px;\n  border-bottom: 1px solid #f1f5f9;\n  background: #fff;\n}\n#hh-app .hh-enable-row input[type=\"checkbox\"] {\n  width: 15px; height: 15px;\n  accent-color: #3a56d4; cursor: pointer; flex-shrink: 0;\n}\n#hh-app .hh-enable-row label { cursor: pointer; }\n\n/* ── Output box ── */\n#hh-app .hh-out-box {\n  background: #0f172a;\n  border-radius: 10px;\n  overflow: hidden;\n  position: sticky;\n  top: 80px;\n}\n#hh-app .hh-out-topbar {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  padding: 10px 14px;\n  background: #1e293b;\n  flex-wrap: wrap;\n  gap: 6px;\n}\n#hh-app .hh-out-label {\n  font-size: 11px;\n  font-weight: 700;\n  color: #94a3b8;\n  letter-spacing: 0.06em;\n  text-transform: uppercase;\n}\n#hh-app .hh-out-actions { display: flex; gap: 6px; flex-wrap: wrap; }\n#hh-app .hh-format-btns { display: flex; gap: 0; border-radius: 6px; overflow: hidden; border: 1px solid #334155; }\n#hh-app .hh-format-btn {\n  padding: 4px 12px;\n  background: #1e293b;\n  color: #94a3b8;\n  border: none;\n  font-size: 11px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s;\n  border-right: 1px solid #334155;\n}\n#hh-app .hh-format-btn:last-child { border-right: none; }\n#hh-app .hh-format-btn:hover { background: #334155; color: #e2e8f0; }\n#hh-app .hh-format-btn.hh-fmt-active { background: #3a56d4; color: #fff; }\n#hh-app .hh-copy-btn {\n  padding: 5px 12px;\n  border-radius: 6px;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  border: none;\n  background: #3a56d4;\n  color: #fff;\n  transition: background 0.15s;\n}\n#hh-app .hh-copy-btn:hover { background: #2d44b0; }\n#hh-app .hh-copy-btn.hh-copied { background: #16a34a; }\n#hh-app .hh-out-code {\n  padding: 16px;\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  font-size: 12px;\n  line-height: 1.7;\n  color: #e2e8f0;\n  white-space: pre;\n  overflow-x: auto;\n  max-height: 540px;\n  overflow-y: auto;\n  min-height: 160px;\n}\n#hh-app .hh-out-code .hh-c-comment { color: #64748b; }\n#hh-app .hh-out-code .hh-c-key     { color: #7dd3fc; }\n#hh-app .hh-out-code .hh-c-val     { color: #86efac; }\n#hh-app .hh-out-code .hh-c-tag     { color: #f9a8d4; }\n#hh-app .hh-out-code .hh-c-dir     { color: #fde68a; }\n\n/* ── Parser ── */\n#hh-app .hh-parser-layout {\n  display: flex;\n  gap: 20px;\n  flex-wrap: wrap;\n}\n#hh-app .hh-parser-left { flex: 1 1 360px; min-width: 0; }\n#hh-app .hh-parser-right { flex: 1 1 380px; min-width: 0; }\n#hh-app .hh-textarea {\n  width: 100%;\n  padding: 12px 14px;\n  border: 2px solid #d1d5db;\n  border-radius: 8px;\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  font-size: 12px;\n  color: #1a1a2e;\n  resize: vertical;\n  outline: none;\n  transition: border-color 0.2s;\n  min-height: 240px;\n  line-height: 1.6;\n}\n#hh-app .hh-textarea:focus { border-color: #3a56d4; }\n#hh-app .hh-parse-btn {\n  margin-top: 10px;\n  width: 100%;\n  padding: 11px;\n  background: linear-gradient(135deg, #3a56d4, #7c3aed);\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 14px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: opacity 0.2s;\n}\n#hh-app .hh-parse-btn:hover { opacity: 0.9; }\n#hh-app .hh-parsed-list { display: flex; flex-direction: column; gap: 8px; }\n#hh-app .hh-parsed-item {\n  border: 1px solid #e2e8f0;\n  border-radius: 8px;\n  overflow: hidden;\n}\n#hh-app .hh-parsed-head {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 9px 13px;\n  background: #f8fafc;\n  font-size: 12px;\n}\n#hh-app .hh-parsed-name {\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  font-weight: 700;\n  color: #1e3a8a;\n  flex: 1;\n}\n#hh-app .hh-parsed-val {\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  font-size: 11px;\n  color: #334155;\n  background: #f1f5f9;\n  padding: 2px 7px;\n  border-radius: 4px;\n  max-width: 220px;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n#hh-app .hh-parsed-desc {\n  padding: 8px 13px;\n  font-size: 12px;\n  color: #475569;\n  border-top: 1px solid #f1f5f9;\n  background: #fff;\n}\n#hh-app .hh-parsed-unknown {\n  color: #94a3b8;\n  font-style: italic;\n}\n#hh-app .hh-parse-placeholder {\n  text-align: center;\n  padding: 40px 20px;\n  color: #94a3b8;\n  font-size: 13px;\n  border: 2px dashed #e2e8f0;\n  border-radius: 8px;\n}\n\n/* ── Misc ── */\n#hh-app .hh-hint { font-size: 12px; color: #94a3b8; margin-top: 5px; }\n#hh-app .hh-section-intro {\n  font-size: 13px;\n  color: #64748b;\n  margin-bottom: 18px;\n  padding: 12px 16px;\n  background: #f8fafc;\n  border-radius: 8px;\n  border-left: 3px solid #3a56d4;\n}\n\n@media (max-width: 680px) {\n  #hh-app .hh-builder-layout,\n  #hh-app .hh-parser-layout { flex-direction: column; }\n  #hh-app .hh-out-box { position: static; }\n  #hh-app .hh-tab { padding: 8px 13px; font-size: 13px; }\n}\n\u003c/style\u003e\n\u003c!-- ═══════════════════════════════════════════════════════ TABS --\u003e\n\u003cdiv class=\"hh-tabs\"\u003e\n  \u003cbutton class=\"hh-tab hh-active\" onclick=\"hhShowTab('reference')\"\u003eReference\u003c/button\u003e\n  \u003cbutton class=\"hh-tab\" onclick=\"hhShowTab('builder')\"\u003eSecurity Builder\u003c/button\u003e\n  \u003cbutton class=\"hh-tab\" onclick=\"hhShowTab('parser')\"\u003eHeader Parser\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- ═══════════════════════════════════════════════════════ TAB 1: REFERENCE --\u003e\n\u003cdiv class=\"hh-panel hh-active\" id=\"hh-panel-reference\"\u003e\n  \u003cdiv class=\"hh-section-intro\"\u003eBrowse and search 50+ HTTP headers grouped by category. Click any header to see its description, common values, and an example.\u003c/div\u003e\n  \u003cdiv class=\"hh-search-row\"\u003e\n    \u003cinput type=\"text\" class=\"hh-search\" id=\"hh-ref-search\" placeholder=\"Search headers... (e.g. Content-Type, cache, CORS)\" oninput=\"hhFilterRef()\"\u003e\n    \u003cdiv class=\"hh-cat-btns\" id=\"hh-cat-btns\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"hh-cards\" id=\"hh-cards\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"hh-no-results\" id=\"hh-no-results\" style=\"display:none;\"\u003eNo headers match your search.\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ═══════════════════════════════════════════════════════ TAB 2: BUILDER --\u003e\n\u003cdiv class=\"hh-panel\" id=\"hh-panel-builder\"\u003e\n  \u003cdiv class=\"hh-section-intro\"\u003eSelect and configure security headers below, then copy the generated snippet for Apache (.htaccess) or Nginx.\u003c/div\u003e\n  \u003cdiv class=\"hh-builder-layout\"\u003e\n    \u003cdiv class=\"hh-builder-left\"\u003e\n\u003cpre\u003e\u003ccode\u003e  \u0026lt;!-- CSP --\u0026gt;\n  \u0026lt;div class=\u0026quot;hh-builder-section\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;hh-builder-sec-head\u0026quot;\u0026gt;\n      Content-Security-Policy\n      \u0026lt;span class=\u0026quot;hh-builder-sec-desc\u0026quot;\u0026gt;— Controls allowed resource origins\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-b-csp\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot;\u0026gt;\n      \u0026lt;label for=\u0026quot;hh-b-csp\u0026quot;\u0026gt;Enable CSP header\u0026lt;/label\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;hh-field-group\u0026quot; id=\u0026quot;hh-b-csp-fields\u0026quot; style=\u0026quot;display:none;\u0026quot;\u0026gt;\n      \u0026lt;div class=\u0026quot;hh-field-row\u0026quot;\u0026gt;\n        \u0026lt;span class=\u0026quot;hh-field-label\u0026quot;\u0026gt;default-src\u0026lt;/span\u0026gt;\n        \u0026lt;select class=\u0026quot;hh-select\u0026quot; id=\u0026quot;hh-csp-default\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot;\u0026gt;\n          \u0026lt;option value=\u0026quot;'self'\u0026quot;\u0026gt;'self'\u0026lt;/option\u0026gt;\n          \u0026lt;option value=\u0026quot;'self' 'unsafe-inline'\u0026quot;\u0026gt;'self' 'unsafe-inline'\u0026lt;/option\u0026gt;\n          \u0026lt;option value=\u0026quot;'none'\u0026quot;\u0026gt;'none'\u0026lt;/option\u0026gt;\n          \u0026lt;option value=\u0026quot;*\u0026quot;\u0026gt;* (any)\u0026lt;/option\u0026gt;\n        \u0026lt;/select\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;hh-field-row\u0026quot;\u0026gt;\n        \u0026lt;span class=\u0026quot;hh-field-label\u0026quot;\u0026gt;script-src\u0026lt;/span\u0026gt;\n        \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;hh-input\u0026quot; id=\u0026quot;hh-csp-script\u0026quot; placeholder=\u0026quot;'self' (leave blank to inherit)\u0026quot; oninput=\u0026quot;hhBuild()\u0026quot;\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;hh-field-row\u0026quot;\u0026gt;\n        \u0026lt;span class=\u0026quot;hh-field-label\u0026quot;\u0026gt;style-src\u0026lt;/span\u0026gt;\n        \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;hh-input\u0026quot; id=\u0026quot;hh-csp-style\u0026quot; placeholder=\u0026quot;'self' (leave blank to inherit)\u0026quot; oninput=\u0026quot;hhBuild()\u0026quot;\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;hh-field-row\u0026quot;\u0026gt;\n        \u0026lt;span class=\u0026quot;hh-field-label\u0026quot;\u0026gt;img-src\u0026lt;/span\u0026gt;\n        \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;hh-input\u0026quot; id=\u0026quot;hh-csp-img\u0026quot; placeholder=\u0026quot;'self' data: (leave blank to inherit)\u0026quot; oninput=\u0026quot;hhBuild()\u0026quot;\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;hh-field-row\u0026quot;\u0026gt;\n        \u0026lt;span class=\u0026quot;hh-field-label\u0026quot;\u0026gt;frame-src\u0026lt;/span\u0026gt;\n        \u0026lt;input type=\u0026quot;text\u0026quot; class=\u0026quot;hh-input\u0026quot; id=\u0026quot;hh-csp-frame\u0026quot; placeholder=\u0026quot;'none' (leave blank to inherit)\u0026quot; oninput=\u0026quot;hhBuild()\u0026quot;\u0026gt;\n      \u0026lt;/div\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;!-- HSTS --\u0026gt;\n  \u0026lt;div class=\u0026quot;hh-builder-section\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;hh-builder-sec-head\u0026quot;\u0026gt;Strict-Transport-Security \u0026lt;span class=\u0026quot;hh-builder-sec-desc\u0026quot;\u0026gt;— HTTPS enforcement\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-b-hsts\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; checked\u0026gt;\n      \u0026lt;label for=\u0026quot;hh-b-hsts\u0026quot;\u0026gt;Enable HSTS\u0026lt;/label\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;hh-field-group\u0026quot;\u0026gt;\n      \u0026lt;div class=\u0026quot;hh-field-row\u0026quot;\u0026gt;\n        \u0026lt;span class=\u0026quot;hh-field-label\u0026quot;\u0026gt;max-age\u0026lt;/span\u0026gt;\n        \u0026lt;select class=\u0026quot;hh-select\u0026quot; id=\u0026quot;hh-hsts-age\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; style=\u0026quot;max-width:220px;\u0026quot;\u0026gt;\n          \u0026lt;option value=\u0026quot;2592000\u0026quot;\u0026gt;30 days (2592000)\u0026lt;/option\u0026gt;\n          \u0026lt;option value=\u0026quot;15552000\u0026quot;\u0026gt;6 months (15552000)\u0026lt;/option\u0026gt;\n          \u0026lt;option value=\u0026quot;31536000\u0026quot; selected\u0026gt;1 year (31536000)\u0026lt;/option\u0026gt;\n        \u0026lt;/select\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot; style=\u0026quot;padding:0;border:none;\u0026quot;\u0026gt;\n        \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-hsts-sub\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; checked\u0026gt;\n        \u0026lt;label for=\u0026quot;hh-hsts-sub\u0026quot;\u0026gt;includeSubDomains\u0026lt;/label\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot; style=\u0026quot;padding:0;border:none;\u0026quot;\u0026gt;\n        \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-hsts-pre\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot;\u0026gt;\n        \u0026lt;label for=\u0026quot;hh-hsts-pre\u0026quot;\u0026gt;preload\u0026lt;/label\u0026gt;\n      \u0026lt;/div\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;!-- X-Frame-Options --\u0026gt;\n  \u0026lt;div class=\u0026quot;hh-builder-section\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;hh-builder-sec-head\u0026quot;\u0026gt;X-Frame-Options \u0026lt;span class=\u0026quot;hh-builder-sec-desc\u0026quot;\u0026gt;— Clickjacking protection\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-b-xframe\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; checked\u0026gt;\n      \u0026lt;label for=\u0026quot;hh-b-xframe\u0026quot;\u0026gt;Enable X-Frame-Options\u0026lt;/label\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;hh-field-group\u0026quot;\u0026gt;\n      \u0026lt;div class=\u0026quot;hh-field-row\u0026quot;\u0026gt;\n        \u0026lt;span class=\u0026quot;hh-field-label\u0026quot;\u0026gt;Value\u0026lt;/span\u0026gt;\n        \u0026lt;select class=\u0026quot;hh-select\u0026quot; id=\u0026quot;hh-xframe-val\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; style=\u0026quot;max-width:220px;\u0026quot;\u0026gt;\n          \u0026lt;option value=\u0026quot;SAMEORIGIN\u0026quot; selected\u0026gt;SAMEORIGIN\u0026lt;/option\u0026gt;\n          \u0026lt;option value=\u0026quot;DENY\u0026quot;\u0026gt;DENY\u0026lt;/option\u0026gt;\n        \u0026lt;/select\u0026gt;\n      \u0026lt;/div\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;!-- X-Content-Type-Options --\u0026gt;\n  \u0026lt;div class=\u0026quot;hh-builder-section\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;hh-builder-sec-head\u0026quot;\u0026gt;X-Content-Type-Options \u0026lt;span class=\u0026quot;hh-builder-sec-desc\u0026quot;\u0026gt;— MIME sniffing prevention\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-b-xcto\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; checked\u0026gt;\n      \u0026lt;label for=\u0026quot;hh-b-xcto\u0026quot;\u0026gt;Enable (always nosniff)\u0026lt;/label\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;!-- Referrer-Policy --\u0026gt;\n  \u0026lt;div class=\u0026quot;hh-builder-section\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;hh-builder-sec-head\u0026quot;\u0026gt;Referrer-Policy \u0026lt;span class=\u0026quot;hh-builder-sec-desc\u0026quot;\u0026gt;— Controls referrer info sent\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-b-rp\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; checked\u0026gt;\n      \u0026lt;label for=\u0026quot;hh-b-rp\u0026quot;\u0026gt;Enable Referrer-Policy\u0026lt;/label\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;hh-field-group\u0026quot;\u0026gt;\n      \u0026lt;div class=\u0026quot;hh-field-row\u0026quot;\u0026gt;\n        \u0026lt;span class=\u0026quot;hh-field-label\u0026quot;\u0026gt;Policy\u0026lt;/span\u0026gt;\n        \u0026lt;select class=\u0026quot;hh-select\u0026quot; id=\u0026quot;hh-rp-val\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot;\u0026gt;\n          \u0026lt;option value=\u0026quot;no-referrer\u0026quot;\u0026gt;no-referrer\u0026lt;/option\u0026gt;\n          \u0026lt;option value=\u0026quot;no-referrer-when-downgrade\u0026quot;\u0026gt;no-referrer-when-downgrade\u0026lt;/option\u0026gt;\n          \u0026lt;option value=\u0026quot;strict-origin-when-cross-origin\u0026quot; selected\u0026gt;strict-origin-when-cross-origin\u0026lt;/option\u0026gt;\n          \u0026lt;option value=\u0026quot;same-origin\u0026quot;\u0026gt;same-origin\u0026lt;/option\u0026gt;\n          \u0026lt;option value=\u0026quot;origin\u0026quot;\u0026gt;origin\u0026lt;/option\u0026gt;\n        \u0026lt;/select\u0026gt;\n      \u0026lt;/div\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;!-- Permissions-Policy --\u0026gt;\n  \u0026lt;div class=\u0026quot;hh-builder-section\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;hh-builder-sec-head\u0026quot;\u0026gt;Permissions-Policy \u0026lt;span class=\u0026quot;hh-builder-sec-desc\u0026quot;\u0026gt;— Browser feature control\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-b-pp\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot;\u0026gt;\n      \u0026lt;label for=\u0026quot;hh-b-pp\u0026quot;\u0026gt;Enable Permissions-Policy\u0026lt;/label\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;hh-field-group\u0026quot; id=\u0026quot;hh-b-pp-fields\u0026quot; style=\u0026quot;display:none;\u0026quot;\u0026gt;\n      \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot; style=\u0026quot;padding:0;border:none;\u0026quot;\u0026gt;\n        \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-pp-cam\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; checked\u0026gt;\n        \u0026lt;label for=\u0026quot;hh-pp-cam\u0026quot;\u0026gt;camera=()\u0026lt;/label\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot; style=\u0026quot;padding:0;border:none;\u0026quot;\u0026gt;\n        \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-pp-mic\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; checked\u0026gt;\n        \u0026lt;label for=\u0026quot;hh-pp-mic\u0026quot;\u0026gt;microphone=()\u0026lt;/label\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot; style=\u0026quot;padding:0;border:none;\u0026quot;\u0026gt;\n        \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-pp-geo\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot; checked\u0026gt;\n        \u0026lt;label for=\u0026quot;hh-pp-geo\u0026quot;\u0026gt;geolocation=()\u0026lt;/label\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot; style=\u0026quot;padding:0;border:none;\u0026quot;\u0026gt;\n        \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-pp-pay\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot;\u0026gt;\n        \u0026lt;label for=\u0026quot;hh-pp-pay\u0026quot;\u0026gt;payment=()\u0026lt;/label\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;hh-enable-row\u0026quot; style=\u0026quot;padding:0;border:none;\u0026quot;\u0026gt;\n        \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;hh-pp-usb\u0026quot; onchange=\u0026quot;hhBuild()\u0026quot;\u0026gt;\n        \u0026lt;label for=\u0026quot;hh-pp-usb\u0026quot;\u0026gt;usb=()\u0026lt;/label\u0026gt;\n      \u0026lt;/div\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n\u0026lt;/div\u0026gt;\u0026lt;!-- /.hh-builder-left --\u0026gt;\n\n\u0026lt;div class=\u0026quot;hh-builder-right\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;hh-out-box\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;hh-out-topbar\u0026quot;\u0026gt;\n      \u0026lt;span class=\u0026quot;hh-out-label\u0026quot;\u0026gt;Generated Config\u0026lt;/span\u0026gt;\n      \u0026lt;div class=\u0026quot;hh-out-actions\u0026quot;\u0026gt;\n        \u0026lt;div class=\u0026quot;hh-format-btns\u0026quot;\u0026gt;\n          \u0026lt;button class=\u0026quot;hh-format-btn hh-fmt-active\u0026quot; id=\u0026quot;hh-fmt-apache\u0026quot; onclick=\u0026quot;hhSetFmt('apache')\u0026quot;\u0026gt;Apache\u0026lt;/button\u0026gt;\n          \u0026lt;button class=\u0026quot;hh-format-btn\u0026quot; id=\u0026quot;hh-fmt-nginx\u0026quot; onclick=\u0026quot;hhSetFmt('nginx')\u0026quot;\u0026gt;Nginx\u0026lt;/button\u0026gt;\n        \u0026lt;/div\u0026gt;\n        \u0026lt;button class=\u0026quot;hh-copy-btn\u0026quot; id=\u0026quot;hh-build-copy\u0026quot; onclick=\u0026quot;hhCopyBuild()\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt;\n      \u0026lt;/div\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;pre class=\u0026quot;hh-out-code\u0026quot; id=\u0026quot;hh-build-out\u0026quot;\u0026gt;\u0026lt;/pre\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\u003c!-- /.hh-builder-layout --\u003e\n\u003c/div\u003e\n\u003c!-- ═══════════════════════════════════════════════════════ TAB 3: PARSER --\u003e\n\u003cdiv class=\"hh-panel\" id=\"hh-panel-parser\"\u003e\n  \u003cdiv class=\"hh-section-intro\"\u003ePaste raw HTTP response headers below and click Parse to get a formatted, explained breakdown of each header.\u003c/div\u003e\n  \u003cdiv class=\"hh-parser-layout\"\u003e\n    \u003cdiv class=\"hh-parser-left\"\u003e\n      \u003ctextarea class=\"hh-textarea\" id=\"hh-parse-input\" placeholder=\"Paste HTTP headers here, e.g.:\n\u003cp\u003eHTTP/1.1 200 OK\nContent-Type: text/html; charset=utf-8\nCache-Control: max-age=3600, public\nStrict-Transport-Security: max-age=31536000; includeSubDomains\nX-Frame-Options: SAMEORIGIN\nContent-Encoding: gzip\nVary: Accept-Encoding\nServer: nginx/1.24.0\u0026quot;\u0026gt;\u003c/textarea\u003e\n\u003cbutton class=\"hh-parse-btn\" onclick=\"hhParse()\"\u003eParse Headers\u003c/button\u003e\n\u003cdiv class=\"hh-hint\" style=\"margin-top:8px;\"\u003eSupports HTTP/1.1 and HTTP/2 response header format.\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"hh-parser-right\"\u003e\n\u003cdiv id=\"hh-parsed-out\"\u003e\n\u003cdiv class=\"hh-parse-placeholder\"\u003ePaste headers above and click Parse to see the breakdown here.\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003c/p\u003e","title":"HTTP Header Analyzer"},{"content":" All 1xx Info 2xx Success 3xx Redirect 4xx Client Error 5xx Server Error No status codes match your search. Related Tools Test regex patterns → Regex Tester Decode JWT tokens → JWT Decoder Check IP address info → IP Address Info ","permalink":"https://productivity-works.com/tools/http-status-codes/","summary":"\u003cdiv id=\"http-app\"\u003e\n\u003cstyle\u003e\n#http-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 0 16px 48px;\n  color: #1a1a2e;\n}\n\n/* ── Controls ── */\n#http-app .hsc-controls {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  margin-bottom: 24px;\n}\n\n#http-app .hsc-search {\n  flex: 1 1 220px;\n  padding: 10px 14px;\n  border: 2px solid #d1d5db;\n  border-radius: 8px;\n  font-size: 15px;\n  outline: none;\n  transition: border-color 0.2s;\n}\n#http-app .hsc-search:focus {\n  border-color: #4f46e5;\n}\n\n#http-app .hsc-filter-group {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 6px;\n}\n\n#http-app .hsc-filter-btn {\n  padding: 8px 14px;\n  border: 2px solid transparent;\n  border-radius: 20px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.18s;\n  background: #f3f4f6;\n  color: #374151;\n}\n#http-app .hsc-filter-btn:hover,\n#http-app .hsc-filter-btn.active {\n  color: #fff;\n}\n#http-app .hsc-filter-btn[data-cat=\"all\"].active  { background: #4f46e5; }\n#http-app .hsc-filter-btn[data-cat=\"1xx\"].active  { background: #6366f1; }\n#http-app .hsc-filter-btn[data-cat=\"2xx\"].active  { background: #16a34a; }\n#http-app .hsc-filter-btn[data-cat=\"3xx\"].active  { background: #d97706; }\n#http-app .hsc-filter-btn[data-cat=\"4xx\"].active  { background: #dc2626; }\n#http-app .hsc-filter-btn[data-cat=\"5xx\"].active  { background: #7c3aed; }\n\n/* ── Result count ── */\n#http-app .hsc-count {\n  font-size: 13px;\n  color: #6b7280;\n  margin-bottom: 16px;\n}\n\n/* ── Category heading ── */\n#http-app .hsc-cat-heading {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin: 28px 0 10px;\n  font-size: 18px;\n  font-weight: 700;\n}\n#http-app .hsc-cat-badge {\n  display: inline-block;\n  padding: 3px 12px;\n  border-radius: 12px;\n  font-size: 13px;\n  font-weight: 700;\n  color: #fff;\n}\n#http-app .hsc-cat-badge.c1xx { background: #6366f1; }\n#http-app .hsc-cat-badge.c2xx { background: #16a34a; }\n#http-app .hsc-cat-badge.c3xx { background: #d97706; }\n#http-app .hsc-cat-badge.c4xx { background: #dc2626; }\n#http-app .hsc-cat-badge.c5xx { background: #7c3aed; }\n\n/* ── Code cards ── */\n#http-app .hsc-card {\n  border-radius: 10px;\n  border: 1.5px solid #e5e7eb;\n  margin-bottom: 8px;\n  overflow: hidden;\n  transition: box-shadow 0.18s;\n}\n#http-app .hsc-card:hover {\n  box-shadow: 0 2px 10px rgba(0,0,0,0.09);\n}\n#http-app .hsc-card.common {\n  border-width: 2px;\n}\n#http-app .hsc-card.c1xx.common { border-color: #6366f1; }\n#http-app .hsc-card.c2xx.common { border-color: #16a34a; }\n#http-app .hsc-card.c3xx.common { border-color: #d97706; }\n#http-app .hsc-card.c4xx.common { border-color: #dc2626; }\n#http-app .hsc-card.c5xx.common { border-color: #7c3aed; }\n\n#http-app .hsc-card-header {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  padding: 12px 16px;\n  cursor: pointer;\n  user-select: none;\n  background: #fff;\n}\n#http-app .hsc-card-header:hover {\n  background: #f9fafb;\n}\n\n#http-app .hsc-code-num {\n  font-size: 20px;\n  font-weight: 800;\n  min-width: 56px;\n  text-align: center;\n  padding: 4px 8px;\n  border-radius: 6px;\n  color: #fff;\n  letter-spacing: -0.5px;\n}\n#http-app .hsc-code-num.c1xx { background: #6366f1; }\n#http-app .hsc-code-num.c2xx { background: #16a34a; }\n#http-app .hsc-code-num.c3xx { background: #d97706; }\n#http-app .hsc-code-num.c4xx { background: #dc2626; }\n#http-app .hsc-code-num.c5xx { background: #7c3aed; }\n\n#http-app .hsc-card-meta {\n  flex: 1;\n  min-width: 0;\n}\n#http-app .hsc-code-name {\n  font-size: 15px;\n  font-weight: 700;\n  color: #111827;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n#http-app .hsc-code-desc {\n  font-size: 13px;\n  color: #6b7280;\n  margin-top: 2px;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n#http-app .hsc-common-star {\n  font-size: 11px;\n  font-weight: 700;\n  color: #fff;\n  background: #f59e0b;\n  padding: 2px 7px;\n  border-radius: 10px;\n  white-space: nowrap;\n  flex-shrink: 0;\n}\n\n#http-app .hsc-copy-btn {\n  flex-shrink: 0;\n  padding: 6px 12px;\n  border: 1.5px solid #d1d5db;\n  border-radius: 6px;\n  font-size: 12px;\n  font-weight: 600;\n  background: #fff;\n  color: #374151;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#http-app .hsc-copy-btn:hover {\n  background: #4f46e5;\n  color: #fff;\n  border-color: #4f46e5;\n}\n#http-app .hsc-copy-btn.copied {\n  background: #16a34a;\n  color: #fff;\n  border-color: #16a34a;\n}\n\n#http-app .hsc-chevron {\n  flex-shrink: 0;\n  width: 20px;\n  height: 20px;\n  color: #9ca3af;\n  transition: transform 0.2s;\n}\n#http-app .hsc-card.open .hsc-chevron {\n  transform: rotate(180deg);\n}\n\n/* ── Expanded detail ── */\n#http-app .hsc-detail {\n  display: none;\n  padding: 14px 20px 18px;\n  border-top: 1px solid #e5e7eb;\n  background: #f9fafb;\n  font-size: 14px;\n  line-height: 1.7;\n  color: #374151;\n}\n#http-app .hsc-card.open .hsc-detail {\n  display: block;\n}\n\n#http-app .hsc-detail h4 {\n  margin: 0 0 6px;\n  font-size: 13px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  color: #6b7280;\n}\n#http-app .hsc-detail p {\n  margin: 0 0 12px;\n}\n#http-app .hsc-detail .hsc-usecase {\n  background: #fff;\n  border: 1px solid #e5e7eb;\n  border-radius: 6px;\n  padding: 10px 14px;\n  font-size: 13px;\n  color: #374151;\n}\n\n/* ── No results ── */\n#http-app .hsc-no-results {\n  text-align: center;\n  padding: 48px 0;\n  color: #9ca3af;\n  font-size: 16px;\n  display: none;\n}\n\n/* ── Mobile ── */\n@media (max-width: 600px) {\n  #http-app .hsc-code-desc { display: none; }\n  #http-app .hsc-copy-btn { display: none; }\n}\n\u003c/style\u003e\n\u003c!-- Controls --\u003e\n\u003cdiv class=\"hsc-controls\"\u003e\n  \u003cinput\n    id=\"hscSearch\"\n    class=\"hsc-search\"\n    type=\"search\"\n    placeholder=\"Search by code number or keyword… e.g. 404 or not found\"\n    aria-label=\"Search HTTP status codes\"\n  /\u003e\n  \u003cdiv class=\"hsc-filter-group\" role=\"group\" aria-label=\"Filter by category\"\u003e\n    \u003cbutton class=\"hsc-filter-btn active\" data-cat=\"all\"\u003eAll\u003c/button\u003e\n    \u003cbutton class=\"hsc-filter-btn\" data-cat=\"1xx\"\u003e1xx Info\u003c/button\u003e\n    \u003cbutton class=\"hsc-filter-btn\" data-cat=\"2xx\"\u003e2xx Success\u003c/button\u003e\n    \u003cbutton class=\"hsc-filter-btn\" data-cat=\"3xx\"\u003e3xx Redirect\u003c/button\u003e\n    \u003cbutton class=\"hsc-filter-btn\" data-cat=\"4xx\"\u003e4xx Client Error\u003c/button\u003e\n    \u003cbutton class=\"hsc-filter-btn\" data-cat=\"5xx\"\u003e5xx Server Error\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"hscCount\" class=\"hsc-count\"\u003e\u003c/div\u003e\n\u003c!-- Code list rendered by JS --\u003e\n\u003cdiv id=\"hscList\"\u003e\u003c/div\u003e\n\u003cdiv id=\"hscNoResults\" class=\"hsc-no-results\"\u003eNo status codes match your search.\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  var COMMON = [200,201,301,302,400,401,403,404,500,502,503];\n\n  var CODES = [\n    /* ── 1xx ── */\n    {\n      code: 100, name: \"Continue\",\n      desc: \"The server has received the request headers.\",\n      detail: \"Indicates that the client should continue with its request. This interim response tells the client that the initial part of the request has been received and the client should proceed with the rest of the request, or ignore this response if the request was already completed.\",\n      usecase: \"Used when a client sends a request with an Expect: 100-continue header before sending a large request body. The server responds 100 to signal it's ready to accept the body.\"\n    },\n    {\n      code: 101, name: \"Switching Protocols\",\n      desc: \"The server agrees to switch protocols.\",\n      detail: \"Sent in response to an Upgrade request header from the client, indicating the protocol the server is switching to. The server will switch protocols immediately after the blank line following this response.\",\n      usecase: \"Used when upgrading an HTTP connection to WebSocket (Upgrade: websocket header). The server sends 101 to confirm the protocol switch.\"\n    },\n    {\n      code: 102, name: \"Processing\",\n      desc: \"The server has received and is processing the request.\",\n      detail: \"A WebDAV response code indicating that the server has received and is processing the request, but no response is available yet. This prevents the client from timing out while the server works on a long-running operation.\",\n      usecase: \"WebDAV operations that take more than a few seconds to complete, preventing client timeout.\"\n    },\n    {\n      code: 103, name: \"Early Hints\",\n      desc: \"Used to return some response headers before final response.\",\n      detail: \"Allows the server to send preliminary HTTP headers before the final HTTP message. Primarily used with the Link header to allow browsers to preload resources while the server prepares a response.\",\n      usecase: \"Server sends Link headers for CSS/JS files while still generating the main HTML response, allowing the browser to start preloading assets sooner.\"\n    },\n    /* ── 2xx ── */\n    {\n      code: 200, name: \"OK\",\n      desc: \"The request succeeded.\",\n      detail: \"The standard response for successful HTTP requests. The actual response depends on the request method: GET returns the resource, POST returns the result of the action, HEAD returns only headers. The most common response code on the web.\",\n      usecase: \"Standard successful response for GET, POST, PUT, PATCH requests. An API returning user data, or a page loading successfully.\"\n    },\n    {\n      code: 201, name: \"Created\",\n      desc: \"The request succeeded and a new resource was created.\",\n      detail: \"Indicates that a request has succeeded and a new resource has been created as a result. The response typically includes a Location header pointing to the newly created resource, and the response body may contain a representation of the new resource.\",\n      usecase: \"REST API response after POST /users creates a new user. Location header points to /users/123.\"\n    },\n    {\n      code: 202, name: \"Accepted\",\n      desc: \"The request has been received but not yet acted upon.\",\n      detail: \"Indicates that the request has been accepted for processing, but the processing has not been completed. The request might or might not be eventually acted upon — it may be disallowed when processing occurs. Typically used for asynchronous operations.\",\n      usecase: \"Submitting a video encoding job: the server accepts the request immediately but encoding happens in the background.\"\n    },\n    {\n      code: 203, name: \"Non-Authoritative Information\",\n      desc: \"Returned metadata is not from the origin server.\",\n      detail: \"The returned metadata is not exactly the same as available from the origin server, but is collected from a local or third-party copy. Used mainly for mirrors or backups of another resource.\",\n      usecase: \"A proxy server returns modified headers or content from a cached copy that differs slightly from the origin.\"\n    },\n    {\n      code: 204, name: \"No Content\",\n      desc: \"No content to send in the response body.\",\n      detail: \"Indicates that the request has succeeded, but the client doesn't need to navigate away from its current page. Often used after a DELETE request, or when updating a resource via PUT/PATCH where no body is needed in the response.\",\n      usecase: \"DELETE /items/42 succeeds — the server returns 204 with no body. Also used for auto-save operations where the UI doesn't need to update.\"\n    },\n    {\n      code: 205, name: \"Reset Content\",\n      desc: \"Reset the document view that sent this request.\",\n      detail: \"Tells the client to reset the document which sent the request. Similar to 204 No Content but requires the requester to reset the document view, for example clearing a form after submission.\",\n      usecase: \"After a form submission, instructs the browser to clear the form fields so the user can enter new data.\"\n    },\n    {\n      code: 206, name: \"Partial Content\",\n      desc: \"Partial content delivered due to a range request.\",\n      detail: \"Used when the client sends a Range header to request only part of a resource. The response must include a Content-Range header indicating the range delivered. Enables resumable downloads and video streaming.\",\n      usecase: \"Streaming video: the player requests bytes 0–1048575 via Range: bytes=0-1048575. The server responds 206 with that chunk.\"\n    },\n    {\n      code: 207, name: \"Multi-Status\",\n      desc: \"Conveys information about multiple resources (WebDAV).\",\n      detail: \"A WebDAV response that conveys information about multiple resources, for situations where multiple status codes might be appropriate. The response body is an XML document with individual status codes for each resource.\",\n      usecase: \"WebDAV PROPFIND or COPY operations that affect multiple resources, each with its own success/failure status.\"\n    },\n    {\n      code: 208, name: \"Already Reported\",\n      desc: \"Members of a DAV binding already enumerated (WebDAV).\",\n      detail: \"Used inside a DAV: propstat response element to avoid enumerating the internal members of multiple bindings to the same collection repeatedly.\",\n      usecase: \"WebDAV: prevents infinite loops when a collection contains circular references.\"\n    },\n    {\n      code: 226, name: \"IM Used\",\n      desc: \"The server fulfilled a GET request for the resource using delta encoding.\",\n      detail: \"The server has fulfilled a GET request for the resource, and the response is a representation of the result of one or more instance-manipulations applied to the current instance. Used with HTTP delta encoding.\",\n      usecase: \"Delta encoding: the server sends only the changes (diff) from the previously cached version of a resource.\"\n    },\n    /* ── 3xx ── */\n    {\n      code: 300, name: \"Multiple Choices\",\n      desc: \"The request has more than one possible response.\",\n      detail: \"Indicates that the request has more than one possible response. The user-agent or user should choose one of them. There is no standardized way to choose automatically, though the server may indicate a preferred choice.\",\n      usecase: \"A resource available in multiple formats (HTML, JSON, XML) — the server presents choices and the client picks one.\"\n    },\n    {\n      code: 301, name: \"Moved Permanently\",\n      desc: \"The URL of the requested resource has changed permanently.\",\n      detail: \"Indicates that the resource has been permanently moved to a new URL. Search engines update their links to use the new URL. Browsers cache this redirect. By convention, the new URL is provided in the Location header. GET method may be changed to GET on redirect.\",\n      usecase: \"Redirecting HTTP to HTTPS, or a website changing its domain. Old URLs pass their SEO value to the new URL.\"\n    },\n    {\n      code: 302, name: \"Found\",\n      desc: \"The URL has been changed temporarily.\",\n      detail: \"Indicates that the resource is found at another URI temporarily. The client should continue to use the original URI. Browsers follow the redirect but don't cache it permanently. Often misused; 307 is preferred when the method must not change.\",\n      usecase: \"Post/Redirect/Get pattern: after a form POST succeeds, redirect to a results page so refresh doesn't resubmit the form.\"\n    },\n    {\n      code: 303, name: \"See Other\",\n      desc: \"Redirects to another page, always using GET.\",\n      detail: \"Indicates that the redirects don't link to the requested resource itself, but to another page (such as a confirmation page). Unlike 302, always triggers a GET on the new URL regardless of the original method.\",\n      usecase: \"After a POST form submission creates a resource, redirect the browser to a GET page showing the new resource.\"\n    },\n    {\n      code: 304, name: \"Not Modified\",\n      desc: \"Tells the client the response has not been modified.\",\n      detail: \"Used for caching. Tells the client that the response has not been modified, so the client can use the same cached version it already has. The response must not contain a body — just headers. Triggered by conditional requests using If-None-Match or If-Modified-Since.\",\n      usecase: \"Browser sends If-None-Match: \\\"abc123\\\" with an ETag. Server confirms the resource hasn't changed — client uses its cached copy.\"\n    },\n    {\n      code: 307, name: \"Temporary Redirect\",\n      desc: \"Temporary redirect; method must not change.\",\n      detail: \"Similar to 302 Found, but guarantees that the method and body will not be changed when the redirected request is made. If a POST is redirected, the new request will also be POST. Use when you need to preserve the HTTP method.\",\n      usecase: \"Temporarily routing an API POST endpoint to a different server during maintenance while preserving the POST method.\"\n    },\n    {\n      code: 308, name: \"Permanent Redirect\",\n      desc: \"Permanent redirect; method must not change.\",\n      detail: \"Similar to 301 Moved Permanently, but guarantees that the method and body will not be changed. Search engines update their links, browsers cache permanently, and the HTTP method is preserved. The permanent equivalent of 307.\",\n      usecase: \"Permanently moving an API endpoint from /v1/users to /v2/users while ensuring clients still send POST (not GET).\"\n    },\n    /* ── 4xx ── */\n    {\n      code: 400, name: \"Bad Request\",\n      desc: \"The server cannot process the request due to client error.\",\n      detail: \"The server cannot or will not process the request due to something perceived as a client error — malformed request syntax, invalid request message framing, or deceptive request routing. The client should not repeat the request without modification.\",\n      usecase: \"Missing required fields in an API request body, malformed JSON, invalid query parameter types.\"\n    },\n    {\n      code: 401, name: \"Unauthorized\",\n      desc: \"Authentication is required and has not been provided.\",\n      detail: \"Although named 'Unauthorized', this code means 'unauthenticated'. The client must authenticate itself to get the requested response. The response must include a WWW-Authenticate header with a challenge applicable to the requested resource.\",\n      usecase: \"Accessing a protected API endpoint without an Authorization header or with an invalid/expired token.\"\n    },\n    {\n      code: 402, name: \"Payment Required\",\n      desc: \"Reserved for future use; sometimes used for rate limits.\",\n      detail: \"Originally intended for digital payment systems. The specification is vague, and the code is rarely used. Some APIs use it to indicate a subscription is required or a payment wall has been hit.\",\n      usecase: \"Some SaaS APIs return 402 when a free-tier quota is exceeded and a paid plan is required.\"\n    },\n    {\n      code: 403, name: \"Forbidden\",\n      desc: \"The server refuses to authorize the request.\",\n      detail: \"The client's identity is known but it does not have access rights to the content. Unlike 401, re-authenticating will make no difference. The server understood the request but refuses to authorize it.\",\n      usecase: \"A logged-in user trying to access admin-only pages. Or accessing another user's private data.\"\n    },\n    {\n      code: 404, name: \"Not Found\",\n      desc: \"The server cannot find the requested resource.\",\n      detail: \"The server cannot find the requested resource. This can mean the URL is wrong, the resource never existed, or the resource has been deleted. Often used as a catch-all for resources that don't exist. Servers may return 404 to hide the existence of a resource from unauthorized clients (vs. 403).\",\n      usecase: \"Requesting a blog post that was deleted, a user ID that doesn't exist, or mistyping a URL.\"\n    },\n    {\n      code: 405, name: \"Method Not Allowed\",\n      desc: \"The request method is not supported.\",\n      detail: \"The request method is known by the server but is not supported by the target resource. The server must include an Allow header listing the supported methods. For example, a read-only resource rejects DELETE requests.\",\n      usecase: \"Sending a DELETE request to an endpoint that only supports GET and POST.\"\n    },\n    {\n      code: 406, name: \"Not Acceptable\",\n      desc: \"No content matching the Accept headers was found.\",\n      detail: \"Indicates that the server cannot produce a response matching the Accept headers sent in the request. For example, the client only accepts application/json but the server only supports text/xml.\",\n      usecase: \"Client sends Accept: application/json but the server only serves XML for that resource.\"\n    },\n    {\n      code: 407, name: \"Proxy Authentication Required\",\n      desc: \"Authentication with the proxy is required.\",\n      detail: \"Similar to 401, but authentication needs to be done by a proxy. The proxy must return a Proxy-Authenticate header for the client to authenticate with.\",\n      usecase: \"Corporate environments where HTTP traffic must authenticate through an intermediate proxy server.\"\n    },\n    {\n      code: 408, name: \"Request Timeout\",\n      desc: \"The server timed out waiting for the request.\",\n      detail: \"Sent by some servers when an idle connection is shut down. This indicates the server would like to close this unused connection. Used heavily by some servers like Apache.\",\n      usecase: \"A slow client took too long to send the request body. The server closes the connection after its idle timeout.\"\n    },\n    {\n      code: 409, name: \"Conflict\",\n      desc: \"The request conflicts with the current state of the resource.\",\n      detail: \"Indicates that the request conflicts with the current state of the server. Most likely with PUT requests where multiple requests are editing a resource simultaneously. The response body should describe the conflict.\",\n      usecase: \"Trying to create a user with an email that already exists. Or a version conflict in an optimistic-locking system.\"\n    },\n    {\n      code: 410, name: \"Gone\",\n      desc: \"The resource is permanently deleted and will not return.\",\n      detail: \"Indicates that access to the target resource is no longer available and that this condition is likely to be permanent. Unlike 404, this indicates intentional, permanent removal. Useful to signal search engines to deindex the URL.\",\n      usecase: \"A product that has been discontinued. Sending 410 tells search engines to remove the URL from their index.\"\n    },\n    {\n      code: 411, name: \"Length Required\",\n      desc: \"Content-Length header is required.\",\n      detail: \"The server refuses to accept the request without a defined Content-Length header. The client may repeat the request if it adds a valid Content-Length header.\",\n      usecase: \"Sending a POST request without a Content-Length header to a server that requires it.\"\n    },\n    {\n      code: 412, name: \"Precondition Failed\",\n      desc: \"Access to the resource has been denied.\",\n      detail: \"The client has indicated preconditions in its headers which the server does not meet. Used in conditional requests with If-Match, If-None-Match, If-Modified-Since, If-Unmodified-Since headers.\",\n      usecase: \"Optimistic concurrency: client sends If-Match: \\\"old-etag\\\" to update a resource, but the resource has already been modified by someone else.\"\n    },\n    {\n      code: 413, name: \"Content Too Large\",\n      desc: \"The request body is larger than the server will accept.\",\n      detail: \"The request entity is larger than limits defined by the server. The server might close the connection or return a Retry-After header if the situation is temporary.\",\n      usecase: \"Uploading a file that exceeds the server's maximum allowed upload size (e.g., an image over 10MB).\"\n    },\n    {\n      code: 414, name: \"URI Too Long\",\n      desc: \"The URI requested is longer than the server will process.\",\n      detail: \"The URI provided was too long for the server to process. This can occur when a client converts a POST request to a GET request with a long query string.\",\n      usecase: \"A search query or filter with thousands of characters in the URL query string.\"\n    },\n    {\n      code: 415, name: \"Unsupported Media Type\",\n      desc: \"The request media type is not supported.\",\n      detail: \"The media format of the requested data is not supported by the server, so the server is rejecting the request. Typically triggered when the Content-Type header doesn't match what the server expects.\",\n      usecase: \"Sending text/plain to an API endpoint that only accepts application/json.\"\n    },\n    {\n      code: 416, name: \"Range Not Satisfiable\",\n      desc: \"The requested range cannot be fulfilled.\",\n      detail: \"The range specified by the Range header field in the request cannot be fulfilled. The range might be outside the size of the target URI's data. The response includes a Content-Range header with an asterisk (*) indicating the actual size.\",\n      usecase: \"Client requests bytes 9000-9999 of a 5000-byte file — the range is beyond the file's end.\"\n    },\n    {\n      code: 417, name: \"Expectation Failed\",\n      desc: \"The expectation in the Expect header cannot be met.\",\n      detail: \"The expectation indicated by the Expect request-header field cannot be met by the server.\",\n      usecase: \"Client sends Expect: 100-continue but the server cannot fulfill the expectation (e.g., request body too large).\"\n    },\n    {\n      code: 418, name: \"I'm a Teapot\",\n      desc: \"The server refuses to brew coffee because it is a teapot.\",\n      detail: \"An April Fools' joke from 1998 (RFC 2324, Hyper Text Coffee Pot Control Protocol). Any attempt to brew coffee with a teapot should result in this error code. It is not expected to be implemented by actual HTTP servers. Kept as a joke in the HTTP spec.\",\n      usecase: \"Easter eggs in APIs and developer tools. Google returns 418 at https://www.google.com/teapot.\"\n    },\n    {\n      code: 422, name: \"Unprocessable Content\",\n      desc: \"The request is well-formed but has semantic errors.\",\n      detail: \"The server understands the content type of the request entity and the syntax is correct but was unable to process the contained instructions. Commonly used by REST APIs to indicate validation failures — the JSON is valid, but the data violates business rules.\",\n      usecase: \"Valid JSON body but a field fails validation: age: -5 or email: 'not-an-email'.\"\n    },\n    {\n      code: 423, name: \"Locked\",\n      desc: \"The resource that is being accessed is locked (WebDAV).\",\n      detail: \"The resource that is being accessed is locked. WebDAV response for when a resource is locked and cannot be modified.\",\n      usecase: \"WebDAV: a document checked out by another user cannot be modified.\"\n    },\n    {\n      code: 424, name: \"Failed Dependency\",\n      desc: \"The request failed because it depended on a failed request (WebDAV).\",\n      detail: \"The method could not be performed on the resource because the requested action depended on another action which failed (WebDAV).\",\n      usecase: \"WebDAV batch operations where a prior operation in the batch failed.\"\n    },\n    {\n      code: 425, name: \"Too Early\",\n      desc: \"The server is unwilling to risk processing a replayed request.\",\n      detail: \"Indicates that the server is unwilling to risk processing a request that might be replayed. Used to prevent replay attacks in TLS early data (0-RTT).\",\n      usecase: \"TLS 1.3 early data: the server declines to process a request sent in the TLS handshake to prevent replay attacks.\"\n    },\n    {\n      code: 426, name: \"Upgrade Required\",\n      desc: \"The client should switch to a different protocol.\",\n      detail: \"The server refuses to perform the request using the current protocol but will be willing to do so after the client upgrades to a different protocol. The server must include an Upgrade header indicating the required protocol.\",\n      usecase: \"Server requires the client to upgrade from HTTP/1.1 to HTTP/2 or from HTTP to HTTPS.\"\n    },\n    {\n      code: 428, name: \"Precondition Required\",\n      desc: \"The origin server requires the request to be conditional.\",\n      detail: \"Indicates that the origin server requires the request to be conditional. This response is meant to prevent the 'lost update' problem where a client GETs a resource's state, modifies it, and PUTs it back, but a third party has modified the state in between.\",\n      usecase: \"API requires If-Match or If-Unmodified-Since headers on update requests to prevent conflicting concurrent updates.\"\n    },\n    {\n      code: 429, name: \"Too Many Requests\",\n      desc: \"The user has sent too many requests (rate limiting).\",\n      detail: \"Indicates the user has sent too many requests in a given amount of time. The response may include a Retry-After header indicating how long the client should wait before making a new request. Fundamental to API rate limiting.\",\n      usecase: \"API rate limiting: a client exceeds 100 requests per minute. The server responds 429 with Retry-After: 30.\"\n    },\n    {\n      code: 431, name: \"Request Header Fields Too Large\",\n      desc: \"The request's HTTP headers are too large.\",\n      detail: \"The server is unwilling to process the request because its header fields are too large. The request may be resubmitted after reducing the size of the request header fields. Often caused by excessively large cookies.\",\n      usecase: \"Too many or too large cookies accumulated over time cause the request headers to exceed the server limit.\"\n    },\n    {\n      code: 451, name: \"Unavailable For Legal Reasons\",\n      desc: \"The resource is unavailable due to legal demands.\",\n      detail: \"The user requested a resource that cannot legally be provided. The response should include a Link header with a rel=blocked-by linking to the entity that imposed the restriction. Named after Fahrenheit 451.\",\n      usecase: \"Content blocked in a specific country due to copyright law, court orders, or government regulations.\"\n    },\n    /* ── 5xx ── */\n    {\n      code: 500, name: \"Internal Server Error\",\n      desc: \"The server encountered an unexpected condition.\",\n      detail: \"A generic error message given when an unexpected condition was encountered and no more specific message is suitable. The server encountered an error it didn't know how to handle. It is a catch-all response for server-side errors.\",\n      usecase: \"An unhandled exception in server code, a database query that throws an error, or a misconfigured server.\"\n    },\n    {\n      code: 501, name: \"Not Implemented\",\n      desc: \"The server does not support the request method.\",\n      detail: \"The server does not support the functionality required to fulfill the request. This is the appropriate response when the server does not recognize the request method and is not capable of supporting it for any resource.\",\n      usecase: \"A server that only implements GET and POST returns 501 when it receives a PATCH request it doesn't support.\"\n    },\n    {\n      code: 502, name: \"Bad Gateway\",\n      desc: \"The server got an invalid response from an upstream server.\",\n      detail: \"The server, while acting as a gateway or proxy, received an invalid response from an upstream server it accessed in attempting to fulfill the request. The upstream server is the problem, not the proxy itself.\",\n      usecase: \"A reverse proxy (Nginx) cannot reach the application server (Node.js), or the application server crashes and returns garbage.\"\n    },\n    {\n      code: 503, name: \"Service Unavailable\",\n      desc: \"The server is not ready to handle the request.\",\n      detail: \"The server is not ready to handle the request. Common causes include a server that is down for maintenance or that is overloaded. A Retry-After header should be included if the service is temporarily unavailable.\",\n      usecase: \"Server under maintenance, database connection pool exhausted, or autoscaling hasn't kicked in yet during a traffic spike.\"\n    },\n    {\n      code: 504, name: \"Gateway Timeout\",\n      desc: \"The gateway timed out waiting for an upstream server.\",\n      detail: \"The server, while acting as a gateway or proxy, did not receive a timely response from an upstream server it needed to access in order to complete the request. Different from 502 — the upstream server responded, just too slowly.\",\n      usecase: \"A load balancer waits too long for the application server to respond (e.g., slow database query causes timeout).\"\n    },\n    {\n      code: 505, name: \"HTTP Version Not Supported\",\n      desc: \"The HTTP version used is not supported.\",\n      detail: \"The server does not support the HTTP version used in the request. The response should contain a description of why that version is not supported and what other protocols are supported.\",\n      usecase: \"Sending an HTTP/3 request to a server that only supports up to HTTP/1.1.\"\n    },\n    {\n      code: 506, name: \"Variant Also Negotiates\",\n      desc: \"Internal server configuration error in content negotiation.\",\n      detail: \"Transparent content negotiation for the request results in a circular reference. Indicates an internal server configuration error.\",\n      usecase: \"Misconfigured content negotiation where a variant resource is itself configured to negotiate its content.\"\n    },\n    {\n      code: 507, name: \"Insufficient Storage\",\n      desc: \"The server cannot store the representation (WebDAV).\",\n      detail: \"The method could not be performed on the resource because the server is unable to store the representation needed to successfully complete the request. May be temporary.\",\n      usecase: \"WebDAV: uploading a file when the server's storage quota is full.\"\n    },\n    {\n      code: 508, name: \"Loop Detected\",\n      desc: \"The server detected an infinite loop (WebDAV).\",\n      detail: \"The server terminated an operation because it encountered an infinite loop while processing a request with Depth: infinity. Used in WebDAV.\",\n      usecase: \"WebDAV COPY or MOVE operation creates a circular directory reference.\"\n    },\n    {\n      code: 510, name: \"Not Extended\",\n      desc: \"Further extensions are required for the server to fulfill the request.\",\n      detail: \"Further extensions to the request are required for the server to fulfill it. Used with the HTTP Extension Framework (RFC 2774).\",\n      usecase: \"An HTTP extension specified in the request is not supported by the server.\"\n    },\n    {\n      code: 511, name: \"Network Authentication Required\",\n      desc: \"The client needs to authenticate to gain network access.\",\n      detail: \"Indicates that the client needs to authenticate to gain network access. Intended for use by intercepting proxies used to control access to the network (e.g., captive portals in hotels, airports).\",\n      usecase: \"Hotel WiFi portal: the browser is redirected to a login page. The intercepting proxy returns 511.\"\n    }\n  ];\n\n  var CAT_CLASS = {\n    \"1xx\": \"c1xx\", \"2xx\": \"c2xx\", \"3xx\": \"c3xx\", \"4xx\": \"c4xx\", \"5xx\": \"c5xx\"\n  };\n\n  function getCat(code) {\n    return Math.floor(code / 100) + \"xx\";\n  }\n\n  function buildCard(item) {\n    var cat = getCat(item.code);\n    var cls = CAT_CLASS[cat];\n    var isCommon = COMMON.indexOf(item.code) !== -1;\n    var id = \"hsc-\" + item.code;\n\n    var commonBadge = isCommon\n      ? '\u003cspan class=\"hsc-common-star\"\u003e\u0026#9733; Common\u003c/span\u003e'\n      : \"\";\n\n    return [\n      '\u003cdiv class=\"hsc-card ' + cls + (isCommon ? \" common\" : \"\") + '\" data-code=\"' + item.code + '\" data-cat=\"' + cat + '\" id=\"' + id + '\"\u003e',\n        '\u003cdiv class=\"hsc-card-header\" onclick=\"hscToggle(\\'' + id + '\\')\"\u003e',\n          '\u003cspan class=\"hsc-code-num ' + cls + '\"\u003e' + item.code + '\u003c/span\u003e',\n          '\u003cdiv class=\"hsc-card-meta\"\u003e',\n            '\u003cdiv class=\"hsc-code-name\"\u003e' + item.name + '\u003c/div\u003e',\n            '\u003cdiv class=\"hsc-code-desc\"\u003e' + item.desc + '\u003c/div\u003e',\n          '\u003c/div\u003e',\n          commonBadge,\n          '\u003cbutton class=\"hsc-copy-btn\" onclick=\"hscCopy(event, ' + item.code + ', \\'' + item.name + '\\')\" aria-label=\"Copy ' + item.code + ' ' + item.name + '\"\u003eCopy\u003c/button\u003e',\n          '\u003csvg class=\"hsc-chevron\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\u003e\u003cpolyline points=\"6 9 12 15 18 9\"/\u003e\u003c/svg\u003e',\n        '\u003c/div\u003e',\n        '\u003cdiv class=\"hsc-detail\"\u003e',\n          '\u003ch4\u003eDescription\u003c/h4\u003e',\n          '\u003cp\u003e' + item.detail + '\u003c/p\u003e","title":"HTTP Status Codes — Quick Reference Guide"},{"content":" Enter IBAN Number Validate Clear Try an example: DE (Germany) GB (UK) FR (France) ES (Spain) IT (Italy) NL (Netherlands) BE (Belgium) AT (Austria) CH (Switzerland) IE (Ireland) Invalid checksum IBAN Length by Country Flag Country Code Length Format Related Tools BIC / SWIFT Code Validator Currency Converter VAT Number Validator Routing Number Lookup Wire Transfer Fee Calculator ","permalink":"https://productivity-works.com/tools/iban-validator/","summary":"\u003cdiv id=\"iban-app\"\u003e\n\u003cstyle\u003e\n#iban-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 760px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#iban-app * { box-sizing: border-box; }\n#iban-app h2 {\n  font-size: 1.3rem;\n  font-weight: 700;\n  color: #1a1a2e;\n  margin: 1.5rem 0 0.75rem;\n}\n#iban-app .iban-input-group {\n  display: flex;\n  gap: 0.5rem;\n  margin-bottom: 1rem;\n}\n#iban-app #iban-input {\n  flex: 1;\n  padding: 0.75rem 1rem;\n  font-size: 1.1rem;\n  font-family: \"Courier New\", Courier, monospace;\n  border: 2px solid #cbd5e1;\n  border-radius: 8px;\n  outline: none;\n  letter-spacing: 0.05em;\n  transition: border-color 0.2s;\n  text-transform: uppercase;\n}\n#iban-app #iban-input:focus { border-color: #3b82f6; }\n#iban-app #iban-input.valid { border-color: #22c55e; }\n#iban-app #iban-input.invalid { border-color: #ef4444; }\n#iban-app .btn {\n  padding: 0.75rem 1.5rem;\n  font-size: 1rem;\n  font-weight: 600;\n  border: none;\n  border-radius: 8px;\n  cursor: pointer;\n  transition: background 0.2s, transform 0.1s;\n}\n#iban-app .btn:active { transform: scale(0.97); }\n#iban-app .btn-primary {\n  background: #3b82f6;\n  color: #fff;\n}\n#iban-app .btn-primary:hover { background: #2563eb; }\n#iban-app .btn-clear {\n  background: #f1f5f9;\n  color: #64748b;\n}\n#iban-app .btn-clear:hover { background: #e2e8f0; }\n#iban-app .result-box {\n  border-radius: 10px;\n  padding: 1.25rem 1.5rem;\n  margin-bottom: 1rem;\n  display: none;\n}\n#iban-app .result-box.visible { display: block; }\n#iban-app .result-valid {\n  background: #f0fdf4;\n  border: 2px solid #22c55e;\n}\n#iban-app .result-invalid {\n  background: #fef2f2;\n  border: 2px solid #ef4444;\n}\n#iban-app .result-status {\n  font-size: 1.15rem;\n  font-weight: 700;\n  margin-bottom: 0.5rem;\n}\n#iban-app .result-valid .result-status { color: #16a34a; }\n#iban-app .result-invalid .result-status { color: #dc2626; }\n#iban-app .result-msg {\n  font-size: 0.95rem;\n  color: #475569;\n}\n#iban-app .details-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));\n  gap: 0.75rem;\n  margin-top: 1.25rem;\n  display: none;\n}\n#iban-app .details-grid.visible { display: grid; }\n#iban-app .detail-card {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 8px;\n  padding: 0.75rem 1rem;\n}\n#iban-app .detail-label {\n  font-size: 0.75rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  color: #94a3b8;\n  margin-bottom: 0.25rem;\n}\n#iban-app .detail-value {\n  font-size: 1rem;\n  font-weight: 600;\n  color: #1e293b;\n  word-break: break-all;\n}\n#iban-app .detail-value.mono {\n  font-family: \"Courier New\", Courier, monospace;\n  font-size: 0.95rem;\n}\n#iban-app .country-flag {\n  font-size: 1.4rem;\n  margin-right: 0.35rem;\n}\n#iban-app .examples-section {\n  margin-top: 1.5rem;\n}\n#iban-app .examples-label {\n  font-size: 0.85rem;\n  color: #64748b;\n  margin-bottom: 0.5rem;\n}\n#iban-app .example-chips {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.4rem;\n}\n#iban-app .chip {\n  padding: 0.3rem 0.75rem;\n  background: #eff6ff;\n  border: 1px solid #bfdbfe;\n  border-radius: 999px;\n  font-size: 0.82rem;\n  font-family: \"Courier New\", Courier, monospace;\n  color: #1d4ed8;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n#iban-app .chip:hover {\n  background: #dbeafe;\n}\n#iban-app table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.9rem;\n  margin-top: 0.5rem;\n}\n#iban-app thead th {\n  background: #f1f5f9;\n  padding: 0.6rem 0.9rem;\n  text-align: left;\n  font-weight: 600;\n  color: #475569;\n  border-bottom: 2px solid #e2e8f0;\n}\n#iban-app tbody td {\n  padding: 0.55rem 0.9rem;\n  border-bottom: 1px solid #f1f5f9;\n  color: #334155;\n}\n#iban-app tbody tr:hover td { background: #f8fafc; }\n#iban-app .related-links {\n  margin-top: 2rem;\n  padding-top: 1.25rem;\n  border-top: 1px solid #e2e8f0;\n}\n#iban-app .related-links h3 {\n  font-size: 1rem;\n  font-weight: 600;\n  color: #475569;\n  margin-bottom: 0.75rem;\n}\n#iban-app .related-links ul {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem;\n}\n#iban-app .related-links ul li a {\n  display: inline-block;\n  padding: 0.35rem 0.85rem;\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 6px;\n  color: #3b82f6;\n  text-decoration: none;\n  font-size: 0.88rem;\n  transition: background 0.15s;\n}\n#iban-app .related-links ul li a:hover {\n  background: #eff6ff;\n  border-color: #bfdbfe;\n}\n@media (max-width: 520px) {\n  #iban-app .iban-input-group { flex-direction: column; }\n  #iban-app .btn { width: 100%; }\n}\n\u003c/style\u003e\n\u003ch2\u003eEnter IBAN Number\u003c/h2\u003e\n\u003cdiv class=\"iban-input-group\"\u003e\n  \u003cinput\n    id=\"iban-input\"\n    type=\"text\"\n    placeholder=\"e.g. DE89 3704 0044 0532 0130 00\"\n    maxlength=\"42\"\n    autocomplete=\"off\"\n    spellcheck=\"false\"\n  /\u003e\n  \u003cbutton class=\"btn btn-primary\" onclick=\"ibanValidate()\"\u003eValidate\u003c/button\u003e\n  \u003cbutton class=\"btn btn-clear\" onclick=\"ibanClear()\"\u003eClear\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv id=\"iban-result\" class=\"result-box\"\u003e\u003c/div\u003e\n\u003cdiv id=\"iban-details\" class=\"details-grid\"\u003e\u003c/div\u003e\n\u003cdiv class=\"examples-section\"\u003e\n  \u003cdiv class=\"examples-label\"\u003eTry an example:\u003c/div\u003e\n  \u003cdiv class=\"example-chips\"\u003e\n    \u003cspan class=\"chip\" onclick=\"ibanSetExample('DE89370400440532013000')\"\u003eDE (Germany)\u003c/span\u003e\n    \u003cspan class=\"chip\" onclick=\"ibanSetExample('GB29NWBK60161331926819')\"\u003eGB (UK)\u003c/span\u003e\n    \u003cspan class=\"chip\" onclick=\"ibanSetExample('FR7630006000011234567890189')\"\u003eFR (France)\u003c/span\u003e\n    \u003cspan class=\"chip\" onclick=\"ibanSetExample('ES9121000418450200051332')\"\u003eES (Spain)\u003c/span\u003e\n    \u003cspan class=\"chip\" onclick=\"ibanSetExample('IT60X0542811101000000123456')\"\u003eIT (Italy)\u003c/span\u003e\n    \u003cspan class=\"chip\" onclick=\"ibanSetExample('NL91ABNA0417164300')\"\u003eNL (Netherlands)\u003c/span\u003e\n    \u003cspan class=\"chip\" onclick=\"ibanSetExample('BE68539007547034')\"\u003eBE (Belgium)\u003c/span\u003e\n    \u003cspan class=\"chip\" onclick=\"ibanSetExample('AT611904300234573201')\"\u003eAT (Austria)\u003c/span\u003e\n    \u003cspan class=\"chip\" onclick=\"ibanSetExample('CH9300762011623852957')\"\u003eCH (Switzerland)\u003c/span\u003e\n    \u003cspan class=\"chip\" onclick=\"ibanSetExample('IE29AIBK93115212345678')\"\u003eIE (Ireland)\u003c/span\u003e\n    \u003cspan class=\"chip\" onclick=\"ibanSetExample('DE00370400440532013000')\"\u003eInvalid checksum\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003ch2\u003eIBAN Length by Country\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n      \u003cth\u003eFlag\u003c/th\u003e\n      \u003cth\u003eCountry\u003c/th\u003e\n      \u003cth\u003eCode\u003c/th\u003e\n      \u003cth\u003eLength\u003c/th\u003e\n      \u003cth\u003eFormat\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody id=\"iban-country-table\"\u003e\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cdiv class=\"related-links\"\u003e\n  \u003ch3\u003eRelated Tools\u003c/h3\u003e\n  \u003cul\u003e\n    \u003cli\u003e\u003ca href=\"/tools/iban-validator/\"\u003eBIC / SWIFT Code Validator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/currency-converter/\"\u003eCurrency Converter\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/tax-calculator/\"\u003eVAT Number Validator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/iban-validator/\"\u003eRouting Number Lookup\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/currency-converter/\"\u003eWire Transfer Fee Calculator\u003c/a\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  const COUNTRIES = [\n    { code: \"AD\", name: \"Andorra\",         flag: \"🇦🇩\", length: 24, format: \"ADkk BBBB SSSS CCCC CCCC CCCC\",  bankLen: 4, branchLen: 4 },\n    { code: \"AT\", name: \"Austria\",         flag: \"🇦🇹\", length: 20, format: \"ATkk BBBB BCCC CCCC CCCC\",        bankLen: 5, branchLen: 0 },\n    { code: \"BE\", name: \"Belgium\",         flag: \"🇧🇪\", length: 16, format: \"BEkk BBBC CCCC CCXX\",             bankLen: 3, branchLen: 0 },\n    { code: \"CH\", name: \"Switzerland\",     flag: \"🇨🇭\", length: 21, format: \"CHkk BBBB BCCC CCCC CCCC C\",      bankLen: 5, branchLen: 0 },\n    { code: \"CY\", name: \"Cyprus\",          flag: \"🇨🇾\", length: 28, format: \"CYkk BBBS SSSS CCCC CCCC CCCC CCCC\", bankLen: 3, branchLen: 5 },\n    { code: \"CZ\", name: \"Czech Republic\",  flag: \"🇨🇿\", length: 24, format: \"CZkk BBBB SSSS SSCC CCCC CCCC\",   bankLen: 4, branchLen: 6 },\n    { code: \"DE\", name: \"Germany\",         flag: \"🇩🇪\", length: 22, format: \"DEkk BBBB BBBB CCCC CCCC CC\",     bankLen: 8, branchLen: 0 },\n    { code: \"DK\", name: \"Denmark\",         flag: \"🇩🇰\", length: 18, format: \"DKkk BBBB CCCC CCCC CC\",          bankLen: 4, branchLen: 0 },\n    { code: \"EE\", name: \"Estonia\",         flag: \"🇪🇪\", length: 20, format: \"EEkk BBSS CCCC CCCC CCCK\",        bankLen: 2, branchLen: 2 },\n    { code: \"ES\", name: \"Spain\",           flag: \"🇪🇸\", length: 24, format: \"ESkk BBBB SSSS KKCC CCCC CCCC\",   bankLen: 4, branchLen: 4 },\n    { code: \"FI\", name: \"Finland\",         flag: \"🇫🇮\", length: 18, format: \"FIkk BBBB BBCC CCCC CK\",          bankLen: 6, branchLen: 0 },\n    { code: \"FR\", name: \"France\",          flag: \"🇫🇷\", length: 27, format: \"FRkk BBBB BSSS SSCC CCCC CCCC CKK\", bankLen: 5, branchLen: 5 },\n    { code: \"GB\", name: \"United Kingdom\",  flag: \"🇬🇧\", length: 22, format: \"GBkk BBBB SSSS SSCC CCCC CC\",     bankLen: 4, branchLen: 6 },\n    { code: \"GR\", name: \"Greece\",          flag: \"🇬🇷\", length: 27, format: \"GRkk BBBS SSSC CCCC CCCC CCCC CCC\", bankLen: 3, branchLen: 4 },\n    { code: \"HR\", name: \"Croatia\",         flag: \"🇭🇷\", length: 21, format: \"HRkk BBBB BBBC CCCC CCCC C\",      bankLen: 7, branchLen: 0 },\n    { code: \"HU\", name: \"Hungary\",         flag: \"🇭🇺\", length: 28, format: \"HUkk BBBS SSSK CCCC CCCC CCCC CCCK\", bankLen: 3, branchLen: 4 },\n    { code: \"IE\", name: \"Ireland\",         flag: \"🇮🇪\", length: 22, format: \"IEkk AAAA BBBB BBCC CCCC CC\",     bankLen: 4, branchLen: 6 },\n    { code: \"IT\", name: \"Italy\",           flag: \"🇮🇹\", length: 27, format: \"ITkk KBBB BBSS SSSC CCCC CCCC CCC\", bankLen: 5, branchLen: 5 },\n    { code: \"LT\", name: \"Lithuania\",       flag: \"🇱🇹\", length: 20, format: \"LTkk BBBB BCCC CCCC CCCC\",        bankLen: 5, branchLen: 0 },\n    { code: \"LU\", name: \"Luxembourg\",      flag: \"🇱🇺\", length: 20, format: \"LUkk BBBС CCCC CCCC CCCC\",        bankLen: 3, branchLen: 0 },\n    { code: \"LV\", name: \"Latvia\",          flag: \"🇱🇻\", length: 21, format: \"LVkk BBBB CCCC CCCC CCCC C\",      bankLen: 4, branchLen: 0 },\n    { code: \"MT\", name: \"Malta\",           flag: \"🇲🇹\", length: 31, format: \"MTkk BBBB SSSS SCCC CCCC CCCC CCCC CCC\", bankLen: 4, branchLen: 5 },\n    { code: \"NL\", name: \"Netherlands\",     flag: \"🇳🇱\", length: 18, format: \"NLkk BBBB CCCC CCCC CC\",          bankLen: 4, branchLen: 0 },\n    { code: \"NO\", name: \"Norway\",          flag: \"🇳🇴\", length: 15, format: \"NOkk BBBB CCCC CCK\",              bankLen: 4, branchLen: 0 },\n    { code: \"PL\", name: \"Poland\",          flag: \"🇵🇱\", length: 28, format: \"PLkk BBBS SSSK CCCC CCCC CCCC CCCC\", bankLen: 3, branchLen: 4 },\n    { code: \"PT\", name: \"Portugal\",        flag: \"🇵🇹\", length: 25, format: \"PTkk BBBB SSSS CCCC CCCC CCCK K\",  bankLen: 4, branchLen: 4 },\n    { code: \"RO\", name: \"Romania\",         flag: \"🇷🇴\", length: 24, format: \"ROkk BBBB CCCC CCCC CCCC CCCC\",   bankLen: 4, branchLen: 0 },\n    { code: \"SE\", name: \"Sweden\",          flag: \"🇸🇪\", length: 24, format: \"SEkk BBBC CCCC CCCC CCCC CCCK\",   bankLen: 3, branchLen: 0 },\n    { code: \"SI\", name: \"Slovenia\",        flag: \"🇸🇮\", length: 19, format: \"SIkk BBSS SCCC CCCC CKK\",         bankLen: 2, branchLen: 3 },\n    { code: \"SK\", name: \"Slovakia\",        flag: \"🇸🇰\", length: 24, format: \"SKkk BBBB SSSS SSCC CCCC CCCC\",   bankLen: 4, branchLen: 6 },\n  ];\n\n  function buildTable() {\n    const tbody = document.getElementById(\"iban-country-table\");\n    COUNTRIES.forEach(c =\u003e {\n      const tr = document.createElement(\"tr\");\n      tr.innerHTML = `\u003ctd\u003e${c.flag}\u003c/td\u003e\u003ctd\u003e${c.name}\u003c/td\u003e\u003ctd\u003e\u003ccode\u003e${c.code}\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e${c.length}\u003c/td\u003e\u003ctd style=\"font-family:monospace;font-size:0.8rem;\"\u003e${c.format}\u003c/td\u003e`;\n      tbody.appendChild(tr);\n    });\n  }\n\n  function getCountry(code) {\n    return COUNTRIES.find(c =\u003e c.code === code.toUpperCase()) || null;\n  }\n\n  function mod97(iban) {\n    const rearranged = iban.slice(4) + iban.slice(0, 4);\n    const numeric = rearranged.split(\"\").map(ch =\u003e {\n      const code = ch.charCodeAt(0);\n      return code \u003e= 65 \u0026\u0026 code \u003c= 90 ? String(code - 55) : ch;\n    }).join(\"\");\n    let remainder = 0;\n    for (let i = 0; i \u003c numeric.length; i++) {\n      remainder = (remainder * 10 + parseInt(numeric[i], 10)) % 97;\n    }\n    return remainder;\n  }\n\n  function formatIban(raw) {\n    return raw.replace(/(.{4})/g, \"$1 \").trim();\n  }\n\n  function extractDetails(iban, country) {\n    const bban = iban.slice(4);\n    const bankCode = country ? bban.slice(0, country.bankLen) : \"\";\n    const branchCode = country \u0026\u0026 country.branchLen \u003e 0 ? bban.slice(country.bankLen, country.bankLen + country.branchLen) : \"\";\n    const accountStart = country ? country.bankLen + country.branchLen : 4;\n    const accountNum = bban.slice(accountStart);\n    return { bankCode, branchCode, accountNum };\n  }\n\n  window.ibanValidate = function() {\n    const raw = document.getElementById(\"iban-input\").value.replace(/\\s+/g, \"\").toUpperCase();\n    const input = document.getElementById(\"iban-input\");\n    const resultBox = document.getElementById(\"iban-result\");\n    const detailsGrid = document.getElementById(\"iban-details\");\n\n    if (!raw) {\n      resultBox.className = \"result-box result-invalid visible\";\n      resultBox.innerHTML = `\u003cdiv class=\"result-status\"\u003e\u0026#10007; No Input\u003c/div\u003e\u003cdiv class=\"result-msg\"\u003ePlease enter an IBAN number to validate.\u003c/div\u003e`;\n      input.className = \"invalid\";\n      detailsGrid.className = \"details-grid\";\n      return;\n    }\n\n    if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]+$/.test(raw)) {\n      resultBox.className = \"result-box result-invalid visible\";\n      resultBox.innerHTML = `\u003cdiv class=\"result-status\"\u003e\u0026#10007; Invalid Format\u003c/div\u003e\u003cdiv class=\"result-msg\"\u003eIBAN must start with a 2-letter country code followed by 2 check digits and alphanumeric characters only.\u003c/div\u003e`;\n      input.className = \"invalid\";\n      detailsGrid.className = \"details-grid\";\n      return;\n    }\n\n    const countryCode = raw.slice(0, 2);\n    const checkDigits = raw.slice(2, 4);\n    const country = getCountry(countryCode);\n\n    if (country \u0026\u0026 raw.length !== country.length) {\n      resultBox.className = \"result-box result-invalid visible\";\n      resultBox.innerHTML = `\u003cdiv class=\"result-status\"\u003e\u0026#10007; Wrong Length\u003c/div\u003e\u003cdiv class=\"result-msg\"\u003e${country.flag} ${country.name} IBANs must be exactly ${country.length} characters. You entered ${raw.length}.\u003c/div\u003e`;\n      input.className = \"invalid\";\n      detailsGrid.className = \"details-grid\";\n      return;\n    }\n\n    const remainder = mod97(raw);\n    if (remainder !== 1) {\n      resultBox.className = \"result-box result-invalid visible\";\n      resultBox.innerHTML = `\u003cdiv class=\"result-status\"\u003e\u0026#10007; Invalid Checksum\u003c/div\u003e\u003cdiv class=\"result-msg\"\u003eThe MOD 97 checksum failed (got ${remainder}, expected 1). The IBAN may contain a typo or the check digits are incorrect.\u003c/div\u003e`;\n      input.className = \"invalid\";\n      detailsGrid.className = \"details-grid\";\n      return;\n    }\n\n    // Valid\n    input.className = \"valid\";\n    const flagHtml = country ? `\u003cspan class=\"country-flag\"\u003e${country.flag}\u003c/span\u003e` : \"\";\n    const countryName = country ? country.name : \"Unknown Country\";\n    resultBox.className = \"result-box result-valid visible\";\n    resultBox.innerHTML = `\u003cdiv class=\"result-status\"\u003e\u0026#10003; Valid IBAN\u003c/div\u003e\u003cdiv class=\"result-msg\"\u003e${flagHtml}${countryName} \u0026mdash; Checksum verified (MOD 97 = 1). Formatted: \u003cstrong style=\"font-family:monospace\"\u003e${formatIban(raw)}\u003c/strong\u003e\u003c/div\u003e`;\n\n    const { bankCode, branchCode, accountNum } = extractDetails(raw, country);\n\n    const cards = [\n      { label: \"Country\", value: `${country ? country.flag + \" \" : \"\"}${countryName}` },\n      { label: \"Country Code\", value: countryCode, mono: true },\n      { label: \"Check Digits\", value: checkDigits, mono: true },\n      { label: \"IBAN (formatted)\", value: formatIban(raw), mono: true },\n      { label: \"BBAN\", value: raw.slice(4), mono: true },\n    ];\n    if (bankCode) cards.push({ label: \"Bank Code\", value: bankCode, mono: true });\n    if (branchCode) cards.push({ label: \"Branch Code\", value: branchCode, mono: true });\n    if (accountNum) cards.push({ label: \"Account Number\", value: accountNum, mono: true });\n    if (country) cards.push({ label: \"IBAN Length\", value: `${country.length} characters` });\n\n    detailsGrid.innerHTML = cards.map(c =\u003e\n      `\u003cdiv class=\"detail-card\"\u003e\u003cdiv class=\"detail-label\"\u003e${c.label}\u003c/div\u003e\u003cdiv class=\"detail-value${c.mono ? \" mono\" : \"\"}\"\u003e${c.value}\u003c/div\u003e\u003c/div\u003e`\n    ).join(\"\");\n    detailsGrid.className = \"details-grid visible\";\n  };\n\n  window.ibanClear = function() {\n    const input = document.getElementById(\"iban-input\");\n    input.value = \"\";\n    input.className = \"\";\n    document.getElementById(\"iban-result\").className = \"result-box\";\n    document.getElementById(\"iban-details\").className = \"details-grid\";\n    input.focus();\n  };\n\n  window.ibanSetExample = function(val) {\n    const input = document.getElementById(\"iban-input\");\n    input.value = val;\n    input.className = \"\";\n    ibanValidate();\n  };\n\n  // Auto-format input as groups of 4\n  document.getElementById(\"iban-input\").addEventListener(\"input\", function(e) {\n    const pos = this.selectionStart;\n    const raw = this.value.replace(/\\s+/g, \"\").toUpperCase();\n    const formatted = raw.replace(/(.{4})(?=.)/g, \"$1 \");\n    if (this.value !== formatted) {\n      this.value = formatted;\n    }\n    this.className = \"\";\n    document.getElementById(\"iban-result\").className = \"result-box\";\n    document.getElementById(\"iban-details\").className = \"details-grid\";\n  });\n\n  document.getElementById(\"iban-input\").addEventListener(\"keydown\", function(e) {\n    if (e.key === \"Enter\") ibanValidate();\n  });\n\n  buildTable();\n})();\n\u003c/script\u003e\n\u003c/div\u003e","title":"IBAN Validator - Check International Bank Account Numbers"},{"content":"Upload any image and click to extract exact colors in HEX, RGB, and HSL — instantly, in your browser. No account, no installs, no data ever leaves your device.\nRelated tools: Color Picker · Color Palette Generator · Color Converter Drop image here or Browse file PNG, JPG, GIF, WebP, SVG — stays in your browser Click anywhere on the image to pick a color. Hover to see the magnifier. No color picked yet — click the image above. HEX — Copy RGB — Copy HSL — Copy Nearest name: — Picked Colors Export as CSS Variables Export as JSON Copy How It Works Upload — drag an image onto the canvas or click \u0026ldquo;Browse file.\u0026rdquo; Hover — a magnifier circle shows a zoomed view for pixel-perfect picking. Click — instantly see HEX, RGB, HSL, and the nearest CSS color name. History — up to 12 swatches are saved. Click any swatch to reload its values. Export — download your palette as CSS custom properties or a JSON object. Everything runs locally in your browser. No image data is sent anywhere.\nTips Zoom in first — use your browser\u0026rsquo;s native zoom (Ctrl/Cmd +) if you need finer control before clicking. SVG images — SVGs are supported if the browser can render them to a canvas (same-origin or inline SVGs without \u0026lt;foreignObject\u0026gt;). Transparent pixels — fully transparent areas return rgb(0, 0, 0) since canvas composites against black by default. Export CSS variables — paste directly into your :root {} block and reference colors as var(--color-1) throughout your stylesheet. More color tools: Color Picker · Color Palette Generator · Color Converter ","permalink":"https://productivity-works.com/tools/image-color-picker/","summary":"\u003cp\u003eUpload any image and click to extract exact colors in HEX, RGB, and HSL — instantly, in your browser. No account, no installs, no data ever leaves your device.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eRelated tools:\u003c/strong\u003e \u003ca href=\"https://productivity-works.com/tools/color-picker/\"\u003eColor Picker\u003c/a\u003e\n · \u003ca href=\"https://productivity-works.com/tools/color-palette-generator/\"\u003eColor Palette Generator\u003c/a\u003e\n · \u003ca href=\"https://productivity-works.com/tools/color-converter/\"\u003eColor Converter\u003c/a\u003e\n\u003c/p\u003e\n\u003chr\u003e\n\u003cdiv id=\"icp-app\"\u003e\n\u003cstyle\u003e\n#icp-app *,#icp-app *::before,#icp-app *::after{box-sizing:border-box;margin:0;padding:0}\n#icp-app{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:#1e293b;background:#f8fafc;border-radius:14px;padding:24px;max-width:860px;margin:0 auto}\n#icp-app h2{font-size:18px;font-weight:700;margin-bottom:4px}\n#icp-drop-zone{border:2.5px dashed #94a3b8;border-radius:12px;background:#fff;min-height:200px;display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:pointer;transition:border-color .2s,background .2s;position:relative;overflow:hidden;margin-bottom:16px}\n#icp-drop-zone.drag-over{border-color:#6366f1;background:#eef2ff}\n#icp-drop-zone.has-image{min-height:unset;cursor:crosshair}\n#icp-drop-label{display:flex;flex-direction:column;align-items:center;gap:10px;color:#64748b;pointer-events:none;user-select:none}\n#icp-drop-label svg{width:48px;height:48px;opacity:.5}\n#icp-drop-label span{font-size:15px}\n#icp-drop-label small{font-size:12px;color:#94a3b8}\n#icp-file-input{display:none}\n#icp-browse-btn{pointer-events:auto;padding:8px 20px;background:#6366f1;color:#fff;border:none;border-radius:7px;font-size:13px;font-weight:600;cursor:pointer;transition:background .15s}\n#icp-browse-btn:hover{background:#4f46e5}\n#icp-canvas-wrap{position:relative;display:inline-block;width:100%;line-height:0}\n#icp-canvas{display:block;width:100%;height:auto;cursor:crosshair;border-radius:8px}\n#icp-magnifier{position:absolute;width:96px;height:96px;border-radius:50%;border:3px solid #6366f1;box-shadow:0 4px 20px rgba(0,0,0,.35);pointer-events:none;overflow:hidden;display:none;background:#000;z-index:10}\n#icp-mag-canvas{position:absolute;top:0;left:0}\n#icp-mag-crosshair{position:absolute;top:50%;left:50%;width:12px;height:12px;transform:translate(-50%,-50%);pointer-events:none}\n#icp-mag-crosshair::before,#icp-mag-crosshair::after{content:'';position:absolute;background:rgba(255,255,255,.9)}\n#icp-mag-crosshair::before{width:1.5px;height:100%;left:50%;transform:translateX(-50%)}\n#icp-mag-crosshair::after{height:1.5px;width:100%;top:50%;transform:translateY(-50%)}\n#icp-result-row{display:flex;gap:14px;margin-bottom:16px;flex-wrap:wrap;align-items:stretch}\n#icp-color-preview{width:90px;min-height:90px;border-radius:10px;border:2px solid #e2e8f0;flex-shrink:0;background:#e2e8f0;transition:background .15s}\n#icp-formats{flex:1;display:flex;flex-direction:column;gap:8px;min-width:200px}\n.icp-format-row{display:flex;align-items:center;gap:8px}\n.icp-format-label{font-size:11px;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.05em;width:34px;flex-shrink:0}\n.icp-format-val{flex:1;font-size:13px;font-family:'SFMono-Regular',Consolas,monospace;background:#f1f5f9;padding:6px 10px;border-radius:6px;border:1px solid #e2e8f0;color:#1e293b;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}\n.icp-copy-btn{padding:5px 12px;font-size:12px;font-weight:600;background:#6366f1;color:#fff;border:none;border-radius:5px;cursor:pointer;transition:background .15s,transform .1s;white-space:nowrap;flex-shrink:0}\n.icp-copy-btn:hover{background:#4f46e5}\n.icp-copy-btn.copied{background:#22c55e;transform:scale(1.05)}\n#icp-name-row{font-size:13px;color:#64748b;margin-top:4px}\n#icp-name-row span{font-weight:600;color:#1e293b}\n#icp-history-section{margin-top:4px}\n#icp-history-section h3{font-size:14px;font-weight:700;color:#475569;margin-bottom:8px}\n#icp-swatches{display:flex;flex-wrap:wrap;gap:8px}\n.icp-swatch{width:36px;height:36px;border-radius:7px;border:2px solid #e2e8f0;cursor:pointer;transition:transform .15s,box-shadow .15s;flex-shrink:0;position:relative}\n.icp-swatch:hover{transform:scale(1.18);box-shadow:0 4px 12px rgba(0,0,0,.2);z-index:5}\n.icp-swatch-tip{display:none;position:absolute;bottom:calc(100% + 6px);left:50%;transform:translateX(-50%);background:#1e293b;color:#fff;font-size:11px;padding:3px 7px;border-radius:4px;white-space:nowrap;pointer-events:none;z-index:20}\n.icp-swatch:hover .icp-swatch-tip{display:block}\n#icp-export-row{display:flex;gap:10px;margin-top:16px;flex-wrap:wrap}\n.icp-export-btn{padding:8px 18px;font-size:13px;font-weight:600;border-radius:7px;cursor:pointer;border:1.5px solid #6366f1;transition:background .15s,color .15s}\n#icp-export-css{background:#6366f1;color:#fff}\n#icp-export-css:hover{background:#4f46e5}\n#icp-export-json{background:#fff;color:#6366f1}\n#icp-export-json:hover{background:#eef2ff}\n#icp-export-out{display:none;margin-top:12px;background:#1e293b;color:#e2e8f0;border-radius:8px;padding:14px 16px;font-family:'SFMono-Regular',Consolas,monospace;font-size:12px;white-space:pre-wrap;word-break:break-all;position:relative;max-height:220px;overflow-y:auto}\n#icp-export-copy{position:absolute;top:8px;right:8px;padding:4px 10px;font-size:11px;font-weight:600;background:#6366f1;color:#fff;border:none;border-radius:4px;cursor:pointer}\n#icp-export-copy:hover{background:#4f46e5}\n#icp-instructions{font-size:13px;color:#64748b;margin-bottom:10px;display:none}\n#icp-no-pick-msg{font-size:13px;color:#94a3b8;font-style:italic;margin-top:6px}\n@media(max-width:520px){\n  #icp-result-row{flex-direction:column}\n  #icp-color-preview{width:100%;min-height:60px}\n  #icp-magnifier{width:72px;height:72px}\n}\n\u003c/style\u003e\n\u003cdiv style=\"margin-bottom:12px\"\u003e\n  \u003cinput type=\"file\" id=\"icp-file-input\" accept=\"image/*\"\u003e\n  \u003cdiv id=\"icp-drop-zone\"\u003e\n    \u003cdiv id=\"icp-drop-label\"\u003e\n      \u003csvg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\u003e\u003crect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\"/\u003e\u003ccircle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/\u003e\u003cpath d=\"M21 15l-5-5L5 21\"/\u003e\u003c/svg\u003e\n      \u003cspan\u003eDrop image here or\u003c/span\u003e\n      \u003cbutton id=\"icp-browse-btn\" type=\"button\"\u003eBrowse file\u003c/button\u003e\n      \u003csmall\u003ePNG, JPG, GIF, WebP, SVG — stays in your browser\u003c/small\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"icp-instructions\"\u003eClick anywhere on the image to pick a color. Hover to see the magnifier.\u003c/div\u003e\n\u003cdiv id=\"icp-canvas-wrap\" style=\"display:none\"\u003e\n  \u003ccanvas id=\"icp-canvas\"\u003e\u003c/canvas\u003e\n  \u003cdiv id=\"icp-magnifier\"\u003e\n    \u003ccanvas id=\"icp-mag-canvas\"\u003e\u003c/canvas\u003e\n    \u003cdiv id=\"icp-mag-crosshair\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"icp-no-pick-msg\" style=\"display:none\"\u003eNo color picked yet — click the image above.\u003c/div\u003e\n\u003cdiv id=\"icp-result-row\" style=\"display:none\"\u003e\n  \u003cdiv id=\"icp-color-preview\"\u003e\u003c/div\u003e\n  \u003cdiv id=\"icp-formats\"\u003e\n    \u003cdiv class=\"icp-format-row\"\u003e\n      \u003cspan class=\"icp-format-label\"\u003eHEX\u003c/span\u003e\n      \u003cspan class=\"icp-format-val\" id=\"icp-hex\"\u003e—\u003c/span\u003e\n      \u003cbutton class=\"icp-copy-btn\" data-target=\"icp-hex\"\u003eCopy\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"icp-format-row\"\u003e\n      \u003cspan class=\"icp-format-label\"\u003eRGB\u003c/span\u003e\n      \u003cspan class=\"icp-format-val\" id=\"icp-rgb\"\u003e—\u003c/span\u003e\n      \u003cbutton class=\"icp-copy-btn\" data-target=\"icp-rgb\"\u003eCopy\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"icp-format-row\"\u003e\n      \u003cspan class=\"icp-format-label\"\u003eHSL\u003c/span\u003e\n      \u003cspan class=\"icp-format-val\" id=\"icp-hsl\"\u003e—\u003c/span\u003e\n      \u003cbutton class=\"icp-copy-btn\" data-target=\"icp-hsl\"\u003eCopy\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv id=\"icp-name-row\"\u003eNearest name: \u003cspan id=\"icp-name\"\u003e—\u003c/span\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"icp-history-section\" style=\"display:none\"\u003e\n  \u003ch3\u003ePicked Colors \u003cspan id=\"icp-count\" style=\"font-weight:400;color:#94a3b8;font-size:12px\"\u003e\u003c/span\u003e\u003c/h3\u003e\n  \u003cdiv id=\"icp-swatches\"\u003e\u003c/div\u003e\n  \u003cdiv id=\"icp-export-row\"\u003e\n    \u003cbutton class=\"icp-export-btn\" id=\"icp-export-css\" type=\"button\"\u003eExport as CSS Variables\u003c/button\u003e\n    \u003cbutton class=\"icp-export-btn\" id=\"icp-export-json\" type=\"button\"\u003eExport as JSON\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv id=\"icp-export-out\"\u003e\u003cbutton id=\"icp-export-copy\" type=\"button\"\u003eCopy\u003c/button\u003e\u003cspan id=\"icp-export-text\"\u003e\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n'use strict';\nvar dropZone=document.getElementById('icp-drop-zone');\nvar fileInput=document.getElementById('icp-file-input');\nvar browseBtn=document.getElementById('icp-browse-btn');\nvar canvasWrap=document.getElementById('icp-canvas-wrap');\nvar canvas=document.getElementById('icp-canvas');\nvar ctx=canvas.getContext('2d',{willReadFrequently:true});\nvar mag=document.getElementById('icp-magnifier');\nvar magCanvas=document.getElementById('icp-mag-canvas');\nvar magCtx=magCanvas.getContext('2d',{willReadFrequently:true});\nvar instructions=document.getElementById('icp-instructions');\nvar nopick=document.getElementById('icp-no-pick-msg');\nvar resultRow=document.getElementById('icp-result-row');\nvar preview=document.getElementById('icp-color-preview');\nvar hexEl=document.getElementById('icp-hex');\nvar rgbEl=document.getElementById('icp-rgb');\nvar hslEl=document.getElementById('icp-hsl');\nvar nameEl=document.getElementById('icp-name');\nvar historySec=document.getElementById('icp-history-section');\nvar swatchesEl=document.getElementById('icp-swatches');\nvar countEl=document.getElementById('icp-count');\nvar exportOut=document.getElementById('icp-export-out');\nvar exportText=document.getElementById('icp-export-text');\nvar exportCopy=document.getElementById('icp-export-copy');\n\nvar MAG_SIZE=96,MAG_ZOOM=6,MAX_HISTORY=12;\nvar history=[];\nvar imgObj=null;\nvar naturalW=0,naturalH=0;\nvar hasPicked=false;\n\nmagCanvas.width=MAG_SIZE;magCanvas.height=MAG_SIZE;\n\n// CSS named colors (nearest match)\nvar CSS_COLORS=[\n  ['aliceblue',[240,248,255]],['antiquewhite',[250,235,215]],['aqua',[0,255,255]],\n  ['aquamarine',[127,255,212]],['azure',[240,255,255]],['beige',[245,245,220]],\n  ['bisque',[255,228,196]],['black',[0,0,0]],['blanchedalmond',[255,235,205]],\n  ['blue',[0,0,255]],['blueviolet',[138,43,226]],['brown',[165,42,42]],\n  ['burlywood',[222,184,135]],['cadetblue',[95,158,160]],['chartreuse',[127,255,0]],\n  ['chocolate',[210,105,30]],['coral',[255,127,80]],['cornflowerblue',[100,149,237]],\n  ['cornsilk',[255,248,220]],['crimson',[220,20,60]],['cyan',[0,255,255]],\n  ['darkblue',[0,0,139]],['darkcyan',[0,139,139]],['darkgoldenrod',[184,134,11]],\n  ['darkgray',[169,169,169]],['darkgreen',[0,100,0]],['darkkhaki',[189,183,107]],\n  ['darkmagenta',[139,0,139]],['darkolivegreen',[85,107,47]],['darkorange',[255,140,0]],\n  ['darkorchid',[153,50,204]],['darkred',[139,0,0]],['darksalmon',[233,150,122]],\n  ['darkseagreen',[143,188,143]],['darkslateblue',[72,61,139]],['darkslategray',[47,79,79]],\n  ['darkturquoise',[0,206,209]],['darkviolet',[148,0,211]],['deeppink',[255,20,147]],\n  ['deepskyblue',[0,191,255]],['dimgray',[105,105,105]],['dodgerblue',[30,144,255]],\n  ['firebrick',[178,34,34]],['floralwhite',[255,250,240]],['forestgreen',[34,139,34]],\n  ['fuchsia',[255,0,255]],['gainsboro',[220,220,220]],['ghostwhite',[248,248,255]],\n  ['gold',[255,215,0]],['goldenrod',[218,165,32]],['gray',[128,128,128]],\n  ['green',[0,128,0]],['greenyellow',[173,255,47]],['honeydew',[240,255,240]],\n  ['hotpink',[255,105,180]],['indianred',[205,92,92]],['indigo',[75,0,130]],\n  ['ivory',[255,255,240]],['khaki',[240,230,140]],['lavender',[230,230,250]],\n  ['lavenderblush',[255,240,245]],['lawngreen',[124,252,0]],['lemonchiffon',[255,250,205]],\n  ['lightblue',[173,216,230]],['lightcoral',[240,128,128]],['lightcyan',[224,255,255]],\n  ['lightgoldenrodyellow',[250,250,210]],['lightgray',[211,211,211]],['lightgreen',[144,238,144]],\n  ['lightpink',[255,182,193]],['lightsalmon',[255,160,122]],['lightseagreen',[32,178,170]],\n  ['lightskyblue',[135,206,250]],['lightslategray',[119,136,153]],['lightsteelblue',[176,196,222]],\n  ['lightyellow',[255,255,224]],['lime',[0,255,0]],['limegreen',[50,205,50]],\n  ['linen',[250,240,230]],['magenta',[255,0,255]],['maroon',[128,0,0]],\n  ['mediumaquamarine',[102,205,170]],['mediumblue',[0,0,205]],['mediumorchid',[186,85,211]],\n  ['mediumpurple',[147,112,219]],['mediumseagreen',[60,179,113]],['mediumslateblue',[123,104,238]],\n  ['mediumspringgreen',[0,250,154]],['mediumturquoise',[72,209,204]],['mediumvioletred',[199,21,133]],\n  ['midnightblue',[25,25,112]],['mintcream',[245,255,250]],['mistyrose',[255,228,225]],\n  ['moccasin',[255,228,181]],['navajowhite',[255,222,173]],['navy',[0,0,128]],\n  ['oldlace',[253,245,230]],['olive',[128,128,0]],['olivedrab',[107,142,35]],\n  ['orange',[255,165,0]],['orangered',[255,69,0]],['orchid',[218,112,214]],\n  ['palegoldenrod',[238,232,170]],['palegreen',[152,251,152]],['paleturquoise',[175,238,238]],\n  ['palevioletred',[219,112,147]],['papayawhip',[255,239,213]],['peachpuff',[255,218,185]],\n  ['peru',[205,133,63]],['pink',[255,192,203]],['plum',[221,160,221]],\n  ['powderblue',[176,224,230]],['purple',[128,0,128]],['rebeccapurple',[102,51,153]],\n  ['red',[255,0,0]],['rosybrown',[188,143,143]],['royalblue',[65,105,225]],\n  ['saddlebrown',[139,69,19]],['salmon',[250,128,114]],['sandybrown',[244,164,96]],\n  ['seagreen',[46,139,87]],['seashell',[255,245,238]],['sienna',[160,82,45]],\n  ['silver',[192,192,192]],['skyblue',[135,206,235]],['slateblue',[106,90,205]],\n  ['slategray',[112,128,144]],['snow',[255,250,250]],['springgreen',[0,255,127]],\n  ['steelblue',[70,130,180]],['tan',[210,180,140]],['teal',[0,128,128]],\n  ['thistle',[216,191,216]],['tomato',[255,99,71]],['turquoise',[64,224,208]],\n  ['violet',[238,130,238]],['wheat',[245,222,179]],['white',[255,255,255]],\n  ['whitesmoke',[245,245,245]],['yellow',[255,255,0]],['yellowgreen',[154,205,50]]\n];\n\nfunction colorDist(a,b){var dr=a[0]-b[0],dg=a[1]-b[1],db=a[2]-b[2];return dr*dr+dg*dg+db*db;}\nfunction nearestName(r,g,b){var best=CSS_COLORS[0][0],bd=Infinity;for(var i=0;i\u003cCSS_COLORS.length;i++){var d=colorDist([r,g,b],CSS_COLORS[i][1]);if(d\u003cbd){bd=d;best=CSS_COLORS[i][0];}}return best;}\n\nfunction toHex(r,g,b){return '#'+[r,g,b].map(function(v){return v.toString(16).padStart(2,'0');}).join('');}\nfunction toRgb(r,g,b){return 'rgb('+r+', '+g+', '+b+')';}\nfunction toHsl(r,g,b){\n  var rn=r/255,gn=g/255,bn=b/255;\n  var max=Math.max(rn,gn,bn),min=Math.min(rn,gn,bn),h,s,l=(max+min)/2;\n  if(max===min){h=s=0;}else{\n    var d=max-min;s=l\u003e0.5?d/(2-max-min):d/(max+min);\n    if(max===rn)h=((gn-bn)/d+(gn\u003cbn?6:0))/6;\n    else if(max===gn)h=((bn-rn)/d+2)/6;\n    else h=((rn-gn)/d+4)/6;\n  }\n  return 'hsl('+Math.round(h*360)+', '+Math.round(s*100)+'%, '+Math.round(l*100)+'%)';\n}\n\nfunction loadImage(file){\n  var url=URL.createObjectURL(file);\n  var img=new Image();\n  img.onload=function(){\n    imgObj=img;naturalW=img.naturalWidth;naturalH=img.naturalHeight;\n    canvas.width=naturalW;canvas.height=naturalH;\n    ctx.drawImage(img,0,0);\n    dropZone.style.display='none';\n    canvasWrap.style.display='block';\n    instructions.style.display='block';\n    nopick.style.display='block';\n    URL.revokeObjectURL(url);\n  };\n  img.src=url;\n}\n\nbrowseBtn.addEventListener('click',function(){fileInput.click();});\nfileInput.addEventListener('change',function(){if(fileInput.files[0])loadImage(fileInput.files[0]);});\n\ndropZone.addEventListener('dragover',function(e){e.preventDefault();dropZone.classList.add('drag-over');});\ndropZone.addEventListener('dragleave',function(){dropZone.classList.remove('drag-over');});\ndropZone.addEventListener('drop',function(e){\n  e.preventDefault();dropZone.classList.remove('drag-over');\n  var f=e.dataTransfer.files[0];if(f\u0026\u0026f.type.startsWith('image/'))loadImage(f);\n});\n\nfunction getPixel(e){\n  var rect=canvas.getBoundingClientRect();\n  var scaleX=naturalW/rect.width,scaleY=naturalH/rect.height;\n  var cx=Math.round((e.clientX-rect.left)*scaleX);\n  var cy=Math.round((e.clientY-rect.top)*scaleY);\n  cx=Math.max(0,Math.min(naturalW-1,cx));\n  cy=Math.max(0,Math.min(naturalH-1,cy));\n  return {x:cx,y:cy,rect:rect,scaleX:scaleX,scaleY:scaleY};\n}\n\nfunction updateMag(e){\n  if(!imgObj)return;\n  var p=getPixel(e);\n  var rect=p.rect;\n  var half=MAG_SIZE/2;\n  var srcX=p.x-Math.round(half/MAG_ZOOM),srcY=p.y-Math.round(half/MAG_ZOOM);\n  var srcW=Math.round(MAG_SIZE/MAG_ZOOM),srcH=Math.round(MAG_SIZE/MAG_ZOOM);\n  magCtx.clearRect(0,0,MAG_SIZE,MAG_SIZE);\n  magCtx.imageSmoothingEnabled=false;\n  magCtx.drawImage(canvas,srcX,srcY,srcW,srcH,0,0,MAG_SIZE,MAG_SIZE);\n\n  var mx=e.clientX-rect.left+canvasWrap.getBoundingClientRect().left-canvasWrap.getBoundingClientRect().left;\n  var relX=e.clientX-canvasWrap.getBoundingClientRect().left;\n  var relY=e.clientY-canvasWrap.getBoundingClientRect().top;\n  var offX=relX+20,offY=relY-MAG_SIZE-12;\n  if(offX+MAG_SIZE\u003ecanvasWrap.offsetWidth-4)offX=relX-MAG_SIZE-20;\n  if(offY\u003c4)offY=relY+20;\n  mag.style.left=offX+'px';\n  mag.style.top=offY+'px';\n  mag.style.display='block';\n}\n\ncanvas.addEventListener('mousemove',function(e){\n  if(!imgObj)return;\n  updateMag(e);\n});\ncanvas.addEventListener('mouseleave',function(){mag.style.display='none';});\n\ncanvas.addEventListener('click',function(e){\n  if(!imgObj)return;\n  var p=getPixel(e);\n  var data=ctx.getImageData(p.x,p.y,1,1).data;\n  var r=data[0],g=data[1],b=data[2];\n  var hex=toHex(r,g,b);\n  var rgb=toRgb(r,g,b);\n  var hsl=toHsl(r,g,b);\n  var name=nearestName(r,g,b);\n\n  preview.style.background=hex;\n  hexEl.textContent=hex;\n  rgbEl.textContent=rgb;\n  hslEl.textContent=hsl;\n  nameEl.textContent=name;\n\n  if(!hasPicked){\n    nopick.style.display='none';\n    resultRow.style.display='flex';\n    historySec.style.display='block';\n    hasPicked=true;\n  }\n\n  // Add to history\n  if(history.length===0||history[0]!==hex){\n    history.unshift(hex);\n    if(history.length\u003eMAX_HISTORY)history.pop();\n    renderSwatches();\n  }\n});\n\nfunction renderSwatches(){\n  swatchesEl.innerHTML='';\n  countEl.textContent='('+history.length+')';\n  history.forEach(function(h,i){\n    var sw=document.createElement('div');\n    sw.className='icp-swatch';\n    sw.style.background=h;\n    sw.title=h;\n    var tip=document.createElement('div');\n    tip.className='icp-swatch-tip';\n    tip.textContent=h;\n    sw.appendChild(tip);\n    sw.addEventListener('click',function(){\n      preview.style.background=h;\n      var rgb2=hexToRgb(h);\n      hexEl.textContent=h;\n      rgbEl.textContent=toRgb(rgb2[0],rgb2[1],rgb2[2]);\n      hslEl.textContent=toHsl(rgb2[0],rgb2[1],rgb2[2]);\n      nameEl.textContent=nearestName(rgb2[0],rgb2[1],rgb2[2]);\n    });\n    swatchesEl.appendChild(sw);\n  });\n}\n\nfunction hexToRgb(h){\n  var r=parseInt(h.slice(1,3),16),g=parseInt(h.slice(3,5),16),b=parseInt(h.slice(5,7),16);\n  return [r,g,b];\n}\n\ndocument.querySelectorAll('.icp-copy-btn').forEach(function(btn){\n  btn.addEventListener('click',function(){\n    var val=document.getElementById(btn.dataset.target).textContent;\n    navigator.clipboard.writeText(val).then(function(){\n      btn.textContent='Copied!';btn.classList.add('copied');\n      setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},1400);\n    });\n  });\n});\n\ndocument.getElementById('icp-export-css').addEventListener('click',function(){\n  if(!history.length)return;\n  var lines=history.map(function(h,i){return '  --color-'+(i+1)+': '+h+';';});\n  var out=':root {\\n'+lines.join('\\n')+'\\n}';\n  exportText.textContent=out;\n  exportOut.style.display='block';\n});\ndocument.getElementById('icp-export-json').addEventListener('click',function(){\n  if(!history.length)return;\n  var obj=Object.fromEntries(history.map(function(h,i){return ['color'+(i+1),h];}));\n  exportText.textContent=JSON.stringify(obj,null,2);\n  exportOut.style.display='block';\n});\nexportCopy.addEventListener('click',function(){\n  navigator.clipboard.writeText(exportText.textContent).then(function(){\n    exportCopy.textContent='Copied!';\n    setTimeout(function(){exportCopy.textContent='Copy';},1400);\n  });\n});\n\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-it-works\"\u003eHow It Works\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eUpload\u003c/strong\u003e — drag an image onto the canvas or click \u0026ldquo;Browse file.\u0026rdquo;\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eHover\u003c/strong\u003e — a magnifier circle shows a zoomed view for pixel-perfect picking.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eClick\u003c/strong\u003e — instantly see HEX, RGB, HSL, and the nearest CSS color name.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eHistory\u003c/strong\u003e — up to 12 swatches are saved. Click any swatch to reload its values.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eExport\u003c/strong\u003e — download your palette as CSS custom properties or a JSON object.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eEverything runs locally in your browser. No image data is sent anywhere.\u003c/p\u003e","title":"Image Color Picker"},{"content":"Free online image compressor — no upload to server, all processing in your browser. Adjust quality, pick output format, and download instantly.\nClick or drag \u0026 drop an image here\nJPEG · PNG · WebP · GIF · BMP\nQuality 80% Output Format JPEG PNG WebP Compress Image\nOriginal Size — Compressed Size — Reduction — Original Compressed Download Compressed Image Reset / Upload Another\nHow it works: Your image never leaves your device. The browser draws it onto an HTML5 Canvas and re-encodes it at the quality level you choose using canvas.toBlob(). Choose a lower quality for smaller files, or keep it high for near-lossless results.\nTips:\nJPEG gives the smallest files for photos; PNG preserves transparency; WebP often beats both. Quality around 75–85% is usually indistinguishable from the original for photos. Compressing a PNG as JPEG will remove transparency — use PNG or WebP if you need it. Related tools\nResize images → Image Resizer Crop images → Image Cropper Related Articles Best AI Image Generators Free 2026 ","permalink":"https://productivity-works.com/tools/image-compressor/","summary":"\u003cp\u003eFree online image compressor — no upload to server, all processing in your browser. Adjust quality, pick output format, and download instantly.\u003c/p\u003e\n\u003cdiv id=\"ic-app\"\u003e\n\u003cstyle\u003e\n#ic-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 780px;\n  margin: 0 auto;\n  color: #1a1a1a;\n}\n#ic-app * { box-sizing: border-box; }\n\u003cp\u003e#ic-drop-zone {\nborder: 2px dashed #ccc;\nborder-radius: 12px;\npadding: 48px 24px;\ntext-align: center;\ncursor: pointer;\ntransition: border-color 0.2s, background 0.2s;\nbackground: #fafafa;\nmargin-bottom: 24px;\n}\n#ic-drop-zone:hover, #ic-drop-zone.ic-dragover {\nborder-color: #4f8ef7;\nbackground: #f0f5ff;\n}\n#ic-drop-zone svg { display: block; margin: 0 auto 12px; }\n#ic-drop-zone p { margin: 0; color: #555; font-size: 0.97rem; }\n#ic-drop-zone strong { color: #333; }\n#ic-file-input { display: none; }\u003c/p\u003e","title":"Image Compressor"},{"content":" ✂️ Image Cropper Upload an image, drag the selection box to choose your crop area, then download as PNG or JPEG. Works entirely in your browser — nothing is uploaded to any server.\n🖼️ Drop an image here or click to browse Supports JPG, PNG, WebP, GIF, BMP \u0026mdash; up to 20 MB Ratio Free 1:1 4:3 16:9 3:2 Transform ↻ Rotate 90° ↔ Flip H ↕ Flip V Zoom 100% 🔄 New Image File: — Size: — Original: — Drag on the image to select crop area \u0026mdash; selection: none Crop Preview Select an area above to preview \u0026lt;div class=\u0026quot;ic-dl-box\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ic-dl-title\u0026quot;\u0026gt;Download\u0026lt;/div\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div style=\u0026quot;font-size:12px;font-weight:600;color:#6b7280;margin-bottom:6px;\u0026quot;\u0026gt;Format\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ic-fmt-row\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;ic-fmt-btn active\u0026quot; data-fmt=\u0026quot;image/png\u0026quot;\u0026gt;PNG\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;ic-fmt-btn\u0026quot; data-fmt=\u0026quot;image/jpeg\u0026quot;\u0026gt;JPEG\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ic-quality-row\u0026quot; id=\u0026quot;ic-quality-row\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ic-quality-label\u0026quot;\u0026gt; \u0026lt;span\u0026gt;Quality (JPEG)\u0026lt;/span\u0026gt; \u0026lt;span id=\u0026quot;ic-quality-val\u0026quot;\u0026gt;90%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; class=\u0026quot;ic-quality-slider\u0026quot; id=\u0026quot;ic-quality-slider\u0026quot; min=\u0026quot;10\u0026quot; max=\u0026quot;100\u0026quot; value=\u0026quot;90\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;ic-dl-btn\u0026quot; id=\u0026quot;ic-dl-btn\u0026quot; disabled\u0026gt;Download Cropped Image\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; 💡 Tips: Drag anywhere on the canvas to draw a crop selection. Drag the selection edges or corners to resize it. Use aspect ratio buttons to lock proportions. Rotate or flip the image before cropping if needed. How to Use the Image Cropper Step 1 — Upload: Drop your image onto the upload area or click to browse. JPG, PNG, WebP, GIF, and BMP files up to 20 MB are supported.\nStep 2 — Draw a selection: Click and drag on the canvas to draw a crop rectangle. The selection is shown with a dashed purple border and a rule-of-thirds grid overlay.\nStep 3 — Resize the selection: Drag any of the eight white handles (corners and midpoints) to resize the crop area. Drag inside the selection to move it.\nStep 4 — Lock an aspect ratio (optional): Click a ratio button (1:1, 4:3, 16:9, 3:2) to snap the selection to a fixed proportion as you drag.\nStep 5 — Rotate or flip (optional): Click \u0026ldquo;Rotate 90°\u0026rdquo; to rotate the image before cropping. Use \u0026ldquo;Flip H\u0026rdquo; or \u0026ldquo;Flip V\u0026rdquo; to mirror it.\nStep 6 — Preview and download: The right-side preview shows the cropped area in real time. Choose PNG or JPEG format, set JPEG quality if needed, then click \u0026ldquo;Download Cropped Image\u0026rdquo;.\nAspect Ratio Guide Ratio Best for Free Any custom crop, no constraints 1:1 Instagram square posts, profile pictures 4:3 Standard photos, presentations 16:9 YouTube thumbnails, widescreen video 3:2 Classic camera photos, prints Why Crop in the Browser? This tool runs entirely client-side using the HTML5 Canvas API. Your image never leaves your device — there is no server upload, no account required, and no file size limit beyond your browser memory. It works on any modern desktop or mobile browser without installing anything.\nRelated Tools Resize an image to exact pixel dimensions → Image Resizer Apply filters and effects to photos → Photo Filter Resize images to social media sizes → Social Media Image Resizer ","permalink":"https://productivity-works.com/tools/image-cropper/","summary":"\u003cdiv id=\"ic-app\"\u003e\n\u003cstyle\u003e\n#ic-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 0 0 48px 0;\n  color: #1a202c;\n}\n\n#ic-app * {\n  box-sizing: border-box;\n}\n\n/* Hero */\n#ic-app .ic-hero {\n  background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 50%, #4c1d95 100%);\n  border-radius: 16px;\n  padding: 32px 28px;\n  margin-bottom: 24px;\n  color: #fff;\n}\n\n#ic-app .ic-hero h2 {\n  margin: 0 0 6px 0;\n  font-size: 24px;\n  font-weight: 800;\n}\n\n#ic-app .ic-hero p {\n  margin: 0;\n  font-size: 14px;\n  opacity: 0.88;\n  line-height: 1.6;\n}\n\n/* Drop Zone */\n#ic-app .ic-dropzone {\n  border: 3px dashed #7c3aed;\n  border-radius: 14px;\n  padding: 44px 24px;\n  text-align: center;\n  background: #f5f3ff;\n  cursor: pointer;\n  transition: background 0.2s, border-color 0.2s;\n  margin-bottom: 20px;\n  position: relative;\n}\n\n#ic-app .ic-dropzone.drag-over {\n  background: #ede9fe;\n  border-color: #6d28d9;\n}\n\n#ic-app .ic-dropzone-icon {\n  font-size: 48px;\n  line-height: 1;\n  margin-bottom: 12px;\n  display: block;\n}\n\n#ic-app .ic-dropzone-title {\n  font-size: 17px;\n  font-weight: 700;\n  color: #4c1d95;\n  margin-bottom: 6px;\n}\n\n#ic-app .ic-dropzone-sub {\n  font-size: 13px;\n  color: #6b7280;\n}\n\n#ic-app .ic-dropzone input[type=\"file\"] {\n  position: absolute;\n  inset: 0;\n  opacity: 0;\n  cursor: pointer;\n  width: 100%;\n  height: 100%;\n}\n\n/* Main layout */\n#ic-app .ic-main {\n  display: none;\n  gap: 20px;\n  flex-direction: column;\n}\n\n#ic-app .ic-main.visible {\n  display: flex;\n}\n\n/* Toolbar */\n#ic-app .ic-toolbar {\n  background: #fff;\n  border: 1px solid #e5e7eb;\n  border-radius: 12px;\n  padding: 16px 18px;\n  display: flex;\n  flex-wrap: wrap;\n  gap: 12px;\n  align-items: center;\n}\n\n#ic-app .ic-toolbar-group {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  flex-wrap: wrap;\n}\n\n#ic-app .ic-toolbar-label {\n  font-size: 12px;\n  font-weight: 700;\n  color: #6b7280;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  white-space: nowrap;\n}\n\n#ic-app .ic-toolbar-sep {\n  width: 1px;\n  height: 28px;\n  background: #e5e7eb;\n}\n\n#ic-app .ic-ratio-btn {\n  padding: 5px 12px;\n  border: 1.5px solid #d1d5db;\n  border-radius: 7px;\n  background: #fff;\n  font-size: 13px;\n  font-weight: 600;\n  color: #374151;\n  cursor: pointer;\n  transition: all 0.15s;\n  white-space: nowrap;\n}\n\n#ic-app .ic-ratio-btn:hover {\n  border-color: #7c3aed;\n  color: #7c3aed;\n}\n\n#ic-app .ic-ratio-btn.active {\n  background: #7c3aed;\n  border-color: #7c3aed;\n  color: #fff;\n}\n\n#ic-app .ic-tool-btn {\n  padding: 6px 14px;\n  border: 1.5px solid #d1d5db;\n  border-radius: 7px;\n  background: #fff;\n  font-size: 13px;\n  font-weight: 600;\n  color: #374151;\n  cursor: pointer;\n  transition: all 0.15s;\n  display: flex;\n  align-items: center;\n  gap: 5px;\n  white-space: nowrap;\n}\n\n#ic-app .ic-tool-btn:hover {\n  border-color: #7c3aed;\n  color: #7c3aed;\n}\n\n/* Zoom */\n#ic-app .ic-zoom-wrap {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n\n#ic-app .ic-zoom-slider {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 100px;\n  height: 4px;\n  border-radius: 2px;\n  background: #e5e7eb;\n  outline: none;\n  cursor: pointer;\n}\n\n#ic-app .ic-zoom-slider::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 16px;\n  height: 16px;\n  border-radius: 50%;\n  background: #7c3aed;\n  cursor: pointer;\n}\n\n#ic-app .ic-zoom-val {\n  font-size: 12px;\n  font-weight: 700;\n  color: #374151;\n  min-width: 36px;\n  text-align: right;\n}\n\n/* Canvas wrapper */\n#ic-app .ic-canvas-wrap {\n  background: #111827;\n  border-radius: 12px;\n  overflow: hidden;\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  min-height: 300px;\n  max-height: 560px;\n}\n\n#ic-app #ic-canvas {\n  display: block;\n  cursor: crosshair;\n  max-width: 100%;\n  max-height: 560px;\n  user-select: none;\n  -webkit-user-select: none;\n}\n\n/* File info */\n#ic-app .ic-info-bar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  font-size: 12px;\n  color: #6b7280;\n  padding: 0 2px;\n}\n\n#ic-app .ic-info-item {\n  background: #f3f4f6;\n  border-radius: 6px;\n  padding: 4px 10px;\n  font-weight: 600;\n}\n\n#ic-app .ic-info-item span {\n  color: #374151;\n}\n\n/* Preview + Download */\n#ic-app .ic-bottom {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 20px;\n}\n\n@media (max-width: 600px) {\n  #ic-app .ic-bottom {\n    grid-template-columns: 1fr;\n  }\n}\n\n#ic-app .ic-preview-box {\n  background: #fff;\n  border: 1px solid #e5e7eb;\n  border-radius: 12px;\n  padding: 16px;\n}\n\n#ic-app .ic-preview-title {\n  font-size: 13px;\n  font-weight: 700;\n  color: #374151;\n  margin-bottom: 10px;\n}\n\n#ic-app .ic-preview-canvas-wrap {\n  background: repeating-conic-gradient(#e5e7eb 0% 25%, #fff 0% 50%) 0 0 / 16px 16px;\n  border-radius: 8px;\n  min-height: 100px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  overflow: hidden;\n}\n\n#ic-app #ic-preview-canvas {\n  display: block;\n  max-width: 100%;\n  max-height: 220px;\n  border-radius: 4px;\n}\n\n#ic-app .ic-preview-dims {\n  font-size: 11px;\n  color: #9ca3af;\n  margin-top: 8px;\n  text-align: center;\n}\n\n/* Download panel */\n#ic-app .ic-dl-box {\n  background: #fff;\n  border: 1px solid #e5e7eb;\n  border-radius: 12px;\n  padding: 16px;\n  display: flex;\n  flex-direction: column;\n  gap: 12px;\n}\n\n#ic-app .ic-dl-title {\n  font-size: 13px;\n  font-weight: 700;\n  color: #374151;\n}\n\n#ic-app .ic-fmt-row {\n  display: flex;\n  gap: 8px;\n}\n\n#ic-app .ic-fmt-btn {\n  flex: 1;\n  padding: 7px 0;\n  border: 1.5px solid #d1d5db;\n  border-radius: 7px;\n  background: #fff;\n  font-size: 13px;\n  font-weight: 700;\n  color: #374151;\n  cursor: pointer;\n  transition: all 0.15s;\n  text-align: center;\n}\n\n#ic-app .ic-fmt-btn.active {\n  background: #7c3aed;\n  border-color: #7c3aed;\n  color: #fff;\n}\n\n#ic-app .ic-fmt-btn:hover:not(.active) {\n  border-color: #7c3aed;\n  color: #7c3aed;\n}\n\n#ic-app .ic-quality-row {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n}\n\n#ic-app .ic-quality-label {\n  font-size: 12px;\n  font-weight: 600;\n  color: #6b7280;\n  display: flex;\n  justify-content: space-between;\n}\n\n#ic-app .ic-quality-slider {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 100%;\n  height: 4px;\n  border-radius: 2px;\n  background: #e5e7eb;\n  outline: none;\n  cursor: pointer;\n}\n\n#ic-app .ic-quality-slider::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 16px;\n  height: 16px;\n  border-radius: 50%;\n  background: #7c3aed;\n  cursor: pointer;\n}\n\n#ic-app .ic-dl-btn {\n  display: block;\n  width: 100%;\n  padding: 13px 0;\n  background: linear-gradient(135deg, #7c3aed, #6d28d9);\n  color: #fff;\n  border: none;\n  border-radius: 10px;\n  font-size: 15px;\n  font-weight: 800;\n  cursor: pointer;\n  transition: opacity 0.15s;\n  text-align: center;\n}\n\n#ic-app .ic-dl-btn:hover {\n  opacity: 0.88;\n}\n\n#ic-app .ic-dl-btn:disabled {\n  opacity: 0.4;\n  cursor: not-allowed;\n}\n\n/* Selection info */\n#ic-app .ic-sel-info {\n  font-size: 12px;\n  color: #6b7280;\n  text-align: center;\n  padding: 6px 0 0;\n}\n\n#ic-app .ic-sel-info span {\n  font-weight: 700;\n  color: #4c1d95;\n}\n\n/* Hint */\n#ic-app .ic-hint {\n  background: #f5f3ff;\n  border-left: 4px solid #7c3aed;\n  border-radius: 0 8px 8px 0;\n  padding: 10px 14px;\n  font-size: 13px;\n  color: #4c1d95;\n  line-height: 1.6;\n}\n\u003c/style\u003e\n\u003c!-- Hero --\u003e\n\u003cdiv class=\"ic-hero\"\u003e\n  \u003ch2\u003e✂️ Image Cropper\u003c/h2\u003e\n  \u003cp\u003eUpload an image, drag the selection box to choose your crop area, then download as PNG or JPEG. Works entirely in your browser — nothing is uploaded to any server.\u003c/p\u003e","title":"Image Cropper - Free Online Crop Tool"},{"content":" Free Image Resizer Resize and compress images instantly — no upload, no account. Everything runs in your browser using the Canvas API.\n🖼️ Drop your image here Supports JPG, PNG, WebP, GIF, BMP · Max 20 MB Choose File Preview 📂 Upload an image to see the preview Dimensions Width (px) 🔒 Height (px) Preset Sizes OG / Facebook 1200 × 630 px Instagram Square 1080 × 1080 px YouTube Thumbnail 1280 × 720 px Avatar / Profile 400 × 400 px Instagram Story 1080 × 1920 px Twitter Header 1500 × 500 px Blog Thumbnail 800 × 800 px Full HD 1920 × 1080 px Output Settings Quality (JPEG / WebP) 85% Output Format JPEG PNG WebP File Size Estimate Original Size — Upload an image Output Size (est.) — — Size Reduction — — Download Resized Image How to Use This Image Resizer Step 1 — Upload: Drag and drop your image onto the upload area, or click \u0026ldquo;Choose File\u0026rdquo;. Supported formats include JPG, PNG, WebP, GIF, and BMP up to 20 MB.\nStep 2 — Set dimensions: Enter a custom width and height in pixels. The lock icon keeps the aspect ratio intact — click it to unlock for freeform resizing. Or click a preset button to snap to a common size instantly.\nStep 3 — Choose format and quality: Select JPEG, PNG, or WebP as the output format. Use the quality slider (10–100%) to balance file size against image clarity. PNG is lossless, so quality only affects JPEG and WebP.\nStep 4 — Check sizes: The size panel shows the original file size, estimated output size, and how much space you save. The before/after preview lets you visually compare the result before downloading.\nStep 5 — Download: Click \u0026ldquo;Download Resized Image\u0026rdquo; to save the result directly to your device.\nChoosing the Right Format JPEG is best for photographs and complex images. It uses lossy compression, so quality settings below 80% may introduce visible artifacts. For web use, 75–85% gives a good balance.\nPNG is ideal for screenshots, graphics with text, logos, and images with transparency. It is lossless — no quality degradation — but produces larger files than JPEG.\nWebP is Google\u0026rsquo;s modern format offering superior compression compared to both JPEG and PNG at equivalent quality. Supported by all major browsers, it is an excellent choice for web images.\nSocial Media Image Sizes (2025) Platform Recommended Size Open Graph / Facebook 1200 × 630 px Instagram Square 1080 × 1080 px Instagram Story / Reel 1080 × 1920 px YouTube Thumbnail 1280 × 720 px Twitter / X Header 1500 × 500 px Profile / Avatar 400 × 400 px All presets above are built into the tool — just click the button and resize in one step.\nRelated Tools Pick colors for your image designs → Color Picker Generate a QR code to link to your image or portfolio → QR Code Generator Check what screen resolution your visitors use → Screen Resolution Checker Related Articles Best AI Image Generators Free 2026 ","permalink":"https://productivity-works.com/tools/image-resizer/","summary":"\u003cdiv id=\"resize-app\"\u003e\n\u003cstyle\u003e\n#resize-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  padding: 0 0 48px 0;\n  color: #1a202c;\n}\n\n#resize-app * {\n  box-sizing: border-box;\n}\n\n/* Hero */\n.ra-hero {\n  background: linear-gradient(135deg, #059669 0%, #047857 50%, #065f46 100%);\n  border-radius: 16px;\n  padding: 32px 28px;\n  margin-bottom: 24px;\n  color: #fff;\n}\n\n.ra-hero h2 {\n  margin: 0 0 6px 0;\n  font-size: 24px;\n  font-weight: 800;\n}\n\n.ra-hero p {\n  margin: 0;\n  font-size: 14px;\n  opacity: 0.88;\n  line-height: 1.6;\n}\n\n/* Drop Zone */\n.ra-dropzone {\n  border: 3px dashed #059669;\n  border-radius: 14px;\n  padding: 44px 24px;\n  text-align: center;\n  background: #f0fdf4;\n  cursor: pointer;\n  transition: background 0.2s, border-color 0.2s;\n  margin-bottom: 20px;\n  position: relative;\n}\n\n.ra-dropzone.drag-over {\n  background: #d1fae5;\n  border-color: #047857;\n}\n\n.ra-dropzone-icon {\n  font-size: 48px;\n  line-height: 1;\n  margin-bottom: 12px;\n  display: block;\n}\n\n.ra-dropzone-title {\n  font-size: 17px;\n  font-weight: 700;\n  color: #065f46;\n  margin-bottom: 6px;\n}\n\n.ra-dropzone-sub {\n  font-size: 13px;\n  color: #6b7280;\n  margin-bottom: 16px;\n}\n\n.ra-browse-btn {\n  display: inline-block;\n  padding: 10px 24px;\n  background: #059669;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n\n.ra-browse-btn:hover {\n  background: #047857;\n}\n\n#ra-file-input {\n  display: none;\n}\n\n/* Card */\n.ra-card {\n  background: #fff;\n  border-radius: 14px;\n  box-shadow: 0 2px 16px rgba(0,0,0,0.08);\n  padding: 22px 24px;\n  margin-bottom: 18px;\n}\n\n.ra-card h3 {\n  margin: 0 0 18px 0;\n  font-size: 13px;\n  font-weight: 700;\n  color: #374151;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  border-bottom: 2px solid #f0fdf4;\n  padding-bottom: 10px;\n}\n\n/* Preview */\n.ra-preview-wrap {\n  display: flex;\n  gap: 20px;\n  align-items: flex-start;\n  flex-wrap: wrap;\n}\n\n.ra-preview-box {\n  flex: 1;\n  min-width: 200px;\n  text-align: center;\n}\n\n.ra-preview-label {\n  font-size: 11px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  color: #6b7280;\n  margin-bottom: 8px;\n}\n\n.ra-preview-img-wrap {\n  border-radius: 10px;\n  overflow: hidden;\n  background: repeating-conic-gradient(#e5e7eb 0% 25%, #fff 0% 50%) 0 0 / 16px 16px;\n  border: 1px solid #e5e7eb;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  min-height: 120px;\n  max-height: 300px;\n}\n\n.ra-preview-img-wrap img {\n  max-width: 100%;\n  max-height: 280px;\n  display: block;\n  object-fit: contain;\n}\n\n.ra-preview-info {\n  margin-top: 8px;\n  font-size: 12px;\n  color: #6b7280;\n}\n\n/* Dimension inputs */\n.ra-dim-row {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  flex-wrap: wrap;\n  margin-bottom: 16px;\n}\n\n.ra-dim-field {\n  display: flex;\n  flex-direction: column;\n  gap: 5px;\n  flex: 1;\n  min-width: 110px;\n}\n\n.ra-dim-field label {\n  font-size: 12px;\n  font-weight: 700;\n  color: #374151;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n\n.ra-dim-input {\n  padding: 9px 12px;\n  border: 2px solid #d1fae5;\n  border-radius: 8px;\n  font-size: 15px;\n  font-weight: 600;\n  color: #1a202c;\n  transition: border-color 0.2s;\n  width: 100%;\n}\n\n.ra-dim-input:focus {\n  outline: none;\n  border-color: #059669;\n}\n\n.ra-lock-btn {\n  margin-top: 20px;\n  padding: 9px 16px;\n  border: 2px solid #d1fae5;\n  border-radius: 8px;\n  background: #fff;\n  font-size: 18px;\n  cursor: pointer;\n  transition: background 0.2s, border-color 0.2s;\n  flex-shrink: 0;\n  title: \"Toggle aspect ratio lock\";\n}\n\n.ra-lock-btn.locked {\n  background: #d1fae5;\n  border-color: #059669;\n}\n\n.ra-lock-btn:hover {\n  background: #ecfdf5;\n}\n\n/* Presets */\n.ra-preset-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));\n  gap: 10px;\n}\n\n.ra-preset-btn {\n  padding: 10px 14px;\n  border: 2px solid #e5e7eb;\n  border-radius: 10px;\n  background: #fff;\n  text-align: left;\n  cursor: pointer;\n  transition: all 0.18s;\n}\n\n.ra-preset-btn:hover {\n  border-color: #059669;\n  background: #f0fdf4;\n}\n\n.ra-preset-name {\n  font-size: 13px;\n  font-weight: 700;\n  color: #1a202c;\n  display: block;\n  margin-bottom: 2px;\n}\n\n.ra-preset-size {\n  font-size: 11px;\n  color: #6b7280;\n}\n\n/* Quality + Format */\n.ra-settings-row {\n  display: flex;\n  gap: 20px;\n  flex-wrap: wrap;\n  align-items: flex-start;\n}\n\n.ra-setting-group {\n  flex: 1;\n  min-width: 180px;\n}\n\n.ra-setting-group label {\n  display: block;\n  font-size: 12px;\n  font-weight: 700;\n  color: #374151;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  margin-bottom: 8px;\n}\n\n.ra-quality-wrap {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n\n.ra-quality-slider {\n  flex: 1;\n  height: 6px;\n  border-radius: 3px;\n  background: linear-gradient(to right, #d1fae5, #059669);\n  -webkit-appearance: none;\n  appearance: none;\n  outline: none;\n  cursor: pointer;\n}\n\n.ra-quality-slider::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 18px;\n  height: 18px;\n  border-radius: 50%;\n  background: #fff;\n  border: 2px solid #059669;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.15);\n  cursor: pointer;\n}\n\n.ra-quality-slider::-moz-range-thumb {\n  width: 18px;\n  height: 18px;\n  border-radius: 50%;\n  background: #fff;\n  border: 2px solid #059669;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.15);\n  cursor: pointer;\n}\n\n.ra-quality-val {\n  font-size: 15px;\n  font-weight: 800;\n  color: #059669;\n  width: 42px;\n  text-align: right;\n}\n\n.ra-format-btns {\n  display: flex;\n  gap: 8px;\n  flex-wrap: wrap;\n}\n\n.ra-format-btn {\n  padding: 8px 18px;\n  border: 2px solid #e5e7eb;\n  border-radius: 20px;\n  background: #fff;\n  font-size: 13px;\n  font-weight: 700;\n  color: #374151;\n  cursor: pointer;\n  transition: all 0.18s;\n}\n\n.ra-format-btn:hover {\n  border-color: #059669;\n  color: #059669;\n}\n\n.ra-format-btn.active {\n  border-color: #059669;\n  background: #059669;\n  color: #fff;\n}\n\n/* Size estimate + comparison */\n.ra-size-comparison {\n  display: flex;\n  gap: 16px;\n  flex-wrap: wrap;\n}\n\n.ra-size-box {\n  flex: 1;\n  min-width: 140px;\n  background: #f9fafb;\n  border-radius: 10px;\n  padding: 14px 16px;\n  text-align: center;\n  border: 1px solid #e5e7eb;\n}\n\n.ra-size-box-label {\n  font-size: 11px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  color: #6b7280;\n  margin-bottom: 6px;\n}\n\n.ra-size-box-value {\n  font-size: 20px;\n  font-weight: 800;\n  color: #1a202c;\n}\n\n.ra-size-box-sub {\n  font-size: 11px;\n  color: #9ca3af;\n  margin-top: 3px;\n}\n\n.ra-size-box.original .ra-size-box-value {\n  color: #374151;\n}\n\n.ra-size-box.output .ra-size-box-value {\n  color: #059669;\n}\n\n.ra-size-box.savings .ra-size-box-value {\n  color: #dc2626;\n}\n\n/* Download button */\n.ra-download-btn {\n  display: block;\n  width: 100%;\n  padding: 14px 24px;\n  background: linear-gradient(135deg, #059669, #047857);\n  color: #fff;\n  border: none;\n  border-radius: 12px;\n  font-size: 16px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: opacity 0.2s, transform 0.1s;\n  text-align: center;\n  margin-top: 4px;\n}\n\n.ra-download-btn:hover:not(:disabled) {\n  opacity: 0.92;\n}\n\n.ra-download-btn:active:not(:disabled) {\n  transform: scale(0.98);\n}\n\n.ra-download-btn:disabled {\n  background: #9ca3af;\n  cursor: not-allowed;\n}\n\n/* Hidden canvas */\n#ra-canvas {\n  display: none;\n}\n\n/* Placeholder state */\n.ra-placeholder {\n  text-align: center;\n  padding: 32px 16px;\n  color: #9ca3af;\n  font-size: 14px;\n}\n\n.ra-placeholder-icon {\n  font-size: 40px;\n  display: block;\n  margin-bottom: 8px;\n}\n\n/* Responsive */\n@media (max-width: 600px) {\n  .ra-hero {\n    padding: 24px 18px;\n  }\n  .ra-hero h2 {\n    font-size: 20px;\n  }\n  .ra-card {\n    padding: 18px 16px;\n  }\n  .ra-dim-row {\n    gap: 8px;\n  }\n  .ra-preset-grid {\n    grid-template-columns: 1fr 1fr;\n  }\n  .ra-settings-row {\n    flex-direction: column;\n    gap: 16px;\n  }\n  .ra-size-comparison {\n    gap: 10px;\n  }\n}\n\u003c/style\u003e\n\u003c!-- Hero --\u003e\n\u003cdiv class=\"ra-hero\"\u003e\n  \u003ch2\u003eFree Image Resizer\u003c/h2\u003e\n  \u003cp\u003eResize and compress images instantly — no upload, no account. Everything runs in your browser using the Canvas API.\u003c/p\u003e","title":"Image Resizer - Free Online Tool to Resize \u0026 Compress Images"},{"content":"Convert any image to a Base64 data URI right in your browser — no upload, no server, completely private.\n🖼 Drag \u0026amp; drop an image here, or click to select a file\nSupports PNG, JPG, GIF, SVG, WebP \u0026nbsp;·\u0026nbsp; Max recommended: 2 MB Type Original size Base64 size Size increase Base64 String (no prefix) Copy \u0026lt;div class=\u0026quot;i2b-output-block\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;i2b-output-header\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;i2b-output-label\u0026quot;\u0026gt;Data URI\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026quot;i2b-copy-btn\u0026quot; data-target=\u0026quot;i2b-out-uri\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;textarea id=\u0026quot;i2b-out-uri\u0026quot; class=\u0026quot;i2b-output-textarea\u0026quot; readonly spellcheck=\u0026quot;false\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;i2b-output-block\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;i2b-output-header\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;i2b-output-label\u0026quot;\u0026gt;CSS background-image\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026quot;i2b-copy-btn\u0026quot; data-target=\u0026quot;i2b-out-css\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;textarea id=\u0026quot;i2b-out-css\u0026quot; class=\u0026quot;i2b-output-textarea\u0026quot; readonly spellcheck=\u0026quot;false\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;i2b-output-block\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;i2b-output-header\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;i2b-output-label\u0026quot;\u0026gt;HTML \u0026amp;lt;img\u0026amp;gt; tag\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026quot;i2b-copy-btn\u0026quot; data-target=\u0026quot;i2b-out-html\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;textarea id=\u0026quot;i2b-out-html\u0026quot; class=\u0026quot;i2b-output-textarea\u0026quot; readonly spellcheck=\u0026quot;false\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;/div\u0026gt; Convert another image\nHow to use Drag and drop an image onto the zone above, or click to open the file picker. The tool reads the file locally using the FileReader API — nothing is sent to any server. Copy the output you need with the corresponding Copy button. Output formats explained Format When to use Base64 string APIs, databases, JSON payloads Data URI Direct src attribute value CSS background-image Paste straight into a stylesheet HTML \u0026lt;img\u0026gt; tag Paste straight into HTML Why Base64-encode an image? Embedding an image as a Base64 data URI lets you ship a self-contained HTML file or email template with no external requests. It is also useful for small icons in CSS, API payloads, and offline-capable web apps. The trade-off is a roughly 33% size increase and slightly slower initial parse — so keep embedded images small (under ~50 KB is a good rule of thumb).\nPrivacy All conversion happens entirely in your browser. The image file never leaves your device.\nRelated tools: Base64 Encoder / Decoder · Image Resizer · Placeholder Image Generator ","permalink":"https://productivity-works.com/tools/image-to-base64/","summary":"\u003cp\u003eConvert any image to a Base64 data URI right in your browser — no upload, no server, completely private.\u003c/p\u003e\n\u003cdiv id=\"i2b-app\"\u003e\n\u003cstyle\u003e\n  #i2b-app *,\n  #i2b-app *::before,\n  #i2b-app *::after {\n    box-sizing: border-box;\n    margin: 0;\n    padding: 0;\n  }\n  #i2b-app {\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n    color: #1a1a2e;\n    max-width: 820px;\n    margin: 0 auto;\n    padding: 0 0 40px;\n  }\n  #i2b-drop-zone {\n    border: 2.5px dashed #7c3aed;\n    border-radius: 14px;\n    padding: 48px 24px;\n    text-align: center;\n    cursor: pointer;\n    background: #f5f3ff;\n    transition: background 0.2s, border-color 0.2s;\n    position: relative;\n  }\n  #i2b-drop-zone.i2b-dragover {\n    background: #ede9fe;\n    border-color: #5b21b6;\n  }\n  #i2b-drop-zone-icon {\n    font-size: 48px;\n    line-height: 1;\n    margin-bottom: 12px;\n  }\n  #i2b-drop-zone p {\n    color: #6d28d9;\n    font-size: 1.05rem;\n    font-weight: 500;\n  }\n  #i2b-drop-zone small {\n    display: block;\n    margin-top: 6px;\n    color: #8b5cf6;\n    font-size: 0.82rem;\n  }\n  #i2b-file-input {\n    position: absolute;\n    inset: 0;\n    opacity: 0;\n    cursor: pointer;\n    width: 100%;\n    height: 100%;\n  }\n  #i2b-warning {\n    display: none;\n    margin-top: 14px;\n    padding: 10px 16px;\n    background: #fef3c7;\n    border: 1px solid #f59e0b;\n    border-radius: 8px;\n    color: #92400e;\n    font-size: 0.88rem;\n    font-weight: 500;\n  }\n  #i2b-results {\n    display: none;\n    margin-top: 28px;\n  }\n  #i2b-preview-row {\n    display: flex;\n    align-items: flex-start;\n    gap: 24px;\n    margin-bottom: 22px;\n    flex-wrap: wrap;\n  }\n  #i2b-preview-img {\n    max-width: 200px;\n    max-height: 200px;\n    border-radius: 10px;\n    border: 1px solid #e5e7eb;\n    object-fit: contain;\n    background: repeating-conic-gradient(#e5e7eb 0% 25%, #fff 0% 50%) 0 0 / 16px 16px;\n    flex-shrink: 0;\n  }\n  #i2b-meta {\n    flex: 1;\n    min-width: 180px;\n  }\n  #i2b-meta h3 {\n    font-size: 1rem;\n    font-weight: 700;\n    color: #1a1a2e;\n    margin-bottom: 10px;\n    word-break: break-all;\n  }\n  .i2b-meta-row {\n    display: flex;\n    justify-content: space-between;\n    padding: 5px 0;\n    border-bottom: 1px solid #f3f4f6;\n    font-size: 0.875rem;\n    color: #374151;\n  }\n  .i2b-meta-row:last-child { border-bottom: none; }\n  .i2b-meta-label { color: #6b7280; font-weight: 500; }\n  .i2b-meta-value { font-weight: 600; color: #1a1a2e; }\n  .i2b-size-bigger { color: #dc2626; }\n  #i2b-outputs { display: flex; flex-direction: column; gap: 16px; }\n  .i2b-output-block {\n    background: #f9fafb;\n    border: 1px solid #e5e7eb;\n    border-radius: 10px;\n    overflow: hidden;\n  }\n  .i2b-output-header {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    padding: 10px 16px;\n    background: #f3f4f6;\n    border-bottom: 1px solid #e5e7eb;\n  }\n  .i2b-output-label {\n    font-size: 0.8rem;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 0.05em;\n    color: #6b7280;\n  }\n  .i2b-copy-btn {\n    display: inline-flex;\n    align-items: center;\n    gap: 5px;\n    padding: 4px 12px;\n    background: #7c3aed;\n    color: #fff;\n    border: none;\n    border-radius: 6px;\n    font-size: 0.8rem;\n    font-weight: 600;\n    cursor: pointer;\n    transition: background 0.15s;\n    white-space: nowrap;\n  }\n  .i2b-copy-btn:hover { background: #6d28d9; }\n  .i2b-copy-btn.i2b-copied { background: #16a34a; }\n  .i2b-output-textarea {\n    width: 100%;\n    min-height: 80px;\n    max-height: 160px;\n    padding: 12px 16px;\n    font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n    font-size: 0.78rem;\n    line-height: 1.5;\n    color: #374151;\n    background: transparent;\n    border: none;\n    resize: vertical;\n    outline: none;\n    word-break: break-all;\n    overflow-y: auto;\n  }\n  #i2b-reset-btn {\n    margin-top: 20px;\n    padding: 10px 24px;\n    background: transparent;\n    border: 2px solid #7c3aed;\n    color: #7c3aed;\n    border-radius: 8px;\n    font-size: 0.9rem;\n    font-weight: 600;\n    cursor: pointer;\n    transition: background 0.15s, color 0.15s;\n  }\n  #i2b-reset-btn:hover { background: #7c3aed; color: #fff; }\n  @media (max-width: 540px) {\n    #i2b-preview-row { flex-direction: column; }\n    #i2b-preview-img { max-width: 100%; }\n    .i2b-output-header { flex-direction: column; align-items: flex-start; gap: 8px; }\n  }\n\u003c/style\u003e\n\u003cdiv id=\"i2b-drop-zone\"\u003e\n  \u003cinput type=\"file\" id=\"i2b-file-input\" accept=\"image/png,image/jpeg,image/gif,image/svg+xml,image/webp\"\u003e\n  \u003cdiv id=\"i2b-drop-zone-icon\"\u003e🖼\u003c/div\u003e\n  \u003cp\u003eDrag \u0026amp; drop an image here, or click to select a file\u003c/p\u003e","title":"Image to Base64 Converter — Data URI Tool"},{"content":" Income Details Gross Annual Income ($) Filing Status Single Married Filing Jointly Head of Household Apply Standard Deduction 2024 Standard Deduction: $14,600 (Single) Calculate Tax Tax Summary Gross Income — Deduction — Taxable Income — Total Federal Tax — Effective Rate — Marginal Rate — Tax Bracket Breakdown Bracket Rate Income in Bracket Tax in Bracket * 2024 US federal income tax brackets. State taxes, FICA (Social Security \u0026 Medicare), AMT, and tax credits are not included. For general estimation only — consult a tax professional for your specific situation.\nRelated Articles How Much Tax Do You Pay on FX Profits in Japan? A Salary Worker\u0026rsquo;s Guide FX Trading and Japan Tax Filing: Can You Carry Forward Losses for 3 Years? How to File a Kakuteishinkoku for FX Income in Japan: 2026 Guide ","permalink":"https://productivity-works.com/tools/tax-calculator/","summary":"\u003cdiv id=\"tc-app\"\u003e\n\u003cstyle\u003e\n#tc-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#tc-app * { box-sizing: border-box; }\n#tc-app h2 {\n  font-size: 1.4rem;\n  font-weight: 700;\n  margin: 0 0 1.2rem;\n  color: #1a1a2e;\n  border-left: 4px solid #22c55e;\n  padding-left: 0.7rem;\n}\n#tc-app .tc-card {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 1.6rem;\n  margin-bottom: 1.4rem;\n  box-shadow: 0 2px 8px rgba(0,0,0,0.05);\n}\n#tc-app .tc-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));\n  gap: 1rem;\n  margin-bottom: 1rem;\n}\n#tc-app label {\n  display: block;\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #4a5568;\n  margin-bottom: 0.35rem;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#tc-app input[type=number], #tc-app select {\n  width: 100%;\n  padding: 0.6rem 0.9rem;\n  border: 1.5px solid #cbd5e0;\n  border-radius: 8px;\n  font-size: 1rem;\n  color: #1a1a2e;\n  background: #f8fafc;\n  transition: border-color 0.2s;\n  outline: none;\n}\n#tc-app input[type=number]:focus, #tc-app select:focus {\n  border-color: #22c55e;\n  background: #fff;\n}\n#tc-app .tc-checkbox-row {\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n  margin-top: 0.2rem;\n}\n#tc-app .tc-checkbox-row input[type=checkbox] {\n  width: 18px;\n  height: 18px;\n  accent-color: #22c55e;\n  cursor: pointer;\n}\n#tc-app .tc-checkbox-row label {\n  font-size: 0.9rem;\n  text-transform: none;\n  letter-spacing: 0;\n  cursor: pointer;\n  margin: 0;\n  color: #2d3748;\n}\n#tc-app .tc-btn {\n  display: inline-block;\n  padding: 0.7rem 2rem;\n  background: linear-gradient(135deg, #22c55e, #16a34a);\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 1rem;\n  font-weight: 600;\n  cursor: pointer;\n  margin-top: 0.5rem;\n  transition: opacity 0.2s;\n}\n#tc-app .tc-btn:hover { opacity: 0.88; }\n#tc-app .tc-results-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n  gap: 1rem;\n  margin-bottom: 1.4rem;\n}\n#tc-app .tc-stat {\n  background: #f0fdf4;\n  border-radius: 10px;\n  padding: 1rem 1.2rem;\n  text-align: center;\n}\n#tc-app .tc-stat .tc-stat-label {\n  font-size: 0.75rem;\n  font-weight: 600;\n  color: #16a34a;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 0.3rem;\n}\n#tc-app .tc-stat .tc-stat-value {\n  font-size: 1.35rem;\n  font-weight: 800;\n  color: #1a1a2e;\n}\n#tc-app .tc-stat.tc-highlight {\n  background: linear-gradient(135deg, #dcfce7, #bbf7d0);\n  border: 1.5px solid #22c55e;\n}\n#tc-app table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.88rem;\n}\n#tc-app thead th {\n  background: #f0fdf4;\n  color: #16a34a;\n  font-weight: 700;\n  padding: 0.6rem 0.9rem;\n  text-align: right;\n  border-bottom: 2px solid #e2e8f0;\n}\n#tc-app thead th:first-child { text-align: left; }\n#tc-app tbody td {\n  padding: 0.5rem 0.9rem;\n  text-align: right;\n  border-bottom: 1px solid #f0f0f0;\n  color: #2d3748;\n}\n#tc-app tbody td:first-child { text-align: left; }\n#tc-app tbody tr:hover { background: #f0fdf4; }\n#tc-app tbody tr.tc-active-row {\n  background: #dcfce7;\n  font-weight: 600;\n}\n#tc-app .tc-bar-wrap {\n  background: #f0f0f0;\n  border-radius: 4px;\n  height: 10px;\n  display: inline-block;\n  width: 60px;\n  vertical-align: middle;\n  margin-left: 6px;\n  overflow: hidden;\n}\n#tc-app .tc-bar-fill {\n  height: 100%;\n  border-radius: 4px;\n  background: linear-gradient(90deg, #22c55e, #16a34a);\n}\n#tc-app .tc-hidden { display: none; }\n#tc-app .tc-note {\n  font-size: 0.78rem;\n  color: #888;\n  margin-top: 1rem;\n  line-height: 1.6;\n}\n#tc-app .tc-deduction-note {\n  font-size: 0.82rem;\n  color: #667;\n  margin-top: 0.5rem;\n  padding: 0.5rem 0.8rem;\n  background: #f8fafc;\n  border-radius: 6px;\n  border-left: 3px solid #22c55e;\n}\n#tc-app canvas { display: block; margin: 0 auto 1rem; }\n\u003c/style\u003e\n\u003cdiv class=\"tc-card\"\u003e\n  \u003ch2\u003eIncome Details\u003c/h2\u003e\n  \u003cdiv class=\"tc-grid\"\u003e\n    \u003cdiv\u003e\n      \u003clabel for=\"tc-income\"\u003eGross Annual Income ($)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"tc-income\" value=\"75000\" min=\"0\" step=\"1000\"\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003clabel for=\"tc-status\"\u003eFiling Status\u003c/label\u003e\n      \u003cselect id=\"tc-status\" onchange=\"tcUpdateDeduction()\"\u003e\n        \u003coption value=\"single\"\u003eSingle\u003c/option\u003e\n        \u003coption value=\"mfj\"\u003eMarried Filing Jointly\u003c/option\u003e\n        \u003coption value=\"hoh\"\u003eHead of Household\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"tc-checkbox-row\"\u003e\n    \u003cinput type=\"checkbox\" id=\"tc-std-ded\" checked onchange=\"tcUpdateDeduction()\"\u003e\n    \u003clabel for=\"tc-std-ded\"\u003eApply Standard Deduction\u003c/label\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"tc-deduction-note\" id=\"tc-ded-note\"\u003e2024 Standard Deduction: $14,600 (Single)\u003c/div\u003e\n  \u003cbr\u003e\n  \u003cbutton class=\"tc-btn\" onclick=\"tcCalculate()\"\u003eCalculate Tax\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"tc-card tc-hidden\" id=\"tc-results\"\u003e\n  \u003ch2\u003eTax Summary\u003c/h2\u003e\n  \u003cdiv class=\"tc-results-grid\"\u003e\n    \u003cdiv class=\"tc-stat\"\u003e\n      \u003cdiv class=\"tc-stat-label\"\u003eGross Income\u003c/div\u003e\n      \u003cdiv class=\"tc-stat-value\" id=\"tc-gross-out\"\u003e—\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tc-stat\"\u003e\n      \u003cdiv class=\"tc-stat-label\"\u003eDeduction\u003c/div\u003e\n      \u003cdiv class=\"tc-stat-value\" id=\"tc-ded-out\"\u003e—\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tc-stat\"\u003e\n      \u003cdiv class=\"tc-stat-label\"\u003eTaxable Income\u003c/div\u003e\n      \u003cdiv class=\"tc-stat-value\" id=\"tc-taxable-out\"\u003e—\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tc-stat tc-highlight\"\u003e\n      \u003cdiv class=\"tc-stat-label\"\u003eTotal Federal Tax\u003c/div\u003e\n      \u003cdiv class=\"tc-stat-value\" id=\"tc-tax-out\"\u003e—\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tc-stat\"\u003e\n      \u003cdiv class=\"tc-stat-label\"\u003eEffective Rate\u003c/div\u003e\n      \u003cdiv class=\"tc-stat-value\" id=\"tc-eff-out\"\u003e—\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tc-stat\"\u003e\n      \u003cdiv class=\"tc-stat-label\"\u003eMarginal Rate\u003c/div\u003e\n      \u003cdiv class=\"tc-stat-value\" id=\"tc-marginal-out\"\u003e—\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003cp\u003e\u003ccanvas id=\"tc-chart\" width=\"700\" height=\"200\"\u003e\u003c/canvas\u003e\u003c/p\u003e","title":"Income Tax Calculator - Tax Bracket Estimator"},{"content":"Re-indent any code instantly — convert between 2-space, 4-space, and tab styles, fix mixed indentation, and preview whitespace markers before copying.\nConvert tabs → Tab Converter Format HTML → HTML Beautifier From: Auto-detect 2 Spaces 4 Spaces Tabs Paste code to detect To: 2 Spaces 4 Spaces Tabs Indent shift: \u0026#8722; Decrease \u0026#43; Increase Trim trailing whitespace Fix mixed indentation Show whitespace markers Convert Swap Clear Upload File INPUT Paste OUTPUT Copy Copied! Lines: 0 Input chars: 0 Output chars: 0 Tab-indented lines: 0 Space-indented lines: 0 Mixed lines: 0 How it works:\nAuto-detect: Scans leading whitespace across all lines to determine whether the file uses 2-space, 4-space, or tab indentation, and flags mixed files. Re-indent: Measures each line\u0026rsquo;s indent level in the source style, then rebuilds it in the target style — preserving relative nesting depth exactly. Indent shift: Increases or decreases every line\u0026rsquo;s nesting level by one step without changing style. Mixed indentation cleanup: Normalises lines that combine tabs and spaces before re-indenting, so no stray characters remain. Whitespace markers: Spaces show as · (purple), tabs show as → (amber) so you can verify the result at a glance. ","permalink":"https://productivity-works.com/tools/indent-converter/","summary":"\u003cp\u003eRe-indent any code instantly — convert between 2-space, 4-space, and tab styles, fix mixed indentation, and preview whitespace markers before copying.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eConvert tabs → \u003ca href=\"https://productivity-works.com/tools/tab-converter/\"\u003eTab Converter\u003c/a\u003e\n\u003c/p\u003e\u003c/blockquote\u003e\n\u003cblockquote\u003e\n\u003cp\u003eFormat HTML → \u003ca href=\"https://productivity-works.com/tools/html-beautifier/\"\u003eHTML Beautifier\u003c/a\u003e\n\u003c/p\u003e\u003c/blockquote\u003e\n\u003cdiv id=\"ic-app\"\u003e\n\u003cstyle\u003e\n#ic-app *,#ic-app *::before,#ic-app *::after{box-sizing:border-box;margin:0;padding:0}\n#ic-app{font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;font-size:14px;color:#1e293b;background:#f8fafc;border:1px solid #e2e8f0;border-radius:12px;padding:20px;max-width:960px}\n#ic-app .ic-row{display:flex;gap:12px;flex-wrap:wrap;align-items:center;margin-bottom:14px}\n#ic-app .ic-label{font-weight:600;font-size:13px;color:#475569;white-space:nowrap;min-width:90px}\n#ic-app .ic-btn-group{display:flex;gap:6px;flex-wrap:wrap}\n#ic-app .ic-btn{padding:7px 14px;border-radius:7px;border:1.5px solid #cbd5e1;background:#fff;color:#334155;font-size:13px;font-weight:500;cursor:pointer;transition:all .15s}\n#ic-app .ic-btn:hover{border-color:#6366f1;color:#6366f1}\n#ic-app .ic-btn.active{background:#6366f1;border-color:#6366f1;color:#fff}\n#ic-app .ic-btn.action{background:#6366f1;border-color:#6366f1;color:#fff;font-weight:600}\n#ic-app .ic-btn.action:hover{background:#4f46e5;border-color:#4f46e5}\n#ic-app .ic-btn.secondary{background:#f1f5f9;border-color:#cbd5e1;color:#475569}\n#ic-app .ic-btn.secondary:hover{border-color:#6366f1;color:#6366f1;background:#f1f5f9}\n#ic-app .ic-btn.warn{background:#fef3c7;border-color:#fcd34d;color:#92400e}\n#ic-app .ic-btn.warn:hover{background:#fde68a;border-color:#f59e0b}\n#ic-app .ic-check-label{display:flex;align-items:center;gap:6px;font-size:13px;color:#475569;cursor:pointer;user-select:none}\n#ic-app .ic-check-label input{accent-color:#6366f1;width:15px;height:15px;cursor:pointer}\n#ic-app .ic-detect-badge{display:inline-flex;align-items:center;gap:6px;padding:5px 12px;background:#f1f5f9;border:1.5px solid #e2e8f0;border-radius:20px;font-size:12px;color:#475569;font-weight:600}\n#ic-app .ic-detect-badge .dot{width:8px;height:8px;border-radius:50%;background:#94a3b8;display:inline-block}\n#ic-app .ic-detect-badge.detected-spaces-2 .dot{background:#10b981}\n#ic-app .ic-detect-badge.detected-spaces-4 .dot{background:#6366f1}\n#ic-app .ic-detect-badge.detected-tabs .dot{background:#f59e0b}\n#ic-app .ic-detect-badge.detected-mixed .dot{background:#ef4444}\n#ic-app .ic-panels{display:grid;grid-template-columns:1fr 1fr;gap:14px}\n@media(max-width:660px){#ic-app .ic-panels{grid-template-columns:1fr}}\n#ic-app .ic-panel-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}\n#ic-app .ic-panel-title{font-weight:600;font-size:13px;color:#64748b;letter-spacing:.02em}\n#ic-app .ic-panel-actions{display:flex;gap:6px;align-items:center}\n#ic-app textarea{width:100%;height:300px;padding:10px 12px;border:1.5px solid #e2e8f0;border-radius:8px;font-family:\"SFMono-Regular\",Consolas,\"Liberation Mono\",Menlo,monospace;font-size:13px;line-height:1.6;color:#1e293b;background:#fff;resize:vertical;outline:none;transition:border-color .15s}\n#ic-app textarea:focus{border-color:#6366f1}\n#ic-app .ic-preview{width:100%;height:300px;padding:10px 12px;border:1.5px solid #e2e8f0;border-radius:8px;font-family:\"SFMono-Regular\",Consolas,\"Liberation Mono\",Menlo,monospace;font-size:13px;line-height:1.6;background:#fff;overflow:auto;white-space:pre;word-break:normal;tab-size:4}\n#ic-app .ic-preview .ws-sp{color:#c4b5fd}\n#ic-app .ic-preview .ws-tab{color:#f59e0b}\n#ic-app .ic-preview .ws-mixed{background:#fef2f2;color:#dc2626}\n#ic-app .ic-stats{display:flex;gap:14px;flex-wrap:wrap;padding:10px 14px;background:#f1f5f9;border-radius:8px;margin-top:12px}\n#ic-app .ic-stat{font-size:12px;color:#64748b}\n#ic-app .ic-stat strong{color:#1e293b;font-weight:600}\n#ic-app .ic-divider{border:none;border-top:1px solid #e2e8f0;margin:14px 0}\n#ic-app .ic-copy-note{font-size:12px;color:#10b981;font-weight:600;display:none}\n#ic-app .ic-upload-wrap{position:relative;display:inline-block}\n#ic-app .ic-upload-wrap input[type=file]{position:absolute;inset:0;opacity:0;cursor:pointer;width:100%}\n#ic-app .ic-increase-decrease{display:flex;gap:4px}\n\u003c/style\u003e\n\u003c!-- Row 1: From style --\u003e\n\u003cdiv class=\"ic-row\"\u003e\n  \u003cspan class=\"ic-label\"\u003eFrom:\u003c/span\u003e\n  \u003cdiv class=\"ic-btn-group\" id=\"ic-from-group\"\u003e\n    \u003cbutton class=\"ic-btn active\" id=\"ic-from-auto\" onclick=\"icSetFrom('auto')\"\u003eAuto-detect\u003c/button\u003e\n    \u003cbutton class=\"ic-btn\" id=\"ic-from-2sp\" onclick=\"icSetFrom('2sp')\"\u003e2 Spaces\u003c/button\u003e\n    \u003cbutton class=\"ic-btn\" id=\"ic-from-4sp\" onclick=\"icSetFrom('4sp')\"\u003e4 Spaces\u003c/button\u003e\n    \u003cbutton class=\"ic-btn\" id=\"ic-from-tab\" onclick=\"icSetFrom('tab')\"\u003eTabs\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cspan class=\"ic-detect-badge\" id=\"ic-detect-badge\"\u003e\u003cspan class=\"dot\"\u003e\u003c/span\u003e\u003cspan id=\"ic-detect-text\"\u003ePaste code to detect\u003c/span\u003e\u003c/span\u003e\n\u003c/div\u003e\n\u003c!-- Row 2: To style --\u003e\n\u003cdiv class=\"ic-row\"\u003e\n  \u003cspan class=\"ic-label\"\u003eTo:\u003c/span\u003e\n  \u003cdiv class=\"ic-btn-group\"\u003e\n    \u003cbutton class=\"ic-btn\" id=\"ic-to-2sp\" onclick=\"icSetTo('2sp')\"\u003e2 Spaces\u003c/button\u003e\n    \u003cbutton class=\"ic-btn active\" id=\"ic-to-4sp\" onclick=\"icSetTo('4sp')\"\u003e4 Spaces\u003c/button\u003e\n    \u003cbutton class=\"ic-btn\" id=\"ic-to-tab\" onclick=\"icSetTo('tab')\"\u003eTabs\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Row 3: Indent level shift --\u003e\n\u003cdiv class=\"ic-row\"\u003e\n  \u003cspan class=\"ic-label\"\u003eIndent shift:\u003c/span\u003e\n  \u003cdiv class=\"ic-increase-decrease\"\u003e\n    \u003cbutton class=\"ic-btn secondary\" onclick=\"icShiftIndent(-1)\"\u003e\u0026#8722; Decrease\u003c/button\u003e\n    \u003cbutton class=\"ic-btn secondary\" onclick=\"icShiftIndent(1)\"\u003e\u0026#43; Increase\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Row 4: Options --\u003e\n\u003cdiv class=\"ic-row\"\u003e\n  \u003clabel class=\"ic-check-label\"\u003e\u003cinput type=\"checkbox\" id=\"ic-trim\" onchange=\"icProcess()\"\u003e Trim trailing whitespace\u003c/label\u003e\n  \u003clabel class=\"ic-check-label\"\u003e\u003cinput type=\"checkbox\" id=\"ic-fix-mixed\" onchange=\"icProcess()\" checked\u003e Fix mixed indentation\u003c/label\u003e\n  \u003clabel class=\"ic-check-label\"\u003e\u003cinput type=\"checkbox\" id=\"ic-show-ws\" onchange=\"icProcess()\" checked\u003e Show whitespace markers\u003c/label\u003e\n\u003c/div\u003e\n\u003c!-- Row 5: Actions --\u003e\n\u003cdiv class=\"ic-row\"\u003e\n  \u003cdiv class=\"ic-btn-group\"\u003e\n    \u003cbutton class=\"ic-btn action\" onclick=\"icProcess()\"\u003eConvert\u003c/button\u003e\n    \u003cbutton class=\"ic-btn secondary\" onclick=\"icSwap()\"\u003eSwap\u003c/button\u003e\n    \u003cbutton class=\"ic-btn secondary\" onclick=\"icClear()\"\u003eClear\u003c/button\u003e\n    \u003cdiv class=\"ic-upload-wrap\"\u003e\n      \u003cbutton class=\"ic-btn secondary\"\u003eUpload File\u003c/button\u003e\n      \u003cinput type=\"file\" accept=\"text/*\" onchange=\"icUpload(event)\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003chr class=\"ic-divider\"\u003e\n\u003c!-- Editor panels --\u003e\n\u003cdiv class=\"ic-panels\"\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"ic-panel-head\"\u003e\n      \u003cspan class=\"ic-panel-title\"\u003eINPUT\u003c/span\u003e\n      \u003cdiv class=\"ic-panel-actions\"\u003e\n        \u003cbutton class=\"ic-btn secondary\" style=\"padding:4px 10px;font-size:12px\" onclick=\"icPaste()\"\u003ePaste\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003ctextarea id=\"ic-input\" placeholder=\"Paste your code here...\" oninput=\"icProcess()\" spellcheck=\"false\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"ic-panel-head\"\u003e\n      \u003cspan class=\"ic-panel-title\"\u003eOUTPUT\u003c/span\u003e\n      \u003cdiv class=\"ic-panel-actions\"\u003e\n        \u003cbutton class=\"ic-btn secondary\" style=\"padding:4px 10px;font-size:12px\" onclick=\"icCopy()\"\u003eCopy\u003c/button\u003e\n        \u003cspan class=\"ic-copy-note\" id=\"ic-copy-note\"\u003eCopied!\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ic-preview\" id=\"ic-output-preview\" aria-live=\"polite\"\u003e\u003c/div\u003e\n    \u003ctextarea id=\"ic-output-raw\" style=\"display:none\" readonly spellcheck=\"false\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Stats --\u003e\n\u003cdiv class=\"ic-stats\" id=\"ic-stats\"\u003e\n  \u003cspan class=\"ic-stat\"\u003eLines: \u003cstrong id=\"ic-stat-lines\"\u003e0\u003c/strong\u003e\u003c/span\u003e\n  \u003cspan class=\"ic-stat\"\u003eInput chars: \u003cstrong id=\"ic-stat-in\"\u003e0\u003c/strong\u003e\u003c/span\u003e\n  \u003cspan class=\"ic-stat\"\u003eOutput chars: \u003cstrong id=\"ic-stat-out\"\u003e0\u003c/strong\u003e\u003c/span\u003e\n  \u003cspan class=\"ic-stat\"\u003eTab-indented lines: \u003cstrong id=\"ic-stat-tabs\"\u003e0\u003c/strong\u003e\u003c/span\u003e\n  \u003cspan class=\"ic-stat\"\u003eSpace-indented lines: \u003cstrong id=\"ic-stat-spaces\"\u003e0\u003c/strong\u003e\u003c/span\u003e\n  \u003cspan class=\"ic-stat\"\u003eMixed lines: \u003cstrong id=\"ic-stat-mixed\"\u003e0\u003c/strong\u003e\u003c/span\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n  var fromStyle = 'auto'; // auto|2sp|4sp|tab\n  var toStyle = '4sp';    // 2sp|4sp|tab\n  var shiftDelta = 0;     // cumulative indent shift\n\n  /* ---- Detection ---- */\n  function detectStyle(lines){\n    var tabLines=0, sp2Lines=0, sp4Lines=0, mixedLines=0;\n    lines.forEach(function(l){\n      var m = l.match(/^(\\s+)/);\n      if(!m) return;\n      var lead = m[1];\n      var hasTab = /\\t/.test(lead);\n      var hasSp = / /.test(lead);\n      if(hasTab \u0026\u0026 hasSp){ mixedLines++; return; }\n      if(hasTab){ tabLines++; return; }\n      // spaces\n      var n = lead.length;\n      if(n % 4 === 0) sp4Lines++;\n      else if(n % 2 === 0) sp2Lines++;\n      else sp2Lines++;\n    });\n    var total = tabLines + sp2Lines + sp4Lines + mixedLines;\n    if(total === 0) return 'unknown';\n    if(mixedLines \u003e total * 0.15) return 'mixed';\n    if(tabLines \u003e sp2Lines \u0026\u0026 tabLines \u003e sp4Lines) return 'tab';\n    if(sp4Lines \u003e= sp2Lines) return '4sp';\n    return '2sp';\n  }\n\n  function updateDetectBadge(style){\n    var badge = document.getElementById('ic-detect-badge');\n    var text = document.getElementById('ic-detect-text');\n    badge.className = 'ic-detect-badge';\n    var labels = {\n      'unknown':'No indentation detected',\n      '2sp':'Detected: 2-space',\n      '4sp':'Detected: 4-space',\n      'tab':'Detected: Tabs',\n      'mixed':'Detected: Mixed (will fix)'\n    };\n    var classes = {'2sp':'detected-spaces-2','4sp':'detected-spaces-4','tab':'detected-tabs','mixed':'detected-mixed'};\n    text.textContent = labels[style] || 'Paste code to detect';\n    if(classes[style]) badge.classList.add(classes[style]);\n  }\n\n  /* ---- Conversion core ---- */\n  function lineToUnified(line, srcStyle){\n    // Resolve actual from-style\n    var src = srcStyle === 'auto' ? detectStyle([line]) : srcStyle;\n    if(src === 'unknown' || src === 'mixed') src = guessLineStyle(line);\n\n    var m = line.match(/^(\\s*)([\\s\\S]*)$/);\n    if(!m) return {level:0, rest:line, raw:''};\n    var lead = m[1];\n    var rest = m[2];\n\n    if(!lead) return {level:0, rest:rest, raw:''};\n\n    // Normalise lead to spaces first\n    var sp = lead.replace(/\\t/g, '    '); // treat tab=4sp for measurement\n    var unitSize = (src === '2sp') ? 2 : 4;\n    var level = Math.round(sp.length / unitSize);\n    return {level:level, rest:rest, raw:lead};\n  }\n\n  function guessLineStyle(line){\n    var m = line.match(/^(\\s+)/);\n    if(!m) return '4sp';\n    var lead = m[1];\n    if(/\\t/.test(lead)) return 'tab';\n    if(lead.length % 4 === 0) return '4sp';\n    return '2sp';\n  }\n\n  function buildIndent(level, style){\n    if(level \u003c= 0) return '';\n    if(style === 'tab') return '\\t'.repeat(level);\n    var size = style === '2sp' ? 2 : 4;\n    return ' '.repeat(size * level);\n  }\n\n  function convertLines(lines, srcStyle, dstStyle, fixMixed, trim, shift){\n    var detected = detectStyle(lines);\n    updateDetectBadge(detected);\n\n    return lines.map(function(line){\n      // Separate leading whitespace from content\n      var m = line.match(/^(\\s*)([\\s\\S]*)$/);\n      var lead = m[1];\n      var rest = m[2];\n\n      if(!lead){\n        if(trim) rest = rest.replace(/[ \\t]+$/, '');\n        return rest;\n      }\n\n      // Fix mixed: normalise to spaces first\n      var normalized = lead;\n      if(fixMixed \u0026\u0026 /\\t/.test(lead) \u0026\u0026 / /.test(lead)){\n        normalized = lead.replace(/\\t/g, '    ');\n      }\n\n      // Determine source unit\n      var hasTabs = /\\t/.test(normalized);\n      var src = srcStyle === 'auto' ? detected : srcStyle;\n      if(src === 'mixed' || src === 'unknown') src = hasTabs ? 'tab' : (normalized.length % 4 === 0 ? '4sp' : '2sp');\n\n      // Measure level\n      var level;\n      if(hasTabs \u0026\u0026 src !== 'tab'){\n        // expand tabs then measure\n        var expanded = normalized.replace(/\\t/g, (src==='2sp'?'  ':'    '));\n        var unit = src==='2sp' ? 2 : 4;\n        level = Math.floor(expanded.length / unit);\n      } else if(!hasTabs \u0026\u0026 src === 'tab'){\n        // treat every group of 4 spaces as 1 tab level\n        level = Math.floor(normalized.length / 4);\n      } else if(src === 'tab'){\n        level = normalized.length; // count of tab chars\n      } else {\n        var u = src==='2sp' ? 2 : 4;\n        level = Math.floor(normalized.length / u);\n      }\n\n      level = Math.max(0, level + shift);\n      var newLead = buildIndent(level, dstStyle);\n      var out = newLead + rest;\n      if(trim) out = out.replace(/[ \\t]+$/, '');\n      return out;\n    });\n  }\n\n  /* ---- Whitespace preview ---- */\n  function escHtml(s){\n    return s.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n  }\n\n  function renderPreview(text, showWs, dstStyle){\n    var el = document.getElementById('ic-output-preview');\n    if(!showWs){ el.textContent = text; return; }\n    var lines = text.split('\\n');\n    var html = lines.map(function(line){\n      var m = line.match(/^(\\s*)([\\s\\S]*)$/);\n      var lead = m[1], rest = m[2];\n      var leadHtml = '';\n      if(dstStyle === 'tab'){\n        leadHtml = escHtml(lead).replace(/\\t/g,'\u003cspan class=\"ws-tab\"\u003e\\u2192   \u003c/span\u003e');\n      } else {\n        leadHtml = escHtml(lead).replace(/ /g,'\u003cspan class=\"ws-sp\"\u003e\\u00b7\u003c/span\u003e');\n      }\n      return leadHtml + escHtml(rest);\n    }).join('\\n');\n    el.innerHTML = html;\n  }\n\n  /* ---- Stats ---- */\n  function computeStats(lines){\n    var tabs=0, spaces=0, mixed=0;\n    lines.forEach(function(l){\n      var m = l.match(/^(\\s+)/);\n      if(!m) return;\n      var lead = m[1];\n      var hasT = /\\t/.test(lead), hasS = / /.test(lead);\n      if(hasT \u0026\u0026 hasS) mixed++;\n      else if(hasT) tabs++;\n      else spaces++;\n    });\n    return {tabs:tabs, spaces:spaces, mixed:mixed};\n  }\n\n  /* ---- Main process ---- */\n  function process(){\n    var input = document.getElementById('ic-input').value;\n    var trim = document.getElementById('ic-trim').checked;\n    var fixMixed = document.getElementById('ic-fix-mixed').checked;\n    var showWs = document.getElementById('ic-show-ws').checked;\n\n    var lines = input.split('\\n');\n    var stats = computeStats(lines);\n    var outLines = convertLines(lines, fromStyle, toStyle, fixMixed, trim, shiftDelta);\n    var output = outLines.join('\\n');\n\n    document.getElementById('ic-output-raw').value = output;\n    renderPreview(output, showWs, toStyle);\n\n    document.getElementById('ic-stat-lines').textContent = lines.length;\n    document.getElementById('ic-stat-in').textContent = input.length;\n    document.getElementById('ic-stat-out').textContent = output.length;\n    document.getElementById('ic-stat-tabs').textContent = stats.tabs;\n    document.getElementById('ic-stat-spaces').textContent = stats.spaces;\n    document.getElementById('ic-stat-mixed').textContent = stats.mixed;\n\n    if(!input){\n      updateDetectBadge('unknown');\n      document.getElementById('ic-detect-text').textContent = 'Paste code to detect';\n    }\n  }\n\n  /* ---- UI setters ---- */\n  function setFrom(s){\n    fromStyle = s;\n    ['auto','2sp','4sp','tab'].forEach(function(k){\n      document.getElementById('ic-from-'+k).classList.toggle('active', k===s);\n    });\n    process();\n  }\n\n  function setTo(s){\n    toStyle = s;\n    ['2sp','4sp','tab'].forEach(function(k){\n      document.getElementById('ic-to-'+k).classList.toggle('active', k===s);\n    });\n    process();\n  }\n\n  function shiftIndent(delta){\n    shiftDelta += delta;\n    process();\n  }\n\n  function swap(){\n    var raw = document.getElementById('ic-output-raw').value;\n    document.getElementById('ic-input').value = raw;\n    shiftDelta = 0;\n    process();\n  }\n\n  function clear(){\n    document.getElementById('ic-input').value = '';\n    document.getElementById('ic-output-raw').value = '';\n    document.getElementById('ic-output-preview').textContent = '';\n    shiftDelta = 0;\n    ['ic-stat-lines','ic-stat-in','ic-stat-out','ic-stat-tabs','ic-stat-spaces','ic-stat-mixed'].forEach(function(id){\n      document.getElementById(id).textContent = '0';\n    });\n    updateDetectBadge('unknown');\n    document.getElementById('ic-detect-text').textContent = 'Paste code to detect';\n  }\n\n  function copy(){\n    var val = document.getElementById('ic-output-raw').value;\n    if(!val) return;\n    if(navigator.clipboard \u0026\u0026 navigator.clipboard.writeText){\n      navigator.clipboard.writeText(val).then(flashCopy);\n    } else {\n      var ta = document.createElement('textarea');\n      ta.value = val; ta.style.position='fixed'; ta.style.opacity='0';\n      document.body.appendChild(ta); ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      flashCopy();\n    }\n  }\n\n  function flashCopy(){\n    var note = document.getElementById('ic-copy-note');\n    note.style.display='inline';\n    setTimeout(function(){ note.style.display='none'; }, 1800);\n  }\n\n  function paste(){\n    if(navigator.clipboard \u0026\u0026 navigator.clipboard.readText){\n      navigator.clipboard.readText().then(function(t){\n        document.getElementById('ic-input').value = t;\n        shiftDelta = 0;\n        process();\n      });\n    }\n  }\n\n  function upload(e){\n    var file = e.target.files[0];\n    if(!file) return;\n    var reader = new FileReader();\n    reader.onload = function(ev){\n      document.getElementById('ic-input').value = ev.target.result;\n      shiftDelta = 0;\n      process();\n    };\n    reader.readAsText(file);\n    e.target.value = '';\n  }\n\n  // Expose to inline handlers\n  window.icSetFrom = setFrom;\n  window.icSetTo = setTo;\n  window.icShiftIndent = shiftIndent;\n  window.icProcess = process;\n  window.icSwap = swap;\n  window.icClear = clear;\n  window.icCopy = copy;\n  window.icPaste = paste;\n  window.icUpload = upload;\n\n  process();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cstrong\u003eHow it works:\u003c/strong\u003e\u003c/p\u003e","title":"Indent Converter"},{"content":" All categories Alkali Metals Alkaline Earth Metals Transition Metals Post-Transition Metals Metalloids Nonmetals Halogens Noble Gases Lanthanides Actinides Unknown Clear ← Scroll horizontally on mobile →\n× Atomic Number Atomic Mass Category State (Room Temp) Electron Config Discovered Period Group More science tools:\nConvert units → Unit Converter Calculate scientifically → Scientific Calculator ","permalink":"https://productivity-works.com/tools/periodic-table/","summary":"\u003cdiv id=\"pt-app\"\u003e\n\u003cstyle\u003e\n#pt-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  color: #1a1a1a;\n  max-width: 100%;\n}\n#pt-app * { box-sizing: border-box; }\n\n#pt-app .pt-controls {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  margin-bottom: 16px;\n  align-items: center;\n}\n#pt-app .pt-search {\n  flex: 1;\n  min-width: 180px;\n  padding: 8px 12px;\n  border: 1.5px solid #d1d5db;\n  border-radius: 8px;\n  font-size: 14px;\n  outline: none;\n  transition: border-color 0.2s;\n}\n#pt-app .pt-search:focus { border-color: #3b82f6; }\n#pt-app .pt-filter {\n  padding: 8px 12px;\n  border: 1.5px solid #d1d5db;\n  border-radius: 8px;\n  font-size: 13px;\n  background: #fff;\n  cursor: pointer;\n  outline: none;\n}\n#pt-app .pt-clear {\n  padding: 8px 14px;\n  background: #6b7280;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 13px;\n  cursor: pointer;\n}\n#pt-app .pt-clear:hover { background: #4b5563; }\n\n/* Legend */\n#pt-app .pt-legend {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 6px;\n  margin-bottom: 14px;\n}\n#pt-app .pt-legend-item {\n  display: flex;\n  align-items: center;\n  gap: 4px;\n  font-size: 11px;\n  color: #374151;\n  cursor: pointer;\n  padding: 2px 6px;\n  border-radius: 4px;\n  border: 1px solid transparent;\n  transition: border-color 0.15s;\n}\n#pt-app .pt-legend-item:hover { border-color: #9ca3af; }\n#pt-app .pt-legend-swatch {\n  width: 12px; height: 12px;\n  border-radius: 3px;\n  flex-shrink: 0;\n}\n\n/* Table wrapper */\n#pt-app .pt-wrapper {\n  overflow-x: auto;\n  -webkit-overflow-scrolling: touch;\n  padding-bottom: 8px;\n}\n#pt-app .pt-grid {\n  display: grid;\n  grid-template-columns: repeat(18, minmax(46px, 1fr));\n  grid-template-rows: repeat(9, 48px);\n  gap: 2px;\n  min-width: 860px;\n}\n\n/* Element cell */\n#pt-app .el {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  border-radius: 4px;\n  cursor: pointer;\n  padding: 2px;\n  transition: transform 0.1s, box-shadow 0.1s, opacity 0.2s;\n  border: 1px solid rgba(0,0,0,0.12);\n  position: relative;\n  user-select: none;\n}\n#pt-app .el:hover {\n  transform: scale(1.12);\n  box-shadow: 0 4px 12px rgba(0,0,0,0.22);\n  z-index: 5;\n}\n#pt-app .el.dimmed { opacity: 0.18; }\n#pt-app .el.highlighted { opacity: 1; box-shadow: 0 0 0 2px #1d4ed8; }\n\n#pt-app .el-num {\n  font-size: 8px;\n  color: rgba(0,0,0,0.55);\n  line-height: 1;\n  align-self: flex-start;\n  padding-left: 2px;\n}\n#pt-app .el-sym {\n  font-size: 16px;\n  font-weight: 700;\n  line-height: 1;\n}\n#pt-app .el-name {\n  font-size: 7px;\n  line-height: 1.1;\n  text-align: center;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n  width: 100%;\n  text-align: center;\n}\n#pt-app .el-mass {\n  font-size: 7px;\n  color: rgba(0,0,0,0.5);\n  line-height: 1;\n}\n\n/* Lanthanide / Actinide label cells */\n#pt-app .pt-label-cell {\n  display: flex;\n  align-items: center;\n  justify-content: flex-end;\n  padding-right: 4px;\n  font-size: 8px;\n  font-weight: 600;\n  color: #6b7280;\n  grid-column: span 3;\n}\n\n/* Category colors */\n#pt-app .cat-alkali       { background: #fca5a5; }\n#pt-app .cat-alkaline     { background: #fdba74; }\n#pt-app .cat-lanthanide   { background: #fde68a; }\n#pt-app .cat-actinide     { background: #d9f99d; }\n#pt-app .cat-transition   { background: #93c5fd; }\n#pt-app .cat-post         { background: #6ee7b7; }\n#pt-app .cat-metalloid    { background: #a5b4fc; }\n#pt-app .cat-nonmetal     { background: #f9a8d4; }\n#pt-app .cat-halogen      { background: #fb923c; color: #fff; }\n#pt-app .cat-noble        { background: #c4b5fd; }\n#pt-app .cat-unknown      { background: #d1d5db; }\n\n/* Popup */\n#pt-app .pt-popup-overlay {\n  display: none;\n  position: fixed;\n  inset: 0;\n  background: rgba(0,0,0,0.45);\n  z-index: 1000;\n  align-items: center;\n  justify-content: center;\n}\n#pt-app .pt-popup-overlay.open { display: flex; }\n#pt-app .pt-popup {\n  background: #fff;\n  border-radius: 14px;\n  padding: 24px 28px;\n  max-width: 440px;\n  width: 90vw;\n  box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n  position: relative;\n  animation: ptPopIn 0.18s ease;\n}\n@keyframes ptPopIn {\n  from { transform: scale(0.88); opacity: 0; }\n  to   { transform: scale(1); opacity: 1; }\n}\n#pt-app .pt-popup-close {\n  position: absolute;\n  top: 12px; right: 14px;\n  background: none;\n  border: none;\n  font-size: 22px;\n  cursor: pointer;\n  color: #6b7280;\n  line-height: 1;\n}\n#pt-app .pt-popup-close:hover { color: #1a1a1a; }\n#pt-app .pt-popup-header {\n  display: flex;\n  align-items: center;\n  gap: 16px;\n  margin-bottom: 16px;\n}\n#pt-app .pt-popup-sym-box {\n  width: 72px; height: 72px;\n  border-radius: 10px;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  flex-shrink: 0;\n}\n#pt-app .pt-popup-sym {\n  font-size: 32px;\n  font-weight: 800;\n  line-height: 1;\n}\n#pt-app .pt-popup-num-small {\n  font-size: 12px;\n  color: rgba(0,0,0,0.5);\n}\n#pt-app .pt-popup-name {\n  font-size: 22px;\n  font-weight: 700;\n  margin: 0 0 4px;\n}\n#pt-app .pt-popup-cat {\n  font-size: 12px;\n  color: #6b7280;\n  text-transform: capitalize;\n}\n#pt-app .pt-popup-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 8px 16px;\n}\n#pt-app .pt-popup-field label {\n  font-size: 11px;\n  color: #9ca3af;\n  display: block;\n  margin-bottom: 1px;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#pt-app .pt-popup-field span {\n  font-size: 14px;\n  font-weight: 600;\n  color: #111;\n}\n\n#pt-app .pt-mobile-hint {\n  text-align: center;\n  font-size: 12px;\n  color: #9ca3af;\n  margin-top: 6px;\n}\n\n@media (max-width: 600px) {\n  #pt-app .pt-grid { min-width: 760px; }\n  #pt-app .el { border-radius: 3px; }\n  #pt-app .el-sym { font-size: 13px; }\n}\n\u003c/style\u003e\n\u003cdiv class=\"pt-controls\"\u003e\n  \u003cinput class=\"pt-search\" id=\"ptSearch\" type=\"text\" placeholder=\"Search element name or symbol…\" autocomplete=\"off\" /\u003e\n  \u003cselect class=\"pt-filter\" id=\"ptFilter\"\u003e\n    \u003coption value=\"\"\u003eAll categories\u003c/option\u003e\n    \u003coption value=\"alkali\"\u003eAlkali Metals\u003c/option\u003e\n    \u003coption value=\"alkaline\"\u003eAlkaline Earth Metals\u003c/option\u003e\n    \u003coption value=\"transition\"\u003eTransition Metals\u003c/option\u003e\n    \u003coption value=\"post\"\u003ePost-Transition Metals\u003c/option\u003e\n    \u003coption value=\"metalloid\"\u003eMetalloids\u003c/option\u003e\n    \u003coption value=\"nonmetal\"\u003eNonmetals\u003c/option\u003e\n    \u003coption value=\"halogen\"\u003eHalogens\u003c/option\u003e\n    \u003coption value=\"noble\"\u003eNoble Gases\u003c/option\u003e\n    \u003coption value=\"lanthanide\"\u003eLanthanides\u003c/option\u003e\n    \u003coption value=\"actinide\"\u003eActinides\u003c/option\u003e\n    \u003coption value=\"unknown\"\u003eUnknown\u003c/option\u003e\n  \u003c/select\u003e\n  \u003cbutton class=\"pt-clear\" id=\"ptClear\"\u003eClear\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"pt-legend\" id=\"ptLegend\"\u003e\u003c/div\u003e\n\u003cdiv class=\"pt-wrapper\"\u003e\n  \u003cdiv class=\"pt-grid\" id=\"ptGrid\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cp class=\"pt-mobile-hint\"\u003e← Scroll horizontally on mobile →\u003c/p\u003e","title":"Interactive Periodic Table"},{"content":" Investment Return Calculator Enter your investment details to calculate ending balance, total growth, CAGR, and a year-by-year breakdown. Compare conservative, moderate, and aggressive return scenarios.\nInitial Investment ($) The amount you invest at the start Monthly Contribution ($) Regular monthly addition to investment Expected Annual Return (%) Historical S\u0026amp;P 500 avg ≈ 7% (inflation-adjusted) Investment Period (Years) How long you plan to stay invested Calculate Returns Results Ending Balance - Total Contributions - Total Growth - CAGR - Growth Chart Year-by-Year Breakdown Year Balance Contributions Growth Growth % Scenario Comparison Same initial investment \u0026amp; contributions — different return assumptions.\nConservative 4% annual return - Moderate 7% annual return - Aggressive 12% annual return - Related Free Tools Compound Interest Calculator — See how interest compounds over time Savings Goal Calculator — How much to save monthly to hit your target Net Worth Tracker — Track assets vs liabilities over time Retirement Calculator — Plan your retirement nest egg Related Tools Compound Interest Calculator Fire Calculator Ideco Simulator Related Articles Beginner Investing Guide 2026: How to Start Building Wealth Today Best Index Funds for Beginners 2026 Best Investment Account for Beginners in Japan: NISA vs. iDeCo vs. Regular Brokerage ","permalink":"https://productivity-works.com/tools/investment-return-calculator/","summary":"\u003cdiv id=\"ir-app\"\u003e\n\u003cstyle\u003e\n#ir-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#ir-app * { box-sizing: border-box; }\n#ir-app h2 {\n  font-size: 1.4rem;\n  font-weight: 700;\n  margin: 1.5rem 0 0.75rem;\n  color: #1a1a2e;\n  border-left: 4px solid #2563eb;\n  padding-left: 0.6rem;\n}\n#ir-app .ir-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1rem;\n  margin-bottom: 1rem;\n}\n@media (max-width: 600px) {\n  #ir-app .ir-grid { grid-template-columns: 1fr; }\n}\n#ir-app label {\n  display: block;\n  font-size: 0.85rem;\n  font-weight: 600;\n  color: #374151;\n  margin-bottom: 0.3rem;\n}\n#ir-app input[type=\"number\"] {\n  width: 100%;\n  padding: 0.6rem 0.75rem;\n  border: 1.5px solid #d1d5db;\n  border-radius: 8px;\n  font-size: 1rem;\n  outline: none;\n  transition: border-color 0.2s;\n  background: #fff;\n}\n#ir-app input[type=\"number\"]:focus {\n  border-color: #2563eb;\n  box-shadow: 0 0 0 3px rgba(37,99,235,0.1);\n}\n#ir-app .ir-hint {\n  font-size: 0.75rem;\n  color: #6b7280;\n  margin-top: 0.25rem;\n}\n#ir-app .ir-btn {\n  display: inline-block;\n  padding: 0.7rem 2rem;\n  background: linear-gradient(135deg, #2563eb, #1d4ed8);\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 1rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: transform 0.1s, box-shadow 0.1s;\n  margin-top: 0.5rem;\n}\n#ir-app .ir-btn:hover {\n  transform: translateY(-1px);\n  box-shadow: 0 4px 14px rgba(37,99,235,0.35);\n}\n#ir-app .ir-btn:active { transform: translateY(0); }\n#ir-app .ir-results {\n  display: none;\n}\n#ir-app .ir-cards {\n  display: grid;\n  grid-template-columns: repeat(4, 1fr);\n  gap: 0.75rem;\n  margin: 1.25rem 0;\n}\n@media (max-width: 700px) {\n  #ir-app .ir-cards { grid-template-columns: repeat(2, 1fr); }\n}\n#ir-app .ir-card {\n  background: #fff;\n  border: 1.5px solid #e5e7eb;\n  border-radius: 12px;\n  padding: 1rem 0.75rem;\n  text-align: center;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.06);\n}\n#ir-app .ir-card .ir-card-label {\n  font-size: 0.75rem;\n  color: #6b7280;\n  margin-bottom: 0.35rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#ir-app .ir-card .ir-card-val {\n  font-size: 1.3rem;\n  font-weight: 800;\n  color: #1a1a2e;\n}\n#ir-app .ir-card.ir-highlight .ir-card-val { color: #2563eb; }\n#ir-app .ir-card.ir-green .ir-card-val { color: #059669; }\n#ir-app .ir-chart-wrap {\n  background: #fff;\n  border: 1.5px solid #e5e7eb;\n  border-radius: 12px;\n  padding: 1rem;\n  margin-bottom: 1.25rem;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.06);\n}\n#ir-app canvas { display: block; width: 100% !important; }\n#ir-app .ir-table-wrap {\n  overflow-x: auto;\n  border-radius: 12px;\n  border: 1.5px solid #e5e7eb;\n  margin-bottom: 1.5rem;\n}\n#ir-app table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.88rem;\n}\n#ir-app thead th {\n  background: #1e3a8a;\n  color: #fff;\n  padding: 0.65rem 0.85rem;\n  text-align: right;\n  font-weight: 700;\n  white-space: nowrap;\n}\n#ir-app thead th:first-child { text-align: center; }\n#ir-app tbody tr:nth-child(even) { background: #f0f4ff; }\n#ir-app tbody tr:hover { background: #dbeafe; }\n#ir-app tbody td {\n  padding: 0.55rem 0.85rem;\n  text-align: right;\n  border-bottom: 1px solid #e5e7eb;\n  white-space: nowrap;\n}\n#ir-app tbody td:first-child { text-align: center; font-weight: 700; }\n#ir-app .ir-scenario {\n  background: #fff;\n  border: 1.5px solid #e5e7eb;\n  border-radius: 12px;\n  padding: 1rem 1.25rem;\n  margin-bottom: 1.5rem;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.06);\n}\n#ir-app .ir-scenario h3 {\n  font-size: 1rem;\n  font-weight: 700;\n  margin: 0 0 0.75rem;\n  color: #374151;\n}\n#ir-app .ir-scenario-grid {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 0.75rem;\n}\n@media (max-width: 600px) {\n  #ir-app .ir-scenario-grid { grid-template-columns: 1fr; }\n}\n#ir-app .ir-s-card {\n  border-radius: 10px;\n  padding: 0.85rem 0.75rem;\n  text-align: center;\n}\n#ir-app .ir-s-card.conservative { background: #f0fdf4; border: 1.5px solid #86efac; }\n#ir-app .ir-s-card.moderate    { background: #eff6ff; border: 1.5px solid #93c5fd; }\n#ir-app .ir-s-card.aggressive  { background: #fef3c7; border: 1.5px solid #fcd34d; }\n#ir-app .ir-s-card .ir-s-label {\n  font-size: 0.75rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 0.3rem;\n}\n#ir-app .ir-s-card.conservative .ir-s-label { color: #059669; }\n#ir-app .ir-s-card.moderate    .ir-s-label { color: #2563eb; }\n#ir-app .ir-s-card.aggressive  .ir-s-label { color: #d97706; }\n#ir-app .ir-s-card .ir-s-rate {\n  font-size: 0.8rem;\n  color: #6b7280;\n  margin-bottom: 0.5rem;\n}\n#ir-app .ir-s-card .ir-s-val {\n  font-size: 1.2rem;\n  font-weight: 800;\n  color: #1a1a2e;\n}\n#ir-app .ir-related {\n  background: #f8fafc;\n  border: 1.5px solid #e5e7eb;\n  border-radius: 12px;\n  padding: 1rem 1.25rem;\n  margin-top: 1rem;\n}\n#ir-app .ir-related h3 {\n  font-size: 0.95rem;\n  font-weight: 700;\n  margin: 0 0 0.6rem;\n  color: #374151;\n}\n#ir-app .ir-related ul {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n}\n#ir-app .ir-related ul li {\n  padding: 0.25rem 0;\n  font-size: 0.9rem;\n}\n#ir-app .ir-related ul li a { color: #2563eb; text-decoration: none; }\n#ir-app .ir-related ul li a:hover { text-decoration: underline; }\n#ir-app .ir-input-section {\n  background: #fff;\n  border: 1.5px solid #e5e7eb;\n  border-radius: 12px;\n  padding: 1.25rem;\n  margin-bottom: 1rem;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.06);\n}\n\u003c/style\u003e\n\u003ch2\u003eInvestment Return Calculator\u003c/h2\u003e\n\u003cp style=\"color:#6b7280;font-size:0.92rem;margin-bottom:1rem;\"\u003eEnter your investment details to calculate ending balance, total growth, CAGR, and a year-by-year breakdown. Compare conservative, moderate, and aggressive return scenarios.\u003c/p\u003e","title":"Investment Return Calculator - CAGR \u0026 Total Growth"},{"content":"Create professional invoices in seconds — fill in your details, add line items, and download a clean PDF. Everything runs in your browser; no data is sent to any server.\nCurrency: USD $ EUR € GBP £ JPY ¥ \u0026#128065; Preview Invoice \u0026#128438; Print / Save PDF Your Business Company / Your Name Email Phone Address Bill To (Client) Client Company / Name Client Email Client Address Invoice Details Invoice Number Invoice Date Due Date Line Items Description Qty Unit Price Total + Add Line Item Totals Subtotal $0.00 Tax Rate (%) Tax Amount $0.00 Discount Flat % Discount Amount $0.00 Grand Total $0.00 Notes / Terms Invoice Preview Estimate retirement savings -\u0026gt; Pension Simulator Calculate take-home pay -\u0026gt; Salary Calculator Plan your budget -\u0026gt; 50/30/20 Budget Calculator Related Articles Freelance Tax Guide 2026: Everything You Need to Know How to Start Freelancing in 2026: Complete Beginner\u0026rsquo;\u0026rsquo;s Guide How to Start Freelancing With No Experience 2026: Full Guide ","permalink":"https://productivity-works.com/tools/invoice-generator/","summary":"\u003cp\u003eCreate professional invoices in seconds — fill in your details, add line items, and download a clean PDF. Everything runs in your browser; no data is sent to any server.\u003c/p\u003e\n\u003cdiv id=\"ig-app\"\u003e\n\u003cstyle\u003e\n#ig-app *,\n#ig-app *::before,\n#ig-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#ig-app {\n  font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n  color: #1e293b;\n  font-size: 15px;\n  line-height: 1.6;\n}\n\u003cp\u003e/* ── Cards ── */\n#ig-app .ig-card {\nbackground: #f8fafc;\nborder: 1px solid #cbd5e1;\nborder-radius: 10px;\npadding: 24px;\nmargin-bottom: 20px;\n}\n#ig-app .ig-section-title {\nfont-size: 12px;\nfont-weight: 700;\ntext-transform: uppercase;\nletter-spacing: 0.07em;\ncolor: #64748b;\nmargin-bottom: 16px;\n}\u003c/p\u003e","title":"Invoice Generator"},{"content":" IP Address Calculator Subnet, CIDR, and network analysis tools — instant results, no server required.\nIP / CIDR Input Calculate Reset Try: 192.168.1.0/24 10.0.0.0/8 172.16.0.0/12 203.0.113.50/28 100.64.0.0/10 Network Summary Mask Details Subnet Subdivision Split the current network into smaller subnets by selecting a new prefix length.\nNew prefix: Subdivide CIDR to Subnet Mask Reference Table CIDR Subnet Mask Wildcard Mask Total Hosts Usable Hosts Related Tools Binary / Hex / Decimal Converter Port Number Lookup MAC Address Vendor Lookup Network Speed \u0026 Bandwidth Calculator Related Tools Api Request Builder Dns Record Guide Http Status Codes ","permalink":"https://productivity-works.com/tools/ip-address-calculator/","summary":"\u003cdiv id=\"ipc-app\"\u003e\n\u003cstyle\u003e\n#ipc-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  color: #1a1a2e;\n  line-height: 1.6;\n}\n#ipc-app h1 {\n  font-size: 1.8rem;\n  font-weight: 700;\n  color: #0f3460;\n  margin-bottom: 0.4rem;\n}\n#ipc-app .ipc-subtitle {\n  color: #555;\n  margin-bottom: 1.5rem;\n  font-size: 0.97rem;\n}\n#ipc-app .ipc-card {\n  background: #fff;\n  border: 1px solid #d0d7e3;\n  border-radius: 10px;\n  padding: 1.4rem 1.6rem;\n  margin-bottom: 1.4rem;\n  box-shadow: 0 2px 8px rgba(0,0,0,0.06);\n}\n#ipc-app .ipc-card h2 {\n  font-size: 1.05rem;\n  font-weight: 600;\n  color: #0f3460;\n  margin: 0 0 1rem 0;\n  padding-bottom: 0.5rem;\n  border-bottom: 2px solid #e8eaf6;\n}\n#ipc-app .ipc-input-row {\n  display: flex;\n  gap: 0.7rem;\n  align-items: center;\n  flex-wrap: wrap;\n}\n#ipc-app .ipc-input {\n  flex: 1;\n  min-width: 220px;\n  padding: 0.65rem 1rem;\n  font-size: 1rem;\n  border: 2px solid #c5cfe0;\n  border-radius: 7px;\n  outline: none;\n  transition: border-color 0.2s;\n  font-family: monospace;\n  background: #f8faff;\n}\n#ipc-app .ipc-input:focus {\n  border-color: #3a7bd5;\n  background: #fff;\n}\n#ipc-app .ipc-btn {\n  padding: 0.65rem 1.5rem;\n  background: #3a7bd5;\n  color: #fff;\n  border: none;\n  border-radius: 7px;\n  font-size: 0.97rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.2s, transform 0.1s;\n  white-space: nowrap;\n}\n#ipc-app .ipc-btn:hover {\n  background: #2563b0;\n}\n#ipc-app .ipc-btn:active {\n  transform: scale(0.98);\n}\n#ipc-app .ipc-btn-sm {\n  padding: 0.4rem 0.9rem;\n  font-size: 0.85rem;\n  border-radius: 5px;\n}\n#ipc-app .ipc-examples {\n  margin-top: 0.6rem;\n  display: flex;\n  gap: 0.5rem;\n  flex-wrap: wrap;\n}\n#ipc-app .ipc-example-chip {\n  background: #eef2fb;\n  border: 1px solid #c5cfe0;\n  border-radius: 20px;\n  padding: 0.2rem 0.75rem;\n  font-size: 0.82rem;\n  cursor: pointer;\n  font-family: monospace;\n  color: #3a7bd5;\n  transition: background 0.15s;\n}\n#ipc-app .ipc-example-chip:hover {\n  background: #dce8fa;\n}\n#ipc-app .ipc-error {\n  color: #c0392b;\n  background: #fdf0ed;\n  border: 1px solid #f5c6bb;\n  border-radius: 7px;\n  padding: 0.6rem 1rem;\n  margin-top: 0.8rem;\n  font-size: 0.92rem;\n  display: none;\n}\n#ipc-app .ipc-results {\n  display: none;\n}\n#ipc-app .ipc-badge-row {\n  display: flex;\n  gap: 0.5rem;\n  flex-wrap: wrap;\n  margin-bottom: 1rem;\n}\n#ipc-app .ipc-badge {\n  border-radius: 20px;\n  padding: 0.25rem 0.85rem;\n  font-size: 0.82rem;\n  font-weight: 600;\n  letter-spacing: 0.02em;\n}\n#ipc-app .badge-class-a { background: #e3f2fd; color: #1565c0; border: 1px solid #90caf9; }\n#ipc-app .badge-class-b { background: #e8f5e9; color: #2e7d32; border: 1px solid #a5d6a7; }\n#ipc-app .badge-class-c { background: #fff3e0; color: #e65100; border: 1px solid #ffcc80; }\n#ipc-app .badge-class-d { background: #fce4ec; color: #880e4f; border: 1px solid #f48fb1; }\n#ipc-app .badge-class-e { background: #f3e5f5; color: #4a148c; border: 1px solid #ce93d8; }\n#ipc-app .badge-private { background: #e8f5e9; color: #1b5e20; border: 1px solid #a5d6a7; }\n#ipc-app .badge-public  { background: #e3f2fd; color: #0d47a1; border: 1px solid #90caf9; }\n#ipc-app .ipc-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));\n  gap: 0.7rem;\n  margin-bottom: 1rem;\n}\n#ipc-app .ipc-field {\n  background: #f5f8ff;\n  border: 1px solid #dde4f0;\n  border-radius: 8px;\n  padding: 0.65rem 0.9rem;\n}\n#ipc-app .ipc-field-label {\n  font-size: 0.75rem;\n  color: #7a8aaa;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  margin-bottom: 0.15rem;\n  font-weight: 600;\n}\n#ipc-app .ipc-field-value {\n  font-size: 1rem;\n  font-weight: 700;\n  color: #0f3460;\n  font-family: monospace;\n  word-break: break-all;\n}\n#ipc-app .ipc-field-sub {\n  font-size: 0.75rem;\n  color: #8899bb;\n  margin-top: 0.1rem;\n  font-family: monospace;\n}\n#ipc-app .ipc-section-title {\n  font-size: 0.88rem;\n  font-weight: 700;\n  color: #556;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  margin: 0.8rem 0 0.4rem 0;\n}\n#ipc-app table.ipc-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.88rem;\n}\n#ipc-app table.ipc-table th {\n  background: #eef2fb;\n  color: #3a5080;\n  font-weight: 700;\n  padding: 0.45rem 0.7rem;\n  text-align: left;\n  border-bottom: 2px solid #c5cfe0;\n}\n#ipc-app table.ipc-table td {\n  padding: 0.38rem 0.7rem;\n  border-bottom: 1px solid #e8eaf6;\n  font-family: monospace;\n  color: #2a3a5a;\n}\n#ipc-app table.ipc-table tr:last-child td { border-bottom: none; }\n#ipc-app table.ipc-table tr:hover td { background: #f5f8ff; }\n#ipc-app .ipc-sub-form {\n  display: flex;\n  gap: 0.7rem;\n  align-items: center;\n  flex-wrap: wrap;\n  margin-bottom: 0.9rem;\n}\n#ipc-app .ipc-select {\n  padding: 0.55rem 0.8rem;\n  border: 2px solid #c5cfe0;\n  border-radius: 7px;\n  font-size: 0.92rem;\n  background: #f8faff;\n  color: #1a1a2e;\n  outline: none;\n}\n#ipc-app .ipc-select:focus { border-color: #3a7bd5; }\n#ipc-app .ipc-divider {\n  border: none;\n  border-top: 2px solid #e8eaf6;\n  margin: 1rem 0;\n}\n#ipc-app .ipc-related {\n  background: #f0f4ff;\n  border: 1px solid #c5d3f0;\n  border-radius: 9px;\n  padding: 1rem 1.2rem;\n  margin-top: 0.5rem;\n}\n#ipc-app .ipc-related h3 {\n  font-size: 0.95rem;\n  font-weight: 700;\n  color: #3a5080;\n  margin: 0 0 0.5rem 0;\n}\n#ipc-app .ipc-related ul {\n  margin: 0;\n  padding-left: 1.2rem;\n}\n#ipc-app .ipc-related li {\n  margin-bottom: 0.25rem;\n  font-size: 0.9rem;\n}\n#ipc-app .ipc-related a {\n  color: #3a7bd5;\n  text-decoration: none;\n}\n#ipc-app .ipc-related a:hover { text-decoration: underline; }\n#ipc-app .ipc-highlight {\n  background: #fffbea;\n  border-left: 3px solid #f0b429;\n  padding: 0.5rem 0.9rem;\n  border-radius: 0 6px 6px 0;\n  font-size: 0.88rem;\n  color: #5c4b00;\n  margin-top: 0.6rem;\n}\n@media (max-width: 600px) {\n  #ipc-app .ipc-grid { grid-template-columns: 1fr; }\n  #ipc-app .ipc-input-row { flex-direction: column; align-items: stretch; }\n  #ipc-app .ipc-btn { width: 100%; }\n}\n\u003c/style\u003e\n\u003ch1\u003eIP Address Calculator\u003c/h1\u003e\n\u003cp class=\"ipc-subtitle\"\u003eSubnet, CIDR, and network analysis tools — instant results, no server required.\u003c/p\u003e","title":"IP Address Calculator - Subnet \u0026 CIDR Tools"},{"content":"Detect your local network info, calculate subnets, convert IPv4 to decimal, check IP classes, and validate addresses — all in your browser with no external requests.\nYour Connection \u0026 Browser Info Connection TypeDetecting… Effective Speed— Screen Resolution— Color Depth— Browser— Platform / OS— Language— Cookies Enabled— Local IP Detection (WebRTC) Attempting WebRTC detection… WebRTC can reveal your local (LAN) IP addresses assigned by your router. This works in most modern browsers without any permissions. Results depend on your network configuration — VPN or firewall may suppress results.\nSubnet Calculator (CIDR) IPv4 Address CIDR Prefix Length Calculate Subnet Network Address— Subnet Mask— Broadcast Address— First Usable Host— Last Usable Host— Number of Hosts— Total Addresses— Wildcard Mask— Usable Host Capacity IP Class \u0026 Range Checker IPv4 Address Check IP Class IPv4 ↔ Decimal Converter IPv4 Address Decimal (32-bit) IPv4 → Decimal Decimal → IPv4 IPv4 Address— Decimal— Binary— Hexadecimal— IPv4 Format Validator IPv4 Address to Validate Related: Check your display details → Screen Resolution Checker Related Articles Best VPN Services 2026: Privacy, Streaming \u0026amp; Remote Work ","permalink":"https://productivity-works.com/tools/ip-address-info/","summary":"\u003cp\u003eDetect your local network info, calculate subnets, convert IPv4 to decimal, check IP classes, and validate addresses — all in your browser with no external requests.\u003c/p\u003e\n\u003cdiv id=\"ip-app\"\u003e\n\u003cstyle\u003e\n#ip-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  background: #0f172a;\n  color: #e2e8f0;\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 1.5rem;\n  border-radius: 14px;\n}\n#ip-app * { box-sizing: border-box; }\n\n#ip-app h2 {\n  font-size: 1.05rem;\n  font-weight: 700;\n  color: #22c55e;\n  margin: 1.8rem 0 0.7rem;\n  border-left: 4px solid #22c55e;\n  padding-left: 0.65rem;\n  letter-spacing: 0.02em;\n}\n#ip-app h2:first-of-type { margin-top: 0; }\n\n#ip-app .ip-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\n  gap: 0.75rem;\n  margin-bottom: 1rem;\n}\n\n#ip-app .ip-card {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 10px;\n  padding: 0.85rem 1rem;\n}\n#ip-app .ip-card .ip-label {\n  font-size: 0.68rem;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  color: #64748b;\n  margin-bottom: 0.3rem;\n}\n#ip-app .ip-card .ip-value {\n  font-size: 1.1rem;\n  font-weight: 700;\n  color: #22c55e;\n  word-break: break-all;\n}\n#ip-app .ip-card .ip-value.muted {\n  color: #94a3b8;\n  font-size: 0.9rem;\n  font-weight: 500;\n}\n\n#ip-app .ip-badge {\n  display: inline-block;\n  padding: 0.2rem 0.6rem;\n  border-radius: 99px;\n  font-size: 0.72rem;\n  font-weight: 600;\n  margin-top: 0.2rem;\n}\n#ip-app .ip-badge.green { background: #14532d; color: #86efac; }\n#ip-app .ip-badge.blue  { background: #1e3a5f; color: #93c5fd; }\n#ip-app .ip-badge.amber { background: #451a03; color: #fcd34d; }\n#ip-app .ip-badge.red   { background: #450a0a; color: #fca5a5; }\n\n#ip-app .ip-section {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 12px;\n  padding: 1.2rem;\n  margin-bottom: 1.2rem;\n}\n\n#ip-app label {\n  display: block;\n  font-size: 0.8rem;\n  color: #94a3b8;\n  margin-bottom: 0.3rem;\n  margin-top: 0.8rem;\n}\n#ip-app label:first-child { margin-top: 0; }\n\n#ip-app input[type=\"text\"], #ip-app input[type=\"number\"], #ip-app select {\n  width: 100%;\n  background: #0f172a;\n  border: 1px solid #475569;\n  border-radius: 8px;\n  color: #e2e8f0;\n  padding: 0.55rem 0.8rem;\n  font-size: 0.95rem;\n  outline: none;\n  transition: border-color 0.2s;\n}\n#ip-app input[type=\"text\"]:focus,\n#ip-app input[type=\"number\"]:focus,\n#ip-app select:focus {\n  border-color: #22c55e;\n}\n#ip-app input.error { border-color: #ef4444; }\n\n#ip-app .ip-row {\n  display: flex;\n  gap: 0.75rem;\n  flex-wrap: wrap;\n}\n#ip-app .ip-row \u003e * { flex: 1; min-width: 140px; }\n\n#ip-app button {\n  background: #22c55e;\n  color: #0f172a;\n  border: none;\n  border-radius: 8px;\n  padding: 0.6rem 1.4rem;\n  font-size: 0.9rem;\n  font-weight: 700;\n  cursor: pointer;\n  margin-top: 0.9rem;\n  transition: background 0.15s;\n}\n#ip-app button:hover { background: #16a34a; }\n\n#ip-app .ip-result-table {\n  width: 100%;\n  border-collapse: collapse;\n  margin-top: 1rem;\n  font-size: 0.88rem;\n}\n#ip-app .ip-result-table td {\n  padding: 0.5rem 0.75rem;\n  border-bottom: 1px solid #1e293b;\n}\n#ip-app .ip-result-table td:first-child {\n  color: #64748b;\n  width: 50%;\n  font-size: 0.8rem;\n}\n#ip-app .ip-result-table td:last-child {\n  color: #22c55e;\n  font-weight: 600;\n  word-break: break-all;\n}\n#ip-app .ip-result-table tr:last-child td { border-bottom: none; }\n\n#ip-app .ip-msg {\n  font-size: 0.82rem;\n  margin-top: 0.6rem;\n  padding: 0.5rem 0.75rem;\n  border-radius: 7px;\n}\n#ip-app .ip-msg.ok  { background: #14532d44; color: #86efac; border: 1px solid #14532d; }\n#ip-app .ip-msg.err { background: #450a0a44; color: #fca5a5; border: 1px solid #450a0a; }\n#ip-app .ip-msg.info { background: #1e3a5f44; color: #93c5fd; border: 1px solid #1e3a5f; }\n\n#ip-app .webrtc-note {\n  font-size: 0.78rem;\n  color: #475569;\n  margin-top: 0.4rem;\n  line-height: 1.5;\n}\n\n#ip-app .ip-divider {\n  border: none;\n  border-top: 1px solid #334155;\n  margin: 1.2rem 0;\n}\n\n#ip-app .subnet-vis {\n  margin-top: 0.9rem;\n  background: #0f172a;\n  border: 1px solid #334155;\n  border-radius: 8px;\n  padding: 0.75rem 1rem;\n}\n#ip-app .subnet-vis .sv-bar {\n  height: 18px;\n  border-radius: 4px;\n  background: linear-gradient(90deg, #22c55e 0%, #16a34a 100%);\n  margin: 0.4rem 0;\n  position: relative;\n  overflow: hidden;\n}\n#ip-app .subnet-vis .sv-label {\n  font-size: 0.72rem;\n  color: #64748b;\n}\n#ip-app .subnet-vis .sv-count {\n  font-size: 1rem;\n  font-weight: 700;\n  color: #22c55e;\n}\n\n@media (max-width: 520px) {\n  #ip-app { padding: 1rem; }\n  #ip-app .ip-row { flex-direction: column; }\n}\n\u003c/style\u003e\n\u003c!-- ===== SECTION 1: LOCAL NETWORK \u0026 BROWSER INFO ===== --\u003e\n\u003ch2\u003eYour Connection \u0026 Browser Info\u003c/h2\u003e\n\u003cdiv class=\"ip-grid\" id=\"info-grid\"\u003e\n  \u003cdiv class=\"ip-card\"\u003e\u003cdiv class=\"ip-label\"\u003eConnection Type\u003c/div\u003e\u003cdiv class=\"ip-value\" id=\"conn-type\"\u003eDetecting…\u003c/div\u003e\u003c/div\u003e\n  \u003cdiv class=\"ip-card\"\u003e\u003cdiv class=\"ip-label\"\u003eEffective Speed\u003c/div\u003e\u003cdiv class=\"ip-value\" id=\"conn-speed\"\u003e—\u003c/div\u003e\u003c/div\u003e\n  \u003cdiv class=\"ip-card\"\u003e\u003cdiv class=\"ip-label\"\u003eScreen Resolution\u003c/div\u003e\u003cdiv class=\"ip-value\" id=\"screen-res\"\u003e—\u003c/div\u003e\u003c/div\u003e\n  \u003cdiv class=\"ip-card\"\u003e\u003cdiv class=\"ip-label\"\u003eColor Depth\u003c/div\u003e\u003cdiv class=\"ip-value\" id=\"color-depth\"\u003e—\u003c/div\u003e\u003c/div\u003e\n  \u003cdiv class=\"ip-card\"\u003e\u003cdiv class=\"ip-label\"\u003eBrowser\u003c/div\u003e\u003cdiv class=\"ip-value muted\" id=\"browser-name\"\u003e—\u003c/div\u003e\u003c/div\u003e\n  \u003cdiv class=\"ip-card\"\u003e\u003cdiv class=\"ip-label\"\u003ePlatform / OS\u003c/div\u003e\u003cdiv class=\"ip-value muted\" id=\"os-name\"\u003e—\u003c/div\u003e\u003c/div\u003e\n  \u003cdiv class=\"ip-card\"\u003e\u003cdiv class=\"ip-label\"\u003eLanguage\u003c/div\u003e\u003cdiv class=\"ip-value\" id=\"lang\"\u003e—\u003c/div\u003e\u003c/div\u003e\n  \u003cdiv class=\"ip-card\"\u003e\u003cdiv class=\"ip-label\"\u003eCookies Enabled\u003c/div\u003e\u003cdiv class=\"ip-value\" id=\"cookies\"\u003e—\u003c/div\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ===== SECTION 2: WebRTC LOCAL IP ===== --\u003e\n\u003ch2\u003eLocal IP Detection (WebRTC)\u003c/h2\u003e\n\u003cdiv class=\"ip-section\"\u003e\n  \u003cdiv id=\"webrtc-ips\"\u003e\u003cspan style=\"color:#64748b;font-size:0.9rem;\"\u003eAttempting WebRTC detection…\u003c/span\u003e\u003c/div\u003e\n  \u003cp class=\"webrtc-note\"\u003eWebRTC can reveal your local (LAN) IP addresses assigned by your router. This works in most modern browsers without any permissions. Results depend on your network configuration — VPN or firewall may suppress results.\u003c/p\u003e","title":"IP Address Info — Subnet Calculator, IP Converter \u0026 Network Tools"},{"content":"Enter any IPv4 address and CIDR prefix (or subnet mask) to instantly calculate the full subnet breakdown — network address, broadcast, usable host range, wildcard mask, and more.\nIPv4 Subnet Calculator IP Address Subnet Mask Calculate Clear CIDR Prefix Length /0 /32 /24 Subnet Mask Bits Subnet Results Binary View Network bits = green \u0026nbsp; Host bits = red Common Subnet Reference Table CIDRSubnet MaskHostsWildcard Check your IP → IP Address Info Convert bytes → Byte Converter DNS records → DNS Record Guide ","permalink":"https://productivity-works.com/tools/ip-subnet-calculator/","summary":"\u003cp\u003eEnter any IPv4 address and CIDR prefix (or subnet mask) to instantly calculate the full subnet breakdown — network address, broadcast, usable host range, wildcard mask, and more.\u003c/p\u003e\n\u003cdiv id=\"ip-app\"\u003e\n\u003cstyle\u003e\n#ip-app *,#ip-app *::before,#ip-app *::after{box-sizing:border-box;margin:0;padding:0}\n#ip-app{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;font-size:15px;color:#1e293b;max-width:780px;margin:0 auto}\n#ip-app .ip-card{background:#fff;border:1.5px solid #e2e8f0;border-radius:12px;padding:24px;margin-bottom:20px}\n#ip-app .ip-title{font-size:17px;font-weight:700;color:#0f172a;margin-bottom:16px;padding-bottom:10px;border-bottom:1px solid #e2e8f0}\n#ip-app .ip-row{display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap;margin-bottom:16px}\n#ip-app label{display:block;font-size:13px;font-weight:600;color:#475569;margin-bottom:5px}\n#ip-app input[type=text],#ip-app select{width:100%;padding:10px 13px;border:1.5px solid #cbd5e1;border-radius:8px;font-size:15px;color:#1e293b;background:#f8fafc;transition:border-color .2s}\n#ip-app input[type=text]:focus,#ip-app select:focus{outline:none;border-color:#3b82f6;background:#fff}\n#ip-app .ip-field{flex:1;min-width:160px}\n#ip-app .ip-field-sm{flex:0 0 130px}\n#ip-app input[type=range]{width:100%;accent-color:#3b82f6;cursor:pointer}\n#ip-app .ip-slider-row{display:flex;align-items:center;gap:12px;margin-bottom:14px}\n#ip-app .ip-slider-val{font-size:20px;font-weight:800;color:#3b82f6;min-width:40px;text-align:center}\n#ip-app .ip-btn{padding:10px 26px;background:#3b82f6;color:#fff;border:none;border-radius:8px;font-size:15px;font-weight:700;cursor:pointer;transition:background .15s;white-space:nowrap}\n#ip-app .ip-btn:hover{background:#2563eb}\n#ip-app .ip-btn-clear{background:#64748b}\n#ip-app .ip-btn-clear:hover{background:#475569}\n#ip-app .ip-error{color:#dc2626;font-size:13px;padding:8px 12px;background:#fef2f2;border:1px solid #fecaca;border-radius:6px;margin-top:-8px;margin-bottom:8px;display:none}\n#ip-app .ip-results{display:none}\n#ip-app .ip-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;margin-bottom:12px}\n#ip-app .ip-stat{background:#f8fafc;border:1px solid #e2e8f0;border-radius:9px;padding:13px 15px}\n#ip-app .ip-stat-label{font-size:12px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:.5px;margin-bottom:4px}\n#ip-app .ip-stat-val{font-size:16px;font-weight:700;color:#0f172a;font-family:'Courier New',monospace;word-break:break-all}\n#ip-app .ip-stat-sub{font-size:12px;color:#94a3b8;margin-top:2px}\n#ip-app .ip-badge{display:inline-block;padding:2px 9px;border-radius:20px;font-size:12px;font-weight:700;margin-left:6px}\n#ip-app .ip-badge-a{background:#dbeafe;color:#1d4ed8}\n#ip-app .ip-badge-b{background:#ede9fe;color:#6d28d9}\n#ip-app .ip-badge-c{background:#dcfce7;color:#15803d}\n#ip-app .ip-badge-d{background:#fef9c3;color:#a16207}\n#ip-app .ip-badge-e{background:#fee2e2;color:#b91c1c}\n#ip-app .ip-badge-priv{background:#f0fdf4;color:#166534}\n#ip-app .ip-badge-pub{background:#eff6ff;color:#1e40af}\n#ip-app .ip-bin-box{background:#0f172a;border-radius:9px;padding:14px 16px;font-family:'Courier New',monospace;font-size:13px;line-height:1.9;overflow-x:auto;margin-bottom:12px}\n#ip-app .ip-bin-row{display:flex;gap:6px;align-items:center;white-space:nowrap}\n#ip-app .ip-bin-label{color:#94a3b8;min-width:80px;font-size:12px}\n#ip-app .ip-net-bit{color:#34d399}\n#ip-app .ip-host-bit{color:#f87171}\n#ip-app .ip-dot{color:#64748b;margin:0 2px}\n#ip-app .ip-bin-sep{color:#475569;margin:0 1px}\n#ip-app .ip-mask-vis{display:flex;gap:3px;flex-wrap:wrap;margin-bottom:14px}\n#ip-app .ip-mask-bit{width:20px;height:20px;border-radius:3px;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700}\n#ip-app .ip-mask-1{background:#3b82f6;color:#fff}\n#ip-app .ip-mask-0{background:#e2e8f0;color:#94a3b8}\n#ip-app .ip-mask-grp{display:flex;gap:3px}\n#ip-app .ip-mask-sep{width:6px}\n#ip-app table{width:100%;border-collapse:collapse;font-size:13px}\n#ip-app th{background:#f1f5f9;color:#475569;font-weight:600;text-align:left;padding:8px 10px;border-bottom:1.5px solid #e2e8f0}\n#ip-app td{padding:7px 10px;border-bottom:1px solid #f1f5f9;font-family:'Courier New',monospace}\n#ip-app tr:last-child td{border-bottom:none}\n#ip-app tr.ip-hl td{background:#eff6ff;font-weight:700}\n#ip-app .ip-tbl-wrap{max-height:320px;overflow-y:auto;border:1px solid #e2e8f0;border-radius:8px}\n#ip-app .ip-section-label{font-size:13px;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.5px;margin-bottom:10px}\n#ip-app .ip-cross{margin-top:8px;font-size:13px;color:#64748b}\n#ip-app .ip-cross a{color:#3b82f6;text-decoration:none}\n#ip-app .ip-cross a:hover{text-decoration:underline}\n@media(max-width:520px){\n  #ip-app .ip-bin-row{flex-direction:column;align-items:flex-start}\n  #ip-app .ip-stat-val{font-size:14px}\n}\n\u003c/style\u003e\n\u003cdiv class=\"ip-card\"\u003e\n  \u003cdiv class=\"ip-title\"\u003eIPv4 Subnet Calculator\u003c/div\u003e\n  \u003cdiv class=\"ip-row\"\u003e\n    \u003cdiv class=\"ip-field\"\u003e\n      \u003clabel for=\"ip-addr\"\u003eIP Address\u003c/label\u003e\n      \u003cinput type=\"text\" id=\"ip-addr\" placeholder=\"e.g. 192.168.1.0\" maxlength=\"18\" autocomplete=\"off\" spellcheck=\"false\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ip-field-sm\"\u003e\n      \u003clabel for=\"ip-mask-sel\"\u003eSubnet Mask\u003c/label\u003e\n      \u003cselect id=\"ip-mask-sel\"\u003e\u003c/select\u003e\n    \u003c/div\u003e\n    \u003cdiv style=\"display:flex;gap:8px;padding-bottom:1px\"\u003e\n      \u003cbutton class=\"ip-btn\" onclick=\"ipCalc()\"\u003eCalculate\u003c/button\u003e\n      \u003cbutton class=\"ip-btn ip-btn-clear\" onclick=\"ipReset()\"\u003eClear\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ip-section-label\"\u003eCIDR Prefix Length\u003c/div\u003e\n  \u003cdiv class=\"ip-slider-row\"\u003e\n    \u003cspan style=\"font-size:12px;color:#94a3b8\"\u003e/0\u003c/span\u003e\n    \u003cinput type=\"range\" id=\"ip-cidr\" min=\"0\" max=\"32\" value=\"24\" oninput=\"ipSliderChange(this.value)\"\u003e\n    \u003cspan style=\"font-size:12px;color:#94a3b8\"\u003e/32\u003c/span\u003e\n    \u003cspan class=\"ip-slider-val\" id=\"ip-cidr-label\"\u003e/24\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ip-section-label\" style=\"margin-bottom:8px\"\u003eSubnet Mask Bits\u003c/div\u003e\n  \u003cdiv class=\"ip-mask-vis\" id=\"ip-mask-vis\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"ip-error\" id=\"ip-error\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ip-results\" id=\"ip-results\"\u003e\n  \u003cdiv class=\"ip-card\"\u003e\n    \u003cdiv class=\"ip-title\"\u003e\n      Subnet Results\n      \u003cspan id=\"ip-class-badge\"\u003e\u003c/span\u003e\n      \u003cspan id=\"ip-scope-badge\"\u003e\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ip-grid\" id=\"ip-grid\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ip-card\"\u003e\n    \u003cdiv class=\"ip-title\"\u003eBinary View\u003c/div\u003e\n    \u003cdiv class=\"ip-section-label\"\u003eNetwork bits = \u003cspan style=\"color:#34d399\"\u003egreen\u003c/span\u003e \u0026nbsp; Host bits = \u003cspan style=\"color:#f87171\"\u003ered\u003c/span\u003e\u003c/div\u003e\n    \u003cdiv class=\"ip-bin-box\" id=\"ip-bin-box\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ip-card\"\u003e\n    \u003cdiv class=\"ip-title\"\u003eCommon Subnet Reference Table\u003c/div\u003e\n    \u003cdiv class=\"ip-tbl-wrap\"\u003e\n      \u003ctable id=\"ip-tbl\"\u003e\n        \u003cthead\u003e\u003ctr\u003e\u003cth\u003eCIDR\u003c/th\u003e\u003cth\u003eSubnet Mask\u003c/th\u003e\u003cth\u003eHosts\u003c/th\u003e\u003cth\u003eWildcard\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e\n        \u003ctbody id=\"ip-tbl-body\"\u003e\u003c/tbody\u003e\n      \u003c/table\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n  var MASKS=[\n    [0,'0.0.0.0'],[1,'128.0.0.0'],[2,'192.0.0.0'],[3,'224.0.0.0'],\n    [4,'240.0.0.0'],[5,'248.0.0.0'],[6,'252.0.0.0'],[7,'254.0.0.0'],\n    [8,'255.0.0.0'],[9,'255.128.0.0'],[10,'255.192.0.0'],[11,'255.224.0.0'],\n    [12,'255.240.0.0'],[13,'255.248.0.0'],[14,'255.252.0.0'],[15,'255.254.0.0'],\n    [16,'255.255.0.0'],[17,'255.255.128.0'],[18,'255.255.192.0'],[19,'255.255.224.0'],\n    [20,'255.255.240.0'],[21,'255.255.248.0'],[22,'255.255.252.0'],[23,'255.255.254.0'],\n    [24,'255.255.255.0'],[25,'255.255.255.128'],[26,'255.255.255.192'],[27,'255.255.255.224'],\n    [28,'255.255.255.240'],[29,'255.255.255.248'],[30,'255.255.255.252'],[31,'255.255.255.254'],\n    [32,'255.255.255.255']\n  ];\n\n  var sel=document.getElementById('ip-mask-sel');\n  MASKS.forEach(function(m){\n    var o=document.createElement('option');\n    o.value=m[0];\n    o.textContent='/'+m[0]+' — '+m[1];\n    if(m[0]===24)o.selected=true;\n    sel.appendChild(o);\n  });\n  sel.addEventListener('change',function(){\n    document.getElementById('ip-cidr').value=this.value;\n    ipSliderChange(this.value);\n  });\n\n  document.getElementById('ip-addr').addEventListener('keydown',function(e){\n    if(e.key==='Enter')ipCalc();\n  });\n\n  renderMaskVis(24);\n\n  window.ipSliderChange=function(v){\n    v=parseInt(v,10);\n    document.getElementById('ip-cidr-label').textContent='/'+v;\n    document.getElementById('ip-cidr').value=v;\n    sel.value=v;\n    renderMaskVis(v);\n  };\n\n  function renderMaskVis(prefix){\n    var el=document.getElementById('ip-mask-vis');\n    el.innerHTML='';\n    for(var g=0;g\u003c4;g++){\n      var grp=document.createElement('div');\n      grp.className='ip-mask-grp';\n      for(var b=0;b\u003c8;b++){\n        var bit=g*8+b;\n        var d=document.createElement('div');\n        d.className='ip-mask-bit '+(bit\u003cprefix?'ip-mask-1':'ip-mask-0');\n        d.textContent=bit\u003cprefix?'1':'0';\n        grp.appendChild(d);\n      }\n      el.appendChild(grp);\n      if(g\u003c3){var sep=document.createElement('div');sep.className='ip-mask-sep';el.appendChild(sep);}\n    }\n  }\n\n  function parseIP(s){\n    var p=s.trim().split('.');\n    if(p.length!==4)return null;\n    var n=[];\n    for(var i=0;i\u003c4;i++){\n      var x=parseInt(p[i],10);\n      if(isNaN(x)||x\u003c0||x\u003e255||p[i].trim()==='')return null;\n      n.push(x);\n    }\n    return n;\n  }\n\n  function ipToNum(oct){return((oct[0]\u003c\u003c24)|(oct[1]\u003c\u003c16)|(oct[2]\u003c\u003c8)|oct[3])\u003e\u003e\u003e0;}\n  function numToIP(n){return [n\u003e\u003e\u003e24,(n\u003e\u003e\u003e16)\u0026255,(n\u003e\u003e\u003e8)\u0026255,n\u0026255].join('.');}\n\n  function toBin8(n){return ('00000000'+n.toString(2)).slice(-8);}\n\n  function detectClass(oct){\n    var f=oct[0];\n    if(f\u003c128)return 'A';\n    if(f\u003c192)return 'B';\n    if(f\u003c224)return 'C';\n    if(f\u003c240)return 'D';\n    return 'E';\n  }\n\n  function isPrivate(oct){\n    var a=oct[0],b=oct[1];\n    if(a===10)return true;\n    if(a===172\u0026\u0026b\u003e=16\u0026\u0026b\u003c=31)return true;\n    if(a===192\u0026\u0026b===168)return true;\n    if(a===127)return true;\n    if(a===169\u0026\u0026b===254)return true;\n    return false;\n  }\n\n  function formatLarge(n){\n    if(n\u003e=1e9)return (n/1e9).toFixed(2).replace(/\\.?0+$/,'')+'B';\n    if(n\u003e=1e6)return (n/1e6).toFixed(2).replace(/\\.?0+$/,'')+'M';\n    if(n\u003e=1e3)return (n/1e3).toFixed(2).replace(/\\.?0+$/,'')+'K';\n    return n.toString();\n  }\n\n  window.ipCalc=function(){\n    var ipStr=document.getElementById('ip-addr').value.trim();\n    var prefix=parseInt(document.getElementById('ip-cidr').value,10);\n    var errEl=document.getElementById('ip-error');\n    var resEl=document.getElementById('ip-results');\n\n    errEl.style.display='none';\n    resEl.style.display='none';\n\n    if(!ipStr){showErr('Please enter an IP address.');return;}\n    var oct=parseIP(ipStr);\n    if(!oct){showErr('Invalid IP address. Use dotted decimal format (e.g. 192.168.1.0).');return;}\n    if(isNaN(prefix)||prefix\u003c0||prefix\u003e32){showErr('CIDR prefix must be between 0 and 32.');return;}\n\n    var ipNum=ipToNum(oct);\n    var maskNum= prefix===0?0:(0xFFFFFFFF\u003c\u003c(32-prefix))\u003e\u003e\u003e0;\n    var wildNum=(~maskNum)\u003e\u003e\u003e0;\n    var netNum=(ipNum\u0026maskNum)\u003e\u003e\u003e0;\n    var bcastNum=(netNum|wildNum)\u003e\u003e\u003e0;\n    var firstHost=prefix\u003c31?(netNum+1)\u003e\u003e\u003e0:netNum;\n    var lastHost=prefix\u003c31?(bcastNum-1)\u003e\u003e\u003e0:bcastNum;\n    var hostCount=prefix\u003e=31?(prefix===32?1:2):Math.pow(2,32-prefix)-2;\n\n    var cls=detectClass(oct);\n    var priv=isPrivate(oct);\n\n    // badges\n    var clsBadgeMap={A:'ip-badge-a',B:'ip-badge-b',C:'ip-badge-c',D:'ip-badge-d',E:'ip-badge-e'};\n    document.getElementById('ip-class-badge').innerHTML='\u003cspan class=\"ip-badge '+clsBadgeMap[cls]+'\"\u003eClass '+cls+'\u003c/span\u003e';\n    document.getElementById('ip-scope-badge').innerHTML='\u003cspan class=\"ip-badge '+(priv?'ip-badge-priv':'ip-badge-pub')+'\"\u003e'+(priv?'Private':'Public')+'\u003c/span\u003e';\n\n    // grid\n    var grid=document.getElementById('ip-grid');\n    var stats=[\n      {label:'IP Address',val:numToIP(ipNum),sub:'Input address'},\n      {label:'Network Address',val:numToIP(netNum),sub:'First address in subnet'},\n      {label:'Broadcast Address',val:numToIP(bcastNum),sub:'Last address in subnet'},\n      {label:'Subnet Mask',val:numToIP(maskNum),sub:'Dotted decimal notation'},\n      {label:'Wildcard Mask',val:numToIP(wildNum),sub:'Inverse of subnet mask'},\n      {label:'CIDR Notation',val:numToIP(netNum)+'/'+prefix,sub:'Network/prefix'},\n      {label:'First Usable Host',val:prefix\u003c=30?numToIP(firstHost):'N/A',sub:prefix===31?'Point-to-point link':prefix===32?'Host route':''},\n      {label:'Last Usable Host',val:prefix\u003c=30?numToIP(lastHost):'N/A',sub:''},\n      {label:'Usable Hosts',val:hostCount\u003e=0?hostCount.toLocaleString():'0',sub:hostCount\u003e=1000?'(~'+formatLarge(hostCount)+')':''},\n      {label:'Total Addresses',val:Math.pow(2,32-prefix).toLocaleString(),sub:'Including net \u0026 broadcast'},\n    ];\n    grid.innerHTML=stats.map(function(s){\n      return '\u003cdiv class=\"ip-stat\"\u003e\u003cdiv class=\"ip-stat-label\"\u003e'+s.label+'\u003c/div\u003e\u003cdiv class=\"ip-stat-val\"\u003e'+s.val+'\u003c/div\u003e'+(s.sub?'\u003cdiv class=\"ip-stat-sub\"\u003e'+s.sub+'\u003c/div\u003e':'')+'\u003c/div\u003e';\n    }).join('');\n\n    // binary\n    var binBox=document.getElementById('ip-bin-box');\n    function renderBinRow(label,num,pref){\n      var bits=[];\n      for(var i=31;i\u003e=0;i--){\n        var b=(num\u003e\u003e\u003ei)\u00261;\n        var bitPos=31-i;\n        var cls2=pref!==undefined?(bitPos\u003cpref?'ip-net-bit':'ip-host-bit'):'';\n        bits.push('\u003cspan class=\"'+cls2+'\"\u003e'+b+'\u003c/span\u003e');\n        if(i\u003e0\u0026\u0026(i%8===0))bits.push('\u003cspan class=\"ip-bin-sep\"\u003e . \u003c/span\u003e');\n      }\n      return '\u003cdiv class=\"ip-bin-row\"\u003e\u003cspan class=\"ip-bin-label\"\u003e'+label+'\u003c/span\u003e\u003cspan\u003e'+bits.join('')+'\u003c/span\u003e\u003c/div\u003e';\n    }\n    binBox.innerHTML=[\n      renderBinRow('IP Address:',ipNum,prefix),\n      renderBinRow('Subnet Mask:',maskNum),\n      renderBinRow('Network:',netNum,prefix),\n      renderBinRow('Broadcast:',bcastNum,prefix),\n    ].join('');\n\n    // reference table\n    var tbody=document.getElementById('ip-tbl-body');\n    var rows=[];\n    for(var p=8;p\u003c=32;p++){\n      var m=p===0?0:(0xFFFFFFFF\u003c\u003c(32-p))\u003e\u003e\u003e0;\n      var w=(~m)\u003e\u003e\u003e0;\n      var h=p\u003e=31?(p===32?1:2):Math.pow(2,32-p)-2;\n      var hl=p===prefix?' class=\"ip-hl\"':'';\n      rows.push('\u003ctr'+hl+'\u003e\u003ctd\u003e/'+p+'\u003c/td\u003e\u003ctd\u003e'+numToIP(m)+'\u003c/td\u003e\u003ctd\u003e'+(h\u003e0?h.toLocaleString():0)+'\u003c/td\u003e\u003ctd\u003e'+numToIP(w)+'\u003c/td\u003e\u003c/tr\u003e');\n    }\n    tbody.innerHTML=rows.join('');\n\n    // scroll highlighted row into view\n    setTimeout(function(){\n      var hlRow=tbody.querySelector('.ip-hl');\n      if(hlRow)hlRow.scrollIntoView({block:'nearest'});\n    },50);\n\n    resEl.style.display='block';\n  };\n\n  window.ipReset=function(){\n    document.getElementById('ip-addr').value='';\n    document.getElementById('ip-cidr').value=24;\n    document.getElementById('ip-mask-sel').value=24;\n    ipSliderChange(24);\n    document.getElementById('ip-error').style.display='none';\n    document.getElementById('ip-results').style.display='none';\n  };\n\n  function showErr(msg){\n    var e=document.getElementById('ip-error');\n    e.textContent=msg;\n    e.style.display='block';\n  }\n\n  renderMaskVis(24);\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003eCheck your IP → \u003ca href=\"https://productivity-works.com/tools/ip-address-info/\"\u003eIP Address Info\u003c/a\u003e\n\nConvert bytes → \u003ca href=\"https://productivity-works.com/tools/byte-converter/\"\u003eByte Converter\u003c/a\u003e\n\nDNS records → \u003ca href=\"https://productivity-works.com/tools/dns-record-guide/\"\u003eDNS Record Guide\u003c/a\u003e\n\u003c/p\u003e","title":"IP Subnet Calculator"},{"content":" IP Subnet Visualizer Calculate Binary Breakdown Network bits Host bits Address Space 0.0.0.0 \u0026#9632; This subnet 255.255.255.255 Subnet Reference Table (/8 – /32) Prefix Subnet Mask Hosts Network Size Enter an IP address with CIDR prefix (e.g. 192.168.1.0/24) IP address info → IP Address Info ","permalink":"https://productivity-works.com/tools/ip-subnet-visualizer/","summary":"\u003cdiv id=\"isv-app\"\u003e\n\u003cstyle\u003e\n#isv-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  background: #0f172a;\n  color: #e2e8f0;\n  border-radius: 12px;\n  padding: 24px;\n  max-width: 900px;\n  margin: 0 auto;\n  box-sizing: border-box;\n}\n#isv-app * { box-sizing: border-box; }\n\n#isv-app h2 {\n  color: #38bdf8;\n  font-size: 1.05rem;\n  margin: 0 0 14px;\n  font-weight: 600;\n  letter-spacing: 0.04em;\n  text-transform: uppercase;\n}\n\n#isv-app .input-row {\n  display: flex;\n  gap: 10px;\n  margin-bottom: 16px;\n  flex-wrap: wrap;\n  align-items: stretch;\n}\n#isv-app .cidr-input {\n  flex: 1;\n  min-width: 200px;\n  background: #1e293b;\n  border: 1.5px solid #334155;\n  color: #e2e8f0;\n  border-radius: 8px;\n  padding: 11px 14px;\n  font-size: 1.05rem;\n  font-family: 'Courier New', monospace;\n  transition: border-color 0.15s;\n}\n#isv-app .cidr-input:focus { outline: none; border-color: #38bdf8; }\n#isv-app .cidr-input.error { border-color: #ef4444; }\n#isv-app .calc-btn {\n  background: #0284c7;\n  border: none;\n  color: #fff;\n  border-radius: 8px;\n  padding: 11px 22px;\n  font-size: 0.95rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.15s;\n  white-space: nowrap;\n}\n#isv-app .calc-btn:hover { background: #0369a1; }\n#isv-app .error-line {\n  color: #ef4444;\n  font-size: 0.83rem;\n  margin: -10px 0 12px;\n  min-height: 18px;\n}\n\n#isv-app .results-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\n  gap: 10px;\n  margin-bottom: 20px;\n}\n#isv-app .res-card {\n  background: #1e293b;\n  border: 1.5px solid #334155;\n  border-radius: 10px;\n  padding: 13px 15px;\n}\n#isv-app .res-card .rc-label {\n  font-size: 0.72rem;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.09em;\n  font-weight: 600;\n  margin-bottom: 5px;\n}\n#isv-app .res-card .rc-value {\n  font-family: 'Courier New', monospace;\n  font-size: 0.97rem;\n  color: #38bdf8;\n  word-break: break-all;\n  min-height: 20px;\n}\n#isv-app .res-card .rc-value.green { color: #4ade80; }\n#isv-app .res-card .rc-value.orange { color: #fb923c; }\n#isv-app .res-card .rc-value.yellow { color: #facc15; }\n\n#isv-app .binary-section {\n  background: #1e293b;\n  border: 1.5px solid #334155;\n  border-radius: 10px;\n  padding: 16px;\n  margin-bottom: 18px;\n}\n#isv-app .bin-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-bottom: 9px;\n  flex-wrap: wrap;\n}\n#isv-app .bin-row:last-child { margin-bottom: 0; }\n#isv-app .bin-label {\n  font-size: 0.75rem;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  font-weight: 600;\n  min-width: 100px;\n}\n#isv-app .bin-bits {\n  display: flex;\n  gap: 2px;\n  flex-wrap: wrap;\n  font-family: 'Courier New', monospace;\n  font-size: 0.82rem;\n}\n#isv-app .bin-octet {\n  display: flex;\n  gap: 2px;\n  margin-right: 6px;\n}\n#isv-app .bin-bit {\n  width: 18px;\n  height: 22px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  border-radius: 3px;\n  font-weight: 700;\n  font-size: 0.8rem;\n}\n#isv-app .bin-bit.net { background: #1d4ed8; color: #93c5fd; }\n#isv-app .bin-bit.host { background: #166534; color: #86efac; }\n#isv-app .bin-bit.zero { background: #1e3a5f; color: #93c5fd; opacity: 0.5; }\n#isv-app .bin-legend {\n  display: flex;\n  gap: 16px;\n  margin-top: 10px;\n  flex-wrap: wrap;\n}\n#isv-app .bin-legend-item {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 0.78rem;\n  color: #94a3b8;\n}\n#isv-app .bin-legend-swatch {\n  width: 14px;\n  height: 14px;\n  border-radius: 3px;\n}\n\n#isv-app .canvas-section {\n  background: #1e293b;\n  border: 1.5px solid #334155;\n  border-radius: 10px;\n  padding: 16px;\n  margin-bottom: 18px;\n}\n#isv-app #isv-canvas {\n  width: 100%;\n  height: 48px;\n  border-radius: 6px;\n  display: block;\n}\n#isv-app .canvas-labels {\n  display: flex;\n  justify-content: space-between;\n  font-size: 0.72rem;\n  color: #64748b;\n  margin-top: 5px;\n  font-family: 'Courier New', monospace;\n}\n\n#isv-app .table-section {\n  background: #1e293b;\n  border: 1.5px solid #334155;\n  border-radius: 10px;\n  padding: 16px;\n  overflow-x: auto;\n}\n#isv-app table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.82rem;\n  font-family: 'Courier New', monospace;\n  min-width: 480px;\n}\n#isv-app thead th {\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  font-size: 0.72rem;\n  font-weight: 600;\n  padding: 7px 10px;\n  text-align: left;\n  border-bottom: 1px solid #334155;\n}\n#isv-app tbody td {\n  padding: 6px 10px;\n  color: #cbd5e1;\n  border-bottom: 1px solid #1e293b;\n  white-space: nowrap;\n}\n#isv-app tbody tr:last-child td { border-bottom: none; }\n#isv-app tbody tr.current-row td {\n  background: #0c2d4a;\n  color: #38bdf8;\n  font-weight: 700;\n}\n#isv-app tbody tr:hover td { background: #263448; }\n#isv-app tbody td.hosts-col { color: #4ade80; }\n\n#isv-app .placeholder {\n  text-align: center;\n  color: #475569;\n  padding: 32px 0;\n  font-size: 0.95rem;\n}\n\n@media (max-width: 560px) {\n  #isv-app .results-grid { grid-template-columns: 1fr 1fr; }\n  #isv-app .bin-label { min-width: 80px; }\n}\n\u003c/style\u003e\n\u003ch2\u003eIP Subnet Visualizer\u003c/h2\u003e\n\u003cdiv class=\"input-row\"\u003e\n  \u003cinput class=\"cidr-input\" id=\"isv-input\" type=\"text\" placeholder=\"e.g. 192.168.1.0/24 or 10.0.5.37/18\"\n    oninput=\"isvCalc()\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"\u003e\n  \u003cbutton class=\"calc-btn\" onclick=\"isvCalc()\"\u003eCalculate\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"error-line\" id=\"isv-error\"\u003e\u003c/div\u003e\n\u003cdiv id=\"isv-results\" style=\"display:none\"\u003e\n  \u003cdiv class=\"results-grid\" id=\"isv-cards\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"binary-section\"\u003e\n    \u003ch2\u003eBinary Breakdown\u003c/h2\u003e\n    \u003cdiv id=\"isv-bin-rows\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"bin-legend\"\u003e\n      \u003cdiv class=\"bin-legend-item\"\u003e\n        \u003cdiv class=\"bin-legend-swatch\" style=\"background:#1d4ed8;\"\u003e\u003c/div\u003e\n        Network bits\n      \u003c/div\u003e\n      \u003cdiv class=\"bin-legend-item\"\u003e\n        \u003cdiv class=\"bin-legend-swatch\" style=\"background:#166534;\"\u003e\u003c/div\u003e\n        Host bits\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"canvas-section\"\u003e\n    \u003ch2\u003eAddress Space\u003c/h2\u003e\n    \u003ccanvas id=\"isv-canvas\" height=\"48\"\u003e\u003c/canvas\u003e\n    \u003cdiv class=\"canvas-labels\"\u003e\n      \u003cspan id=\"isv-cv-start\"\u003e0.0.0.0\u003c/span\u003e\n      \u003cspan style=\"color:#38bdf8;\"\u003e\u0026#9632; This subnet\u003c/span\u003e\n      \u003cspan id=\"isv-cv-end\"\u003e255.255.255.255\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"table-section\"\u003e\n    \u003ch2\u003eSubnet Reference Table (/8 – /32)\u003c/h2\u003e\n    \u003ctable\u003e\n      \u003cthead\u003e\n        \u003ctr\u003e\n          \u003cth\u003ePrefix\u003c/th\u003e\n          \u003cth\u003eSubnet Mask\u003c/th\u003e\n          \u003cth\u003eHosts\u003c/th\u003e\n          \u003cth\u003eNetwork Size\u003c/th\u003e\n        \u003c/tr\u003e\n      \u003c/thead\u003e\n      \u003ctbody id=\"isv-tbl-body\"\u003e\u003c/tbody\u003e\n    \u003c/table\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"placeholder\" id=\"isv-placeholder\"\u003eEnter an IP address with CIDR prefix (e.g. 192.168.1.0/24)\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  function ipToNum(ip) {\n    var parts = ip.split('.');\n    if (parts.length !== 4) return null;\n    var n = 0;\n    for (var i = 0; i \u003c 4; i++) {\n      var p = parseInt(parts[i], 10);\n      if (isNaN(p) || p \u003c 0 || p \u003e 255) return null;\n      n = (n * 256) + p;\n    }\n    return n \u003e\u003e\u003e 0;\n  }\n\n  function numToIp(n) {\n    n = n \u003e\u003e\u003e 0;\n    return [(n \u003e\u003e\u003e 24) \u0026 255, (n \u003e\u003e\u003e 16) \u0026 255, (n \u003e\u003e\u003e 8) \u0026 255, n \u0026 255].join('.');\n  }\n\n  function maskFromPrefix(prefix) {\n    if (prefix === 0) return 0;\n    return (0xFFFFFFFF \u003c\u003c (32 - prefix)) \u003e\u003e\u003e 0;\n  }\n\n  function numToHostCount(prefix) {\n    if (prefix \u003e= 32) return 1;\n    if (prefix === 31) return 2;\n    return Math.pow(2, 32 - prefix) - 2;\n  }\n\n  function formatHosts(n) {\n    if (n \u003c= 0) return n === 1 ? '1 (host route)' : '0';\n    if (n \u003e= 1000000) return (n / 1000000).toFixed(2) + 'M';\n    if (n \u003e= 1000) return n.toLocaleString();\n    return n.toString();\n  }\n\n  function parseCIDR(val) {\n    val = val.trim();\n    var slash = val.indexOf('/');\n    if (slash === -1) return null;\n    var ipStr = val.slice(0, slash);\n    var prefixStr = val.slice(slash + 1);\n    var prefix = parseInt(prefixStr, 10);\n    if (isNaN(prefix) || prefix \u003c 0 || prefix \u003e 32) return null;\n    var ipNum = ipToNum(ipStr);\n    if (ipNum === null) return null;\n    return { ipNum: ipNum, prefix: prefix, ipStr: ipStr };\n  }\n\n  function renderBinaryRow(label, num, prefix, colorMode) {\n    // colorMode: 'ip' shows net/host split, 'mask' shows all net bits, 'addr' flat color\n    var bits = [];\n    for (var i = 31; i \u003e= 0; i--) {\n      bits.push((num \u003e\u003e\u003e i) \u0026 1);\n    }\n    var octets = [];\n    for (var o = 0; o \u003c 4; o++) {\n      var octetBits = bits.slice(o * 8, o * 8 + 8);\n      var cells = octetBits.map(function(b, idx) {\n        var bitPos = o * 8 + idx;\n        var cls = 'bin-bit';\n        if (colorMode === 'ip') {\n          cls += bitPos \u003c prefix ? ' net' : ' host';\n        } else if (colorMode === 'mask') {\n          cls += b === 1 ? ' net' : ' zero';\n        } else {\n          cls += b === 1 ? ' net' : ' zero';\n        }\n        return '\u003cdiv class=\"' + cls + '\"\u003e' + b + '\u003c/div\u003e';\n      }).join('');\n      octets.push('\u003cdiv class=\"bin-octet\"\u003e' + cells + '\u003c/div\u003e');\n    }\n    return '\u003cdiv class=\"bin-row\"\u003e\u003cspan class=\"bin-label\"\u003e' + label + '\u003c/span\u003e\u003cdiv class=\"bin-bits\"\u003e' + octets.join('') + '\u003c/div\u003e\u003c/div\u003e';\n  }\n\n  function renderCanvas(networkNum, broadcastNum) {\n    var canvas = document.getElementById('isv-canvas');\n    var W = canvas.offsetWidth || 800;\n    canvas.width = W;\n    canvas.height = 48;\n    var ctx = canvas.getContext('2d');\n\n    // Background gradient: full IPv4 space\n    var grad = ctx.createLinearGradient(0, 0, W, 0);\n    grad.addColorStop(0, '#0f2744');\n    grad.addColorStop(1, '#1e3a5f');\n    ctx.fillStyle = grad;\n    ctx.roundRect ? ctx.roundRect(0, 0, W, 48, 6) : ctx.fillRect(0, 0, W, 48);\n    ctx.fill();\n\n    // Subnet block\n    var total = 4294967295;\n    var x1 = Math.floor((networkNum / total) * W);\n    var x2 = Math.ceil((broadcastNum / total) * W);\n    if (x2 - x1 \u003c 2) x2 = x1 + 2;\n\n    var subGrad = ctx.createLinearGradient(x1, 0, x2, 0);\n    subGrad.addColorStop(0, '#0284c7');\n    subGrad.addColorStop(1, '#38bdf8');\n    ctx.fillStyle = subGrad;\n    ctx.fillRect(x1, 6, x2 - x1, 36);\n\n    // Border on subnet\n    ctx.strokeStyle = '#7dd3fc';\n    ctx.lineWidth = 1;\n    ctx.strokeRect(x1, 6, x2 - x1, 36);\n\n    document.getElementById('isv-cv-start').textContent = '0.0.0.0';\n    document.getElementById('isv-cv-end').textContent = '255.255.255.255';\n  }\n\n  function renderTable(currentPrefix) {\n    var body = document.getElementById('isv-tbl-body');\n    var rows = [];\n    for (var p = 8; p \u003c= 32; p++) {\n      var mask = maskFromPrefix(p);\n      var hosts = numToHostCount(p);\n      var size = Math.pow(2, 32 - p);\n      var sizeStr = size \u003e= 1000000 ? (size / 1000000).toFixed(0) + 'M' : size \u003e= 1000 ? size.toLocaleString() : size.toString();\n      var isCurrent = (p === currentPrefix);\n      rows.push(\n        '\u003ctr class=\"' + (isCurrent ? 'current-row' : '') + '\"\u003e' +\n        '\u003ctd\u003e/' + p + '\u003c/td\u003e' +\n        '\u003ctd\u003e' + numToIp(mask) + '\u003c/td\u003e' +\n        '\u003ctd class=\"hosts-col\"\u003e' + (hosts \u003e 0 ? hosts.toLocaleString() : (p === 32 ? '1' : '0')) + '\u003c/td\u003e' +\n        '\u003ctd\u003e' + sizeStr + ' addr\u003c/td\u003e' +\n        '\u003c/tr\u003e'\n      );\n    }\n    body.innerHTML = rows.join('');\n    // Scroll current row into view\n    var cur = body.querySelector('.current-row');\n    if (cur) { setTimeout(function() { cur.scrollIntoView({ block: 'nearest' }); }, 50); }\n  }\n\n  window.isvCalc = function() {\n    var val = document.getElementById('isv-input').value;\n    var errEl = document.getElementById('isv-error');\n    var inp = document.getElementById('isv-input');\n    var resultsEl = document.getElementById('isv-results');\n    var placeholderEl = document.getElementById('isv-placeholder');\n\n    if (!val.trim()) {\n      errEl.textContent = '';\n      inp.classList.remove('error');\n      resultsEl.style.display = 'none';\n      placeholderEl.style.display = '';\n      return;\n    }\n\n    var parsed = parseCIDR(val);\n    if (!parsed) {\n      errEl.textContent = 'Invalid CIDR notation. Use format: 192.168.1.0/24';\n      inp.classList.add('error');\n      resultsEl.style.display = 'none';\n      placeholderEl.style.display = '';\n      return;\n    }\n    errEl.textContent = '';\n    inp.classList.remove('error');\n\n    var prefix = parsed.prefix;\n    var ipNum = parsed.ipNum;\n    var mask = maskFromPrefix(prefix);\n    var networkNum = (ipNum \u0026 mask) \u003e\u003e\u003e 0;\n    var broadcastNum = (networkNum | (~mask)) \u003e\u003e\u003e 0;\n    var firstHost = prefix \u003c 31 ? ((networkNum + 1) \u003e\u003e\u003e 0) : networkNum;\n    var lastHost = prefix \u003c 31 ? ((broadcastNum - 1) \u003e\u003e\u003e 0) : broadcastNum;\n    var hostCount = numToHostCount(prefix);\n    var totalAddrs = Math.pow(2, 32 - prefix);\n\n    // Cards\n    var cards = [\n      { label: 'IP Address', value: parsed.ipStr, cls: '' },\n      { label: 'Network Address', value: numToIp(networkNum), cls: '' },\n      { label: 'Broadcast', value: numToIp(broadcastNum), cls: 'orange' },\n      { label: 'Subnet Mask', value: numToIp(mask), cls: '' },\n      { label: 'First Host', value: prefix \u003c 31 ? numToIp(firstHost) : 'N/A', cls: 'green' },\n      { label: 'Last Host', value: prefix \u003c 31 ? numToIp(lastHost) : 'N/A', cls: 'green' },\n      { label: 'Usable Hosts', value: hostCount \u003e 0 ? hostCount.toLocaleString() : (prefix === 32 ? '1' : '0'), cls: 'yellow' },\n      { label: 'Total Addresses', value: totalAddrs.toLocaleString(), cls: '' },\n    ];\n    var cardsHtml = cards.map(function(c) {\n      return '\u003cdiv class=\"res-card\"\u003e\u003cdiv class=\"rc-label\"\u003e' + c.label + '\u003c/div\u003e\u003cdiv class=\"rc-value ' + c.cls + '\"\u003e' + c.value + '\u003c/div\u003e\u003c/div\u003e';\n    }).join('');\n    document.getElementById('isv-cards').innerHTML = cardsHtml;\n\n    // Binary rows\n    var binHtml = '';\n    binHtml += renderBinaryRow('IP Address', ipNum, prefix, 'ip');\n    binHtml += renderBinaryRow('Subnet Mask', mask, prefix, 'mask');\n    binHtml += renderBinaryRow('Network Addr', networkNum, prefix, 'ip');\n    binHtml += renderBinaryRow('Broadcast', broadcastNum, prefix, 'ip');\n    document.getElementById('isv-bin-rows').innerHTML = binHtml;\n\n    // Canvas\n    renderCanvas(networkNum, broadcastNum);\n\n    // Table\n    renderTable(prefix);\n\n    resultsEl.style.display = '';\n    placeholderEl.style.display = 'none';\n  };\n\n  // Auto-calc on load if value preset\n  window.addEventListener('load', function() {\n    var inp = document.getElementById('isv-input');\n    if (inp.value) isvCalc();\n    // Redraw canvas on resize\n    window.addEventListener('resize', function() {\n      var val = document.getElementById('isv-input').value;\n      if (val.trim()) isvCalc();\n    });\n  });\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003eIP address info → \u003ca href=\"https://productivity-works.com/tools/ip-address-info/\"\u003eIP Address Info\u003c/a\u003e\n\u003c/p\u003e","title":"IP Subnet Visualizer - Free CIDR Calculator \u0026 Subnet Tool"},{"content":"Paste two JSON objects and instantly see every added, removed, or changed key — color-coded, with full dot-notation paths to each change. No server, no sign-up, no tracking.\n\u0026#8644; Compare \u0026#8645; Swap { } Format Left { } Format Right Load Sample Clear Left (Original) Right (Modified) Paste JSON into both panels and click Compare. How It Works The diff engine walks both JSON trees recursively. At each node it checks:\nObject keys — keys present only in the right are added; keys only in the left are removed; shared keys are compared recursively. Array indices — compared index-by-index; excess elements on either side are flagged. Primitives — a strict !== comparison detects value changes, including type coercion differences (1 vs \u0026quot;1\u0026quot;). Each change is reported with its full dot-notation path (e.g. user.email, tags[2]) so you can locate it instantly in the original JSON.\nAll processing is client-side — your JSON never leaves the browser.\nColor Code Reference Color Symbol Meaning Green + Key/value exists only in the Right (added) Red − Key/value exists only in the Left (removed) Yellow ~ Key exists in both but the value changed Tips Format First — use \u0026ldquo;Format Left / Right\u0026rdquo; to pretty-print minified JSON before comparing, making paths easier to read. Swap — reverse the comparison direction in one click. Sample — click \u0026ldquo;Load Sample\u0026rdquo; to see a realistic user-object diff in action. Keyboard shortcut — press Ctrl+Enter (or Cmd+Enter on Mac) to compare without reaching for the mouse. Nested objects — the tool recurses into any depth; paths like config.database.pool.max are reported correctly. Validate and pretty-print JSON → JSON Formatter Find values in deep JSON → JSON Path Finder Compare plain text line by line → Text Diff Checker ","permalink":"https://productivity-works.com/tools/json-diff/","summary":"\u003cp\u003ePaste two JSON objects and instantly see every added, removed, or changed key — color-coded, with full dot-notation paths to each change. No server, no sign-up, no tracking.\u003c/p\u003e\n\u003cdiv id=\"jd-app\"\u003e\n\u003cstyle\u003e\n#jd-app *,\n#jd-app *::before,\n#jd-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#jd-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 14px;\n  color: #1e293b;\n  line-height: 1.5;\n}\n#jd-app .jd-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  align-items: center;\n  margin-bottom: 14px;\n}\n#jd-app .jd-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 5px;\n  padding: 7px 14px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  background: #fff;\n  color: #334155;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, border-color 0.15s;\n  white-space: nowrap;\n}\n#jd-app .jd-btn:hover { background: #f1f5f9; border-color: #94a3b8; }\n#jd-app .jd-btn.primary {\n  background: #6366f1;\n  color: #fff;\n  border-color: #6366f1;\n}\n#jd-app .jd-btn.primary:hover { background: #4f46e5; border-color: #4f46e5; }\n#jd-app .jd-panels {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 12px;\n  margin-bottom: 12px;\n}\n@media (max-width: 640px) {\n  #jd-app .jd-panels { grid-template-columns: 1fr; }\n}\n#jd-app .jd-panel-label {\n  font-size: 12px;\n  font-weight: 700;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 5px;\n}\n#jd-app textarea.jd-input {\n  width: 100%;\n  height: 220px;\n  padding: 10px 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-family: \"SF Mono\", \"Fira Code\", \"Cascadia Code\", monospace;\n  font-size: 12.5px;\n  line-height: 1.6;\n  color: #1e293b;\n  background: #f8fafc;\n  resize: vertical;\n  outline: none;\n  transition: border-color 0.15s;\n}\n#jd-app textarea.jd-input:focus { border-color: #6366f1; background: #fff; }\n#jd-app textarea.jd-input.error { border-color: #ef4444; background: #fff5f5; }\n#jd-app .jd-parse-error {\n  font-size: 12px;\n  color: #ef4444;\n  margin-top: 4px;\n  min-height: 16px;\n}\n#jd-app .jd-stats {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n  margin-bottom: 14px;\n}\n#jd-app .jd-stat {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  padding: 5px 12px;\n  border-radius: 20px;\n  font-size: 13px;\n  font-weight: 600;\n}\n#jd-app .jd-stat.added   { background: #dcfce7; color: #15803d; }\n#jd-app .jd-stat.removed { background: #fee2e2; color: #b91c1c; }\n#jd-app .jd-stat.changed { background: #fef9c3; color: #92400e; }\n#jd-app .jd-stat.same    { background: #f1f5f9; color: #475569; }\n#jd-app .jd-stat-dot {\n  width: 9px; height: 9px;\n  border-radius: 50%;\n  flex-shrink: 0;\n}\n#jd-app .jd-stat.added   .jd-stat-dot { background: #16a34a; }\n#jd-app .jd-stat.removed .jd-stat-dot { background: #dc2626; }\n#jd-app .jd-stat.changed .jd-stat-dot { background: #d97706; }\n#jd-app .jd-stat.same    .jd-stat-dot { background: #94a3b8; }\n#jd-app .jd-result-header {\n  font-size: 13px;\n  font-weight: 700;\n  color: #475569;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 10px;\n}\n#jd-app .jd-tree {\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  overflow: hidden;\n  background: #fff;\n}\n#jd-app .jd-row {\n  display: flex;\n  align-items: baseline;\n  gap: 0;\n  border-bottom: 1px solid #f1f5f9;\n  font-family: \"SF Mono\", \"Fira Code\", \"Cascadia Code\", monospace;\n  font-size: 12.5px;\n  line-height: 1.7;\n}\n#jd-app .jd-row:last-child { border-bottom: none; }\n#jd-app .jd-row.added   { background: #f0fdf4; }\n#jd-app .jd-row.removed { background: #fff5f5; }\n#jd-app .jd-row.changed { background: #fefce8; }\n#jd-app .jd-row.context { background: #fff; }\n#jd-app .jd-row-badge {\n  width: 26px;\n  flex-shrink: 0;\n  text-align: center;\n  font-weight: 700;\n  font-size: 13px;\n  padding: 0 4px;\n  align-self: stretch;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n#jd-app .jd-row.added   .jd-row-badge { color: #16a34a; background: #dcfce7; }\n#jd-app .jd-row.removed .jd-row-badge { color: #dc2626; background: #fee2e2; }\n#jd-app .jd-row.changed .jd-row-badge { color: #d97706; background: #fef9c3; }\n#jd-app .jd-row.context .jd-row-badge { color: #94a3b8; background: #f8fafc; }\n#jd-app .jd-row-body {\n  padding: 4px 12px;\n  flex: 1;\n  overflow-wrap: anywhere;\n}\n#jd-app .jd-row-path {\n  color: #6366f1;\n  font-weight: 600;\n}\n#jd-app .jd-row-sep {\n  color: #94a3b8;\n  margin: 0 5px;\n}\n#jd-app .jd-val-old {\n  text-decoration: line-through;\n  color: #dc2626;\n}\n#jd-app .jd-val-new { color: #16a34a; }\n#jd-app .jd-val-added   { color: #15803d; }\n#jd-app .jd-val-removed { color: #b91c1c; }\n#jd-app .jd-empty-state {\n  text-align: center;\n  padding: 40px 20px;\n  color: #94a3b8;\n  font-size: 14px;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n}\n#jd-app .jd-identical {\n  text-align: center;\n  padding: 28px 20px;\n  color: #16a34a;\n  font-size: 15px;\n  font-weight: 600;\n  background: #f0fdf4;\n  border-radius: 10px;\n  border: 1.5px solid #bbf7d0;\n}\n#jd-app .jd-section-header {\n  padding: 5px 12px 5px 0;\n  font-size: 11.5px;\n  color: #94a3b8;\n  font-style: italic;\n  border-bottom: 1px solid #f1f5f9;\n  background: #f8fafc;\n  padding-left: 38px;\n}\n\u003c/style\u003e\n\u003cdiv class=\"jd-toolbar\"\u003e\n  \u003cbutton class=\"jd-btn primary\" id=\"jd-compare\"\u003e\u0026#8644; Compare\u003c/button\u003e\n  \u003cbutton class=\"jd-btn\" id=\"jd-swap\"\u003e\u0026#8645; Swap\u003c/button\u003e\n  \u003cbutton class=\"jd-btn\" id=\"jd-fmt-left\"\u003e{ } Format Left\u003c/button\u003e\n  \u003cbutton class=\"jd-btn\" id=\"jd-fmt-right\"\u003e{ } Format Right\u003c/button\u003e\n  \u003cbutton class=\"jd-btn\" id=\"jd-sample\"\u003eLoad Sample\u003c/button\u003e\n  \u003cbutton class=\"jd-btn\" id=\"jd-clear\"\u003eClear\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"jd-panels\"\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"jd-panel-label\"\u003eLeft (Original)\u003c/div\u003e\n    \u003ctextarea class=\"jd-input\" id=\"jd-left\" placeholder='{\"name\": \"Alice\", \"age\": 30}'\u003e\u003c/textarea\u003e\n    \u003cdiv class=\"jd-parse-error\" id=\"jd-err-left\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"jd-panel-label\"\u003eRight (Modified)\u003c/div\u003e\n    \u003ctextarea class=\"jd-input\" id=\"jd-right\" placeholder='{\"name\": \"Alice\", \"age\": 31}'\u003e\u003c/textarea\u003e\n    \u003cdiv class=\"jd-parse-error\" id=\"jd-err-right\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"jd-stats\" id=\"jd-stats\" style=\"display:none;\"\u003e\u003c/div\u003e\n\u003cdiv id=\"jd-result-wrap\"\u003e\n  \u003cdiv class=\"jd-empty-state\"\u003ePaste JSON into both panels and click Compare.\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  'use strict';\n\n  /* ── Helpers ── */\n  function esc(s) {\n    return String(s)\n      .replace(/\u0026/g,'\u0026amp;')\n      .replace(/\u003c/g,'\u0026lt;')\n      .replace(/\u003e/g,'\u0026gt;')\n      .replace(/\"/g,'\u0026quot;');\n  }\n\n  function formatVal(v) {\n    if (v === null) return '\u003cspan style=\"color:#7c3aed\"\u003enull\u003c/span\u003e';\n    if (typeof v === 'boolean') return '\u003cspan style=\"color:#0891b2\"\u003e' + v + '\u003c/span\u003e';\n    if (typeof v === 'number') return '\u003cspan style=\"color:#0369a1\"\u003e' + v + '\u003c/span\u003e';\n    if (typeof v === 'string') return '\u003cspan style=\"color:#b45309\"\u003e\"' + esc(v) + '\"\u003c/span\u003e';\n    if (Array.isArray(v)) return '\u003cspan style=\"color:#64748b\"\u003e[Array(' + v.length + ')]\u003c/span\u003e';\n    return '\u003cspan style=\"color:#64748b\"\u003e{Object}\u003c/span\u003e';\n  }\n\n  function isPrimitive(v) {\n    return v === null || typeof v !== 'object';\n  }\n\n  /* ── Deep diff ── */\n  // Returns array of {type, path, oldVal, newVal}\n  function deepDiff(left, right, path, results) {\n    results = results || [];\n    path = path || '';\n\n    const lt = typeOf(left);\n    const rt = typeOf(right);\n\n    if (lt !== rt) {\n      // Type changed — treat as changed\n      results.push({ type: 'changed', path: path || '(root)', oldVal: left, newVal: right });\n      return results;\n    }\n\n    if (lt === 'object') {\n      const allKeys = new Set([...Object.keys(left), ...Object.keys(right)]);\n      for (const key of allKeys) {\n        const childPath = path ? path + '.' + key : key;\n        if (!Object.prototype.hasOwnProperty.call(left, key)) {\n          collectAll(right[key], childPath, 'added', results);\n        } else if (!Object.prototype.hasOwnProperty.call(right, key)) {\n          collectAll(left[key], childPath, 'removed', results);\n        } else {\n          deepDiff(left[key], right[key], childPath, results);\n        }\n      }\n    } else if (lt === 'array') {\n      const maxLen = Math.max(left.length, right.length);\n      for (let i = 0; i \u003c maxLen; i++) {\n        const childPath = (path || '(root)') + '[' + i + ']';\n        if (i \u003e= left.length) {\n          collectAll(right[i], childPath, 'added', results);\n        } else if (i \u003e= right.length) {\n          collectAll(left[i], childPath, 'removed', results);\n        } else {\n          deepDiff(left[i], right[i], childPath, results);\n        }\n      }\n    } else {\n      // Primitive comparison\n      if (left !== right) {\n        results.push({ type: 'changed', path: path || '(root)', oldVal: left, newVal: right });\n      }\n      // If equal — no entry (identical)\n    }\n\n    return results;\n  }\n\n  function typeOf(v) {\n    if (v === null) return 'null';\n    if (Array.isArray(v)) return 'array';\n    return typeof v;\n  }\n\n  // Collect all keys under a subtree as added/removed\n  function collectAll(node, path, type, results) {\n    if (isPrimitive(node) || typeOf(node) === 'null') {\n      results.push({ type: type, path: path, val: node });\n      return;\n    }\n    if (Array.isArray(node)) {\n      if (node.length === 0) {\n        results.push({ type: type, path: path, val: node });\n        return;\n      }\n      node.forEach(function(item, i) {\n        collectAll(item, path + '[' + i + ']', type, results);\n      });\n    } else {\n      const keys = Object.keys(node);\n      if (keys.length === 0) {\n        results.push({ type: type, path: path, val: node });\n        return;\n      }\n      keys.forEach(function(k) {\n        collectAll(node[k], path + '.' + k, type, results);\n      });\n    }\n  }\n\n  /* ── Render ── */\n  function renderDiff(diffs) {\n    if (diffs.length === 0) {\n      return '\u003cdiv class=\"jd-identical\"\u003e\u0026#10003; Objects are identical — no differences found.\u003c/div\u003e';\n    }\n\n    let html = '\u003cdiv class=\"jd-tree\"\u003e';\n\n    diffs.forEach(function(d) {\n      if (d.type === 'added') {\n        html += '\u003cdiv class=\"jd-row added\"\u003e';\n        html += '\u003cdiv class=\"jd-row-badge\"\u003e+\u003c/div\u003e';\n        html += '\u003cdiv class=\"jd-row-body\"\u003e';\n        html += '\u003cspan class=\"jd-row-path\"\u003e' + esc(d.path) + '\u003c/span\u003e';\n        html += '\u003cspan class=\"jd-row-sep\"\u003e\u0026#8594;\u003c/span\u003e';\n        html += '\u003cspan class=\"jd-val-added\"\u003e' + formatVal(d.val) + '\u003c/span\u003e';\n        html += '\u003c/div\u003e\u003c/div\u003e';\n      } else if (d.type === 'removed') {\n        html += '\u003cdiv class=\"jd-row removed\"\u003e';\n        html += '\u003cdiv class=\"jd-row-badge\"\u003e\u0026minus;\u003c/div\u003e';\n        html += '\u003cdiv class=\"jd-row-body\"\u003e';\n        html += '\u003cspan class=\"jd-row-path\"\u003e' + esc(d.path) + '\u003c/span\u003e';\n        html += '\u003cspan class=\"jd-row-sep\"\u003e\u0026#8594;\u003c/span\u003e';\n        html += '\u003cspan class=\"jd-val-removed\"\u003e' + formatVal(d.val) + '\u003c/span\u003e';\n        html += '\u003c/div\u003e\u003c/div\u003e';\n      } else if (d.type === 'changed') {\n        html += '\u003cdiv class=\"jd-row changed\"\u003e';\n        html += '\u003cdiv class=\"jd-row-badge\"\u003e~\u003c/div\u003e';\n        html += '\u003cdiv class=\"jd-row-body\"\u003e';\n        html += '\u003cspan class=\"jd-row-path\"\u003e' + esc(d.path) + '\u003c/span\u003e';\n        html += '\u003cspan class=\"jd-row-sep\"\u003e:\u003c/span\u003e ';\n        html += '\u003cspan class=\"jd-val-old\"\u003e' + formatVal(d.oldVal) + '\u003c/span\u003e';\n        html += ' \u003cspan class=\"jd-row-sep\"\u003e\u0026#8594;\u003c/span\u003e ';\n        html += '\u003cspan class=\"jd-val-new\"\u003e' + formatVal(d.newVal) + '\u003c/span\u003e';\n        html += '\u003c/div\u003e\u003c/div\u003e';\n      }\n    });\n\n    html += '\u003c/div\u003e';\n    return html;\n  }\n\n  function renderStats(diffs) {\n    var added = 0, removed = 0, changed = 0;\n    diffs.forEach(function(d) {\n      if (d.type === 'added') added++;\n      else if (d.type === 'removed') removed++;\n      else if (d.type === 'changed') changed++;\n    });\n    var total = added + removed + changed;\n    return (\n      '\u003cdiv class=\"jd-stat added\"\u003e\u003cdiv class=\"jd-stat-dot\"\u003e\u003c/div\u003e' + added + ' added\u003c/div\u003e' +\n      '\u003cdiv class=\"jd-stat removed\"\u003e\u003cdiv class=\"jd-stat-dot\"\u003e\u003c/div\u003e' + removed + ' removed\u003c/div\u003e' +\n      '\u003cdiv class=\"jd-stat changed\"\u003e\u003cdiv class=\"jd-stat-dot\"\u003e\u003c/div\u003e' + changed + ' changed\u003c/div\u003e' +\n      '\u003cdiv class=\"jd-stat same\"\u003e\u003cdiv class=\"jd-stat-dot\"\u003e\u003c/div\u003e' + total + ' total differences\u003c/div\u003e'\n    );\n  }\n\n  /* ── Parse helper ── */\n  function tryParse(str, errEl, inputEl) {\n    errEl.textContent = '';\n    inputEl.classList.remove('error');\n    var s = str.trim();\n    if (!s) return null;\n    try {\n      return JSON.parse(s);\n    } catch (e) {\n      errEl.textContent = 'Parse error: ' + e.message;\n      inputEl.classList.add('error');\n      return undefined; // distinct from null (valid JSON null)\n    }\n  }\n\n  /* ── DOM refs ── */\n  var leftEl    = document.getElementById('jd-left');\n  var rightEl   = document.getElementById('jd-right');\n  var errLeft   = document.getElementById('jd-err-left');\n  var errRight  = document.getElementById('jd-err-right');\n  var statsEl   = document.getElementById('jd-stats');\n  var resultEl  = document.getElementById('jd-result-wrap');\n\n  /* ── Compare ── */\n  function runCompare() {\n    var leftParsed  = tryParse(leftEl.value,  errLeft,  leftEl);\n    var rightParsed = tryParse(rightEl.value, errRight, rightEl);\n\n    if (leftParsed === undefined || rightParsed === undefined) {\n      statsEl.style.display = 'none';\n      resultEl.innerHTML = '\u003cdiv class=\"jd-empty-state\"\u003eFix the JSON parse errors above to compare.\u003c/div\u003e';\n      return;\n    }\n    if (leftParsed === null \u0026\u0026 rightParsed === null \u0026\u0026 !leftEl.value.trim() \u0026\u0026 !rightEl.value.trim()) {\n      statsEl.style.display = 'none';\n      resultEl.innerHTML = '\u003cdiv class=\"jd-empty-state\"\u003ePaste JSON into both panels and click Compare.\u003c/div\u003e';\n      return;\n    }\n\n    var diffs = deepDiff(leftParsed, rightParsed);\n    statsEl.style.display = 'flex';\n    statsEl.innerHTML = renderStats(diffs);\n    resultEl.innerHTML = renderDiff(diffs);\n  }\n\n  /* ── Format ── */\n  function formatInput(el, errEl) {\n    var parsed = tryParse(el.value, errEl, el);\n    if (parsed === undefined) return;\n    if (parsed === null \u0026\u0026 !el.value.trim()) return;\n    el.value = JSON.stringify(parsed, null, 2);\n    errEl.textContent = '';\n    el.classList.remove('error');\n  }\n\n  /* ── Sample JSON ── */\n  var sampleLeft = {\n    \"user\": {\n      \"id\": 1,\n      \"name\": \"Alice Johnson\",\n      \"email\": \"alice@example.com\",\n      \"role\": \"admin\",\n      \"active\": true,\n      \"score\": 95\n    },\n    \"settings\": {\n      \"theme\": \"dark\",\n      \"notifications\": true,\n      \"language\": \"en\"\n    },\n    \"tags\": [\"developer\", \"tester\"]\n  };\n\n  var sampleRight = {\n    \"user\": {\n      \"id\": 1,\n      \"name\": \"Alice Johnson\",\n      \"email\": \"alice@newdomain.com\",\n      \"role\": \"editor\",\n      \"active\": false,\n      \"score\": 98,\n      \"department\": \"Engineering\"\n    },\n    \"settings\": {\n      \"theme\": \"light\",\n      \"notifications\": true,\n      \"language\": \"en\",\n      \"timezone\": \"UTC\"\n    },\n    \"tags\": [\"developer\", \"reviewer\", \"lead\"]\n  };\n\n  /* ── Event listeners ── */\n  document.getElementById('jd-compare').addEventListener('click', runCompare);\n\n  document.getElementById('jd-swap').addEventListener('click', function() {\n    var tmp = leftEl.value;\n    leftEl.value = rightEl.value;\n    rightEl.value = tmp;\n    errLeft.textContent = '';\n    errRight.textContent = '';\n    leftEl.classList.remove('error');\n    rightEl.classList.remove('error');\n  });\n\n  document.getElementById('jd-fmt-left').addEventListener('click', function() {\n    formatInput(leftEl, errLeft);\n  });\n\n  document.getElementById('jd-fmt-right').addEventListener('click', function() {\n    formatInput(rightEl, errRight);\n  });\n\n  document.getElementById('jd-sample').addEventListener('click', function() {\n    leftEl.value  = JSON.stringify(sampleLeft,  null, 2);\n    rightEl.value = JSON.stringify(sampleRight, null, 2);\n    errLeft.textContent  = '';\n    errRight.textContent = '';\n    leftEl.classList.remove('error');\n    rightEl.classList.remove('error');\n    runCompare();\n  });\n\n  document.getElementById('jd-clear').addEventListener('click', function() {\n    leftEl.value  = '';\n    rightEl.value = '';\n    errLeft.textContent  = '';\n    errRight.textContent = '';\n    leftEl.classList.remove('error');\n    rightEl.classList.remove('error');\n    statsEl.style.display = 'none';\n    resultEl.innerHTML = '\u003cdiv class=\"jd-empty-state\"\u003ePaste JSON into both panels and click Compare.\u003c/div\u003e';\n  });\n\n  /* ── Auto-compare on input ── */\n  var debTimer;\n  function maybeAutoCompare() {\n    clearTimeout(debTimer);\n    debTimer = setTimeout(function() {\n      if (leftEl.value.trim() \u0026\u0026 rightEl.value.trim()) {\n        runCompare();\n      }\n    }, 700);\n  }\n  leftEl.addEventListener('input',  maybeAutoCompare);\n  rightEl.addEventListener('input', maybeAutoCompare);\n\n  /* ── Keyboard shortcut ── */\n  document.addEventListener('keydown', function(e) {\n    if ((e.ctrlKey || e.metaKey) \u0026\u0026 e.key === 'Enter') {\n      e.preventDefault();\n      runCompare();\n    }\n  });\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-it-works\"\u003eHow It Works\u003c/h2\u003e\n\u003cp\u003eThe diff engine walks both JSON trees recursively. At each node it checks:\u003c/p\u003e","title":"JSON Diff Checker"},{"content":" Format / Beautify Indent: 2 spaces 4 spaces Tab Minify Validate Tree View Copy Output Sample JSON Clear Input Chars: 0 Lines: 1 Output // Formatted output will appear here Copied to clipboard! What is JSON? JSON (JavaScript Object Notation) is a lightweight, human-readable data interchange format originally derived from JavaScript object syntax. It uses a simple structure of key-value pairs, arrays, strings, numbers, booleans, and null values, making it straightforward to read and write for humans while remaining easy to parse and generate for machines. Today JSON has become the de facto standard for data exchange in web APIs, configuration files, and inter-service communication across virtually every programming language and platform.\nBecause JSON is strictly text-based and language-independent, it is widely used for transmitting structured data between a web server and a browser, between microservices, and in NoSQL databases such as MongoDB. However, even minor syntax issues — a missing comma, an extra bracket, or an unquoted key — will render a JSON document invalid and cause parsing failures. Tools like this formatter help you instantly spot and fix such errors, beautify minified payloads for readability, and verify that your data conforms to the JSON specification before integrating it into your application.\nHow to Use This JSON Formatter Format / Beautify — Paste raw or minified JSON into the left pane and click \u0026ldquo;Format / Beautify\u0026rdquo; (or press Ctrl+Enter). The tool parses the input and displays it with consistent indentation and syntax coloring on the right. Use the indent selector to choose 2 spaces, 4 spaces, or tabs.\nMinify — Click \u0026ldquo;Minify\u0026rdquo; to strip all whitespace and produce the most compact JSON string, ideal for reducing payload size in API calls.\nValidate — Click \u0026ldquo;Validate\u0026rdquo; to check whether your JSON is well-formed. If errors are found, the exact error message and line number are displayed below the output pane so you can jump straight to the problem.\nTree View — Toggle \u0026ldquo;Tree View\u0026rdquo; to explore nested objects and arrays as a collapsible tree. Click the [-] / [+] buttons to expand or collapse any branch.\nCopy Output — After formatting or minifying, click \u0026ldquo;Copy Output\u0026rdquo; to copy the result to your clipboard with one click.\nSample JSON — Click \u0026ldquo;Sample JSON\u0026rdquo; to load a built-in example and see the tool in action immediately.\nRelated Tools Count words and characters in your text → Word Counter Generate secure passwords → Password Generator Stay focused while coding → Pomodoro Timer Need a markdown editor? → Markdown Preview — write and preview markdown live\nRelated Articles Best AI Tools for Small Business 2026: The Complete Roundup Best Free Alternatives to ChatGPT 2026: Complete Comparison ChatGPT vs Claude vs Gemini: The Definitive AI Comparison 2026 ","permalink":"https://productivity-works.com/tools/json-formatter/","summary":"\u003cdiv id=\"json-app\"\u003e\n\u003cstyle\u003e\n#json-app {\n  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;\n  background: #1e1e2e;\n  color: #cdd6f4;\n  border-radius: 12px;\n  padding: 20px;\n  margin: 0 auto;\n  max-width: 1100px;\n  box-sizing: border-box;\n}\n\n#json-app * {\n  box-sizing: border-box;\n}\n\n.json-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  align-items: center;\n  margin-bottom: 16px;\n}\n\n.json-toolbar label {\n  color: #a6adc8;\n  font-size: 13px;\n  margin-right: 2px;\n}\n\n.json-btn {\n  padding: 7px 16px;\n  border: none;\n  border-radius: 7px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: opacity 0.15s, transform 0.1s;\n  outline: none;\n}\n\n.json-btn:active {\n  transform: scale(0.97);\n}\n\n.json-btn:hover {\n  opacity: 0.88;\n}\n\n.btn-format  { background: #89b4fa; color: #1e1e2e; }\n.btn-minify  { background: #a6e3a1; color: #1e1e2e; }\n.btn-validate{ background: #f9e2af; color: #1e1e2e; }\n.btn-copy    { background: #cba6f7; color: #1e1e2e; }\n.btn-clear   { background: #45475a; color: #cdd6f4; }\n.btn-sample  { background: #313244; color: #89b4fa; border: 1px solid #89b4fa; }\n.btn-tree    { background: #313244; color: #94e2d5; border: 1px solid #94e2d5; }\n.btn-tree.active { background: #94e2d5; color: #1e1e2e; }\n\n.indent-select {\n  background: #313244;\n  color: #cdd6f4;\n  border: 1px solid #45475a;\n  border-radius: 6px;\n  padding: 6px 10px;\n  font-size: 13px;\n  cursor: pointer;\n  outline: none;\n}\n\n.indent-select:focus {\n  border-color: #89b4fa;\n}\n\n.json-panes {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 16px;\n}\n\n@media (max-width: 700px) {\n  .json-panes {\n    grid-template-columns: 1fr;\n  }\n}\n\n.json-pane {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n}\n\n.pane-label {\n  font-size: 12px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #a6adc8;\n}\n\n.json-input {\n  width: 100%;\n  min-height: 340px;\n  background: #11111b;\n  color: #cdd6f4;\n  border: 2px solid #313244;\n  border-radius: 9px;\n  padding: 14px;\n  font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;\n  font-size: 13px;\n  line-height: 1.65;\n  resize: vertical;\n  outline: none;\n  transition: border-color 0.2s;\n  tab-size: 2;\n}\n\n.json-input:focus {\n  border-color: #89b4fa;\n}\n\n.json-input.error {\n  border-color: #f38ba8;\n}\n\n.json-output {\n  width: 100%;\n  min-height: 340px;\n  background: #11111b;\n  border: 2px solid #313244;\n  border-radius: 9px;\n  padding: 14px;\n  font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;\n  font-size: 13px;\n  line-height: 1.65;\n  overflow: auto;\n  white-space: pre;\n  word-break: break-all;\n  white-space: pre-wrap;\n  transition: border-color 0.2s;\n}\n\n.json-output.valid {\n  border-color: #a6e3a1;\n}\n\n.json-output.error {\n  border-color: #f38ba8;\n}\n\n.json-tree {\n  width: 100%;\n  min-height: 340px;\n  background: #11111b;\n  border: 2px solid #45475a;\n  border-radius: 9px;\n  padding: 14px;\n  font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;\n  font-size: 13px;\n  line-height: 1.7;\n  overflow: auto;\n  display: none;\n}\n\n.json-tree.visible {\n  display: block;\n}\n\n.status-bar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 12px;\n  margin-top: 10px;\n  font-size: 12px;\n  color: #6c7086;\n}\n\n.status-item {\n  display: flex;\n  align-items: center;\n  gap: 4px;\n}\n\n.status-ok   { color: #a6e3a1; }\n.status-err  { color: #f38ba8; }\n.status-info { color: #89b4fa; }\n\n.error-msg {\n  background: #1a0a0a;\n  border: 1px solid #f38ba8;\n  border-radius: 7px;\n  color: #f38ba8;\n  font-size: 13px;\n  padding: 10px 14px;\n  margin-top: 6px;\n  display: none;\n  font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;\n}\n\n.error-msg.visible {\n  display: block;\n}\n\n.copy-toast {\n  position: fixed;\n  bottom: 32px;\n  right: 32px;\n  background: #a6e3a1;\n  color: #1e1e2e;\n  font-weight: 700;\n  font-size: 14px;\n  padding: 10px 22px;\n  border-radius: 8px;\n  box-shadow: 0 4px 24px rgba(0,0,0,0.4);\n  opacity: 0;\n  pointer-events: none;\n  transition: opacity 0.25s;\n  z-index: 9999;\n}\n\n.copy-toast.show {\n  opacity: 1;\n}\n\n/* Syntax highlight colors */\n.jt-key    { color: #89b4fa; }\n.jt-str    { color: #a6e3a1; }\n.jt-num    { color: #fab387; }\n.jt-bool   { color: #cba6f7; }\n.jt-null   { color: #f38ba8; }\n.jt-punc   { color: #6c7086; }\n\n/* Tree view */\n.tree-node { }\n.tree-toggle {\n  cursor: pointer;\n  user-select: none;\n  color: #89b4fa;\n  font-weight: 700;\n  margin-right: 2px;\n}\n.tree-toggle:hover { color: #cdd6f4; }\n.tree-children { margin-left: 20px; }\n.tree-key   { color: #89b4fa; }\n.tree-str   { color: #a6e3a1; }\n.tree-num   { color: #fab387; }\n.tree-bool  { color: #cba6f7; }\n.tree-null  { color: #f38ba8; }\n.tree-meta  { color: #6c7086; font-size: 11px; margin-left: 6px; }\n.tree-collapsed \u003e .tree-children { display: none; }\n\u003c/style\u003e\n\u003cdiv class=\"json-toolbar\"\u003e\n  \u003cbutton class=\"json-btn btn-format\" onclick=\"jsonFormat()\"\u003eFormat / Beautify\u003c/button\u003e\n  \u003clabel for=\"json-indent\"\u003eIndent:\u003c/label\u003e\n  \u003cselect id=\"json-indent\" class=\"indent-select\"\u003e\n    \u003coption value=\"2\"\u003e2 spaces\u003c/option\u003e\n    \u003coption value=\"4\"\u003e4 spaces\u003c/option\u003e\n    \u003coption value=\"tab\"\u003eTab\u003c/option\u003e\n  \u003c/select\u003e\n  \u003cbutton class=\"json-btn btn-minify\" onclick=\"jsonMinify()\"\u003eMinify\u003c/button\u003e\n  \u003cbutton class=\"json-btn btn-validate\" onclick=\"jsonValidate()\"\u003eValidate\u003c/button\u003e\n  \u003cbutton class=\"json-btn btn-tree\" id=\"treeToggleBtn\" onclick=\"jsonToggleTree()\"\u003eTree View\u003c/button\u003e\n  \u003cbutton class=\"json-btn btn-copy\" onclick=\"jsonCopy()\"\u003eCopy Output\u003c/button\u003e\n  \u003cbutton class=\"json-btn btn-sample\" onclick=\"jsonSample()\"\u003eSample JSON\u003c/button\u003e\n  \u003cbutton class=\"json-btn btn-clear\" onclick=\"jsonClear()\"\u003eClear\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"json-panes\"\u003e\n  \u003cdiv class=\"json-pane\"\u003e\n    \u003cdiv class=\"pane-label\"\u003eInput\u003c/div\u003e\n    \u003ctextarea\n      id=\"json-input\"\n      class=\"json-input\"\n      placeholder='Paste your JSON here…\u0026#10;\u0026#10;{\"name\": \"example\", \"value\": 42}'\n      oninput=\"jsonOnInput()\"\n      spellcheck=\"false\"\n    \u003e\u003c/textarea\u003e\n    \u003cdiv class=\"status-bar\"\u003e\n      \u003cspan class=\"status-item\" id=\"stat-chars\"\u003eChars: 0\u003c/span\u003e\n      \u003cspan class=\"status-item\" id=\"stat-lines\"\u003eLines: 1\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"json-pane\"\u003e\n    \u003cdiv class=\"pane-label\"\u003eOutput\u003c/div\u003e\n    \u003cdiv id=\"json-output\" class=\"json-output\"\u003e\u003cspan style=\"color:#45475a\"\u003e// Formatted output will appear here\u003c/span\u003e\u003c/div\u003e\n    \u003cdiv id=\"json-tree-view\" class=\"json-tree\"\u003e\u003c/div\u003e\n    \u003cdiv id=\"json-error\" class=\"error-msg\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"status-bar\" id=\"stat-output-bar\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"copy-toast\" id=\"copy-toast\"\u003eCopied to clipboard!\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  var treeVisible = false;\n\n  /* ── Helpers ── */\n  function getInput() {\n    return document.getElementById('json-input').value;\n  }\n\n  function getIndent() {\n    var v = document.getElementById('json-indent').value;\n    return v === 'tab' ? '\\t' : parseInt(v, 10);\n  }\n\n  function setOutput(html, state) {\n    var el = document.getElementById('json-output');\n    el.innerHTML = html;\n    el.className = 'json-output' + (state ? ' ' + state : '');\n  }\n\n  function setError(msg) {\n    var el = document.getElementById('json-error');\n    if (msg) {\n      el.textContent = msg;\n      el.classList.add('visible');\n    } else {\n      el.textContent = '';\n      el.classList.remove('visible');\n    }\n  }\n\n  function setInputState(state) {\n    var el = document.getElementById('json-input');\n    el.className = 'json-input' + (state ? ' ' + state : '');\n  }\n\n  function updateStats(text) {\n    document.getElementById('stat-chars').textContent = 'Chars: ' + text.length;\n    document.getElementById('stat-lines').textContent = 'Lines: ' + (text.split('\\n').length);\n  }\n\n  function setOutputStats(text, state) {\n    var bar = document.getElementById('stat-output-bar');\n    if (!text) { bar.innerHTML = ''; return; }\n    var cls = state === 'valid' ? 'status-ok' : state === 'error' ? 'status-err' : 'status-info';\n    bar.innerHTML =\n      '\u003cspan class=\"status-item ' + cls + '\"\u003e' +\n      (state === 'valid' ? 'Valid JSON' : state === 'error' ? 'Invalid JSON' : '') +\n      '\u003c/span\u003e' +\n      '\u003cspan class=\"status-item\"\u003eChars: ' + text.length + '\u003c/span\u003e' +\n      '\u003cspan class=\"status-item\"\u003eLines: ' + text.split('\\n').length + '\u003c/span\u003e';\n  }\n\n  /* ── Syntax Highlighting ── */\n  function escapeHtml(s) {\n    return s\n      .replace(/\u0026/g, '\u0026amp;')\n      .replace(/\u003c/g, '\u0026lt;')\n      .replace(/\u003e/g, '\u0026gt;');\n  }\n\n  function syntaxHighlight(json) {\n    var escaped = escapeHtml(json);\n    return escaped.replace(\n      /(\"(\\\\u[a-fA-F0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?|[{}\\[\\],:])/g,\n      function (match) {\n        var cls = 'jt-num';\n        if (/^\"/.test(match)) {\n          if (/:$/.test(match)) {\n            cls = 'jt-key';\n          } else {\n            cls = 'jt-str';\n          }\n        } else if (/true|false/.test(match)) {\n          cls = 'jt-bool';\n        } else if (/null/.test(match)) {\n          cls = 'jt-null';\n        } else if (/[{}\\[\\],:]/.test(match)) {\n          cls = 'jt-punc';\n        }\n        return '\u003cspan class=\"' + cls + '\"\u003e' + match + '\u003c/span\u003e';\n      }\n    );\n  }\n\n  /* ── Parse with error location ── */\n  function parseJSON(text) {\n    try {\n      return { ok: true, value: JSON.parse(text) };\n    } catch (e) {\n      var msg = e.message || String(e);\n      // Try to extract position info\n      var posMatch = msg.match(/position (\\d+)/i);\n      var lineNum = null;\n      if (posMatch) {\n        var pos = parseInt(posMatch[1], 10);\n        var before = text.slice(0, pos);\n        lineNum = before.split('\\n').length;\n      }\n      return {\n        ok: false,\n        error: msg,\n        line: lineNum\n      };\n    }\n  }\n\n  /* ── Actions ── */\n  window.jsonFormat = function () {\n    var text = getInput().trim();\n    if (!text) { setOutput('\u003cspan style=\"color:#45475a\"\u003e// Nothing to format\u003c/span\u003e', ''); setError(''); return; }\n    var result = parseJSON(text);\n    if (!result.ok) {\n      setInputState('error');\n      setOutput('\u003cspan style=\"color:#45475a\"\u003e// Fix errors first\u003c/span\u003e', 'error');\n      setError('Parse error' + (result.line ? ' (line ' + result.line + ')' : '') + ': ' + result.error);\n      setOutputStats('', 'error');\n      return;\n    }\n    setInputState('');\n    setError('');\n    var indent = getIndent();\n    var formatted = JSON.stringify(result.value, null, indent);\n    setOutput(syntaxHighlight(formatted), 'valid');\n    setOutputStats(formatted, 'valid');\n    if (treeVisible) renderTree(result.value);\n  };\n\n  window.jsonMinify = function () {\n    var text = getInput().trim();\n    if (!text) { setOutput('\u003cspan style=\"color:#45475a\"\u003e// Nothing to minify\u003c/span\u003e', ''); setError(''); return; }\n    var result = parseJSON(text);\n    if (!result.ok) {\n      setInputState('error');\n      setOutput('\u003cspan style=\"color:#45475a\"\u003e// Fix errors first\u003c/span\u003e', 'error');\n      setError('Parse error' + (result.line ? ' (line ' + result.line + ')' : '') + ': ' + result.error);\n      setOutputStats('', 'error');\n      return;\n    }\n    setInputState('');\n    setError('');\n    var minified = JSON.stringify(result.value);\n    setOutput('\u003cspan class=\"jt-punc\"\u003e' + escapeHtml(minified) + '\u003c/span\u003e', 'valid');\n    setOutputStats(minified, 'valid');\n  };\n\n  window.jsonValidate = function () {\n    var text = getInput().trim();\n    if (!text) {\n      setOutput('\u003cspan style=\"color:#45475a\"\u003e// Enter JSON to validate\u003c/span\u003e', '');\n      setError('');\n      setOutputStats('', '');\n      return;\n    }\n    var result = parseJSON(text);\n    if (result.ok) {\n      setInputState('');\n      setError('');\n      setOutput('\u003cspan class=\"status-ok\" style=\"font-size:15px;font-weight:700;\"\u003eValid JSON\u003c/span\u003e\\n\\n\u003cspan style=\"color:#6c7086\"\u003e// Your JSON is well-formed and valid.\u003c/span\u003e', 'valid');\n      setOutputStats(text, 'valid');\n    } else {\n      setInputState('error');\n      var errHtml = '\u003cspan class=\"status-err\" style=\"font-size:15px;font-weight:700;\"\u003eInvalid JSON\u003c/span\u003e';\n      setOutput(errHtml, 'error');\n      setError('Parse error' + (result.line ? ' at line ' + result.line : '') + ': ' + result.error);\n      setOutputStats('', 'error');\n    }\n  };\n\n  window.jsonClear = function () {\n    document.getElementById('json-input').value = '';\n    setOutput('\u003cspan style=\"color:#45475a\"\u003e// Formatted output will appear here\u003c/span\u003e', '');\n    setError('');\n    setInputState('');\n    updateStats('');\n    setOutputStats('', '');\n    hideTree();\n    document.getElementById('json-tree-view').innerHTML = '';\n  };\n\n  window.jsonSample = function () {\n    var sample = {\n      \"name\": \"JSON Formatter\",\n      \"version\": \"1.0.0\",\n      \"description\": \"A free online JSON tool\",\n      \"features\": [\"format\", \"minify\", \"validate\", \"syntax highlighting\", \"tree view\"],\n      \"author\": {\n        \"name\": \"Developer\",\n        \"email\": \"dev@example.com\",\n        \"active\": true\n      },\n      \"stats\": {\n        \"users\": 42000,\n        \"rating\": 4.9,\n        \"premium\": false,\n        \"deprecated\": null\n      },\n      \"tags\": [\"json\", \"developer\", \"tool\"]\n    };\n    document.getElementById('json-input').value = JSON.stringify(sample, null, 2);\n    jsonOnInput();\n    jsonFormat();\n  };\n\n  window.jsonCopy = function () {\n    var el = document.getElementById('json-output');\n    var text = el.innerText || el.textContent;\n    if (!text || text.trim() === '// Formatted output will appear here') return;\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(text).then(showToast);\n    } else {\n      var ta = document.createElement('textarea');\n      ta.value = text;\n      ta.style.position = 'fixed';\n      ta.style.opacity = '0';\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      showToast();\n    }\n  };\n\n  function showToast() {\n    var t = document.getElementById('copy-toast');\n    t.classList.add('show');\n    setTimeout(function () { t.classList.remove('show'); }, 1800);\n  }\n\n  window.jsonOnInput = function () {\n    var text = getInput();\n    updateStats(text);\n    setInputState('');\n    setError('');\n  };\n\n  /* ── Tree View ── */\n  window.jsonToggleTree = function () {\n    treeVisible = !treeVisible;\n    var btn = document.getElementById('treeToggleBtn');\n    var treeEl = document.getElementById('json-tree-view');\n    var outputEl = document.getElementById('json-output');\n\n    if (treeVisible) {\n      btn.classList.add('active');\n      btn.textContent = 'Hide Tree';\n      treeEl.classList.add('visible');\n      outputEl.style.display = 'none';\n      // Try to render tree from current input\n      var text = getInput().trim();\n      if (text) {\n        var result = parseJSON(text);\n        if (result.ok) {\n          renderTree(result.value);\n        } else {\n          treeEl.innerHTML = '\u003cspan style=\"color:#f38ba8\"\u003e// Render tree after fixing JSON errors.\u003c/span\u003e';\n        }\n      } else {\n        treeEl.innerHTML = '\u003cspan style=\"color:#45475a\"\u003e// Tree view will appear here after formatting.\u003c/span\u003e';\n      }\n    } else {\n      hideTree();\n    }\n  };\n\n  function hideTree() {\n    treeVisible = false;\n    var btn = document.getElementById('treeToggleBtn');\n    var treeEl = document.getElementById('json-tree-view');\n    var outputEl = document.getElementById('json-output');\n    btn.classList.remove('active');\n    btn.textContent = 'Tree View';\n    treeEl.classList.remove('visible');\n    outputEl.style.display = '';\n  }\n\n  function renderTree(data) {\n    var treeEl = document.getElementById('json-tree-view');\n    treeEl.innerHTML = buildTreeHTML(data, 0);\n    // Attach toggle events\n    treeEl.querySelectorAll('.tree-toggle').forEach(function (tog) {\n      tog.addEventListener('click', function () {\n        var node = tog.parentElement;\n        node.classList.toggle('tree-collapsed');\n        tog.textContent = node.classList.contains('tree-collapsed') ? '[+]' : '[-]';\n      });\n    });\n  }\n\n  function buildTreeHTML(val, depth) {\n    if (val === null) return '\u003cspan class=\"tree-null\"\u003enull\u003c/span\u003e';\n    if (typeof val === 'boolean') return '\u003cspan class=\"tree-bool\"\u003e' + val + '\u003c/span\u003e';\n    if (typeof val === 'number') return '\u003cspan class=\"tree-num\"\u003e' + val + '\u003c/span\u003e';\n    if (typeof val === 'string') return '\u003cspan class=\"tree-str\"\u003e\"' + escapeHtml(val) + '\"\u003c/span\u003e';\n\n    if (Array.isArray(val)) {\n      if (val.length === 0) return '\u003cspan class=\"jt-punc\"\u003e[]\u003c/span\u003e';\n      var html = '\u003cspan class=\"tree-node\"\u003e';\n      html += '\u003cspan class=\"tree-toggle\"\u003e[-]\u003c/span\u003e \u003cspan class=\"jt-punc\"\u003e[\u003c/span\u003e';\n      html += '\u003cspan class=\"tree-meta\"\u003e' + val.length + ' item' + (val.length !== 1 ? 's' : '') + '\u003c/span\u003e';\n      html += '\u003cdiv class=\"tree-children\"\u003e';\n      val.forEach(function (item, i) {\n        html += '\u003cdiv\u003e';\n        html += '\u003cspan class=\"tree-num\"\u003e[' + i + ']\u003c/span\u003e: ';\n        html += buildTreeHTML(item, depth + 1);\n        html += (i \u003c val.length - 1 ? '\u003cspan class=\"jt-punc\"\u003e,\u003c/span\u003e' : '');\n        html += '\u003c/div\u003e';\n      });\n      html += '\u003c/div\u003e';\n      html += '\u003cspan class=\"jt-punc\"\u003e]\u003c/span\u003e';\n      html += '\u003c/span\u003e';\n      return html;\n    }\n\n    if (typeof val === 'object') {\n      var keys = Object.keys(val);\n      if (keys.length === 0) return '\u003cspan class=\"jt-punc\"\u003e{}\u003c/span\u003e';\n      var html = '\u003cspan class=\"tree-node\"\u003e';\n      html += '\u003cspan class=\"tree-toggle\"\u003e[-]\u003c/span\u003e \u003cspan class=\"jt-punc\"\u003e{\u003c/span\u003e';\n      html += '\u003cspan class=\"tree-meta\"\u003e' + keys.length + ' key' + (keys.length !== 1 ? 's' : '') + '\u003c/span\u003e';\n      html += '\u003cdiv class=\"tree-children\"\u003e';\n      keys.forEach(function (k, i) {\n        html += '\u003cdiv\u003e';\n        html += '\u003cspan class=\"tree-key\"\u003e\"' + escapeHtml(k) + '\"\u003c/span\u003e: ';\n        html += buildTreeHTML(val[k], depth + 1);\n        html += (i \u003c keys.length - 1 ? '\u003cspan class=\"jt-punc\"\u003e,\u003c/span\u003e' : '');\n        html += '\u003c/div\u003e';\n      });\n      html += '\u003c/div\u003e';\n      html += '\u003cspan class=\"jt-punc\"\u003e}\u003c/span\u003e';\n      html += '\u003c/span\u003e';\n      return html;\n    }\n\n    return escapeHtml(String(val));\n  }\n\n  // Keyboard shortcut: Ctrl+Enter to format\n  document.getElementById('json-input').addEventListener('keydown', function (e) {\n    if ((e.ctrlKey || e.metaKey) \u0026\u0026 e.key === 'Enter') {\n      e.preventDefault();\n      jsonFormat();\n    }\n  });\n\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"what-is-json\"\u003eWhat is JSON?\u003c/h2\u003e\n\u003cp\u003eJSON (JavaScript Object Notation) is a lightweight, human-readable data interchange format originally derived from JavaScript object syntax. It uses a simple structure of key-value pairs, arrays, strings, numbers, booleans, and null values, making it straightforward to read and write for humans while remaining easy to parse and generate for machines. Today JSON has become the de facto standard for data exchange in web APIs, configuration files, and inter-service communication across virtually every programming language and platform.\u003c/p\u003e","title":"JSON Formatter \u0026 Validator - Free Online JSON Tool"},{"content":"Paste any JSON and instantly explore its structure with an interactive tree view. Click any node to reveal its exact JSONPath expression in both dot notation and bracket notation — no plugins, no sign-up, runs entirely in your browser.\nSample Presets User Object E-Commerce Order GitHub API Deep Nested Paste JSON \u0026#128269; Explore Expand All Collapse All Clear Nodes: 0 Keys: 0 Arrays: 0 Max Depth: 0 Matches: 0 Dot Copy Bracket Copy Copy Value Paste JSON above and click Explore to begin. Copy Dot Path Copy Bracket Path Copy Value How to Use Paste JSON into the text area above (or pick a sample preset). Click Explore (or press Ctrl+Enter / Cmd+Enter). Click any node in the tree to see its JSONPath in dot and bracket notation. Use the Copy buttons to grab the path or value. Search to highlight nodes whose key or value matches your query. What Is JSONPath? JSONPath is a query language for JSON, analogous to XPath for XML. It lets you pinpoint values inside complex JSON documents using path expressions like $.user.address.city or $[\u0026quot;items\u0026quot;][0][\u0026quot;price\u0026quot;]. JSONPath expressions are used in:\nREST API testing (Postman, Insomnia assertions) AWS Step Functions (Input/OutputPath, ResultPath) Kubernetes manifests and JSON patches Log analysis (Elasticsearch, Grafana) Data transformation pipelines Path Notation Guide Notation Example Notes Dot (simple) $.user.name Clean, readable; works for simple keys Dot (index) $.items[0].price Array index in brackets Bracket $[\u0026quot;user\u0026quot;][\u0026quot;name\u0026quot;] Always safe; works with any key Bracket (index) $[\u0026quot;items\u0026quot;][0][\u0026quot;price\u0026quot;] Fully explicit Related Tools JSON Formatter \u0026amp; Beautifier — Pretty-print and validate JSON JSON Schema Generator — Auto-generate a JSON Schema from sample data JSON to CSV Converter — Flatten JSON arrays into spreadsheet format ","permalink":"https://productivity-works.com/tools/json-path-finder/","summary":"\u003cp\u003ePaste any JSON and instantly explore its structure with an interactive tree view. Click any node to reveal its exact \u003cstrong\u003eJSONPath expression\u003c/strong\u003e in both dot notation and bracket notation — no plugins, no sign-up, runs entirely in your browser.\u003c/p\u003e\n\u003cdiv id=\"jpf-app\"\u003e\n\u003cstyle\u003e\n  #jpf-app *,\n  #jpf-app *::before,\n  #jpf-app *::after {\n    box-sizing: border-box;\n    margin: 0;\n    padding: 0;\n  }\n  #jpf-app {\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n    font-size: 15px;\n    color: #1a1a2e;\n    background: #f7f9fc;\n    border-radius: 12px;\n    padding: 20px;\n    margin: 24px 0;\n    border: 1px solid #dde3ef;\n  }\n  #jpf-app .jpf-toolbar {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 8px;\n    margin-bottom: 14px;\n    align-items: center;\n  }\n  #jpf-app .jpf-search-wrap {\n    flex: 1;\n    min-width: 180px;\n    position: relative;\n  }\n  #jpf-app .jpf-search {\n    width: 100%;\n    padding: 8px 12px 8px 34px;\n    border: 1px solid #c8d0e0;\n    border-radius: 7px;\n    font-size: 14px;\n    background: #fff;\n    outline: none;\n    transition: border-color .2s;\n  }\n  #jpf-app .jpf-search:focus { border-color: #4f8ef7; }\n  #jpf-app .jpf-search-icon {\n    position: absolute;\n    left: 10px;\n    top: 50%;\n    transform: translateY(-50%);\n    color: #8896b3;\n    font-size: 14px;\n    pointer-events: none;\n  }\n  #jpf-app .jpf-btn {\n    padding: 8px 14px;\n    border: 1px solid #c8d0e0;\n    border-radius: 7px;\n    background: #fff;\n    font-size: 13px;\n    cursor: pointer;\n    color: #3a4a6b;\n    transition: background .15s, border-color .15s;\n    white-space: nowrap;\n  }\n  #jpf-app .jpf-btn:hover { background: #eef2fb; border-color: #4f8ef7; }\n  #jpf-app .jpf-btn.jpf-btn-primary {\n    background: #4f8ef7;\n    color: #fff;\n    border-color: #4f8ef7;\n  }\n  #jpf-app .jpf-btn.jpf-btn-primary:hover { background: #3a7de0; }\n  #jpf-app .jpf-btn.jpf-btn-danger {\n    border-color: #e05a5a;\n    color: #c0392b;\n  }\n  #jpf-app .jpf-btn.jpf-btn-danger:hover { background: #fdecea; }\n  #jpf-app .jpf-presets {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 6px;\n    margin-bottom: 12px;\n  }\n  #jpf-app .jpf-preset-btn {\n    padding: 5px 11px;\n    border: 1px solid #c8d0e0;\n    border-radius: 20px;\n    background: #fff;\n    font-size: 12px;\n    cursor: pointer;\n    color: #4f6394;\n    transition: background .15s, border-color .15s;\n  }\n  #jpf-app .jpf-preset-btn:hover { background: #eef2fb; border-color: #4f8ef7; }\n  #jpf-app .jpf-label {\n    font-size: 11px;\n    font-weight: 600;\n    text-transform: uppercase;\n    letter-spacing: .05em;\n    color: #7a88a8;\n    margin-bottom: 5px;\n  }\n  #jpf-app .jpf-input-area {\n    width: 100%;\n    min-height: 130px;\n    padding: 12px;\n    border: 1px solid #c8d0e0;\n    border-radius: 8px;\n    font-family: \"SF Mono\", \"Fira Code\", \"Cascadia Code\", monospace;\n    font-size: 13px;\n    background: #fff;\n    resize: vertical;\n    outline: none;\n    transition: border-color .2s;\n    line-height: 1.55;\n    color: #1a1a2e;\n  }\n  #jpf-app .jpf-input-area:focus { border-color: #4f8ef7; }\n  #jpf-app .jpf-input-area.jpf-error { border-color: #e05a5a; }\n  #jpf-app .jpf-error-msg {\n    display: none;\n    color: #c0392b;\n    font-size: 13px;\n    margin-top: 6px;\n    padding: 7px 10px;\n    background: #fdecea;\n    border-radius: 6px;\n    border-left: 3px solid #e05a5a;\n  }\n  #jpf-app .jpf-stats-bar {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 14px;\n    padding: 9px 14px;\n    background: #eef2fb;\n    border-radius: 8px;\n    margin: 12px 0;\n    font-size: 13px;\n    color: #4a5a80;\n  }\n  #jpf-app .jpf-stat { display: flex; align-items: center; gap: 5px; }\n  #jpf-app .jpf-stat-val { font-weight: 700; color: #2d3f6e; }\n  #jpf-app .jpf-path-box {\n    background: #fff;\n    border: 1px solid #c8d0e0;\n    border-radius: 8px;\n    padding: 12px 14px;\n    margin-bottom: 12px;\n    min-height: 56px;\n    display: none;\n  }\n  #jpf-app .jpf-path-box.jpf-active { display: block; }\n  #jpf-app .jpf-path-row {\n    display: flex;\n    align-items: flex-start;\n    gap: 10px;\n    margin-bottom: 6px;\n    flex-wrap: wrap;\n  }\n  #jpf-app .jpf-path-row:last-child { margin-bottom: 0; }\n  #jpf-app .jpf-path-label {\n    font-size: 11px;\n    font-weight: 700;\n    text-transform: uppercase;\n    color: #7a88a8;\n    min-width: 72px;\n    padding-top: 2px;\n    letter-spacing: .04em;\n  }\n  #jpf-app .jpf-path-val {\n    flex: 1;\n    font-family: \"SF Mono\", \"Fira Code\", monospace;\n    font-size: 13px;\n    color: #1a5fc8;\n    word-break: break-all;\n    background: #f0f5ff;\n    padding: 3px 8px;\n    border-radius: 5px;\n    min-width: 0;\n  }\n  #jpf-app .jpf-copy-inline {\n    flex-shrink: 0;\n    padding: 3px 9px;\n    border: 1px solid #c8d0e0;\n    border-radius: 5px;\n    background: #fff;\n    font-size: 12px;\n    cursor: pointer;\n    color: #4f6394;\n    transition: background .15s;\n  }\n  #jpf-app .jpf-copy-inline:hover { background: #eef2fb; }\n  #jpf-app .jpf-copy-inline.jpf-copied {\n    background: #e6f9ef;\n    border-color: #2ecc71;\n    color: #27ae60;\n  }\n  #jpf-app .jpf-value-preview {\n    margin-top: 8px;\n    padding: 8px 12px;\n    background: #fafbff;\n    border: 1px solid #e0e7f5;\n    border-radius: 6px;\n    font-family: \"SF Mono\", \"Fira Code\", monospace;\n    font-size: 13px;\n    word-break: break-all;\n    color: #2d3f6e;\n    display: flex;\n    gap: 10px;\n    align-items: flex-start;\n    flex-wrap: wrap;\n  }\n  #jpf-app .jpf-value-text { flex: 1; min-width: 0; }\n  #jpf-app .jpf-type-badge {\n    display: inline-flex;\n    align-items: center;\n    padding: 2px 8px;\n    border-radius: 4px;\n    font-size: 11px;\n    font-weight: 700;\n    letter-spacing: .03em;\n    text-transform: uppercase;\n    flex-shrink: 0;\n  }\n  #jpf-app .jpf-type-string  { background: #e8f5e9; color: #2e7d32; }\n  #jpf-app .jpf-type-number  { background: #e3f2fd; color: #1565c0; }\n  #jpf-app .jpf-type-boolean { background: #fff3e0; color: #e65100; }\n  #jpf-app .jpf-type-null    { background: #f3e5f5; color: #6a1b9a; }\n  #jpf-app .jpf-type-object  { background: #e8eaf6; color: #283593; }\n  #jpf-app .jpf-type-array   { background: #fce4ec; color: #880e4f; }\n  #jpf-app .jpf-main-layout {\n    display: grid;\n    grid-template-columns: 1fr;\n    gap: 12px;\n  }\n  #jpf-app .jpf-tree-wrap {\n    background: #fff;\n    border: 1px solid #dde3ef;\n    border-radius: 8px;\n    padding: 12px;\n    min-height: 200px;\n    overflow-x: auto;\n  }\n  #jpf-app .jpf-tree-empty {\n    color: #9aa5be;\n    text-align: center;\n    padding: 40px 20px;\n    font-size: 14px;\n  }\n  #jpf-app .jpf-node {\n    user-select: none;\n    line-height: 1.6;\n  }\n  #jpf-app .jpf-node-row {\n    display: flex;\n    align-items: baseline;\n    gap: 4px;\n    padding: 2px 4px;\n    border-radius: 5px;\n    cursor: pointer;\n    transition: background .1s;\n    flex-wrap: nowrap;\n  }\n  #jpf-app .jpf-node-row:hover { background: #eef2fb; }\n  #jpf-app .jpf-node-row.jpf-selected { background: #dce8ff; }\n  #jpf-app .jpf-node-row.jpf-search-match { background: #fff8dc; }\n  #jpf-app .jpf-node-row.jpf-selected.jpf-search-match { background: #c8dfff; }\n  #jpf-app .jpf-toggle {\n    display: inline-block;\n    width: 16px;\n    font-size: 11px;\n    color: #7a88a8;\n    text-align: center;\n    cursor: pointer;\n    flex-shrink: 0;\n    transition: transform .15s;\n  }\n  #jpf-app .jpf-toggle.jpf-open { transform: rotate(0deg); }\n  #jpf-app .jpf-key {\n    color: #8b2252;\n    font-family: \"SF Mono\", \"Fira Code\", monospace;\n    font-size: 13px;\n    font-weight: 600;\n    flex-shrink: 0;\n  }\n  #jpf-app .jpf-colon { color: #888; font-size: 13px; flex-shrink: 0; }\n  #jpf-app .jpf-val-inline {\n    font-family: \"SF Mono\", \"Fira Code\", monospace;\n    font-size: 13px;\n    min-width: 0;\n    word-break: break-word;\n  }\n  #jpf-app .jpf-val-string  { color: #1e7c1e; }\n  #jpf-app .jpf-val-number  { color: #1565c0; }\n  #jpf-app .jpf-val-boolean { color: #e65100; }\n  #jpf-app .jpf-val-null    { color: #6a1b9a; font-style: italic; }\n  #jpf-app .jpf-val-collapsed { color: #7a88a8; font-size: 12px; }\n  #jpf-app .jpf-children {\n    margin-left: 20px;\n    border-left: 1px dashed #d0d8ec;\n    padding-left: 8px;\n  }\n  #jpf-app .jpf-node-count {\n    font-size: 11px;\n    color: #9aa5be;\n    margin-left: 4px;\n    flex-shrink: 0;\n  }\n  #jpf-app .jpf-actions-row {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 6px;\n    margin-top: 14px;\n  }\n  #jpf-app .jpf-toast {\n    position: fixed;\n    bottom: 24px;\n    right: 24px;\n    background: #2d3f6e;\n    color: #fff;\n    padding: 10px 18px;\n    border-radius: 8px;\n    font-size: 14px;\n    z-index: 9999;\n    opacity: 0;\n    pointer-events: none;\n    transition: opacity .3s;\n    box-shadow: 0 4px 16px rgba(0,0,0,.18);\n  }\n  #jpf-app .jpf-toast.jpf-show { opacity: 1; }\n  @media (max-width: 600px) {\n    #jpf-app { padding: 14px; }\n    #jpf-app .jpf-path-label { min-width: 56px; font-size: 10px; }\n    #jpf-app .jpf-path-val { font-size: 12px; }\n    #jpf-app .jpf-stats-bar { gap: 8px; font-size: 12px; }\n    #jpf-app .jpf-btn { padding: 7px 10px; font-size: 12px; }\n  }\n\u003c/style\u003e\n\u003cdiv class=\"jpf-label\"\u003eSample Presets\u003c/div\u003e\n\u003cdiv class=\"jpf-presets\"\u003e\n  \u003cbutton class=\"jpf-preset-btn\" data-preset=\"user\"\u003eUser Object\u003c/button\u003e\n  \u003cbutton class=\"jpf-preset-btn\" data-preset=\"ecommerce\"\u003eE-Commerce Order\u003c/button\u003e\n  \u003cbutton class=\"jpf-preset-btn\" data-preset=\"github\"\u003eGitHub API\u003c/button\u003e\n  \u003cbutton class=\"jpf-preset-btn\" data-preset=\"nested\"\u003eDeep Nested\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"jpf-label\"\u003ePaste JSON\u003c/div\u003e\n\u003ctextarea class=\"jpf-input-area\" id=\"jpfInput\" placeholder='Paste your JSON here, e.g. {\"name\": \"Alice\", \"age\": 30}'\u003e\u003c/textarea\u003e\n\u003cdiv class=\"jpf-error-msg\" id=\"jpfError\"\u003e\u003c/div\u003e\n\u003cdiv class=\"jpf-toolbar\" style=\"margin-top:10px;\"\u003e\n  \u003cdiv class=\"jpf-search-wrap\"\u003e\n    \u003cspan class=\"jpf-search-icon\"\u003e\u0026#128269;\u003c/span\u003e\n    \u003cinput type=\"text\" class=\"jpf-search\" id=\"jpfSearch\" placeholder=\"Search keys or values...\" /\u003e\n  \u003c/div\u003e\n  \u003cbutton class=\"jpf-btn jpf-btn-primary\" id=\"jpfParse\"\u003eExplore\u003c/button\u003e\n  \u003cbutton class=\"jpf-btn\" id=\"jpfExpandAll\"\u003eExpand All\u003c/button\u003e\n  \u003cbutton class=\"jpf-btn\" id=\"jpfCollapseAll\"\u003eCollapse All\u003c/button\u003e\n  \u003cbutton class=\"jpf-btn jpf-btn-danger\" id=\"jpfClear\"\u003eClear\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"jpf-stats-bar\" id=\"jpfStats\" style=\"display:none;\"\u003e\n  \u003cdiv class=\"jpf-stat\"\u003eNodes: \u003cspan class=\"jpf-stat-val\" id=\"jpfStatNodes\"\u003e0\u003c/span\u003e\u003c/div\u003e\n  \u003cdiv class=\"jpf-stat\"\u003eKeys: \u003cspan class=\"jpf-stat-val\" id=\"jpfStatKeys\"\u003e0\u003c/span\u003e\u003c/div\u003e\n  \u003cdiv class=\"jpf-stat\"\u003eArrays: \u003cspan class=\"jpf-stat-val\" id=\"jpfStatArrays\"\u003e0\u003c/span\u003e\u003c/div\u003e\n  \u003cdiv class=\"jpf-stat\"\u003eMax Depth: \u003cspan class=\"jpf-stat-val\" id=\"jpfStatDepth\"\u003e0\u003c/span\u003e\u003c/div\u003e\n  \u003cdiv class=\"jpf-stat\"\u003eMatches: \u003cspan class=\"jpf-stat-val\" id=\"jpfStatMatches\" style=\"display:none\"\u003e0\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"jpf-path-box\" id=\"jpfPathBox\"\u003e\n  \u003cdiv class=\"jpf-path-row\"\u003e\n    \u003cspan class=\"jpf-path-label\"\u003eDot\u003c/span\u003e\n    \u003cspan class=\"jpf-path-val\" id=\"jpfDotPath\"\u003e\u003c/span\u003e\n    \u003cbutton class=\"jpf-copy-inline\" data-target=\"dot\"\u003eCopy\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"jpf-path-row\"\u003e\n    \u003cspan class=\"jpf-path-label\"\u003eBracket\u003c/span\u003e\n    \u003cspan class=\"jpf-path-val\" id=\"jpfBracketPath\"\u003e\u003c/span\u003e\n    \u003cbutton class=\"jpf-copy-inline\" data-target=\"bracket\"\u003eCopy\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"jpf-value-preview\" id=\"jpfValuePreview\"\u003e\n    \u003cspan class=\"jpf-value-text\" id=\"jpfValueText\"\u003e\u003c/span\u003e\n    \u003cbutton class=\"jpf-copy-inline\" data-target=\"value\"\u003eCopy Value\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"jpf-tree-wrap\"\u003e\n  \u003cdiv class=\"jpf-tree-empty\" id=\"jpfEmpty\"\u003ePaste JSON above and click \u003cstrong\u003eExplore\u003c/strong\u003e to begin.\u003c/div\u003e\n  \u003cdiv id=\"jpfTree\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"jpf-actions-row\"\u003e\n  \u003cbutton class=\"jpf-btn\" id=\"jpfCopyDotAll\"\u003eCopy Dot Path\u003c/button\u003e\n  \u003cbutton class=\"jpf-btn\" id=\"jpfCopyBracketAll\"\u003eCopy Bracket Path\u003c/button\u003e\n  \u003cbutton class=\"jpf-btn\" id=\"jpfCopyVal\"\u003eCopy Value\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"jpf-toast\" id=\"jpfToast\"\u003e\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var el = function(id) { return document.getElementById(id); };\n\n  var presets = {\n    user: {\n      \"id\": 1,\n      \"name\": \"Alice Johnson\",\n      \"email\": \"alice@example.com\",\n      \"age\": 30,\n      \"active\": true,\n      \"address\": {\n        \"street\": \"123 Main St\",\n        \"city\": \"Springfield\",\n        \"zip\": \"62701\",\n        \"country\": \"US\"\n      },\n      \"roles\": [\"admin\", \"editor\"],\n      \"preferences\": {\n        \"theme\": \"dark\",\n        \"notifications\": { \"email\": true, \"sms\": false }\n      },\n      \"createdAt\": \"2024-01-15T09:30:00Z\"\n    },\n    ecommerce: {\n      \"orderId\": \"ORD-2024-88821\",\n      \"status\": \"shipped\",\n      \"customer\": { \"id\": 42, \"name\": \"Bob Smith\", \"tier\": \"gold\" },\n      \"items\": [\n        { \"sku\": \"WIDGET-A\", \"qty\": 2, \"price\": 19.99 },\n        { \"sku\": \"GADGET-B\", \"qty\": 1, \"price\": 49.99 }\n      ],\n      \"totals\": { \"subtotal\": 89.97, \"tax\": 7.20, \"shipping\": 5.00, \"total\": 102.17 },\n      \"shippedAt\": \"2024-05-10T14:22:00Z\",\n      \"trackingNumber\": \"1Z999AA10123456784\"\n    },\n    github: {\n      \"id\": 583231,\n      \"login\": \"octocat\",\n      \"name\": \"The Octocat\",\n      \"public_repos\": 8,\n      \"followers\": 14987,\n      \"following\": 9,\n      \"created_at\": \"2011-01-25T18:44:36Z\",\n      \"plan\": { \"name\": \"pro\", \"space\": 976562499, \"private_repos\": 9999 },\n      \"repos\": [\n        { \"name\": \"Hello-World\", \"stars\": 2341, \"language\": \"Ruby\" },\n        { \"name\": \"linguist\", \"stars\": 11239, \"language\": \"Ruby\" }\n      ]\n    },\n    nested: {\n      \"level1\": {\n        \"level2\": {\n          \"level3\": {\n            \"level4\": {\n              \"level5\": {\n                \"value\": \"deep value\",\n                \"flags\": [true, false, null, 42]\n              }\n            }\n          }\n        },\n        \"sibling\": [1, 2, { \"x\": \"nested in array\" }]\n      },\n      \"meta\": { \"version\": \"1.0\", \"tags\": [\"alpha\", \"beta\"] }\n    }\n  };\n\n  var state = {\n    parsed: null,\n    selectedPath: null,\n    selectedDot: '',\n    selectedBracket: '',\n    selectedValue: null,\n    searchTerm: '',\n    nodeCount: 0,\n    keyCount: 0,\n    arrayCount: 0,\n    maxDepth: 0\n  };\n\n  function showToast(msg) {\n    var t = el('jpfToast');\n    t.textContent = msg;\n    t.classList.add('jpf-show');\n    setTimeout(function() { t.classList.remove('jpf-show'); }, 1800);\n  }\n\n  function copyText(text) {\n    if (navigator.clipboard) {\n      navigator.clipboard.writeText(text).catch(function() { fallbackCopy(text); });\n    } else { fallbackCopy(text); }\n  }\n\n  function fallbackCopy(text) {\n    var ta = document.createElement('textarea');\n    ta.value = text;\n    ta.style.position = 'fixed';\n    ta.style.opacity = '0';\n    document.body.appendChild(ta);\n    ta.select();\n    document.execCommand('copy');\n    document.body.removeChild(ta);\n  }\n\n  function typeOf(val) {\n    if (val === null) return 'null';\n    if (Array.isArray(val)) return 'array';\n    return typeof val;\n  }\n\n  function typeBadge(t) {\n    return '\u003cspan class=\"jpf-type-badge jpf-type-' + t + '\"\u003e' + t + '\u003c/span\u003e';\n  }\n\n  function valueDisplay(val, t) {\n    if (t === 'string') return '\"' + val + '\"';\n    if (t === 'null') return 'null';\n    if (t === 'boolean') return String(val);\n    if (t === 'number') return String(val);\n    if (t === 'array') return 'Array[' + val.length + ']';\n    if (t === 'object') {\n      var keys = Object.keys(val);\n      return 'Object{' + keys.length + '}';\n    }\n    return String(val);\n  }\n\n  function dotPath(segments) {\n    var out = '$';\n    for (var i = 0; i \u003c segments.length; i++) {\n      var s = segments[i];\n      if (typeof s === 'number') {\n        out += '[' + s + ']';\n      } else {\n        out += /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(s) ? '.' + s : '[\"' + s + '\"]';\n      }\n    }\n    return out;\n  }\n\n  function bracketPath(segments) {\n    var out = '$';\n    for (var i = 0; i \u003c segments.length; i++) {\n      var s = segments[i];\n      if (typeof s === 'number') {\n        out += '[' + s + ']';\n      } else {\n        out += '[\"' + s + '\"]';\n      }\n    }\n    return out;\n  }\n\n  function gatherStats(obj, depth) {\n    depth = depth || 0;\n    if (depth \u003e state.maxDepth) state.maxDepth = depth;\n    state.nodeCount++;\n    var t = typeOf(obj);\n    if (t === 'object' \u0026\u0026 obj !== null) {\n      state.keyCount += Object.keys(obj).length;\n      Object.keys(obj).forEach(function(k) { gatherStats(obj[k], depth + 1); });\n    } else if (t === 'array') {\n      state.arrayCount++;\n      obj.forEach(function(v) { gatherStats(v, depth + 1); });\n    }\n  }\n\n  function matchesSearch(key, val, t) {\n    if (!state.searchTerm) return false;\n    var q = state.searchTerm.toLowerCase();\n    if (String(key).toLowerCase().indexOf(q) !== -1) return true;\n    if (t !== 'object' \u0026\u0026 t !== 'array') {\n      if (String(val).toLowerCase().indexOf(q) !== -1) return true;\n    }\n    return false;\n  }\n\n  function buildNode(key, val, segments, isRoot) {\n    var t = typeOf(val);\n    var isExpandable = t === 'object' || t === 'array';\n    var segs = segments.slice();\n    if (!isRoot) segs.push(key);\n\n    var node = document.createElement('div');\n    node.className = 'jpf-node';\n    node.setAttribute('data-type', t);\n\n    var row = document.createElement('div');\n    row.className = 'jpf-node-row';\n\n    var hasMatch = matchesSearch(key, val, t);\n    if (hasMatch) row.classList.add('jpf-search-match');\n\n    var toggle = document.createElement('span');\n    toggle.className = 'jpf-toggle';\n    if (isExpandable) {\n      toggle.textContent = '▶';\n      toggle.classList.add('jpf-open');\n    } else {\n      toggle.innerHTML = '\u0026nbsp;';\n    }\n\n    var keyEl = document.createElement('span');\n    keyEl.className = 'jpf-key';\n    if (isRoot) {\n      keyEl.textContent = t === 'array' ? 'root []' : 'root {}';\n    } else {\n      keyEl.textContent = typeof key === 'number' ? '[' + key + ']' : key;\n    }\n\n    var colon = document.createElement('span');\n    colon.className = 'jpf-colon';\n    colon.textContent = ':';\n\n    var valEl = document.createElement('span');\n    valEl.className = 'jpf-val-inline';\n\n    var children;\n\n    if (isExpandable) {\n      var count = t === 'array' ? val.length : Object.keys(val).length;\n      valEl.className += ' jpf-val-collapsed';\n      valEl.textContent = t === 'array' ? '[' + count + ' items]' : '{' + count + ' keys}';\n\n      var countBadge = document.createElement('span');\n      countBadge.className = 'jpf-node-count';\n      countBadge.textContent = '';\n\n      children = document.createElement('div');\n      children.className = 'jpf-children';\n\n      if (t === 'object') {\n        Object.keys(val).forEach(function(k) {\n          children.appendChild(buildNode(k, val[k], segs, false));\n        });\n      } else {\n        val.forEach(function(v, i) {\n          children.appendChild(buildNode(i, v, segs, false));\n        });\n      }\n\n      row.appendChild(toggle);\n      if (!isRoot) {\n        row.appendChild(keyEl);\n        row.appendChild(colon);\n      } else {\n        row.appendChild(keyEl);\n      }\n      row.appendChild(valEl);\n      row.appendChild(countBadge);\n\n      var expanded = true;\n\n      toggle.addEventListener('click', function(e) {\n        e.stopPropagation();\n        expanded = !expanded;\n        children.style.display = expanded ? '' : 'none';\n        toggle.style.transform = expanded ? 'rotate(90deg)' : 'rotate(0deg)';\n      });\n      toggle.style.transform = 'rotate(90deg)';\n\n      row.addEventListener('click', function() {\n        selectNode(row, segs, key, val, t, isRoot);\n      });\n\n      node.appendChild(row);\n      node.appendChild(children);\n    } else {\n      valEl.className += ' jpf-val-' + t;\n      valEl.textContent = t === 'string' ? '\"' + val + '\"' : (val === null ? 'null' : String(val));\n\n      row.appendChild(toggle);\n      row.appendChild(keyEl);\n      row.appendChild(colon);\n      row.appendChild(valEl);\n      row.appendChild(document.createTextNode(' '));\n      var badge = document.createElement('span');\n      badge.innerHTML = typeBadge(t);\n      row.appendChild(badge);\n\n      row.addEventListener('click', function() {\n        selectNode(row, segs, key, val, t, isRoot);\n      });\n\n      node.appendChild(row);\n    }\n\n    node._expand = function() {\n      if (isExpandable \u0026\u0026 !expanded) {\n        expanded = true;\n        children.style.display = '';\n        toggle.style.transform = 'rotate(90deg)';\n      }\n      if (isExpandable) {\n        Array.from(children.children).forEach(function(c) {\n          if (c._expand) c._expand();\n        });\n      }\n    };\n    node._collapse = function() {\n      if (isExpandable \u0026\u0026 expanded) {\n        expanded = false;\n        children.style.display = 'none';\n        toggle.style.transform = 'rotate(0deg)';\n      }\n    };\n\n    return node;\n  }\n\n  var lastSelectedRow = null;\n\n  function selectNode(row, segs, key, val, t, isRoot) {\n    if (lastSelectedRow) lastSelectedRow.classList.remove('jpf-selected');\n    row.classList.add('jpf-selected');\n    lastSelectedRow = row;\n\n    var dp = isRoot ? '$' : dotPath(segs);\n    var bp = isRoot ? '$' : bracketPath(segs);\n    state.selectedDot = dp;\n    state.selectedBracket = bp;\n    state.selectedValue = val;\n\n    el('jpfDotPath').textContent = dp;\n    el('jpfBracketPath').textContent = bp;\n\n    var valStr = t === 'object' || t === 'array' ? JSON.stringify(val, null, 2) : String(val);\n    el('jpfValueText').textContent = valStr;\n    el('jpfValuePreview').innerHTML = '';\n    var vt = document.createElement('span');\n    vt.className = 'jpf-value-text';\n    vt.textContent = valStr.length \u003e 200 ? valStr.slice(0, 200) + '...' : valStr;\n    var copyValBtn = document.createElement('button');\n    copyValBtn.className = 'jpf-copy-inline';\n    copyValBtn.setAttribute('data-target', 'value');\n    copyValBtn.textContent = 'Copy Value';\n    copyValBtn.addEventListener('click', function() {\n      copyText(t === 'object' || t === 'array' ? JSON.stringify(val, null, 2) : String(val));\n      showToast('Value copied!');\n      copyValBtn.classList.add('jpf-copied');\n      setTimeout(function() { copyValBtn.classList.remove('jpf-copied'); }, 1500);\n    });\n    var badgeEl = document.createElement('span');\n    badgeEl.innerHTML = typeBadge(t);\n    el('jpfValuePreview').appendChild(vt);\n    el('jpfValuePreview').appendChild(badgeEl);\n    el('jpfValuePreview').appendChild(copyValBtn);\n\n    el('jpfPathBox').classList.add('jpf-active');\n  }\n\n  function parseAndRender() {\n    var raw = el('jpfInput').value.trim();\n    el('jpfInput').classList.remove('jpf-error');\n    el('jpfError').style.display = 'none';\n    el('jpfError').textContent = '';\n    el('jpfTree').innerHTML = '';\n    el('jpfEmpty').style.display = 'none';\n    el('jpfStats').style.display = 'none';\n    el('jpfPathBox').classList.remove('jpf-active');\n    lastSelectedRow = null;\n\n    if (!raw) {\n      el('jpfEmpty').style.display = '';\n      el('jpfEmpty').textContent = 'Nothing to explore. Paste some JSON above.';\n      return;\n    }\n\n    var parsed;\n    try { parsed = JSON.parse(raw); }\n    catch(e) {\n      el('jpfInput').classList.add('jpf-error');\n      el('jpfError').style.display = '';\n      el('jpfError').textContent = 'JSON Parse Error: ' + e.message;\n      el('jpfEmpty').style.display = '';\n      el('jpfEmpty').textContent = 'Fix the JSON error above to see the tree.';\n      return;\n    }\n\n    state.parsed = parsed;\n    state.nodeCount = 0;\n    state.keyCount = 0;\n    state.arrayCount = 0;\n    state.maxDepth = 0;\n    gatherStats(parsed, 0);\n\n    el('jpfStatNodes').textContent = state.nodeCount;\n    el('jpfStatKeys').textContent = state.keyCount;\n    el('jpfStatArrays').textContent = state.arrayCount;\n    el('jpfStatDepth').textContent = state.maxDepth;\n    el('jpfStats').style.display = '';\n\n    var root = buildNode('root', parsed, [], true);\n    el('jpfTree').appendChild(root);\n    if (root._expand) root._expand();\n\n    if (state.searchTerm) applySearch();\n  }\n\n  function applySearch() {\n    var q = state.searchTerm.toLowerCase();\n    var rows = el('jpfTree').querySelectorAll('.jpf-node-row');\n    var matchCount = 0;\n    rows.forEach(function(r) {\n      r.classList.remove('jpf-search-match');\n    });\n    if (!q) {\n      el('jpfStatMatches').style.display = 'none';\n      return;\n    }\n    rows.forEach(function(r) {\n      var keyEl = r.querySelector('.jpf-key');\n      var valEl = r.querySelector('.jpf-val-inline');\n      var keyTxt = keyEl ? keyEl.textContent.toLowerCase() : '';\n      var valTxt = valEl ? valEl.textContent.toLowerCase() : '';\n      if (keyTxt.indexOf(q) !== -1 || valTxt.indexOf(q) !== -1) {\n        r.classList.add('jpf-search-match');\n        matchCount++;\n      }\n    });\n    el('jpfStatMatches').textContent = matchCount + ' matches';\n    el('jpfStatMatches').style.display = '';\n  }\n\n  // Wire up events\n  el('jpfParse').addEventListener('click', parseAndRender);\n  el('jpfInput').addEventListener('keydown', function(e) {\n    if ((e.ctrlKey || e.metaKey) \u0026\u0026 e.key === 'Enter') parseAndRender();\n  });\n  el('jpfClear').addEventListener('click', function() {\n    el('jpfInput').value = '';\n    el('jpfInput').classList.remove('jpf-error');\n    el('jpfError').style.display = 'none';\n    el('jpfTree').innerHTML = '';\n    el('jpfEmpty').style.display = '';\n    el('jpfEmpty').textContent = 'Paste JSON above and click Explore to begin.';\n    el('jpfStats').style.display = 'none';\n    el('jpfPathBox').classList.remove('jpf-active');\n    el('jpfSearch').value = '';\n    state.searchTerm = '';\n    lastSelectedRow = null;\n  });\n\n  el('jpfExpandAll').addEventListener('click', function() {\n    el('jpfTree').querySelectorAll('.jpf-node').forEach(function(n) {\n      if (n._expand) n._expand();\n    });\n  });\n\n  el('jpfCollapseAll').addEventListener('click', function() {\n    var roots = el('jpfTree').children;\n    Array.from(roots).forEach(function(r) { if (r._collapse) r._collapse(); });\n  });\n\n  el('jpfSearch').addEventListener('input', function() {\n    state.searchTerm = this.value.trim();\n    applySearch();\n  });\n\n  document.querySelectorAll('#jpf-app .jpf-copy-inline[data-target]').forEach(function(btn) {\n    btn.addEventListener('click', function() {\n      var t = btn.getAttribute('data-target');\n      var txt = t === 'dot' ? state.selectedDot : t === 'bracket' ? state.selectedBracket : '';\n      if (!txt) return;\n      copyText(txt);\n      showToast('Copied!');\n      btn.classList.add('jpf-copied');\n      btn.textContent = 'Copied!';\n      setTimeout(function() { btn.classList.remove('jpf-copied'); btn.textContent = 'Copy'; }, 1500);\n    });\n  });\n\n  el('jpfCopyDotAll').addEventListener('click', function() {\n    if (!state.selectedDot) { showToast('Select a node first'); return; }\n    copyText(state.selectedDot);\n    showToast('Dot path copied!');\n  });\n  el('jpfCopyBracketAll').addEventListener('click', function() {\n    if (!state.selectedBracket) { showToast('Select a node first'); return; }\n    copyText(state.selectedBracket);\n    showToast('Bracket path copied!');\n  });\n  el('jpfCopyVal').addEventListener('click', function() {\n    if (state.selectedValue === null \u0026\u0026 state.selectedDot === '') { showToast('Select a node first'); return; }\n    var v = state.selectedValue;\n    var txt = (typeof v === 'object') ? JSON.stringify(v, null, 2) : String(v);\n    copyText(txt);\n    showToast('Value copied!');\n  });\n\n  document.querySelectorAll('#jpf-app .jpf-preset-btn').forEach(function(btn) {\n    btn.addEventListener('click', function() {\n      var key = btn.getAttribute('data-preset');\n      if (presets[key]) {\n        el('jpfInput').value = JSON.stringify(presets[key], null, 2);\n        parseAndRender();\n      }\n    });\n  });\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003ch2 id=\"how-to-use\"\u003eHow to Use\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003ePaste JSON\u003c/strong\u003e into the text area above (or pick a sample preset).\u003c/li\u003e\n\u003cli\u003eClick \u003cstrong\u003eExplore\u003c/strong\u003e (or press Ctrl+Enter / Cmd+Enter).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eClick any node\u003c/strong\u003e in the tree to see its JSONPath in dot and bracket notation.\u003c/li\u003e\n\u003cli\u003eUse the \u003cstrong\u003eCopy\u003c/strong\u003e buttons to grab the path or value.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSearch\u003c/strong\u003e to highlight nodes whose key or value matches your query.\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"what-is-jsonpath\"\u003eWhat Is JSONPath?\u003c/h2\u003e\n\u003cp\u003eJSONPath is a query language for JSON, analogous to XPath for XML. It lets you pinpoint values inside complex JSON documents using path expressions like \u003ccode\u003e$.user.address.city\u003c/code\u003e or \u003ccode\u003e$[\u0026quot;items\u0026quot;][0][\u0026quot;price\u0026quot;]\u003c/code\u003e. JSONPath expressions are used in:\u003c/p\u003e","title":"JSON Path Finder — Tree Explorer"},{"content":" JSON Path Tester JSON Input Result Enter JSON and a JSONPath expression to see results Run Try: $ $.store $.store.book[0] $.store.book[*].title $..title $.store.book[?(@.price\u0026lt;10)] $.store.* $..price Validate JSON → JSON Validator Find JSON paths → JSON Path Finder ","permalink":"https://productivity-works.com/tools/json-path-tester/","summary":"\u003cdiv id=\"jp-app\"\u003e\n\u003cstyle\u003e\n#jp-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  background: #0f172a;\n  color: #e2e8f0;\n  border-radius: 12px;\n  padding: 24px;\n  max-width: 900px;\n  margin: 0 auto;\n  box-sizing: border-box;\n}\n#jp-app * { box-sizing: border-box; }\n\n#jp-app h2 {\n  color: #a78bfa;\n  font-size: 1.05rem;\n  margin: 0 0 12px;\n  font-weight: 600;\n  letter-spacing: 0.04em;\n  text-transform: uppercase;\n}\n\n#jp-app .two-col {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 14px;\n  margin-bottom: 14px;\n}\n@media (max-width: 640px) {\n  #jp-app .two-col { grid-template-columns: 1fr; }\n}\n\n#jp-app .panel {\n  background: #1e293b;\n  border: 1.5px solid #334155;\n  border-radius: 10px;\n  padding: 14px;\n  display: flex;\n  flex-direction: column;\n}\n#jp-app .panel-label {\n  font-size: 0.73rem;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.09em;\n  font-weight: 600;\n  margin-bottom: 8px;\n}\n#jp-app textarea {\n  flex: 1;\n  background: #0f172a;\n  border: 1.5px solid #334155;\n  color: #e2e8f0;\n  border-radius: 7px;\n  padding: 10px 12px;\n  font-family: 'Courier New', monospace;\n  font-size: 0.88rem;\n  resize: vertical;\n  min-height: 220px;\n  line-height: 1.6;\n  transition: border-color 0.15s;\n}\n#jp-app textarea:focus { outline: none; border-color: #a78bfa; }\n#jp-app textarea.error { border-color: #ef4444; }\n\n#jp-app .path-row {\n  display: flex;\n  gap: 10px;\n  margin-bottom: 14px;\n  align-items: stretch;\n  flex-wrap: wrap;\n}\n#jp-app .path-input-wrap {\n  flex: 1;\n  min-width: 200px;\n  position: relative;\n}\n#jp-app .path-input {\n  width: 100%;\n  background: #1e293b;\n  border: 1.5px solid #334155;\n  color: #e2e8f0;\n  border-radius: 8px;\n  padding: 11px 14px;\n  font-size: 0.97rem;\n  font-family: 'Courier New', monospace;\n  transition: border-color 0.15s;\n}\n#jp-app .path-input:focus { outline: none; border-color: #a78bfa; }\n#jp-app .path-input.error { border-color: #ef4444; }\n\n#jp-app .run-btn {\n  background: #7c3aed;\n  border: none;\n  color: #fff;\n  border-radius: 8px;\n  padding: 11px 22px;\n  font-size: 0.95rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.15s;\n  white-space: nowrap;\n}\n#jp-app .run-btn:hover { background: #6d28d9; }\n\n#jp-app .suggestions {\n  display: flex;\n  gap: 6px;\n  flex-wrap: wrap;\n  margin-bottom: 14px;\n}\n#jp-app .sug-label {\n  font-size: 0.73rem;\n  color: #64748b;\n  align-self: center;\n  white-space: nowrap;\n}\n#jp-app .sug-chip {\n  background: #1e293b;\n  border: 1px solid #334155;\n  color: #a78bfa;\n  border-radius: 5px;\n  padding: 4px 10px;\n  font-size: 0.78rem;\n  font-family: 'Courier New', monospace;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#jp-app .sug-chip:hover {\n  background: #2e1065;\n  border-color: #a78bfa;\n}\n\n#jp-app .result-panel {\n  background: #1e293b;\n  border: 1.5px solid #334155;\n  border-radius: 10px;\n  padding: 16px;\n  margin-bottom: 14px;\n}\n#jp-app .result-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin-bottom: 10px;\n  flex-wrap: wrap;\n  gap: 8px;\n}\n#jp-app .result-count {\n  font-size: 0.82rem;\n  color: #64748b;\n}\n#jp-app .result-count .match-num {\n  color: #4ade80;\n  font-weight: 700;\n}\n#jp-app .copy-result-btn {\n  background: #2e1065;\n  border: 1px solid #a78bfa;\n  color: #a78bfa;\n  border-radius: 6px;\n  padding: 5px 13px;\n  font-size: 0.78rem;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#jp-app .copy-result-btn:hover { background: #a78bfa; color: #0f172a; }\n#jp-app .copy-result-btn.copied { background: #a78bfa; color: #0f172a; }\n\n#jp-app .result-items {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n  max-height: 360px;\n  overflow-y: auto;\n}\n#jp-app .result-item {\n  background: #0f172a;\n  border: 1px solid #334155;\n  border-radius: 6px;\n  padding: 8px 12px;\n  font-family: 'Courier New', monospace;\n  font-size: 0.87rem;\n  line-height: 1.55;\n  color: #c4b5fd;\n  position: relative;\n}\n#jp-app .result-item .item-idx {\n  position: absolute;\n  top: 6px;\n  right: 10px;\n  font-size: 0.68rem;\n  color: #475569;\n}\n#jp-app .result-item .item-val { color: #e2e8f0; }\n#jp-app .result-item .item-val .str { color: #86efac; }\n#jp-app .result-item .item-val .num { color: #fbbf24; }\n#jp-app .result-item .item-val .bool { color: #f87171; }\n#jp-app .result-item .item-val .null { color: #94a3b8; }\n#jp-app .result-item .item-key { color: #a78bfa; }\n\n#jp-app .error-panel {\n  background: #1e293b;\n  border: 1.5px solid #7f1d1d;\n  border-radius: 10px;\n  padding: 13px 16px;\n  color: #fca5a5;\n  font-size: 0.87rem;\n  font-family: 'Courier New', monospace;\n  margin-bottom: 14px;\n}\n\n#jp-app .placeholder-msg {\n  text-align: center;\n  color: #475569;\n  padding: 28px 0;\n  font-size: 0.92rem;\n}\n\n#jp-app .path-err {\n  color: #ef4444;\n  font-size: 0.8rem;\n  margin-top: 4px;\n  margin-bottom: 0;\n  min-height: 16px;\n}\n\n#jp-app .json-err {\n  color: #ef4444;\n  font-size: 0.8rem;\n  margin-top: 4px;\n}\n\u003c/style\u003e\n\u003ch2\u003eJSON Path Tester\u003c/h2\u003e\n\u003cdiv class=\"two-col\"\u003e\n  \u003cdiv class=\"panel\"\u003e\n    \u003cdiv class=\"panel-label\"\u003eJSON Input\u003c/div\u003e\n    \u003ctextarea id=\"jp-json\" placeholder='{\"store\":{\"book\":[{\"title\":\"Foo\",\"price\":8},{\"title\":\"Bar\",\"price\":15}],\"name\":\"MyStore\"}}' oninput=\"jpRun()\"\u003e\u003c/textarea\u003e\n    \u003cdiv class=\"json-err\" id=\"jp-json-err\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"panel\"\u003e\n    \u003cdiv class=\"panel-label\"\u003eResult\u003c/div\u003e\n    \u003cdiv id=\"jp-result-area\"\u003e\n      \u003cdiv class=\"placeholder-msg\"\u003eEnter JSON and a JSONPath expression to see results\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"path-row\"\u003e\n  \u003cdiv class=\"path-input-wrap\"\u003e\n    \u003cinput class=\"path-input\" id=\"jp-path\" type=\"text\" placeholder=\"$.store.book[0].title\" oninput=\"jpRun()\" autocomplete=\"off\" spellcheck=\"false\"\u003e\n  \u003c/div\u003e\n  \u003cbutton class=\"run-btn\" onclick=\"jpRun()\"\u003eRun\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"path-err\" id=\"jp-path-err\"\u003e\u003c/div\u003e\n\u003cdiv class=\"suggestions\"\u003e\n  \u003cspan class=\"sug-label\"\u003eTry:\u003c/span\u003e\n  \u003cspan class=\"sug-chip\" onclick=\"jpSug('$')\"\u003e$\u003c/span\u003e\n  \u003cspan class=\"sug-chip\" onclick=\"jpSug('$.store')\"\u003e$.store\u003c/span\u003e\n  \u003cspan class=\"sug-chip\" onclick=\"jpSug('$.store.book[0]')\"\u003e$.store.book[0]\u003c/span\u003e\n  \u003cspan class=\"sug-chip\" onclick=\"jpSug('$.store.book[*].title')\"\u003e$.store.book[*].title\u003c/span\u003e\n  \u003cspan class=\"sug-chip\" onclick=\"jpSug('$..title')\"\u003e$..title\u003c/span\u003e\n  \u003cspan class=\"sug-chip\" onclick=\"jpSug('$.store.book[?(@.price\u003c10)]')\"\u003e$.store.book[?(@.price\u0026lt;10)]\u003c/span\u003e\n  \u003cspan class=\"sug-chip\" onclick=\"jpSug('$.store.*')\"\u003e$.store.*\u003c/span\u003e\n  \u003cspan class=\"sug-chip\" onclick=\"jpSug('$..price')\"\u003e$..price\u003c/span\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  // ── JSONPath Evaluator ──────────────────────────────────────────────────────\n\n  function jpEval(obj, path) {\n    path = path.trim();\n    if (!path.startsWith('$')) throw new Error('Path must start with $');\n\n    var tokens = tokenize(path.slice(1)); // remove leading $\n    return query([obj], tokens);\n  }\n\n  function tokenize(path) {\n    var tokens = [];\n    var i = 0;\n    while (i \u003c path.length) {\n      // Recursive descent ..\n      if (path[i] === '.' \u0026\u0026 path[i + 1] === '.') {\n        tokens.push({ type: 'recurse' });\n        i += 2;\n        // Read next key if present\n        var key = readKey(path, i);\n        if (key !== null) {\n          tokens.push({ type: 'key', value: key.value });\n          i += key.len;\n        }\n        continue;\n      }\n      // Dot notation\n      if (path[i] === '.') {\n        i++;\n        var key2 = readKey(path, i);\n        if (key2 !== null) {\n          tokens.push({ type: 'key', value: key2.value });\n          i += key2.len;\n        }\n        continue;\n      }\n      // Bracket notation\n      if (path[i] === '[') {\n        var close = findClose(path, i);\n        var inner = path.slice(i + 1, close).trim();\n        tokens.push(parseBracket(inner));\n        i = close + 1;\n        continue;\n      }\n      i++;\n    }\n    return tokens;\n  }\n\n  function readKey(path, i) {\n    var match = path.slice(i).match(/^([a-zA-Z_$][a-zA-Z0-9_$]*|\\*)/);\n    if (!match) return null;\n    return { value: match[1], len: match[1].length };\n  }\n\n  function findClose(path, start) {\n    var depth = 0;\n    for (var i = start; i \u003c path.length; i++) {\n      if (path[i] === '[') depth++;\n      else if (path[i] === ']') { depth--; if (depth === 0) return i; }\n    }\n    return path.length - 1;\n  }\n\n  function parseBracket(inner) {\n    // Wildcard\n    if (inner === '*') return { type: 'key', value: '*' };\n    // Array index\n    if (/^-?\\d+$/.test(inner)) return { type: 'index', value: parseInt(inner, 10) };\n    // Slice a:b\n    if (/^-?\\d*:-?\\d*$/.test(inner)) {\n      var parts = inner.split(':');\n      return { type: 'slice', start: parts[0] !== '' ? parseInt(parts[0], 10) : 0, end: parts[1] !== '' ? parseInt(parts[1], 10) : null };\n    }\n    // Multi-index [0,1,2]\n    if (/^[\\d\\s,]+$/.test(inner)) {\n      var idxs = inner.split(',').map(function(s) { return parseInt(s.trim(), 10); });\n      return { type: 'multi-index', values: idxs };\n    }\n    // Filter [?(@.key op val)]\n    var filterMatch = inner.match(/^\\?\\(@\\.([a-zA-Z_$][a-zA-Z0-9_$.]*)\\s*(==|!=|\u003c=|\u003e=|\u003c|\u003e)\\s*(.+)\\)$/);\n    if (filterMatch) {\n      return { type: 'filter', key: filterMatch[1], op: filterMatch[2], val: parseFilterVal(filterMatch[3].trim()) };\n    }\n    // Quoted string key\n    var quotedMatch = inner.match(/^['\"](.+)['\"]$/);\n    if (quotedMatch) return { type: 'key', value: quotedMatch[1] };\n    // Bare key\n    return { type: 'key', value: inner };\n  }\n\n  function parseFilterVal(s) {\n    if (s === 'true') return true;\n    if (s === 'false') return false;\n    if (s === 'null') return null;\n    var n = Number(s);\n    if (!isNaN(n) \u0026\u0026 s !== '') return n;\n    // String\n    var m = s.match(/^['\"](.*)['\"]$/);\n    if (m) return m[1];\n    return s;\n  }\n\n  function query(nodes, tokens) {\n    if (tokens.length === 0) return nodes;\n    var tok = tokens[0];\n    var rest = tokens.slice(1);\n    var results = [];\n\n    if (tok.type === 'recurse') {\n      // Collect all descendants and apply rest\n      nodes.forEach(function(node) {\n        var descendants = collectAll(node);\n        // For recurse followed by a key token, filter to apply the next token\n        if (rest.length \u003e 0 \u0026\u0026 rest[0].type === 'key') {\n          var keyTok = rest[0];\n          var restRest = rest.slice(1);\n          descendants.forEach(function(d) {\n            var r = applyToken(d, keyTok);\n            if (r.length \u003e 0) {\n              var sub = query(r, restRest);\n              results = results.concat(sub);\n            }\n          });\n        } else {\n          descendants.forEach(function(d) {\n            var sub = query([d], rest);\n            results = results.concat(sub);\n          });\n        }\n      });\n      return results;\n    }\n\n    nodes.forEach(function(node) {\n      var r = applyToken(node, tok);\n      results = results.concat(query(r, rest));\n    });\n    return results;\n  }\n\n  function applyToken(node, tok) {\n    if (tok.type === 'key') {\n      if (tok.value === '*') {\n        if (Array.isArray(node)) return node;\n        if (node !== null \u0026\u0026 typeof node === 'object') return Object.values(node);\n        return [];\n      }\n      if (node !== null \u0026\u0026 typeof node === 'object' \u0026\u0026 !Array.isArray(node)) {\n        return node.hasOwnProperty(tok.value) ? [node[tok.value]] : [];\n      }\n      return [];\n    }\n    if (tok.type === 'index') {\n      if (!Array.isArray(node)) return [];\n      var idx = tok.value \u003c 0 ? node.length + tok.value : tok.value;\n      return idx \u003e= 0 \u0026\u0026 idx \u003c node.length ? [node[idx]] : [];\n    }\n    if (tok.type === 'multi-index') {\n      if (!Array.isArray(node)) return [];\n      var res = [];\n      tok.values.forEach(function(v) {\n        var idx = v \u003c 0 ? node.length + v : v;\n        if (idx \u003e= 0 \u0026\u0026 idx \u003c node.length) res.push(node[idx]);\n      });\n      return res;\n    }\n    if (tok.type === 'slice') {\n      if (!Array.isArray(node)) return [];\n      var start = tok.start \u003c 0 ? Math.max(0, node.length + tok.start) : tok.start;\n      var end = tok.end === null ? node.length : (tok.end \u003c 0 ? node.length + tok.end : tok.end);\n      return node.slice(start, end);\n    }\n    if (tok.type === 'filter') {\n      if (!Array.isArray(node)) return [];\n      return node.filter(function(item) {\n        if (item === null || typeof item !== 'object') return false;\n        var keyParts = tok.key.split('.');\n        var val = item;\n        for (var i = 0; i \u003c keyParts.length; i++) {\n          if (val === null || typeof val !== 'object') return false;\n          val = val[keyParts[i]];\n        }\n        return compare(val, tok.op, tok.val);\n      });\n    }\n    return [];\n  }\n\n  function compare(a, op, b) {\n    switch (op) {\n      case '==': return a == b;\n      case '!=': return a != b;\n      case '\u003c':  return a \u003c b;\n      case '\u003c=': return a \u003c= b;\n      case '\u003e':  return a \u003e b;\n      case '\u003e=': return a \u003e= b;\n    }\n    return false;\n  }\n\n  function collectAll(node) {\n    var results = [node];\n    if (Array.isArray(node)) {\n      node.forEach(function(item) { results = results.concat(collectAll(item)); });\n    } else if (node !== null \u0026\u0026 typeof node === 'object') {\n      Object.values(node).forEach(function(v) { results = results.concat(collectAll(v)); });\n    }\n    return results;\n  }\n\n  // ── Syntax Highlighting ────────────────────────────────────────────────────\n\n  function colorizeValue(val) {\n    var json = JSON.stringify(val, null, 2);\n    // Simple token coloring\n    json = json\n      .replace(/\u0026/g, '\u0026amp;').replace(/\u003c/g, '\u0026lt;').replace(/\u003e/g, '\u0026gt;')\n      .replace(/\"((?:[^\"\\\\]|\\\\.)*)\"/g, function(m, s) { return '\u003cspan class=\"str\"\u003e\"' + s + '\"\u003c/span\u003e'; })\n      .replace(/\\b(-?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?)\\b/g, '\u003cspan class=\"num\"\u003e$1\u003c/span\u003e')\n      .replace(/\\b(true|false)\\b/g, '\u003cspan class=\"bool\"\u003e$1\u003c/span\u003e')\n      .replace(/\\bnull\\b/g, '\u003cspan class=\"null\"\u003enull\u003c/span\u003e');\n    return '\u003cdiv class=\"item-val\"\u003e' + json + '\u003c/div\u003e';\n  }\n\n  // ── Auto-suggest from JSON keys ────────────────────────────────────────────\n\n  function collectKeys(obj, prefix, depth) {\n    if (depth \u003e 4 || obj === null || typeof obj !== 'object') return [];\n    var keys = [];\n    if (Array.isArray(obj)) {\n      if (obj.length \u003e 0) keys = keys.concat(collectKeys(obj[0], prefix + '[0]', depth + 1));\n    } else {\n      Object.keys(obj).forEach(function(k) {\n        keys.push(prefix + '.' + k);\n        keys = keys.concat(collectKeys(obj[k], prefix + '.' + k, depth + 1));\n      });\n    }\n    return keys;\n  }\n\n  // ── Main Run ───────────────────────────────────────────────────────────────\n\n  window.jpRun = function() {\n    var jsonStr = document.getElementById('jp-json').value.trim();\n    var pathStr = document.getElementById('jp-path').value.trim();\n    var jsonErrEl = document.getElementById('jp-json-err');\n    var pathErrEl = document.getElementById('jp-path-err');\n    var resultArea = document.getElementById('jp-result-area');\n    var jsonInp = document.getElementById('jp-json');\n    var pathInp = document.getElementById('jp-path');\n\n    // Parse JSON\n    var data;\n    jsonErrEl.textContent = '';\n    jsonInp.classList.remove('error');\n    if (!jsonStr) {\n      resultArea.innerHTML = '\u003cdiv class=\"placeholder-msg\"\u003eEnter JSON and a JSONPath expression to see results\u003c/div\u003e';\n      return;\n    }\n    try {\n      data = JSON.parse(jsonStr);\n    } catch (e) {\n      jsonErrEl.textContent = 'JSON parse error: ' + e.message;\n      jsonInp.classList.add('error');\n      resultArea.innerHTML = '\u003cdiv class=\"error-panel\"\u003eInvalid JSON: ' + escHtml(e.message) + '\u003c/div\u003e';\n      return;\n    }\n\n    if (!pathStr) {\n      resultArea.innerHTML = '\u003cdiv class=\"placeholder-msg\"\u003eEnter a JSONPath expression above\u003c/div\u003e';\n      return;\n    }\n\n    // Run JSONPath\n    pathErrEl.textContent = '';\n    pathInp.classList.remove('error');\n    var results;\n    try {\n      results = jpEval(data, pathStr);\n    } catch (e) {\n      pathErrEl.textContent = 'Path error: ' + e.message;\n      pathInp.classList.add('error');\n      resultArea.innerHTML = '\u003cdiv class=\"error-panel\"\u003ePath error: ' + escHtml(e.message) + '\u003c/div\u003e';\n      return;\n    }\n\n    // Render results\n    var count = results.length;\n    var itemsHtml = results.map(function(r, i) {\n      return '\u003cdiv class=\"result-item\"\u003e' +\n        '\u003cdiv class=\"item-idx\"\u003e[' + i + ']\u003c/div\u003e' +\n        colorizeValue(r) +\n        '\u003c/div\u003e';\n    }).join('');\n\n    var resultJson = JSON.stringify(results, null, 2);\n\n    resultArea.innerHTML =\n      '\u003cdiv class=\"result-panel\"\u003e' +\n        '\u003cdiv class=\"result-header\"\u003e' +\n          '\u003cspan class=\"result-count\"\u003e\u003cspan class=\"match-num\"\u003e' + count + '\u003c/span\u003e match' + (count !== 1 ? 'es' : '') + '\u003c/span\u003e' +\n          (count \u003e 0 ? '\u003cbutton class=\"copy-result-btn\" id=\"jp-copy-btn\" onclick=\"jpCopyResult()\"\u003eCopy Result\u003c/button\u003e' : '') +\n        '\u003c/div\u003e' +\n        (count === 0\n          ? '\u003cdiv class=\"placeholder-msg\" style=\"padding:16px 0;\"\u003eNo matches found\u003c/div\u003e'\n          : '\u003cdiv class=\"result-items\"\u003e' + itemsHtml + '\u003c/div\u003e') +\n      '\u003c/div\u003e';\n\n    window._jpLastResult = resultJson;\n  };\n\n  window.jpCopyResult = function() {\n    if (!window._jpLastResult) return;\n    navigator.clipboard.writeText(window._jpLastResult).then(function() {\n      var btn = document.getElementById('jp-copy-btn');\n      if (!btn) return;\n      var orig = btn.textContent;\n      btn.textContent = 'Copied!';\n      btn.classList.add('copied');\n      setTimeout(function() { btn.textContent = orig; btn.classList.remove('copied'); }, 1500);\n    });\n  };\n\n  window.jpSug = function(path) {\n    document.getElementById('jp-path').value = path;\n    jpRun();\n  };\n\n  function escHtml(s) {\n    return String(s).replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n  }\n\n  // Seed with sample JSON\n  window.addEventListener('load', function() {\n    var ta = document.getElementById('jp-json');\n    if (!ta.value) {\n      ta.value = JSON.stringify({\n        \"store\": {\n          \"name\": \"BookWorld\",\n          \"book\": [\n            { \"title\": \"Sayings of the Century\", \"author\": \"Nigel Rees\", \"price\": 8.95 },\n            { \"title\": \"Sword of Honour\", \"author\": \"Evelyn Waugh\", \"price\": 12.99 },\n            { \"title\": \"Moby Dick\", \"author\": \"Herman Melville\", \"price\": 8.99 },\n            { \"title\": \"The Lord of the Rings\", \"author\": \"J. R. R. Tolkien\", \"price\": 22.99 }\n          ],\n          \"bicycle\": { \"color\": \"red\", \"price\": 19.95 }\n        }\n      }, null, 2);\n    }\n  });\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003eValidate JSON → \u003ca href=\"https://productivity-works.com/tools/json-validator/\"\u003eJSON Validator\u003c/a\u003e\n\u003c/p\u003e","title":"JSON Path Tester - Free Online JSONPath Query Tool"},{"content":" Generate Schema Validate JSON Schema Version: Draft-07 2020-12 Sample: — choose preset — User Object Product Catalog API Response Copy Schema Clear Input JSON Chars: 0 Lines: 1 Generated JSON Schema // Schema will appear here after clicking Generate Schema Edit Schema Properties Property Type Description Required Apply Edits \u0026amp; Regenerate Copied to clipboard! What is JSON Schema? JSON Schema is a vocabulary that allows you to annotate and validate JSON documents. It describes the structure of JSON data — what properties an object should have, what types those properties must be, which fields are required, and what formats or constraints apply. A schema acts as a contract between producers and consumers of JSON data, making APIs more predictable and enabling automated validation, documentation generation, and code generation.\nTwo widely used drafts are Draft-07, which is supported by virtually every validation library across all languages, and Draft 2020-12, the current stable specification that introduces features like $dynamicRef and improved unevaluatedProperties handling. This tool lets you switch between both with a single click.\nHow to Use This JSON Schema Generator Generate Schema — Paste any valid JSON object into the left pane and click \u0026ldquo;Generate Schema.\u0026rdquo; The tool inspects every value recursively: it maps true/false to boolean, whole numbers to integer, decimals to number, null to null, and arrays and objects to their respective schema types. String values are scanned for common patterns — if a value looks like a date-time, email, URI, or UUID, the corresponding format keyword is added automatically.\nSchema Version Toggle — Click \u0026ldquo;Draft-07\u0026rdquo; or \u0026ldquo;2020-12\u0026rdquo; to switch the $schema URI in the output. The toggle regenerates the schema immediately if input is already present.\nEdit Properties — After generating, the \u0026ldquo;Edit Schema Properties\u0026rdquo; table appears below the output. Add a human-readable description to any property, and toggle the \u0026ldquo;Required\u0026rdquo; checkbox to control which fields appear in the required array. Click \u0026ldquo;Apply Edits \u0026amp; Regenerate\u0026rdquo; to update the schema.\nValidate JSON — Click \u0026ldquo;Validate JSON\u0026rdquo; to check your input against the generated schema. The validator checks types, required fields, and nested structures, reporting each error with its JSON path.\nSample Presets — Use the \u0026ldquo;Sample\u0026rdquo; dropdown to load one of three built-in examples: a User Object (with UUID, email, date-time, nested address, and array of tags), a Product Catalog entry (with price, rating, image array), or a full API Response envelope (with pagination, nested data, and metadata).\nCopy Schema — Click \u0026ldquo;Copy Schema\u0026rdquo; to copy the generated schema to your clipboard for use in your codebase, API docs, or configuration.\nRelated Tools Format JSON → JSON Formatter Convert JSON to CSV → JSON to CSV Convert YAML ↔ JSON → YAML↔JSON Converter ","permalink":"https://productivity-works.com/tools/json-schema-generator/","summary":"\u003cdiv id=\"jsg-app\"\u003e\n\u003cstyle\u003e\n#jsg-app {\n  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;\n  background: #1e1e2e;\n  color: #cdd6f4;\n  border-radius: 12px;\n  padding: 20px;\n  margin: 0 auto;\n  max-width: 1200px;\n  box-sizing: border-box;\n}\n\n#jsg-app * {\n  box-sizing: border-box;\n}\n\n/* ── Toolbar ── */\n#jsg-app .jsg-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  align-items: center;\n  margin-bottom: 16px;\n}\n\n#jsg-app .jsg-toolbar-group {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  flex-wrap: wrap;\n}\n\n#jsg-app .jsg-sep {\n  width: 1px;\n  height: 26px;\n  background: #45475a;\n  margin: 0 4px;\n}\n\n#jsg-app .jsg-label {\n  font-size: 12px;\n  color: #a6adc8;\n  white-space: nowrap;\n}\n\n#jsg-app .jsg-btn {\n  padding: 7px 15px;\n  border: none;\n  border-radius: 7px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: opacity 0.15s, transform 0.1s;\n  outline: none;\n  white-space: nowrap;\n}\n\n#jsg-app .jsg-btn:active { transform: scale(0.97); }\n#jsg-app .jsg-btn:hover  { opacity: 0.88; }\n\n#jsg-app .jsg-btn-generate { background: #89b4fa; color: #1e1e2e; }\n#jsg-app .jsg-btn-validate { background: #a6e3a1; color: #1e1e2e; }\n#jsg-app .jsg-btn-copy     { background: #cba6f7; color: #1e1e2e; }\n#jsg-app .jsg-btn-clear    { background: #45475a; color: #cdd6f4; }\n#jsg-app .jsg-btn-sample   { background: #313244; color: #89b4fa; border: 1px solid #89b4fa; }\n\n#jsg-app .jsg-select {\n  background: #313244;\n  color: #cdd6f4;\n  border: 1px solid #45475a;\n  border-radius: 6px;\n  padding: 6px 10px;\n  font-size: 13px;\n  cursor: pointer;\n  outline: none;\n}\n#jsg-app .jsg-select:focus { border-color: #89b4fa; }\n\n#jsg-app .jsg-version-toggle {\n  display: flex;\n  gap: 0;\n  border-radius: 7px;\n  overflow: hidden;\n  border: 1px solid #45475a;\n}\n\n#jsg-app .jsg-version-btn {\n  padding: 6px 13px;\n  background: #313244;\n  color: #a6adc8;\n  border: none;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s;\n  outline: none;\n}\n#jsg-app .jsg-version-btn.active {\n  background: #89b4fa;\n  color: #1e1e2e;\n}\n\n/* ── Main layout ── */\n#jsg-app .jsg-main {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 16px;\n}\n\n@media (max-width: 750px) {\n  #jsg-app .jsg-main { grid-template-columns: 1fr; }\n  #jsg-app .jsg-sep  { display: none; }\n}\n\n#jsg-app .jsg-pane {\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n}\n\n#jsg-app .jsg-pane-label {\n  font-size: 11px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #a6adc8;\n}\n\n#jsg-app .jsg-textarea {\n  width: 100%;\n  min-height: 320px;\n  background: #11111b;\n  color: #cdd6f4;\n  border: 2px solid #313244;\n  border-radius: 9px;\n  padding: 14px;\n  font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;\n  font-size: 13px;\n  line-height: 1.65;\n  resize: vertical;\n  outline: none;\n  transition: border-color 0.2s;\n  tab-size: 2;\n}\n#jsg-app .jsg-textarea:focus { border-color: #89b4fa; }\n#jsg-app .jsg-textarea.jsg-error  { border-color: #f38ba8; }\n#jsg-app .jsg-textarea.jsg-valid  { border-color: #a6e3a1; }\n\n#jsg-app .jsg-output-box {\n  width: 100%;\n  min-height: 320px;\n  background: #11111b;\n  border: 2px solid #313244;\n  border-radius: 9px;\n  padding: 14px;\n  font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;\n  font-size: 13px;\n  line-height: 1.65;\n  overflow: auto;\n  white-space: pre-wrap;\n  word-break: break-all;\n  transition: border-color 0.2s;\n}\n#jsg-app .jsg-output-box.jsg-valid { border-color: #a6e3a1; }\n#jsg-app .jsg-output-box.jsg-error { border-color: #f38ba8; }\n\n/* ── Status \u0026 error ── */\n#jsg-app .jsg-status-bar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 12px;\n  font-size: 12px;\n  color: #6c7086;\n  margin-top: 2px;\n}\n#jsg-app .jsg-ok   { color: #a6e3a1; font-weight: 600; }\n#jsg-app .jsg-err  { color: #f38ba8; font-weight: 600; }\n#jsg-app .jsg-info { color: #89b4fa; }\n\n#jsg-app .jsg-error-box {\n  background: #1a0a0a;\n  border: 1px solid #f38ba8;\n  border-radius: 7px;\n  color: #f38ba8;\n  font-size: 13px;\n  padding: 10px 14px;\n  display: none;\n  font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;\n}\n#jsg-app .jsg-error-box.visible { display: block; }\n\n/* ── Property editor ── */\n#jsg-app .jsg-editor-section {\n  margin-top: 18px;\n  background: #181825;\n  border: 1px solid #313244;\n  border-radius: 10px;\n  padding: 14px;\n}\n\n#jsg-app .jsg-editor-title {\n  font-size: 12px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  color: #a6adc8;\n  margin-bottom: 12px;\n}\n\n#jsg-app .jsg-prop-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 13px;\n}\n\n#jsg-app .jsg-prop-table th {\n  text-align: left;\n  color: #6c7086;\n  font-weight: 600;\n  font-size: 11px;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  padding: 4px 8px 8px 8px;\n  border-bottom: 1px solid #313244;\n}\n\n#jsg-app .jsg-prop-table td {\n  padding: 6px 8px;\n  vertical-align: middle;\n  border-bottom: 1px solid #1e1e2e;\n}\n\n#jsg-app .jsg-prop-table tr:last-child td { border-bottom: none; }\n\n#jsg-app .jsg-prop-key {\n  color: #89b4fa;\n  font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;\n  white-space: nowrap;\n}\n\n#jsg-app .jsg-prop-type {\n  color: #fab387;\n  font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;\n  font-size: 12px;\n}\n\n#jsg-app .jsg-prop-desc-input {\n  background: #11111b;\n  color: #cdd6f4;\n  border: 1px solid #313244;\n  border-radius: 5px;\n  padding: 4px 8px;\n  font-size: 12px;\n  width: 100%;\n  outline: none;\n  transition: border-color 0.15s;\n}\n#jsg-app .jsg-prop-desc-input:focus { border-color: #89b4fa; }\n\n#jsg-app .jsg-prop-req-chk {\n  accent-color: #89b4fa;\n  width: 16px;\n  height: 16px;\n  cursor: pointer;\n}\n\n#jsg-app .jsg-apply-btn {\n  margin-top: 12px;\n  padding: 7px 18px;\n  background: #89b4fa;\n  color: #1e1e2e;\n  border: none;\n  border-radius: 7px;\n  font-size: 13px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: opacity 0.15s;\n}\n#jsg-app .jsg-apply-btn:hover { opacity: 0.85; }\n\n/* ── Syntax highlight ── */\n#jsg-app .jst-key    { color: #89b4fa; }\n#jsg-app .jst-str    { color: #a6e3a1; }\n#jsg-app .jst-num    { color: #fab387; }\n#jsg-app .jst-bool   { color: #cba6f7; }\n#jsg-app .jst-null   { color: #f38ba8; }\n#jsg-app .jst-punc   { color: #6c7086; }\n\n/* ── Toast ── */\n#jsg-app .jsg-toast {\n  position: fixed;\n  bottom: 32px;\n  right: 32px;\n  background: #a6e3a1;\n  color: #1e1e2e;\n  font-weight: 700;\n  font-size: 14px;\n  padding: 10px 22px;\n  border-radius: 8px;\n  box-shadow: 0 4px 24px rgba(0,0,0,0.4);\n  opacity: 0;\n  pointer-events: none;\n  transition: opacity 0.25s;\n  z-index: 9999;\n}\n#jsg-app .jsg-toast.show { opacity: 1; }\n\u003c/style\u003e\n\u003c!-- Toolbar --\u003e\n\u003cdiv class=\"jsg-toolbar\"\u003e\n  \u003cdiv class=\"jsg-toolbar-group\"\u003e\n    \u003cbutton class=\"jsg-btn jsg-btn-generate\" onclick=\"jsgGenerate()\"\u003eGenerate Schema\u003c/button\u003e\n    \u003cbutton class=\"jsg-btn jsg-btn-validate\" onclick=\"jsgValidate()\"\u003eValidate JSON\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"jsg-sep\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"jsg-toolbar-group\"\u003e\n    \u003cspan class=\"jsg-label\"\u003eSchema Version:\u003c/span\u003e\n    \u003cdiv class=\"jsg-version-toggle\"\u003e\n      \u003cbutton class=\"jsg-version-btn active\" id=\"jsg-v07\" onclick=\"jsgSetVersion('draft-07')\"\u003eDraft-07\u003c/button\u003e\n      \u003cbutton class=\"jsg-version-btn\" id=\"jsg-v20\" onclick=\"jsgSetVersion('2020-12')\"\u003e2020-12\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"jsg-sep\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"jsg-toolbar-group\"\u003e\n    \u003cspan class=\"jsg-label\"\u003eSample:\u003c/span\u003e\n    \u003cselect class=\"jsg-select\" id=\"jsg-sample-select\" onchange=\"jsgLoadSample(this.value)\"\u003e\n      \u003coption value=\"\"\u003e— choose preset —\u003c/option\u003e\n      \u003coption value=\"user\"\u003eUser Object\u003c/option\u003e\n      \u003coption value=\"product\"\u003eProduct Catalog\u003c/option\u003e\n      \u003coption value=\"api\"\u003eAPI Response\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"jsg-sep\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"jsg-toolbar-group\"\u003e\n    \u003cbutton class=\"jsg-btn jsg-btn-copy\"  onclick=\"jsgCopy()\"\u003eCopy Schema\u003c/button\u003e\n    \u003cbutton class=\"jsg-btn jsg-btn-clear\" onclick=\"jsgClear()\"\u003eClear\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Main panes --\u003e\n\u003cdiv class=\"jsg-main\"\u003e\n  \u003c!-- Left: JSON input --\u003e\n  \u003cdiv class=\"jsg-pane\"\u003e\n    \u003cdiv class=\"jsg-pane-label\"\u003eInput JSON\u003c/div\u003e\n    \u003ctextarea\n      id=\"jsg-input\"\n      class=\"jsg-textarea\"\n      placeholder='Paste sample JSON here…\u0026#10;\u0026#10;{\"name\": \"Alice\", \"age\": 30, \"email\": \"alice@example.com\"}'\n      oninput=\"jsgOnInput()\"\n      spellcheck=\"false\"\n    \u003e\u003c/textarea\u003e\n    \u003cdiv class=\"jsg-status-bar\"\u003e\n      \u003cspan id=\"jsg-in-chars\"\u003eChars: 0\u003c/span\u003e\n      \u003cspan id=\"jsg-in-lines\"\u003eLines: 1\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv id=\"jsg-error-box\" class=\"jsg-error-box\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Right: Schema output --\u003e\n  \u003cdiv class=\"jsg-pane\"\u003e\n    \u003cdiv class=\"jsg-pane-label\"\u003eGenerated JSON Schema\u003c/div\u003e\n    \u003cdiv id=\"jsg-output\" class=\"jsg-output-box\"\u003e\u003cspan style=\"color:#45475a\"\u003e// Schema will appear here after clicking Generate Schema\u003c/span\u003e\u003c/div\u003e\n    \u003cdiv class=\"jsg-status-bar\" id=\"jsg-out-status\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Property editor (hidden until schema generated) --\u003e\n\u003cdiv class=\"jsg-editor-section\" id=\"jsg-editor-section\" style=\"display:none;\"\u003e\n  \u003cdiv class=\"jsg-editor-title\"\u003eEdit Schema Properties\u003c/div\u003e\n  \u003ctable class=\"jsg-prop-table\" id=\"jsg-prop-table\"\u003e\n    \u003cthead\u003e\n      \u003ctr\u003e\n        \u003cth\u003eProperty\u003c/th\u003e\n        \u003cth\u003eType\u003c/th\u003e\n        \u003cth\u003eDescription\u003c/th\u003e\n        \u003cth\u003eRequired\u003c/th\u003e\n      \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody id=\"jsg-prop-tbody\"\u003e\u003c/tbody\u003e\n  \u003c/table\u003e\n  \u003cbutton class=\"jsg-apply-btn\" onclick=\"jsgApplyEdits()\"\u003eApply Edits \u0026amp; Regenerate\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"jsg-toast\" id=\"jsg-toast\"\u003eCopied to clipboard!\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  'use strict';\n\n  /* ────────────────────────────────────────\n     State\n  ──────────────────────────────────────── */\n  var schemaVersion = 'draft-07';\n  var lastSchema = null;      // raw schema object\n  var lastInput  = null;      // last successfully parsed JSON\n  // propMeta[key] = { description, required }  (top-level only)\n  var propMeta   = {};\n\n  /* ────────────────────────────────────────\n     Utility\n  ──────────────────────────────────────── */\n  function esc(s) {\n    return String(s)\n      .replace(/\u0026/g, '\u0026amp;')\n      .replace(/\u003c/g, '\u0026lt;')\n      .replace(/\u003e/g, '\u0026gt;')\n      .replace(/\"/g, '\u0026quot;');\n  }\n\n  function syntaxHL(json) {\n    return esc(json).replace(\n      /(\"(\\\\u[a-fA-F0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?|[{}\\[\\],:])/g,\n      function (m) {\n        var cls = 'jst-num';\n        if (/^\"/.test(m))          cls = /:$/.test(m) ? 'jst-key' : 'jst-str';\n        else if (/true|false/.test(m)) cls = 'jst-bool';\n        else if (/null/.test(m))       cls = 'jst-null';\n        else if (/[{}\\[\\],:]/.test(m)) cls = 'jst-punc';\n        return '\u003cspan class=\"' + cls + '\"\u003e' + m + '\u003c/span\u003e';\n      }\n    );\n  }\n\n  function showToast(msg) {\n    var t = document.getElementById('jsg-toast');\n    t.textContent = msg || 'Copied to clipboard!';\n    t.classList.add('show');\n    setTimeout(function () { t.classList.remove('show'); }, 1800);\n  }\n\n  function setError(msg) {\n    var el = document.getElementById('jsg-error-box');\n    if (msg) { el.textContent = msg; el.classList.add('visible'); }\n    else      { el.textContent = ''; el.classList.remove('visible'); }\n  }\n\n  function setInputState(state) {\n    var el = document.getElementById('jsg-input');\n    el.className = 'jsg-textarea' + (state ? ' jsg-' + state : '');\n  }\n\n  function setOutputState(state) {\n    var el = document.getElementById('jsg-output');\n    el.className = 'jsg-output-box' + (state ? ' jsg-' + state : '');\n  }\n\n  function setOutputHTML(html) {\n    document.getElementById('jsg-output').innerHTML = html;\n  }\n\n  function updateInputStats() {\n    var v = document.getElementById('jsg-input').value;\n    document.getElementById('jsg-in-chars').textContent = 'Chars: ' + v.length;\n    document.getElementById('jsg-in-lines').textContent = 'Lines: ' + (v ? v.split('\\n').length : 1);\n  }\n\n  function setOutStatus(html) {\n    document.getElementById('jsg-out-status').innerHTML = html;\n  }\n\n  function parseInput() {\n    var raw = document.getElementById('jsg-input').value.trim();\n    if (!raw) return null;\n    try {\n      return { ok: true, value: JSON.parse(raw) };\n    } catch (e) {\n      var msg = e.message || String(e);\n      var pm = msg.match(/position (\\d+)/i);\n      var line = null;\n      if (pm) {\n        var pos = parseInt(pm[1], 10);\n        line = raw.slice(0, pos).split('\\n').length;\n      }\n      return { ok: false, error: msg, line: line };\n    }\n  }\n\n  /* ────────────────────────────────────────\n     Format detection helpers\n  ──────────────────────────────────────── */\n  var RE_DATETIME = /^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})?)?$/;\n  var RE_EMAIL    = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n  var RE_URI      = /^https?:\\/\\/.+/;\n  var RE_UUID     = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n  function detectStringFormat(val) {\n    if (RE_DATETIME.test(val)) return 'date-time';\n    if (RE_EMAIL.test(val))    return 'email';\n    if (RE_URI.test(val))      return 'uri';\n    if (RE_UUID.test(val))     return 'uuid';\n    return null;\n  }\n\n  function isInteger(n) {\n    return Number.isFinite(n) \u0026\u0026 Math.floor(n) === n;\n  }\n\n  /* ────────────────────────────────────────\n     Schema generation — recursive\n  ──────────────────────────────────────── */\n  function inferSchema(val, key) {\n    if (val === null) return { type: 'null' };\n\n    if (typeof val === 'boolean') return { type: 'boolean' };\n\n    if (typeof val === 'number') {\n      return { type: isInteger(val) ? 'integer' : 'number' };\n    }\n\n    if (typeof val === 'string') {\n      var fmt = detectStringFormat(val);\n      var s = { type: 'string' };\n      if (fmt) s.format = fmt;\n      return s;\n    }\n\n    if (Array.isArray(val)) {\n      var schema = { type: 'array' };\n      if (val.length \u003e 0) {\n        // Infer items from first element (heuristic)\n        schema.items = inferSchema(val[0], null);\n      }\n      return schema;\n    }\n\n    if (typeof val === 'object') {\n      var props = {};\n      var reqArr = [];\n      var keys = Object.keys(val);\n      for (var i = 0; i \u003c keys.length; i++) {\n        var k = keys[i];\n        props[k] = inferSchema(val[k], k);\n        reqArr.push(k);\n      }\n      var objSchema = {\n        type: 'object',\n        properties: props,\n        required: reqArr\n      };\n      return objSchema;\n    }\n\n    return {};\n  }\n\n  function buildRootSchema(parsed) {\n    var $schema = schemaVersion === '2020-12'\n      ? 'https://json-schema.org/draft/2020-12/schema'\n      : 'http://json-schema.org/draft-07/schema#';\n\n    var inner = inferSchema(parsed, null);\n\n    var root = Object.assign({ $schema: $schema }, inner);\n\n    // Apply propMeta to top-level properties\n    if (root.properties) {\n      var reqSet = {};\n      var keys = Object.keys(root.properties);\n      var newReq = [];\n      for (var i = 0; i \u003c keys.length; i++) {\n        var k = keys[i];\n        var meta = propMeta[k] || {};\n        if (meta.description) root.properties[k].description = meta.description;\n        if (meta.required !== false) newReq.push(k); // default required\n      }\n      // override required from propMeta\n      var hasMeta = Object.keys(propMeta).length \u003e 0;\n      if (hasMeta) {\n        newReq = [];\n        for (var j = 0; j \u003c keys.length; j++) {\n          var kk = keys[j];\n          var m = propMeta[kk];\n          if (!m || m.required !== false) newReq.push(kk);\n        }\n        root.required = newReq;\n      }\n    }\n\n    return root;\n  }\n\n  /* ────────────────────────────────────────\n     Render property editor\n  ──────────────────────────────────────── */\n  function renderEditor(schema) {\n    var section = document.getElementById('jsg-editor-section');\n    var tbody   = document.getElementById('jsg-prop-tbody');\n\n    if (!schema || schema.type !== 'object' || !schema.properties) {\n      section.style.display = 'none';\n      return;\n    }\n\n    var keys = Object.keys(schema.properties);\n    var reqSet = {};\n    if (schema.required) {\n      for (var i = 0; i \u003c schema.required.length; i++) reqSet[schema.required[i]] = true;\n    }\n\n    tbody.innerHTML = '';\n    for (var j = 0; j \u003c keys.length; j++) {\n      var k = keys[j];\n      var prop = schema.properties[k];\n      var meta = propMeta[k] || {};\n      var typeStr = prop.type || '?';\n      if (prop.format) typeStr += ' (' + prop.format + ')';\n      var isReq = propMeta[k] ? propMeta[k].required !== false : (reqSet[k] || false);\n      var descVal = meta.description || prop.description || '';\n\n      var tr = document.createElement('tr');\n      tr.innerHTML =\n        '\u003ctd class=\"jsg-prop-key\"\u003e' + esc(k) + '\u003c/td\u003e' +\n        '\u003ctd class=\"jsg-prop-type\"\u003e' + esc(typeStr) + '\u003c/td\u003e' +\n        '\u003ctd\u003e\u003cinput class=\"jsg-prop-desc-input\" type=\"text\" data-key=\"' + esc(k) + '\" placeholder=\"Add description…\" value=\"' + esc(descVal) + '\"\u003e\u003c/td\u003e' +\n        '\u003ctd style=\"text-align:center;\"\u003e\u003cinput class=\"jsg-prop-req-chk\" type=\"checkbox\" data-key=\"' + esc(k) + '\"' + (isReq ? ' checked' : '') + '\u003e\u003c/td\u003e';\n      tbody.appendChild(tr);\n    }\n    section.style.display = 'block';\n  }\n\n  /* ────────────────────────────────────────\n     Public actions\n  ──────────────────────────────────────── */\n  window.jsgOnInput = function () {\n    updateInputStats();\n    setInputState('');\n    setError('');\n  };\n\n  window.jsgSetVersion = function (v) {\n    schemaVersion = v;\n    document.getElementById('jsg-v07').classList.toggle('active', v === 'draft-07');\n    document.getElementById('jsg-v20').classList.toggle('active', v === '2020-12');\n    // Regenerate if we already have a schema\n    if (lastInput !== null) jsgGenerate();\n  };\n\n  window.jsgGenerate = function () {\n    var r = parseInput();\n    if (!r) {\n      setOutputHTML('\u003cspan style=\"color:#45475a\"\u003e// Paste JSON on the left and click Generate Schema\u003c/span\u003e');\n      setOutputState('');\n      setOutStatus('');\n      document.getElementById('jsg-editor-section').style.display = 'none';\n      return;\n    }\n    if (!r.ok) {\n      setInputState('error');\n      setError('Parse error' + (r.line ? ' at line ' + r.line : '') + ': ' + r.error);\n      setOutputState('error');\n      setOutputHTML('\u003cspan style=\"color:#45475a\"\u003e// Fix JSON errors first\u003c/span\u003e');\n      setOutStatus('\u003cspan class=\"jsg-err\"\u003eInvalid JSON — cannot generate schema\u003c/span\u003e');\n      document.getElementById('jsg-editor-section').style.display = 'none';\n      return;\n    }\n\n    setInputState('valid');\n    setError('');\n    lastInput = r.value;\n    // Reset propMeta on fresh generate\n    propMeta = {};\n\n    lastSchema = buildRootSchema(lastInput);\n    var pretty = JSON.stringify(lastSchema, null, 2);\n    setOutputHTML(syntaxHL(pretty));\n    setOutputState('valid');\n\n    var propCount = lastSchema.properties ? Object.keys(lastSchema.properties).length : 0;\n    setOutStatus(\n      '\u003cspan class=\"jsg-ok\"\u003eSchema generated\u003c/span\u003e' +\n      '\u003cspan class=\"jsg-info\"\u003e' + schemaVersion + '\u003c/span\u003e' +\n      '\u003cspan\u003eProperties: ' + propCount + '\u003c/span\u003e' +\n      '\u003cspan\u003eChars: ' + pretty.length + '\u003c/span\u003e'\n    );\n\n    renderEditor(lastSchema);\n  };\n\n  window.jsgApplyEdits = function () {\n    if (!lastInput) return;\n\n    // Collect edits from table\n    var descInputs = document.querySelectorAll('#jsg-prop-tbody .jsg-prop-desc-input');\n    var reqChecks  = document.querySelectorAll('#jsg-prop-tbody .jsg-prop-req-chk');\n\n    propMeta = {};\n    for (var i = 0; i \u003c descInputs.length; i++) {\n      var k = descInputs[i].getAttribute('data-key');\n      propMeta[k] = propMeta[k] || {};\n      propMeta[k].description = descInputs[i].value.trim();\n    }\n    for (var j = 0; j \u003c reqChecks.length; j++) {\n      var kk = reqChecks[j].getAttribute('data-key');\n      propMeta[kk] = propMeta[kk] || {};\n      propMeta[kk].required = reqChecks[j].checked;\n    }\n\n    lastSchema = buildRootSchema(lastInput);\n    var pretty = JSON.stringify(lastSchema, null, 2);\n    setOutputHTML(syntaxHL(pretty));\n    setOutputState('valid');\n\n    var propCount = lastSchema.properties ? Object.keys(lastSchema.properties).length : 0;\n    setOutStatus(\n      '\u003cspan class=\"jsg-ok\"\u003eSchema updated\u003c/span\u003e' +\n      '\u003cspan class=\"jsg-info\"\u003e' + schemaVersion + '\u003c/span\u003e' +\n      '\u003cspan\u003eProperties: ' + propCount + '\u003c/span\u003e' +\n      '\u003cspan\u003eChars: ' + pretty.length + '\u003c/span\u003e'\n    );\n    showToast('Schema updated!');\n  };\n\n  window.jsgValidate = function () {\n    var r = parseInput();\n    if (!r) {\n      setError('Enter JSON to validate.');\n      return;\n    }\n    if (!r.ok) {\n      setInputState('error');\n      setError('Parse error' + (r.line ? ' at line ' + r.line : '') + ': ' + r.error);\n      setOutputState('error');\n      setOutputHTML('\u003cspan class=\"jsg-err\" style=\"font-size:15px;font-weight:700;\"\u003eInvalid JSON\u003c/span\u003e');\n      setOutStatus('\u003cspan class=\"jsg-err\"\u003eJSON is not well-formed\u003c/span\u003e');\n      return;\n    }\n\n    if (!lastSchema) {\n      setInputState('valid');\n      setError('');\n      setOutputHTML('\u003cspan class=\"jsg-ok\" style=\"font-size:14px;font-weight:700;\"\u003eValid JSON\u003c/span\u003e\\n\\n\u003cspan style=\"color:#6c7086\"\u003e// Generate a schema first to validate against it.\u003c/span\u003e');\n      setOutputState('valid');\n      setOutStatus('\u003cspan class=\"jsg-ok\"\u003eJSON is well-formed\u003c/span\u003e');\n      return;\n    }\n\n    // Validate parsed value against lastSchema (structural, type-based)\n    var errs = validateAgainstSchema(r.value, lastSchema, '$');\n    if (errs.length === 0) {\n      setInputState('valid');\n      setError('');\n      setOutputHTML('\u003cspan class=\"jsg-ok\" style=\"font-size:15px;font-weight:700;\"\u003eValidation Passed\u003c/span\u003e\\n\\n\u003cspan style=\"color:#6c7086\"\u003e// JSON is valid against the generated schema.\u003c/span\u003e');\n      setOutputState('valid');\n      setOutStatus('\u003cspan class=\"jsg-ok\"\u003ePassed all schema checks\u003c/span\u003e');\n    } else {\n      setInputState('error');\n      setError('');\n      var errLines = errs.map(function (e) { return '  ' + e; }).join('\\n');\n      setOutputHTML('\u003cspan class=\"jsg-err\" style=\"font-size:15px;font-weight:700;\"\u003eValidation Failed\u003c/span\u003e\\n\\n\u003cspan style=\"color:#f38ba8\"\u003e' + esc(errLines) + '\u003c/span\u003e');\n      setOutputState('error');\n      setOutStatus('\u003cspan class=\"jsg-err\"\u003e' + errs.length + ' validation error' + (errs.length \u003e 1 ? 's' : '') + '\u003c/span\u003e');\n    }\n  };\n\n  /* ────────────────────────────────────────\n     Structural validator\n  ──────────────────────────────────────── */\n  function validateAgainstSchema(val, schema, path) {\n    var errors = [];\n    if (!schema) return errors;\n\n    // type check\n    if (schema.type) {\n      var types = Array.isArray(schema.type) ? schema.type : [schema.type];\n      var actualType = getType(val);\n      var matched = false;\n      for (var i = 0; i \u003c types.length; i++) {\n        if (types[i] === actualType) { matched = true; break; }\n        // integer is a subtype of number\n        if (types[i] === 'integer' \u0026\u0026 actualType === 'number' \u0026\u0026 isInteger(val)) { matched = true; break; }\n      }\n      if (!matched) {\n        errors.push(path + ': expected ' + types.join('|') + ', got ' + actualType);\n      }\n    }\n\n    // required\n    if (schema.required \u0026\u0026 typeof val === 'object' \u0026\u0026 val !== null \u0026\u0026 !Array.isArray(val)) {\n      for (var r = 0; r \u003c schema.required.length; r++) {\n        if (!Object.prototype.hasOwnProperty.call(val, schema.required[r])) {\n          errors.push(path + ': missing required property \"' + schema.required[r] + '\"');\n        }\n      }\n    }\n\n    // properties\n    if (schema.properties \u0026\u0026 typeof val === 'object' \u0026\u0026 val !== null \u0026\u0026 !Array.isArray(val)) {\n      var pkeys = Object.keys(schema.properties);\n      for (var p = 0; p \u003c pkeys.length; p++) {\n        var pk = pkeys[p];\n        if (Object.prototype.hasOwnProperty.call(val, pk)) {\n          var subErrs = validateAgainstSchema(val[pk], schema.properties[pk], path + '.' + pk);\n          errors = errors.concat(subErrs);\n        }\n      }\n    }\n\n    // items\n    if (schema.items \u0026\u0026 Array.isArray(val)) {\n      for (var ai = 0; ai \u003c val.length; ai++) {\n        var aErrs = validateAgainstSchema(val[ai], schema.items, path + '[' + ai + ']');\n        errors = errors.concat(aErrs);\n      }\n    }\n\n    return errors;\n  }\n\n  function getType(val) {\n    if (val === null)          return 'null';\n    if (typeof val === 'boolean') return 'boolean';\n    if (typeof val === 'number')  return 'number';\n    if (typeof val === 'string')  return 'string';\n    if (Array.isArray(val))       return 'array';\n    if (typeof val === 'object')  return 'object';\n    return 'unknown';\n  }\n\n  /* ────────────────────────────────────────\n     Copy\n  ──────────────────────────────────────── */\n  window.jsgCopy = function () {\n    var el = document.getElementById('jsg-output');\n    var text = el.innerText || el.textContent;\n    if (!text || text.trim().startsWith('//')) return;\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(text).then(function () { showToast('Copied to clipboard!'); });\n    } else {\n      var ta = document.createElement('textarea');\n      ta.value = text;\n      ta.style.cssText = 'position:fixed;opacity:0';\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      showToast('Copied to clipboard!');\n    }\n  };\n\n  /* ────────────────────────────────────────\n     Clear\n  ──────────────────────────────────────── */\n  window.jsgClear = function () {\n    document.getElementById('jsg-input').value = '';\n    setInputState('');\n    setError('');\n    updateInputStats();\n    setOutputHTML('\u003cspan style=\"color:#45475a\"\u003e// Schema will appear here after clicking Generate Schema\u003c/span\u003e');\n    setOutputState('');\n    setOutStatus('');\n    lastSchema = null;\n    lastInput  = null;\n    propMeta   = {};\n    document.getElementById('jsg-editor-section').style.display = 'none';\n    document.getElementById('jsg-sample-select').value = '';\n  };\n\n  /* ────────────────────────────────────────\n     Sample presets\n  ──────────────────────────────────────── */\n  var SAMPLES = {\n    user: {\n      \"id\": \"a3f8c2d1-4b7e-4f9a-8c1d-2e3f4a5b6c7d\",\n      \"username\": \"alice_wonder\",\n      \"email\": \"alice@example.com\",\n      \"age\": 30,\n      \"isPremium\": true,\n      \"score\": 98.5,\n      \"registeredAt\": \"2024-01-15T08:30:00Z\",\n      \"address\": {\n        \"street\": \"123 Main St\",\n        \"city\": \"Springfield\",\n        \"country\": \"US\",\n        \"postalCode\": \"62701\"\n      },\n      \"tags\": [\"admin\", \"verified\"],\n      \"preferences\": null\n    },\n    product: {\n      \"sku\": \"PROD-00421\",\n      \"name\": \"Ergonomic Keyboard Pro\",\n      \"description\": \"Full-size mechanical keyboard with RGB backlight\",\n      \"price\": 129.99,\n      \"currency\": \"USD\",\n      \"inStock\": true,\n      \"quantity\": 42,\n      \"rating\": 4.7,\n      \"categories\": [\"electronics\", \"peripherals\", \"keyboards\"],\n      \"images\": [\n        {\n          \"url\": \"https://example.com/images/kbd-front.jpg\",\n          \"alt\": \"Front view\",\n          \"isPrimary\": true\n        }\n      ],\n      \"createdAt\": \"2023-11-01T00:00:00Z\",\n      \"updatedAt\": \"2024-06-15T12:45:00Z\"\n    },\n    api: {\n      \"status\": \"success\",\n      \"code\": 200,\n      \"message\": \"Request completed successfully\",\n      \"data\": {\n        \"totalCount\": 1250,\n        \"page\": 1,\n        \"pageSize\": 25,\n        \"hasNextPage\": true,\n        \"items\": [\n          {\n            \"id\": 1,\n            \"title\": \"Sample Item\",\n            \"active\": true,\n            \"score\": 0.92\n          }\n        ]\n      },\n      \"meta\": {\n        \"requestId\": \"req_9f3a2c1d\",\n        \"timestamp\": \"2025-05-16T10:00:00Z\",\n        \"version\": \"v2\"\n      },\n      \"errors\": null\n    }\n  };\n\n  window.jsgLoadSample = function (key) {\n    if (!key || !SAMPLES[key]) return;\n    document.getElementById('jsg-input').value = JSON.stringify(SAMPLES[key], null, 2);\n    updateInputStats();\n    setInputState('');\n    setError('');\n    propMeta = {};\n    jsgGenerate();\n  };\n\n  // Init stats\n  updateInputStats();\n\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"what-is-json-schema\"\u003eWhat is JSON Schema?\u003c/h2\u003e\n\u003cp\u003eJSON Schema is a vocabulary that allows you to annotate and validate JSON documents. It describes the structure of JSON data — what properties an object should have, what types those properties must be, which fields are required, and what formats or constraints apply. A schema acts as a contract between producers and consumers of JSON data, making APIs more predictable and enabling automated validation, documentation generation, and code generation.\u003c/p\u003e","title":"JSON Schema Generator — Free Online Tool"},{"content":"Convert JSON arrays to CSV and CSV back to JSON — right in your browser. No server, no upload, no external libraries.\nJSON → CSV CSV → JSON Delimiter Comma ( , ) Semicolon ( ; ) Tab ( ⇥ ) Pipe ( | ) Include header row Flatten nested JSON JSON Input Load Sample Clear CSV Output Copy Download Convert ↓ Preview How to use Choose the conversion direction — JSON → CSV or CSV → JSON — using the tabs. Paste your data into the left panel (or click Load Sample to try it). Pick a delimiter and toggle header / flatten options as needed. Click Convert. Use Copy or Download to grab the result. Nested JSON When Flatten nested JSON is checked, nested objects are flattened using dot notation. For example:\n{ \u0026#34;address\u0026#34;: { \u0026#34;city\u0026#34;: \u0026#34;Tokyo\u0026#34;, \u0026#34;zip\u0026#34;: \u0026#34;100\u0026#34; } } becomes columns address.city and address.zip. Arrays inside objects are serialised as JSON strings.\nDelimiter reference Name Character Common use Comma , Standard CSV (Excel, Google Sheets) Semicolon ; European locales where comma is decimal separator Tab ⇥ TSV files, paste-friendly Pipe | Log files, markdown-style tables Related: Format JSON → JSON Formatter ","permalink":"https://productivity-works.com/tools/json-to-csv/","summary":"\u003cp\u003eConvert JSON arrays to CSV and CSV back to JSON — right in your browser. No server, no upload, no external libraries.\u003c/p\u003e\n\u003cstyle\u003e\n#j2c-app *,\n#j2c-app *::before,\n#j2c-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\n#j2c-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 15px;\n  color: #1f2937;\n  line-height: 1.5;\n  max-width: 900px;\n  margin: 2rem auto;\n}\n\n#j2c-app h2 {\n  font-size: 1.1rem;\n  font-weight: 600;\n  color: #374151;\n  margin-bottom: 0.5rem;\n}\n\n#j2c-app .j2c-tabs {\n  display: flex;\n  gap: 0.5rem;\n  margin-bottom: 1.5rem;\n}\n\n#j2c-app .j2c-tab {\n  padding: 0.55rem 1.2rem;\n  border: 2px solid #d1fae5;\n  border-radius: 6px;\n  background: #fff;\n  color: #059669;\n  font-weight: 600;\n  font-size: 0.95rem;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s;\n}\n\n#j2c-app .j2c-tab.active {\n  background: #059669;\n  color: #fff;\n  border-color: #059669;\n}\n\n#j2c-app .j2c-tab:hover:not(.active) {\n  background: #d1fae5;\n}\n\n#j2c-app .j2c-controls {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 1rem;\n  align-items: center;\n  background: #f9fafb;\n  border: 1px solid #e5e7eb;\n  border-radius: 8px;\n  padding: 0.85rem 1rem;\n  margin-bottom: 1.25rem;\n}\n\n#j2c-app .j2c-control-group {\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n}\n\n#j2c-app label {\n  font-size: 0.88rem;\n  font-weight: 500;\n  color: #6b7280;\n}\n\n#j2c-app select {\n  padding: 0.3rem 0.6rem;\n  border: 1px solid #d1d5db;\n  border-radius: 5px;\n  font-size: 0.88rem;\n  background: #fff;\n  color: #374151;\n  cursor: pointer;\n}\n\n#j2c-app .j2c-toggle {\n  display: flex;\n  align-items: center;\n  gap: 0.45rem;\n  cursor: pointer;\n  user-select: none;\n}\n\n#j2c-app .j2c-toggle input[type=\"checkbox\"] {\n  width: 16px;\n  height: 16px;\n  accent-color: #059669;\n  cursor: pointer;\n}\n\n#j2c-app .j2c-io-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1rem;\n  margin-bottom: 1.25rem;\n}\n\n@media (max-width: 640px) {\n  #j2c-app .j2c-io-grid {\n    grid-template-columns: 1fr;\n  }\n}\n\n#j2c-app .j2c-pane {\n  display: flex;\n  flex-direction: column;\n  gap: 0.4rem;\n}\n\n#j2c-app textarea {\n  width: 100%;\n  height: 220px;\n  padding: 0.65rem 0.8rem;\n  border: 1px solid #d1d5db;\n  border-radius: 8px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 0.82rem;\n  line-height: 1.5;\n  color: #1f2937;\n  background: #fff;\n  resize: vertical;\n  transition: border-color 0.15s;\n}\n\n#j2c-app textarea:focus {\n  outline: none;\n  border-color: #059669;\n  box-shadow: 0 0 0 3px rgba(5,150,105,0.12);\n}\n\n#j2c-app textarea.j2c-error {\n  border-color: #ef4444;\n  box-shadow: 0 0 0 3px rgba(239,68,68,0.1);\n}\n\n#j2c-app .j2c-pane-actions {\n  display: flex;\n  gap: 0.5rem;\n}\n\n#j2c-app .j2c-btn {\n  padding: 0.4rem 0.9rem;\n  border: none;\n  border-radius: 6px;\n  font-size: 0.82rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, opacity 0.15s;\n}\n\n#j2c-app .j2c-btn-primary {\n  background: #059669;\n  color: #fff;\n}\n\n#j2c-app .j2c-btn-primary:hover {\n  background: #047857;\n}\n\n#j2c-app .j2c-btn-secondary {\n  background: #e5e7eb;\n  color: #374151;\n}\n\n#j2c-app .j2c-btn-secondary:hover {\n  background: #d1d5db;\n}\n\n#j2c-app .j2c-btn:disabled {\n  opacity: 0.45;\n  cursor: not-allowed;\n}\n\n#j2c-app .j2c-convert-row {\n  display: flex;\n  justify-content: center;\n  margin-bottom: 1.25rem;\n}\n\n#j2c-app .j2c-btn-convert {\n  padding: 0.6rem 2rem;\n  background: #059669;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 1rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.15s;\n  letter-spacing: 0.01em;\n}\n\n#j2c-app .j2c-btn-convert:hover {\n  background: #047857;\n}\n\n#j2c-app .j2c-msg {\n  font-size: 0.82rem;\n  padding: 0.35rem 0.7rem;\n  border-radius: 5px;\n  margin-top: 0.25rem;\n  display: none;\n}\n\n#j2c-app .j2c-msg.error {\n  background: #fef2f2;\n  color: #dc2626;\n  border: 1px solid #fca5a5;\n  display: block;\n}\n\n#j2c-app .j2c-msg.success {\n  background: #ecfdf5;\n  color: #059669;\n  border: 1px solid #6ee7b7;\n  display: block;\n}\n\n#j2c-app .j2c-preview-section {\n  margin-top: 0.5rem;\n}\n\n#j2c-app .j2c-preview-section h2 {\n  margin-bottom: 0.6rem;\n}\n\n#j2c-app .j2c-table-wrap {\n  overflow-x: auto;\n  border: 1px solid #e5e7eb;\n  border-radius: 8px;\n  max-height: 320px;\n  overflow-y: auto;\n}\n\n#j2c-app table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.82rem;\n}\n\n#j2c-app th {\n  background: #ecfdf5;\n  color: #065f46;\n  font-weight: 600;\n  padding: 0.5rem 0.75rem;\n  text-align: left;\n  border-bottom: 2px solid #6ee7b7;\n  white-space: nowrap;\n  position: sticky;\n  top: 0;\n}\n\n#j2c-app td {\n  padding: 0.4rem 0.75rem;\n  border-bottom: 1px solid #f3f4f6;\n  color: #374151;\n  max-width: 220px;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n#j2c-app tr:last-child td {\n  border-bottom: none;\n}\n\n#j2c-app tr:nth-child(even) td {\n  background: #f9fafb;\n}\n\n#j2c-app .j2c-preview-meta {\n  font-size: 0.78rem;\n  color: #9ca3af;\n  margin-top: 0.4rem;\n}\n\n#j2c-app .j2c-sample-link {\n  font-size: 0.8rem;\n  color: #059669;\n  text-decoration: underline;\n  cursor: pointer;\n  background: none;\n  border: none;\n  padding: 0;\n}\n\u003c/style\u003e\n\u003cdiv id=\"j2c-app\"\u003e\n  \u003cdiv class=\"j2c-tabs\"\u003e\n    \u003cbutton class=\"j2c-tab active\" id=\"j2c-tab-j2c\" onclick=\"j2cSetMode('j2c')\"\u003eJSON → CSV\u003c/button\u003e\n    \u003cbutton class=\"j2c-tab\" id=\"j2c-tab-c2j\" onclick=\"j2cSetMode('c2j')\"\u003eCSV → JSON\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"j2c-controls\"\u003e\n    \u003cdiv class=\"j2c-control-group\"\u003e\n      \u003clabel for=\"j2c-delim\"\u003eDelimiter\u003c/label\u003e\n      \u003cselect id=\"j2c-delim\"\u003e\n        \u003coption value=\",\"\u003eComma ( , )\u003c/option\u003e\n        \u003coption value=\";\"\u003eSemicolon ( ; )\u003c/option\u003e\n        \u003coption value=\"\u0026#9;\"\u003eTab ( ⇥ )\u003c/option\u003e\n        \u003coption value=\"|\"\u003ePipe ( | )\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"j2c-control-group\"\u003e\n      \u003clabel class=\"j2c-toggle\"\u003e\n        \u003cinput type=\"checkbox\" id=\"j2c-header\" checked\u003e\n        Include header row\n      \u003c/label\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"j2c-control-group\"\u003e\n      \u003clabel class=\"j2c-toggle\"\u003e\n        \u003cinput type=\"checkbox\" id=\"j2c-flatten\" checked\u003e\n        Flatten nested JSON\n      \u003c/label\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"j2c-io-grid\"\u003e\n    \u003cdiv class=\"j2c-pane\"\u003e\n      \u003ch2 id=\"j2c-input-label\"\u003eJSON Input\u003c/h2\u003e\n      \u003ctextarea id=\"j2c-input\" placeholder='Paste JSON here, e.g.\u0026#10;[\u0026#10;  {\"name\":\"Alice\",\"age\":30},\u0026#10;  {\"name\":\"Bob\",\"age\":25}\u0026#10;]'\u003e\u003c/textarea\u003e\n      \u003cdiv class=\"j2c-pane-actions\"\u003e\n        \u003cbutton class=\"j2c-btn j2c-btn-secondary\" onclick=\"j2cLoadSample()\"\u003eLoad Sample\u003c/button\u003e\n        \u003cbutton class=\"j2c-btn j2c-btn-secondary\" onclick=\"j2cClear()\"\u003eClear\u003c/button\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"j2c-msg\" id=\"j2c-input-msg\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"j2c-pane\"\u003e\n      \u003ch2 id=\"j2c-output-label\"\u003eCSV Output\u003c/h2\u003e\n      \u003ctextarea id=\"j2c-output\" readonly placeholder=\"Output will appear here after conversion…\"\u003e\u003c/textarea\u003e\n      \u003cdiv class=\"j2c-pane-actions\"\u003e\n        \u003cbutton class=\"j2c-btn j2c-btn-primary\" id=\"j2c-copy-btn\" onclick=\"j2cCopy()\" disabled\u003eCopy\u003c/button\u003e\n        \u003cbutton class=\"j2c-btn j2c-btn-primary\" id=\"j2c-download-btn\" onclick=\"j2cDownload()\" disabled\u003eDownload\u003c/button\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"j2c-msg\" id=\"j2c-output-msg\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"j2c-convert-row\"\u003e\n    \u003cbutton class=\"j2c-btn-convert\" onclick=\"j2cConvert()\"\u003eConvert ↓\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"j2c-preview-section\" id=\"j2c-preview-section\" style=\"display:none;\"\u003e\n    \u003ch2\u003ePreview\u003c/h2\u003e\n    \u003cdiv class=\"j2c-table-wrap\"\u003e\n      \u003ctable id=\"j2c-preview-table\"\u003e\u003c/table\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"j2c-preview-meta\" id=\"j2c-preview-meta\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var mode = 'j2c'; // 'j2c' or 'c2j'\n\n  window.j2cSetMode = function(m) {\n    mode = m;\n    document.getElementById('j2c-tab-j2c').classList.toggle('active', m === 'j2c');\n    document.getElementById('j2c-tab-c2j').classList.toggle('active', m === 'c2j');\n    document.getElementById('j2c-input-label').textContent  = m === 'j2c' ? 'JSON Input'  : 'CSV Input';\n    document.getElementById('j2c-output-label').textContent = m === 'j2c' ? 'CSV Output'  : 'JSON Output';\n    document.getElementById('j2c-input').placeholder =\n      m === 'j2c'\n        ? 'Paste JSON here, e.g.\\n[\\n  {\"name\":\"Alice\",\"age\":30},\\n  {\"name\":\"Bob\",\"age\":25}\\n]'\n        : 'Paste CSV here, e.g.\\nname,age\\nAlice,30\\nBob,25';\n    j2cClearOutput();\n    hidePreview();\n  };\n\n  // ── Flatten nested object ──────────────────────────────────────────────────\n  function flattenObj(obj, prefix) {\n    prefix = prefix || '';\n    var out = {};\n    for (var key in obj) {\n      if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;\n      var full = prefix ? prefix + '.' + key : key;\n      var val  = obj[key];\n      if (val !== null \u0026\u0026 typeof val === 'object' \u0026\u0026 !Array.isArray(val)) {\n        var nested = flattenObj(val, full);\n        for (var nk in nested) out[nk] = nested[nk];\n      } else {\n        out[full] = Array.isArray(val) ? JSON.stringify(val) : val;\n      }\n    }\n    return out;\n  }\n\n  // ── Escape a CSV cell ──────────────────────────────────────────────────────\n  function csvCell(val, delim) {\n    if (val === null || val === undefined) return '';\n    var s = String(val);\n    if (s.indexOf(delim) !== -1 || s.indexOf('\"') !== -1 || s.indexOf('\\n') !== -1) {\n      return '\"' + s.replace(/\"/g, '\"\"') + '\"';\n    }\n    return s;\n  }\n\n  // ── JSON → CSV ─────────────────────────────────────────────────────────────\n  function jsonToCsv(jsonStr, delim, includeHeader, flatten) {\n    var data;\n    try { data = JSON.parse(jsonStr); } catch(e) { throw new Error('Invalid JSON: ' + e.message); }\n    if (!Array.isArray(data)) throw new Error('JSON must be an array of objects at the top level.');\n    if (data.length === 0) throw new Error('JSON array is empty.');\n\n    var rows = data.map(function(item) {\n      if (typeof item !== 'object' || item === null) throw new Error('Each array element must be an object.');\n      return flatten ? flattenObj(item) : item;\n    });\n\n    // Collect all keys preserving insertion order\n    var keySeen = {};\n    var keys = [];\n    rows.forEach(function(r) {\n      Object.keys(r).forEach(function(k) {\n        if (!keySeen[k]) { keySeen[k] = true; keys.push(k); }\n      });\n    });\n\n    var lines = [];\n    if (includeHeader) lines.push(keys.map(function(k) { return csvCell(k, delim); }).join(delim));\n    rows.forEach(function(r) {\n      lines.push(keys.map(function(k) { return csvCell(r[k], delim); }).join(delim));\n    });\n    return { csv: lines.join('\\n'), keys: keys, rows: rows };\n  }\n\n  // ── CSV → JSON ─────────────────────────────────────────────────────────────\n  function csvToJson(csvStr, delim, hasHeader) {\n    var lines = csvStr.trim().split(/\\r?\\n/);\n    if (lines.length === 0) throw new Error('CSV is empty.');\n\n    function parseLine(line) {\n      var cells = [];\n      var cur = '';\n      var inQ = false;\n      for (var i = 0; i \u003c line.length; i++) {\n        var ch = line[i];\n        if (inQ) {\n          if (ch === '\"' \u0026\u0026 line[i+1] === '\"') { cur += '\"'; i++; }\n          else if (ch === '\"') { inQ = false; }\n          else { cur += ch; }\n        } else {\n          if (ch === '\"') { inQ = true; }\n          else if (ch === delim) { cells.push(cur); cur = ''; }\n          else { cur += ch; }\n        }\n      }\n      cells.push(cur);\n      return cells;\n    }\n\n    var keys, dataLines;\n    if (hasHeader) {\n      keys = parseLine(lines[0]);\n      dataLines = lines.slice(1);\n    } else {\n      var first = parseLine(lines[0]);\n      keys = first.map(function(_, i) { return 'col' + (i+1); });\n      dataLines = lines;\n    }\n\n    var rows = dataLines.filter(function(l) { return l.trim() !== ''; }).map(function(line) {\n      var cells = parseLine(line);\n      var obj = {};\n      keys.forEach(function(k, i) {\n        var v = cells[i] !== undefined ? cells[i] : '';\n        // coerce numbers and booleans\n        if (v === 'true') obj[k] = true;\n        else if (v === 'false') obj[k] = false;\n        else if (v !== '' \u0026\u0026 !isNaN(Number(v))) obj[k] = Number(v);\n        else obj[k] = v;\n      });\n      return obj;\n    });\n\n    return { json: JSON.stringify(rows, null, 2), keys: keys, rows: rows };\n  }\n\n  // ── Preview ────────────────────────────────────────────────────────────────\n  function renderPreview(keys, rows, total) {\n    var section = document.getElementById('j2c-preview-section');\n    var table   = document.getElementById('j2c-preview-table');\n    var meta    = document.getElementById('j2c-preview-meta');\n    section.style.display = '';\n\n    var preview = rows.slice(0, 50);\n    var thead = '\u003cthead\u003e\u003ctr\u003e' + keys.map(function(k) { return '\u003cth\u003e' + esc(k) + '\u003c/th\u003e'; }).join('') + '\u003c/tr\u003e\u003c/thead\u003e';\n    var tbody = '\u003ctbody\u003e' + preview.map(function(r) {\n      return '\u003ctr\u003e' + keys.map(function(k) {\n        var v = r[k]; if (v === null || v === undefined) v = '';\n        return '\u003ctd title=\"' + esc(String(v)) + '\"\u003e' + esc(String(v)) + '\u003c/td\u003e';\n      }).join('') + '\u003c/tr\u003e';\n    }).join('') + '\u003c/tbody\u003e';\n    table.innerHTML = thead + tbody;\n\n    var shown = preview.length;\n    meta.textContent = shown + ' of ' + total + ' row(s) shown' + (total \u003e 50 ? ' (first 50)' : '') + ' · ' + keys.length + ' column(s)';\n  }\n\n  function hidePreview() {\n    document.getElementById('j2c-preview-section').style.display = 'none';\n  }\n\n  function esc(s) {\n    return s.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;').replace(/\"/g,'\u0026quot;');\n  }\n\n  // ── UI helpers ─────────────────────────────────────────────────────────────\n  function showMsg(id, text, type) {\n    var el = document.getElementById(id);\n    el.textContent = text;\n    el.className = 'j2c-msg ' + type;\n  }\n  function clearMsg(id) {\n    var el = document.getElementById(id);\n    el.textContent = '';\n    el.className = 'j2c-msg';\n  }\n  function setOutput(val) {\n    var out = document.getElementById('j2c-output');\n    out.value = val;\n    var haVal = val \u0026\u0026 val.trim() !== '';\n    document.getElementById('j2c-copy-btn').disabled     = !haVal;\n    document.getElementById('j2c-download-btn').disabled = !haVal;\n  }\n  function j2cClearOutput() {\n    setOutput('');\n    clearMsg('j2c-input-msg');\n    clearMsg('j2c-output-msg');\n  }\n\n  window.j2cClear = function() {\n    document.getElementById('j2c-input').value = '';\n    j2cClearOutput();\n    hidePreview();\n  };\n\n  window.j2cLoadSample = function() {\n    if (mode === 'j2c') {\n      document.getElementById('j2c-input').value = JSON.stringify([\n        { name: \"Alice\", age: 30, address: { city: \"New York\", zip: \"10001\" }, active: true },\n        { name: \"Bob\",   age: 25, address: { city: \"London\",   zip: \"EC1A\"  }, active: false },\n        { name: \"Carol\", age: 35, address: { city: \"Tokyo\",    zip: \"100-0001\" }, active: true }\n      ], null, 2);\n    } else {\n      document.getElementById('j2c-input').value =\n        'name,age,city\\nAlice,30,New York\\nBob,25,London\\nCarol,35,Tokyo';\n    }\n    j2cClearOutput();\n    hidePreview();\n  };\n\n  window.j2cConvert = function() {\n    clearMsg('j2c-input-msg');\n    clearMsg('j2c-output-msg');\n    var input  = document.getElementById('j2c-input').value.trim();\n    var delim  = document.getElementById('j2c-delim').value;\n    var header = document.getElementById('j2c-header').checked;\n    var flatten = document.getElementById('j2c-flatten').checked;\n\n    if (!input) {\n      showMsg('j2c-input-msg', 'Please paste some input first.', 'error');\n      document.getElementById('j2c-input').classList.add('j2c-error');\n      return;\n    }\n    document.getElementById('j2c-input').classList.remove('j2c-error');\n\n    try {\n      if (mode === 'j2c') {\n        var res = jsonToCsv(input, delim, header, flatten);\n        setOutput(res.csv);\n        renderPreview(res.keys, res.rows, res.rows.length);\n        showMsg('j2c-output-msg', 'Converted successfully — ' + res.rows.length + ' row(s), ' + res.keys.length + ' column(s).', 'success');\n      } else {\n        var res = csvToJson(input, delim, header);\n        setOutput(res.json);\n        renderPreview(res.keys, res.rows, res.rows.length);\n        showMsg('j2c-output-msg', 'Converted successfully — ' + res.rows.length + ' row(s), ' + res.keys.length + ' column(s).', 'success');\n      }\n    } catch(e) {\n      showMsg('j2c-input-msg', e.message, 'error');\n      document.getElementById('j2c-input').classList.add('j2c-error');\n      setOutput('');\n      hidePreview();\n    }\n  };\n\n  window.j2cCopy = function() {\n    var val = document.getElementById('j2c-output').value;\n    if (!val) return;\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(val).then(function() {\n        showMsg('j2c-output-msg', 'Copied to clipboard!', 'success');\n      }).catch(function() { fallbackCopy(val); });\n    } else {\n      fallbackCopy(val);\n    }\n  };\n\n  function fallbackCopy(text) {\n    var ta = document.createElement('textarea');\n    ta.value = text;\n    ta.style.position = 'fixed';\n    ta.style.opacity = '0';\n    document.body.appendChild(ta);\n    ta.select();\n    try {\n      document.execCommand('copy');\n      showMsg('j2c-output-msg', 'Copied to clipboard!', 'success');\n    } catch(e) {\n      showMsg('j2c-output-msg', 'Copy failed — please select and copy manually.', 'error');\n    }\n    document.body.removeChild(ta);\n  }\n\n  window.j2cDownload = function() {\n    var val = document.getElementById('j2c-output').value;\n    if (!val) return;\n    var ext  = mode === 'j2c' ? 'csv' : 'json';\n    var mime = mode === 'j2c' ? 'text/csv' : 'application/json';\n    var blob = new Blob([val], { type: mime + ';charset=utf-8;' });\n    var url  = URL.createObjectURL(blob);\n    var a    = document.createElement('a');\n    a.href     = url;\n    a.download = 'converted.' + ext;\n    a.style.display = 'none';\n    document.body.appendChild(a);\n    a.click();\n    setTimeout(function() { URL.revokeObjectURL(url); document.body.removeChild(a); }, 500);\n  };\n\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-to-use\"\u003eHow to use\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003eChoose the conversion direction — \u003cstrong\u003eJSON → CSV\u003c/strong\u003e or \u003cstrong\u003eCSV → JSON\u003c/strong\u003e — using the tabs.\u003c/li\u003e\n\u003cli\u003ePaste your data into the left panel (or click \u003cstrong\u003eLoad Sample\u003c/strong\u003e to try it).\u003c/li\u003e\n\u003cli\u003ePick a delimiter and toggle header / flatten options as needed.\u003c/li\u003e\n\u003cli\u003eClick \u003cstrong\u003eConvert\u003c/strong\u003e.\u003c/li\u003e\n\u003cli\u003eUse \u003cstrong\u003eCopy\u003c/strong\u003e or \u003cstrong\u003eDownload\u003c/strong\u003e to grab the result.\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"nested-json\"\u003eNested JSON\u003c/h2\u003e\n\u003cp\u003eWhen \u003cstrong\u003eFlatten nested JSON\u003c/strong\u003e is checked, nested objects are flattened using dot notation. For example:\u003c/p\u003e","title":"JSON to CSV Converter — Free Online Tool"},{"content":" \u0026#10003; Validate \u0026#9776; Format \u0026#8644; Minify \u0026#9654; Tree View \u0026#10005; Clear \u0026#8613; Upload JSON Indent: 2 spaces 4 spaces Tab Sample: — load sample — User object Array of items Nested structure \u0026#128193; Drop a .json file here, or click to browse \u0026#9432; Paste or type JSON above, then click Validate or Format. Path: — INPUT OUTPUT Copy Tree View Raw Related Tools Convert JSON to CSV → JSON to CSV Converter Format HTML → HTML Beautifier Test regex patterns → Regex Tester ","permalink":"https://productivity-works.com/tools/json-validator/","summary":"\u003cdiv id=\"jv-app\"\u003e\n\u003cstyle\u003e\n#jv-app *, #jv-app *::before, #jv-app *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n#jv-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 14px;\n  color: #1e293b;\n  line-height: 1.5;\n}\n\n/* ── Toolbar ── */\n#jv-app .jv-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  align-items: center;\n  margin-bottom: 10px;\n}\n\n#jv-app .jv-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 5px;\n  padding: 7px 14px;\n  border: none;\n  border-radius: 6px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s;\n  white-space: nowrap;\n  line-height: 1;\n}\n#jv-app .jv-btn:active { transform: scale(0.97); }\n#jv-app .jv-btn-primary   { background: #2563eb; color: #fff; }\n#jv-app .jv-btn-primary:hover   { background: #1d4ed8; }\n#jv-app .jv-btn-secondary { background: #e2e8f0; color: #334155; }\n#jv-app .jv-btn-secondary:hover { background: #cbd5e1; }\n#jv-app .jv-btn-success   { background: #16a34a; color: #fff; }\n#jv-app .jv-btn-success:hover   { background: #15803d; }\n#jv-app .jv-btn-orange    { background: #d97706; color: #fff; }\n#jv-app .jv-btn-orange:hover    { background: #b45309; }\n#jv-app .jv-btn-danger    { background: #dc2626; color: #fff; }\n#jv-app .jv-btn-danger:hover    { background: #b91c1c; }\n#jv-app .jv-btn-purple    { background: #7c3aed; color: #fff; }\n#jv-app .jv-btn-purple:hover    { background: #6d28d9; }\n\n/* ── Options bar ── */\n#jv-app .jv-options-bar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  align-items: center;\n  padding: 8px 12px;\n  background: #f1f5f9;\n  border-radius: 8px;\n  margin-bottom: 10px;\n}\n\n#jv-app .jv-select {\n  padding: 6px 10px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 13px;\n  background: #fff;\n  color: #334155;\n  cursor: pointer;\n}\n#jv-app .jv-options-bar label {\n  font-size: 13px;\n  color: #475569;\n  display: flex;\n  align-items: center;\n  gap: 5px;\n}\n\n/* ── Panels ── */\n#jv-app .jv-panels {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 12px;\n  margin-bottom: 12px;\n}\n@media (max-width: 660px) {\n  #jv-app .jv-panels { grid-template-columns: 1fr; }\n}\n\n#jv-app .jv-pane-label {\n  font-size: 11px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  color: #64748b;\n  margin-bottom: 5px;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n}\n\n#jv-app .jv-pane-label .jv-copy-btn {\n  padding: 3px 9px;\n  background: #e2e8f0;\n  color: #334155;\n  border: none;\n  border-radius: 5px;\n  font-size: 11px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s;\n  text-transform: none;\n  letter-spacing: 0;\n}\n#jv-app .jv-pane-label .jv-copy-btn:hover { background: #cbd5e1; }\n\n#jv-app textarea {\n  width: 100%;\n  height: 320px;\n  padding: 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-family: \"JetBrains Mono\", \"Fira Code\", \"Cascadia Code\", Consolas, monospace;\n  font-size: 12.5px;\n  line-height: 1.65;\n  resize: vertical;\n  background: #f8fafc;\n  color: #0f172a;\n  transition: border-color 0.15s;\n}\n#jv-app textarea:focus { outline: none; border-color: #2563eb; }\n#jv-app textarea.jv-error  { border-color: #dc2626; background: #fff5f5; }\n#jv-app textarea.jv-valid  { border-color: #16a34a; }\n\n/* ── Status badge ── */\n#jv-app .jv-status {\n  display: flex;\n  align-items: flex-start;\n  gap: 8px;\n  padding: 10px 14px;\n  border-radius: 8px;\n  font-size: 13px;\n  font-weight: 500;\n  margin-bottom: 12px;\n  min-height: 42px;\n}\n#jv-app .jv-status.jv-status-idle    { background: #f1f5f9; color: #64748b; }\n#jv-app .jv-status.jv-status-valid   { background: #f0fdf4; color: #15803d; border: 1px solid #bbf7d0; }\n#jv-app .jv-status.jv-status-error   { background: #fef2f2; color: #b91c1c; border: 1px solid #fecaca; }\n#jv-app .jv-status-icon { font-size: 16px; line-height: 1.3; flex-shrink: 0; }\n#jv-app .jv-status-msg  { flex: 1; word-break: break-all; font-family: \"JetBrains Mono\", Consolas, monospace; font-size: 12px; }\n\n/* ── Stats bar ── */\n#jv-app .jv-stats {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 12px;\n}\n#jv-app .jv-stat-pill {\n  padding: 4px 11px;\n  background: #e0f2fe;\n  color: #0369a1;\n  border-radius: 20px;\n  font-size: 12px;\n  font-weight: 600;\n}\n\n/* ── Path display ── */\n#jv-app .jv-path-bar {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 7px 12px;\n  background: #fefce8;\n  border: 1px solid #fde68a;\n  border-radius: 7px;\n  margin-bottom: 10px;\n  font-size: 12px;\n  font-family: \"JetBrains Mono\", Consolas, monospace;\n  color: #92400e;\n  min-height: 34px;\n  word-break: break-all;\n}\n#jv-app .jv-path-bar .jv-path-label {\n  font-weight: 700;\n  flex-shrink: 0;\n  color: #78350f;\n}\n\n/* ── Tree view ── */\n#jv-app .jv-tree-wrap {\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  background: #f8fafc;\n  padding: 12px 14px;\n  max-height: 420px;\n  overflow: auto;\n  font-family: \"JetBrains Mono\", \"Fira Code\", Consolas, monospace;\n  font-size: 12px;\n  line-height: 1.7;\n  display: none;\n}\n#jv-app .jv-tree-wrap.jv-visible { display: block; }\n\n#jv-app .jv-tree-node { padding-left: 0; }\n#jv-app .jv-tree-children { padding-left: 18px; border-left: 1.5px dotted #cbd5e1; margin-left: 6px; }\n\n#jv-app .jv-node-row {\n  display: flex;\n  align-items: baseline;\n  gap: 4px;\n  cursor: pointer;\n  border-radius: 4px;\n  padding: 1px 4px;\n  user-select: none;\n}\n#jv-app .jv-node-row:hover { background: #e0f2fe; }\n\n#jv-app .jv-toggle {\n  display: inline-block;\n  width: 14px;\n  text-align: center;\n  color: #94a3b8;\n  font-size: 10px;\n  flex-shrink: 0;\n}\n#jv-app .jv-key   { color: #7c3aed; font-weight: 600; }\n#jv-app .jv-colon { color: #64748b; margin-right: 2px; }\n#jv-app .jv-val-string  { color: #059669; }\n#jv-app .jv-val-number  { color: #2563eb; }\n#jv-app .jv-val-bool    { color: #d97706; font-weight: 600; }\n#jv-app .jv-val-null    { color: #94a3b8; font-style: italic; }\n#jv-app .jv-val-bracket { color: #64748b; }\n#jv-app .jv-count       { color: #94a3b8; font-size: 10px; margin-left: 3px; }\n\n/* ── Tab group ── */\n#jv-app .jv-tabs {\n  display: flex;\n  gap: 0;\n  border-bottom: 2px solid #e2e8f0;\n  margin-bottom: 10px;\n}\n#jv-app .jv-tab {\n  padding: 7px 16px;\n  font-size: 13px;\n  font-weight: 600;\n  color: #64748b;\n  border: none;\n  background: none;\n  cursor: pointer;\n  border-bottom: 2px solid transparent;\n  margin-bottom: -2px;\n  transition: color 0.15s, border-color 0.15s;\n}\n#jv-app .jv-tab:hover { color: #2563eb; }\n#jv-app .jv-tab.active { color: #2563eb; border-bottom-color: #2563eb; }\n\n/* ── File upload ── */\n#jv-app .jv-upload-zone {\n  border: 2px dashed #94a3b8;\n  border-radius: 8px;\n  padding: 14px 20px;\n  text-align: center;\n  font-size: 13px;\n  color: #64748b;\n  cursor: pointer;\n  transition: border-color 0.15s, background 0.15s;\n  margin-bottom: 10px;\n}\n#jv-app .jv-upload-zone:hover { border-color: #2563eb; background: #eff6ff; color: #1d4ed8; }\n#jv-app input[type=\"file\"] { display: none; }\n\n/* ── Output section ── */\n#jv-app .jv-output-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 5px;\n}\n\u003c/style\u003e\n\u003c!-- ── Toolbar ── --\u003e\n\u003cdiv class=\"jv-toolbar\"\u003e\n  \u003cbutton class=\"jv-btn jv-btn-primary\" onclick=\"jvValidate()\"\u003e\u0026#10003; Validate\u003c/button\u003e\n  \u003cbutton class=\"jv-btn jv-btn-success\" onclick=\"jvFormat()\"\u003e\u0026#9776; Format\u003c/button\u003e\n  \u003cbutton class=\"jv-btn jv-btn-orange\"  onclick=\"jvMinify()\"\u003e\u0026#8644; Minify\u003c/button\u003e\n  \u003cbutton class=\"jv-btn jv-btn-purple\"  onclick=\"jvToggleTree()\"\u003e\u0026#9654; Tree View\u003c/button\u003e\n  \u003cbutton class=\"jv-btn jv-btn-secondary\" onclick=\"jvClear()\"\u003e\u0026#10005; Clear\u003c/button\u003e\n  \u003clabel class=\"jv-btn jv-btn-secondary\" style=\"cursor:pointer;\" for=\"jv-file-input\"\u003e\u0026#8613; Upload JSON\u003c/label\u003e\n  \u003cinput type=\"file\" id=\"jv-file-input\" accept=\".json,application/json\" onchange=\"jvLoadFile(event)\"\u003e\n\u003c/div\u003e\n\u003c!-- ── Options ── --\u003e\n\u003cdiv class=\"jv-options-bar\"\u003e\n  \u003clabel\u003eIndent:\n    \u003cselect class=\"jv-select\" id=\"jv-indent\"\u003e\n      \u003coption value=\"2\" selected\u003e2 spaces\u003c/option\u003e\n      \u003coption value=\"4\"\u003e4 spaces\u003c/option\u003e\n      \u003coption value=\"tab\"\u003eTab\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/label\u003e\n  \u003clabel\u003eSample:\n    \u003cselect class=\"jv-select\" id=\"jv-sample\" onchange=\"jvLoadSample()\"\u003e\n      \u003coption value=\"\"\u003e— load sample —\u003c/option\u003e\n      \u003coption value=\"user\"\u003eUser object\u003c/option\u003e\n      \u003coption value=\"array\"\u003eArray of items\u003c/option\u003e\n      \u003coption value=\"nested\"\u003eNested structure\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/label\u003e\n\u003c/div\u003e\n\u003c!-- ── Upload drop zone ── --\u003e\n\u003cdiv class=\"jv-upload-zone\" id=\"jv-drop-zone\"\n  ondragover=\"event.preventDefault();this.style.borderColor='#2563eb';this.style.background='#eff6ff';\"\n  ondragleave=\"this.style.borderColor='';this.style.background='';\"\n  ondrop=\"jvHandleDrop(event)\"\n  onclick=\"document.getElementById('jv-file-input').click()\"\u003e\n  \u0026#128193; Drop a \u003cstrong\u003e.json\u003c/strong\u003e file here, or click to browse\n\u003c/div\u003e\n\u003c!-- ── Status ── --\u003e\n\u003cdiv class=\"jv-status jv-status-idle\" id=\"jv-status\"\u003e\n  \u003cspan class=\"jv-status-icon\"\u003e\u0026#9432;\u003c/span\u003e\n  \u003cspan class=\"jv-status-msg\"\u003ePaste or type JSON above, then click Validate or Format.\u003c/span\u003e\n\u003c/div\u003e\n\u003c!-- ── Stats ── --\u003e\n\u003cdiv class=\"jv-stats\" id=\"jv-stats\" style=\"display:none;\"\u003e\u003c/div\u003e\n\u003c!-- ── Path display ── --\u003e\n\u003cdiv class=\"jv-path-bar\" id=\"jv-path-bar\" style=\"display:none;\"\u003e\n  \u003cspan class=\"jv-path-label\"\u003ePath:\u003c/span\u003e\n  \u003cspan id=\"jv-path-text\"\u003e—\u003c/span\u003e\n\u003c/div\u003e\n\u003c!-- ── Input / Output panels ── --\u003e\n\u003cdiv class=\"jv-panels\"\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"jv-pane-label\"\u003eINPUT\u003c/div\u003e\n    \u003ctextarea id=\"jv-input\" placeholder='Paste JSON here…\u0026#10;\u0026#10;Example:\u0026#10;{\u0026#10;  \"name\": \"Alice\",\u0026#10;  \"age\": 30\u0026#10;}' spellcheck=\"false\" oninput=\"jvOnInput()\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"jv-pane-label\"\u003e\n      OUTPUT\n      \u003cbutton class=\"jv-copy-btn\" onclick=\"jvCopyOutput()\"\u003eCopy\u003c/button\u003e\n    \u003c/div\u003e\n    \u003ctextarea id=\"jv-output\" readonly placeholder=\"Formatted / minified result will appear here…\" spellcheck=\"false\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ── Tabs: Tree / Raw ── --\u003e\n\u003cdiv class=\"jv-tabs\"\u003e\n  \u003cbutton class=\"jv-tab active\" id=\"jv-tab-tree\" onclick=\"jvSwitchTab('tree')\"\u003eTree View\u003c/button\u003e\n  \u003cbutton class=\"jv-tab\" id=\"jv-tab-raw\"  onclick=\"jvSwitchTab('raw')\"\u003eRaw\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- ── Tree view container ── --\u003e\n\u003cdiv class=\"jv-tree-wrap jv-visible\" id=\"jv-tree\"\u003e\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  /* ── Samples ── */\n  const SAMPLES = {\n    user: '{\\n  \"id\": 1,\\n  \"name\": \"Alice Johnson\",\\n  \"age\": 30,\\n  \"email\": \"alice@example.com\",\\n  \"address\": {\\n    \"street\": \"123 Main St\",\\n    \"city\": \"Springfield\",\\n    \"zip\": \"12345\"\\n  },\\n  \"tags\": [\"developer\", \"designer\"],\\n  \"active\": true,\\n  \"score\": null\\n}',\n    array: '[\\n  { \"id\": 1, \"product\": \"Widget A\", \"price\": 9.99, \"inStock\": true },\\n  { \"id\": 2, \"product\": \"Widget B\", \"price\": 14.50, \"inStock\": false },\\n  { \"id\": 3, \"product\": \"Widget C\", \"price\": 4.25, \"inStock\": true }\\n]',\n    nested: '{\\n  \"company\": \"Acme Corp\",\\n  \"founded\": 1985,\\n  \"departments\": [\\n    {\\n      \"name\": \"Engineering\",\\n      \"headcount\": 42,\\n      \"lead\": { \"name\": \"Bob Smith\", \"email\": \"bob@acme.com\" }\\n    },\\n    {\\n      \"name\": \"Marketing\",\\n      \"headcount\": 18,\\n      \"lead\": { \"name\": \"Carol Lee\", \"email\": \"carol@acme.com\" }\\n    }\\n  ],\\n  \"public\": false,\\n  \"revenue\": null\\n}'\n  };\n\n  /* ── Helpers ── */\n  function el(id) { return document.getElementById(id); }\n\n  function getIndent() {\n    const v = el('jv-indent').value;\n    return v === 'tab' ? '\\t' : parseInt(v, 10);\n  }\n\n  function setStatus(type, icon, msg) {\n    const s = el('jv-status');\n    s.className = 'jv-status jv-status-' + type;\n    s.innerHTML = '\u003cspan class=\"jv-status-icon\"\u003e' + icon + '\u003c/span\u003e\u003cspan class=\"jv-status-msg\"\u003e' + escHtml(msg) + '\u003c/span\u003e';\n  }\n\n  function escHtml(s) {\n    return String(s).replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n  }\n\n  function showStats(parsed, rawLen) {\n    const statsEl = el('jv-stats');\n    const depth = calcDepth(parsed);\n    const keys  = countKeys(parsed);\n    statsEl.style.display = 'flex';\n    statsEl.innerHTML =\n      pill('Keys: ' + keys) +\n      pill('Depth: ' + depth) +\n      pill('Size: ' + fmtBytes(rawLen)) +\n      pill('Type: ' + (Array.isArray(parsed) ? 'Array' : typeof parsed === 'object' \u0026\u0026 parsed !== null ? 'Object' : typeof parsed));\n  }\n\n  function pill(t) { return '\u003cspan class=\"jv-stat-pill\"\u003e' + escHtml(t) + '\u003c/span\u003e'; }\n\n  function fmtBytes(n) {\n    if (n \u003c 1024) return n + ' B';\n    if (n \u003c 1024*1024) return (n/1024).toFixed(1) + ' KB';\n    return (n/1024/1024).toFixed(2) + ' MB';\n  }\n\n  function calcDepth(v, d) {\n    d = d || 0;\n    if (v \u0026\u0026 typeof v === 'object') {\n      const vals = Object.values(v);\n      if (!vals.length) return d + 1;\n      return Math.max(...vals.map(c =\u003e calcDepth(c, d + 1)));\n    }\n    return d;\n  }\n\n  function countKeys(v) {\n    if (!v || typeof v !== 'object') return 0;\n    let c = Object.keys(v).length;\n    Object.values(v).forEach(ch =\u003e { c += countKeys(ch); });\n    return c;\n  }\n\n  /* ── Parse with error location ── */\n  function parseWithLocation(text) {\n    try {\n      return { ok: true, value: JSON.parse(text) };\n    } catch (e) {\n      // Extract position from native error\n      const msg = e.message;\n      // Try to find line/column from position mention\n      const posMatch = msg.match(/position (\\d+)/i);\n      let lineNum = null, colNum = null, hint = msg;\n      if (posMatch) {\n        const pos = parseInt(posMatch[1], 10);\n        const before = text.slice(0, pos);\n        const lines = before.split('\\n');\n        lineNum = lines.length;\n        colNum = lines[lines.length - 1].length + 1;\n        hint = msg.replace(/position \\d+/i, 'line ' + lineNum + ', col ' + colNum);\n      }\n      return { ok: false, message: hint, line: lineNum, col: colNum };\n    }\n  }\n\n  /* ── Core actions ── */\n  window.jvValidate = function () {\n    const raw = el('jv-input').value.trim();\n    if (!raw) { setStatus('idle', '\u0026#9432;', 'Input is empty.'); return; }\n    const result = parseWithLocation(raw);\n    if (result.ok) {\n      el('jv-input').className = 'jv-valid';\n      setStatus('valid', '\u0026#10003;', 'Valid JSON');\n      showStats(result.value, raw.length);\n      renderTree(result.value);\n    } else {\n      el('jv-input').className = 'jv-error';\n      setStatus('error', '\u0026#10007;', 'SyntaxError: ' + result.message);\n      el('jv-stats').style.display = 'none';\n      el('jv-tree').innerHTML = '';\n    }\n  };\n\n  window.jvFormat = function () {\n    const raw = el('jv-input').value.trim();\n    if (!raw) { setStatus('idle', '\u0026#9432;', 'Input is empty.'); return; }\n    const result = parseWithLocation(raw);\n    if (!result.ok) {\n      el('jv-input').className = 'jv-error';\n      setStatus('error', '\u0026#10007;', 'SyntaxError: ' + result.message);\n      return;\n    }\n    const formatted = JSON.stringify(result.value, null, getIndent());\n    el('jv-output').value = formatted;\n    el('jv-input').className = 'jv-valid';\n    setStatus('valid', '\u0026#10003;', 'Formatted successfully');\n    showStats(result.value, raw.length);\n    renderTree(result.value);\n  };\n\n  window.jvMinify = function () {\n    const raw = el('jv-input').value.trim();\n    if (!raw) { setStatus('idle', '\u0026#9432;', 'Input is empty.'); return; }\n    const result = parseWithLocation(raw);\n    if (!result.ok) {\n      el('jv-input').className = 'jv-error';\n      setStatus('error', '\u0026#10007;', 'SyntaxError: ' + result.message);\n      return;\n    }\n    const minified = JSON.stringify(result.value);\n    el('jv-output').value = minified;\n    el('jv-input').className = 'jv-valid';\n    setStatus('valid', '\u0026#10003;', 'Minified — ' + fmtBytes(minified.length));\n    showStats(result.value, raw.length);\n    renderTree(result.value);\n  };\n\n  window.jvClear = function () {\n    el('jv-input').value = '';\n    el('jv-output').value = '';\n    el('jv-input').className = '';\n    el('jv-stats').style.display = 'none';\n    el('jv-tree').innerHTML = '';\n    el('jv-path-bar').style.display = 'none';\n    el('jv-sample').value = '';\n    setStatus('idle', '\u0026#9432;', 'Paste or type JSON above, then click Validate or Format.');\n  };\n\n  window.jvCopyOutput = function () {\n    const out = el('jv-output');\n    if (!out.value) return;\n    navigator.clipboard.writeText(out.value).then(function () {\n      const btn = document.querySelector('#jv-app .jv-copy-btn');\n      const orig = btn.textContent;\n      btn.textContent = 'Copied!';\n      setTimeout(function () { btn.textContent = orig; }, 1500);\n    }).catch(function () {\n      out.select();\n      document.execCommand('copy');\n    });\n  };\n\n  window.jvOnInput = function () {\n    el('jv-input').className = '';\n    setStatus('idle', '\u0026#9432;', 'Paste or type JSON above, then click Validate or Format.');\n    el('jv-stats').style.display = 'none';\n    el('jv-tree').innerHTML = '';\n    el('jv-path-bar').style.display = 'none';\n  };\n\n  window.jvLoadSample = function () {\n    const key = el('jv-sample').value;\n    if (!key || !SAMPLES[key]) return;\n    el('jv-input').value = SAMPLES[key];\n    el('jv-input').className = '';\n    setStatus('idle', '\u0026#9432;', 'Sample loaded. Click Format or Validate.');\n    el('jv-stats').style.display = 'none';\n    el('jv-tree').innerHTML = '';\n    el('jv-path-bar').style.display = 'none';\n  };\n\n  /* ── File upload ── */\n  window.jvLoadFile = function (e) {\n    const file = e.target.files[0];\n    if (!file) return;\n    const reader = new FileReader();\n    reader.onload = function (ev) {\n      el('jv-input').value = ev.target.result;\n      el('jv-input').className = '';\n      setStatus('idle', '\u0026#9432;', 'File loaded: ' + file.name + ' (' + fmtBytes(file.size) + '). Click Validate or Format.');\n      el('jv-stats').style.display = 'none';\n      el('jv-tree').innerHTML = '';\n    };\n    reader.readAsText(file);\n    e.target.value = '';\n  };\n\n  window.jvHandleDrop = function (e) {\n    e.preventDefault();\n    const dropZone = el('jv-drop-zone');\n    dropZone.style.borderColor = '';\n    dropZone.style.background = '';\n    const file = e.dataTransfer.files[0];\n    if (!file) return;\n    const reader = new FileReader();\n    reader.onload = function (ev) {\n      el('jv-input').value = ev.target.result;\n      el('jv-input').className = '';\n      setStatus('idle', '\u0026#9432;', 'File dropped: ' + file.name + '. Click Validate or Format.');\n      el('jv-stats').style.display = 'none';\n      el('jv-tree').innerHTML = '';\n    };\n    reader.readAsText(file);\n  };\n\n  /* ── Tab switching ── */\n  window.jvSwitchTab = function (tab) {\n    el('jv-tab-tree').classList.toggle('active', tab === 'tree');\n    el('jv-tab-raw').classList.toggle('active',  tab === 'raw');\n    el('jv-tree').classList.toggle('jv-visible', tab === 'tree');\n  };\n\n  /* ── Tree toggle ── */\n  window.jvToggleTree = function () {\n    const wrap = el('jv-tree');\n    const raw  = el('jv-input').value.trim();\n    if (!raw) { setStatus('idle', '\u0026#9432;', 'Nothing to show — input is empty.'); return; }\n    const result = parseWithLocation(raw);\n    if (!result.ok) {\n      setStatus('error', '\u0026#10007;', 'SyntaxError: ' + result.message);\n      return;\n    }\n    renderTree(result.value);\n    jvSwitchTab('tree');\n    showStats(result.value, raw.length);\n    setStatus('valid', '\u0026#10003;', 'Tree rendered');\n  };\n\n  /* ── Tree renderer ── */\n  function renderTree(data) {\n    const wrap = el('jv-tree');\n    wrap.innerHTML = '';\n    wrap.appendChild(buildNode(data, '$', null, '$'));\n  }\n\n  function buildNode(value, key, parentEl, path) {\n    const div = document.createElement('div');\n    div.className = 'jv-tree-node';\n\n    if (value !== null \u0026\u0026 typeof value === 'object') {\n      const isArr   = Array.isArray(value);\n      const entries = isArr ? value.map((v, i) =\u003e [i, v]) : Object.entries(value);\n      const count   = entries.length;\n      const open    = isArr ? '[' : '{';\n      const close   = isArr ? ']' : '}';\n\n      // Row\n      const row = document.createElement('div');\n      row.className = 'jv-node-row';\n\n      const toggle = document.createElement('span');\n      toggle.className = 'jv-toggle';\n      toggle.textContent = count ? '▼' : ' ';\n\n      const keySpan = document.createElement('span');\n      keySpan.className = 'jv-key';\n      if (key !== null) keySpan.textContent = isNaN(key) ? '\"' + key + '\"' : key;\n\n      const colonSpan = document.createElement('span');\n      colonSpan.className = 'jv-colon';\n      if (key !== null) colonSpan.textContent = ': ';\n\n      const bracketSpan = document.createElement('span');\n      bracketSpan.className = 'jv-val-bracket';\n      bracketSpan.textContent = open;\n\n      const countSpan = document.createElement('span');\n      countSpan.className = 'jv-count';\n      countSpan.textContent = count + ' item' + (count !== 1 ? 's' : '');\n\n      row.appendChild(toggle);\n      if (key !== null) { row.appendChild(keySpan); row.appendChild(colonSpan); }\n      row.appendChild(bracketSpan);\n      row.appendChild(countSpan);\n      div.appendChild(row);\n\n      // Children\n      const childWrap = document.createElement('div');\n      childWrap.className = 'jv-tree-children';\n      entries.forEach(function ([k, v]) {\n        const childPath = isArr ? path + '[' + k + ']' : path + '.' + k;\n        childWrap.appendChild(buildNode(v, k, childWrap, childPath));\n      });\n      div.appendChild(childWrap);\n\n      // Closing bracket row\n      const closeRow = document.createElement('div');\n      closeRow.className = 'jv-node-row';\n      closeRow.innerHTML = '\u003cspan class=\"jv-toggle\"\u003e \u003c/span\u003e\u003cspan class=\"jv-val-bracket\"\u003e' + close + '\u003c/span\u003e';\n      div.appendChild(closeRow);\n\n      // Collapse/expand\n      if (count \u003e 0) {\n        let collapsed = false;\n        row.addEventListener('click', function (e) {\n          e.stopPropagation();\n          collapsed = !collapsed;\n          childWrap.style.display = collapsed ? 'none' : '';\n          closeRow.style.display  = collapsed ? 'none' : '';\n          toggle.textContent = collapsed ? '▶' : '▼';\n          countSpan.style.display = collapsed ? '' : 'none';\n          if (collapsed) {\n            bracketSpan.textContent = open + '…' + close;\n          } else {\n            bracketSpan.textContent = open;\n          }\n          showPath(path);\n        });\n      }\n\n      row.addEventListener('click', function (e) { e.stopPropagation(); showPath(path); });\n\n    } else {\n      // Leaf node\n      const row = document.createElement('div');\n      row.className = 'jv-node-row';\n      row.title = path;\n\n      const toggle = document.createElement('span');\n      toggle.className = 'jv-toggle';\n      toggle.textContent = ' ';\n\n      const keySpan = document.createElement('span');\n      keySpan.className = 'jv-key';\n      if (key !== null) keySpan.textContent = isNaN(key) ? '\"' + key + '\"' : key;\n\n      const colonSpan = document.createElement('span');\n      colonSpan.className = 'jv-colon';\n      if (key !== null) colonSpan.textContent = ': ';\n\n      const valSpan = document.createElement('span');\n      if (typeof value === 'string') {\n        valSpan.className = 'jv-val-string';\n        valSpan.textContent = '\"' + value + '\"';\n      } else if (typeof value === 'boolean') {\n        valSpan.className = 'jv-val-bool';\n        valSpan.textContent = String(value);\n      } else if (value === null) {\n        valSpan.className = 'jv-val-null';\n        valSpan.textContent = 'null';\n      } else {\n        valSpan.className = 'jv-val-number';\n        valSpan.textContent = String(value);\n      }\n\n      row.appendChild(toggle);\n      if (key !== null) { row.appendChild(keySpan); row.appendChild(colonSpan); }\n      row.appendChild(valSpan);\n      div.appendChild(row);\n\n      row.addEventListener('click', function (e) { e.stopPropagation(); showPath(path); });\n    }\n\n    return div;\n  }\n\n  function showPath(path) {\n    const bar = el('jv-path-bar');\n    bar.style.display = 'flex';\n    el('jv-path-text').textContent = path;\n  }\n\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch3 id=\"related-tools\"\u003eRelated Tools\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003eConvert JSON to CSV → \u003ca href=\"https://productivity-works.com/tools/json-to-csv/\"\u003eJSON to CSV Converter\u003c/a\u003e\n\nFormat HTML → \u003ca href=\"https://productivity-works.com/tools/html-beautifier/\"\u003eHTML Beautifier\u003c/a\u003e\n\nTest regex patterns → \u003ca href=\"https://productivity-works.com/tools/regex-tester/\"\u003eRegex Tester\u003c/a\u003e\n\u003c/p\u003e","title":"JSON Validator \u0026 Formatter"},{"content":" \u0026#x1F512; Privacy first: All decoding happens entirely in your browser using JavaScript. Your token is never sent to any server. Signature verification is not performed — this tool is for inspection only. Paste JWT Token Load Sample JWT Clear Paste a JWT above to inspect it\nHeader Copy JSON Payload Copy JSON Signature Copy Hex Signature bytes displayed as hexadecimal. Verification requires the secret/public key and is not performed here. Standard Claim Descriptions Claim Value Description Related Tools Encode/decode Base64 → Base64 Encoder Format JSON → JSON Formatter Generate hashes → Hash Generator ","permalink":"https://productivity-works.com/tools/jwt-decoder/","summary":"\u003cdiv id=\"jwt-app\"\u003e\n\u003cstyle\u003e\n#jwt-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  padding: 0 12px;\n  box-sizing: border-box;\n}\n#jwt-app * {\n  box-sizing: border-box;\n}\n#jwt-app .jwt-notice {\n  background: #fff8e1;\n  border: 1px solid #ffe082;\n  border-radius: 6px;\n  padding: 10px 14px;\n  font-size: 0.85rem;\n  color: #6d4c00;\n  margin-bottom: 18px;\n  display: flex;\n  align-items: flex-start;\n  gap: 8px;\n}\n#jwt-app .jwt-notice-icon {\n  font-size: 1.1rem;\n  flex-shrink: 0;\n  margin-top: 1px;\n}\n#jwt-app .jwt-input-label {\n  font-weight: 600;\n  font-size: 0.9rem;\n  margin-bottom: 6px;\n  color: #222;\n}\n#jwt-app textarea#jwt-input {\n  width: 100%;\n  min-height: 110px;\n  padding: 12px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 0.82rem;\n  border: 2px solid #d0d0d0;\n  border-radius: 8px;\n  resize: vertical;\n  outline: none;\n  transition: border-color 0.2s;\n  background: #fafafa;\n  color: #1a1a1a;\n  line-height: 1.5;\n}\n#jwt-app textarea#jwt-input:focus {\n  border-color: #1a73e8;\n  background: #fff;\n}\n#jwt-app .jwt-actions-row {\n  display: flex;\n  gap: 10px;\n  margin-top: 10px;\n  flex-wrap: wrap;\n}\n#jwt-app .jwt-btn {\n  padding: 8px 16px;\n  border: none;\n  border-radius: 6px;\n  cursor: pointer;\n  font-size: 0.85rem;\n  font-weight: 600;\n  transition: background 0.15s, opacity 0.15s;\n}\n#jwt-app .jwt-btn-primary {\n  background: #1a73e8;\n  color: #fff;\n}\n#jwt-app .jwt-btn-primary:hover {\n  background: #1558b0;\n}\n#jwt-app .jwt-btn-secondary {\n  background: #f1f3f4;\n  color: #333;\n  border: 1px solid #ccc;\n}\n#jwt-app .jwt-btn-secondary:hover {\n  background: #e2e5e8;\n}\n#jwt-app .jwt-btn-danger {\n  background: #fce8e6;\n  color: #c5221f;\n  border: 1px solid #f5c6c4;\n}\n#jwt-app .jwt-btn-danger:hover {\n  background: #f5c6c4;\n}\n#jwt-app .jwt-structure-box {\n  margin: 18px 0 10px;\n  padding: 12px 14px;\n  background: #1e1e2e;\n  border-radius: 8px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 0.82rem;\n  word-break: break-all;\n  line-height: 1.6;\n  display: none;\n}\n#jwt-app .jwt-structure-box.visible {\n  display: block;\n}\n#jwt-app .jwt-part-header-color {\n  color: #f28b82;\n}\n#jwt-app .jwt-part-sep {\n  color: #aaa;\n  margin: 0 1px;\n}\n#jwt-app .jwt-part-payload-color {\n  color: #81c995;\n}\n#jwt-app .jwt-part-sig-color {\n  color: #8ab4f8;\n}\n#jwt-app .jwt-status-bar {\n  display: none;\n  margin: 12px 0;\n  padding: 10px 14px;\n  border-radius: 6px;\n  font-size: 0.88rem;\n  font-weight: 600;\n  align-items: center;\n  gap: 8px;\n}\n#jwt-app .jwt-status-bar.visible {\n  display: flex;\n}\n#jwt-app .jwt-status-bar.valid {\n  background: #e6f4ea;\n  color: #1e6e35;\n  border: 1px solid #a8d5b5;\n}\n#jwt-app .jwt-status-bar.expired {\n  background: #fce8e6;\n  color: #c5221f;\n  border: 1px solid #f5c6c4;\n}\n#jwt-app .jwt-status-bar.no-exp {\n  background: #e8f0fe;\n  color: #1a55b0;\n  border: 1px solid #aac4f8;\n}\n#jwt-app .jwt-panels {\n  display: none;\n  grid-template-columns: 1fr 1fr;\n  gap: 14px;\n  margin-top: 16px;\n}\n#jwt-app .jwt-panels.visible {\n  display: grid;\n}\n@media (max-width: 600px) {\n  #jwt-app .jwt-panels.visible {\n    grid-template-columns: 1fr;\n  }\n}\n#jwt-app .jwt-panel {\n  border: 1px solid #dde1e7;\n  border-radius: 8px;\n  overflow: hidden;\n  background: #fff;\n}\n#jwt-app .jwt-panel-header {\n  padding: 8px 12px;\n  font-weight: 700;\n  font-size: 0.8rem;\n  letter-spacing: 0.04em;\n  text-transform: uppercase;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n}\n#jwt-app .jwt-panel-header-alg {\n  font-size: 0.72rem;\n  font-weight: 500;\n  opacity: 0.85;\n  margin-left: 6px;\n  text-transform: none;\n  letter-spacing: 0;\n}\n#jwt-app .jwt-panel.panel-header .jwt-panel-header {\n  background: #fde8e7;\n  color: #b31412;\n  border-bottom: 1px solid #f5c6c4;\n}\n#jwt-app .jwt-panel.panel-payload .jwt-panel-header {\n  background: #e6f4ea;\n  color: #1a6b31;\n  border-bottom: 1px solid #a8d5b5;\n}\n#jwt-app .jwt-panel.panel-signature .jwt-panel-header {\n  background: #e8f0fe;\n  color: #1a55b0;\n  border-bottom: 1px solid #aac4f8;\n}\n#jwt-app .jwt-panel.panel-signature {\n  grid-column: 1 / -1;\n}\n#jwt-app .jwt-panel-body {\n  padding: 12px;\n}\n#jwt-app pre.jwt-json {\n  margin: 0;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 0.8rem;\n  white-space: pre-wrap;\n  word-break: break-all;\n  line-height: 1.6;\n  color: #1a1a1a;\n  background: none;\n  border: none;\n  padding: 0;\n}\n#jwt-app .jwt-copy-btn {\n  font-size: 0.72rem;\n  padding: 3px 8px;\n  border-radius: 4px;\n  background: rgba(255,255,255,0.7);\n  border: 1px solid rgba(0,0,0,0.12);\n  cursor: pointer;\n  white-space: nowrap;\n  transition: background 0.15s;\n  color: inherit;\n  font-weight: 600;\n}\n#jwt-app .jwt-copy-btn:hover {\n  background: rgba(255,255,255,0.95);\n}\n#jwt-app .jwt-claims-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.8rem;\n  margin-top: 8px;\n}\n#jwt-app .jwt-claims-table th {\n  text-align: left;\n  font-weight: 600;\n  color: #555;\n  padding: 4px 8px 4px 0;\n  border-bottom: 1px solid #eee;\n  white-space: nowrap;\n}\n#jwt-app .jwt-claims-table td {\n  padding: 5px 8px 5px 0;\n  border-bottom: 1px solid #f5f5f5;\n  vertical-align: top;\n  color: #222;\n}\n#jwt-app .jwt-claims-table td.claim-name {\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  color: #1a73e8;\n  font-weight: 600;\n  white-space: nowrap;\n}\n#jwt-app .jwt-claims-table td.claim-desc {\n  color: #777;\n  font-style: italic;\n}\n#jwt-app .jwt-sig-hex {\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  font-size: 0.78rem;\n  word-break: break-all;\n  color: #333;\n  padding: 8px;\n  background: #f8f9fa;\n  border-radius: 4px;\n  border: 1px solid #e0e0e0;\n}\n#jwt-app .jwt-sig-note {\n  margin-top: 8px;\n  font-size: 0.78rem;\n  color: #888;\n  font-style: italic;\n}\n#jwt-app .jwt-error-box {\n  display: none;\n  background: #fce8e6;\n  border: 1px solid #f5c6c4;\n  border-radius: 6px;\n  padding: 10px 14px;\n  color: #c5221f;\n  font-size: 0.85rem;\n  margin-top: 10px;\n}\n#jwt-app .jwt-error-box.visible {\n  display: block;\n}\n#jwt-app .jwt-empty-state {\n  text-align: center;\n  color: #aaa;\n  font-size: 0.9rem;\n  padding: 32px 0 16px;\n  display: block;\n}\n#jwt-app .jwt-empty-state.hidden {\n  display: none;\n}\n/* Syntax highlight tokens */\n#jwt-app .sh-key { color: #c62828; }\n#jwt-app .sh-str { color: #2e7d32; }\n#jwt-app .sh-num { color: #1565c0; }\n#jwt-app .sh-bool { color: #6a1b9a; }\n#jwt-app .sh-null { color: #bf360c; }\n\u003c/style\u003e\n\u003cdiv class=\"jwt-notice\"\u003e\n  \u003cspan class=\"jwt-notice-icon\"\u003e\u0026#x1F512;\u003c/span\u003e\n  \u003cspan\u003e\u003cstrong\u003ePrivacy first:\u003c/strong\u003e All decoding happens entirely in your browser using JavaScript. Your token is never sent to any server. Signature verification is not performed — this tool is for inspection only.\u003c/span\u003e\n\u003c/div\u003e\n\u003cdiv class=\"jwt-input-label\"\u003ePaste JWT Token\u003c/div\u003e\n\u003ctextarea id=\"jwt-input\" placeholder=\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c\" spellcheck=\"false\" autocomplete=\"off\" autocorrect=\"off\" autocapitalize=\"off\"\u003e\u003c/textarea\u003e\n\u003cdiv class=\"jwt-actions-row\"\u003e\n  \u003cbutton class=\"jwt-btn jwt-btn-secondary\" id=\"jwt-sample-btn\"\u003eLoad Sample JWT\u003c/button\u003e\n  \u003cbutton class=\"jwt-btn jwt-btn-danger\" id=\"jwt-clear-btn\"\u003eClear\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"jwt-error-box\" id=\"jwt-error\"\u003e\u003c/div\u003e\n\u003cdiv class=\"jwt-structure-box\" id=\"jwt-structure\"\u003e\u003c/div\u003e\n\u003cdiv class=\"jwt-status-bar\" id=\"jwt-status\"\u003e\u003c/div\u003e\n\u003cp\u003e\u003cspan class=\"jwt-empty-state\" id=\"jwt-empty\"\u003ePaste a JWT above to inspect it\u003c/span\u003e\u003c/p\u003e","title":"JWT Decoder — Token Inspector"},{"content":" Kanban Board Import JSON Export JSON Clear Board Add Card Title Description (optional) Priority label Cancel Delete Card Save Track habits alongside your tasks → Habit Tracker Focus with timed sessions → Pomodoro Timer Generate changelogs from your completed work → Changelog Generator Related Articles Best Productivity Apps for Remote Workers 2026: Tested and Ranked The Ultimate Notion Setup for Maximum Productivity Best Notion AI Templates for Productivity 2026 ","permalink":"https://productivity-works.com/tools/kanban-board/","summary":"\u003cdiv id=\"kb-app\"\u003e\n\u003cstyle\u003e\n/* ── Reset \u0026 base ── */\n#kb-app *, #kb-app *::before, #kb-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#kb-app {\n  --bg:        #0f172a;\n  --surface:   #1e293b;\n  --surface2:  #334155;\n  --border:    #475569;\n  --text:      #f1f5f9;\n  --text-muted:#94a3b8;\n  --accent:    #38bdf8;\n  --danger:    #f87171;\n  --radius:    8px;\n  --col-w:     300px;\n  --shadow:    0 4px 16px rgba(0,0,0,.45);\n\n  --lbl-red:   #ef4444;\n  --lbl-orange:#f97316;\n  --lbl-green: #22c55e;\n  --lbl-blue:  #3b82f6;\n  --lbl-none:  #475569;\n\n  font-family: system-ui, -apple-system, \"Segoe UI\", sans-serif;\n  font-size: 15px;\n  color: var(--text);\n  background: var(--bg);\n  padding: 20px 16px 40px;\n  min-height: 60vh;\n}\n\n/* ── Toolbar ── */\n#kb-app .kb-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  align-items: center;\n  margin-bottom: 20px;\n}\n#kb-app .kb-toolbar h2 {\n  font-size: 1.25rem;\n  font-weight: 700;\n  letter-spacing: .02em;\n  margin-right: auto;\n}\n#kb-app button {\n  cursor: pointer;\n  border: none;\n  border-radius: var(--radius);\n  font-size: 13px;\n  font-weight: 600;\n  padding: 7px 14px;\n  transition: opacity .15s, background .15s;\n}\n#kb-app button:hover { opacity: .85; }\n#kb-app .btn-primary  { background: var(--accent); color: #0f172a; }\n#kb-app .btn-ghost    { background: var(--surface2); color: var(--text); }\n#kb-app .btn-danger   { background: var(--danger);  color: #fff; }\n#kb-app .btn-icon     { background: transparent; color: var(--text-muted); padding: 4px 8px; font-size: 16px; }\n#kb-app .btn-icon:hover { color: var(--text); }\n\n/* ── Board ── */\n#kb-app .kb-board {\n  display: flex;\n  gap: 16px;\n  overflow-x: auto;\n  align-items: flex-start;\n  padding-bottom: 12px;\n}\n#kb-app .kb-board::-webkit-scrollbar { height: 6px; }\n#kb-app .kb-board::-webkit-scrollbar-track { background: transparent; }\n#kb-app .kb-board::-webkit-scrollbar-thumb { background: var(--border); border-radius: 99px; }\n\n/* ── Column ── */\n#kb-app .kb-col {\n  background: var(--surface);\n  border: 1px solid var(--border);\n  border-radius: 10px;\n  width: var(--col-w);\n  min-width: var(--col-w);\n  display: flex;\n  flex-direction: column;\n  max-height: 80vh;\n  transition: box-shadow .15s;\n}\n#kb-app .kb-col.drag-over {\n  box-shadow: 0 0 0 2px var(--accent);\n}\n#kb-app .kb-col-header {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  padding: 12px 12px 10px;\n  border-bottom: 1px solid var(--border);\n  flex-shrink: 0;\n}\n#kb-app .kb-col-title {\n  flex: 1;\n  font-weight: 700;\n  font-size: .95rem;\n  background: transparent;\n  border: 1px solid transparent;\n  border-radius: 4px;\n  color: var(--text);\n  padding: 2px 4px;\n  cursor: text;\n}\n#kb-app .kb-col-title:focus {\n  outline: none;\n  border-color: var(--accent);\n  background: var(--surface2);\n}\n#kb-app .kb-col-count {\n  background: var(--surface2);\n  color: var(--text-muted);\n  border-radius: 99px;\n  font-size: 11px;\n  font-weight: 700;\n  padding: 1px 8px;\n  min-width: 24px;\n  text-align: center;\n}\n\n#kb-app .kb-cards {\n  flex: 1;\n  overflow-y: auto;\n  padding: 10px 10px 6px;\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n  min-height: 60px;\n}\n#kb-app .kb-cards::-webkit-scrollbar { width: 4px; }\n#kb-app .kb-cards::-webkit-scrollbar-thumb { background: var(--border); border-radius: 99px; }\n\n#kb-app .kb-col-footer {\n  padding: 8px 10px 12px;\n  flex-shrink: 0;\n}\n#kb-app .btn-add-card {\n  width: 100%;\n  background: var(--surface2);\n  color: var(--text-muted);\n  border-radius: var(--radius);\n  padding: 7px 10px;\n  font-size: 13px;\n  text-align: left;\n}\n#kb-app .btn-add-card:hover { color: var(--text); }\n\n/* ── Card ── */\n#kb-app .kb-card {\n  background: var(--surface2);\n  border: 1px solid var(--border);\n  border-left: 4px solid var(--lbl-none);\n  border-radius: var(--radius);\n  padding: 10px 10px 8px;\n  cursor: grab;\n  user-select: none;\n  position: relative;\n  transition: box-shadow .15s, transform .1s;\n}\n#kb-app .kb-card:active { cursor: grabbing; }\n#kb-app .kb-card.dragging {\n  opacity: .4;\n  transform: scale(.97);\n}\n#kb-app .kb-card[data-label=\"red\"]    { border-left-color: var(--lbl-red); }\n#kb-app .kb-card[data-label=\"orange\"] { border-left-color: var(--lbl-orange); }\n#kb-app .kb-card[data-label=\"green\"]  { border-left-color: var(--lbl-green); }\n#kb-app .kb-card[data-label=\"blue\"]   { border-left-color: var(--lbl-blue); }\n\n#kb-app .kb-card-title {\n  font-weight: 600;\n  font-size: .9rem;\n  margin-bottom: 4px;\n  word-break: break-word;\n}\n#kb-app .kb-card-desc {\n  font-size: .8rem;\n  color: var(--text-muted);\n  word-break: break-word;\n  white-space: pre-wrap;\n}\n#kb-app .kb-card-actions {\n  display: flex;\n  gap: 4px;\n  justify-content: flex-end;\n  margin-top: 8px;\n}\n\n/* ── Add-column button ── */\n#kb-app .kb-add-col {\n  background: var(--surface);\n  border: 2px dashed var(--border);\n  border-radius: 10px;\n  width: 220px;\n  min-width: 220px;\n  height: 56px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: var(--text-muted);\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  flex-shrink: 0;\n  transition: border-color .15s, color .15s;\n}\n#kb-app .kb-add-col:hover { border-color: var(--accent); color: var(--text); }\n\n/* ── Modal overlay ── */\n#kb-app .kb-overlay {\n  position: fixed;\n  inset: 0;\n  background: rgba(0,0,0,.65);\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  z-index: 9999;\n  padding: 16px;\n}\n#kb-app .kb-overlay.hidden { display: none; }\n#kb-app .kb-modal {\n  background: var(--surface);\n  border: 1px solid var(--border);\n  border-radius: 12px;\n  width: 100%;\n  max-width: 440px;\n  padding: 24px;\n  box-shadow: var(--shadow);\n  display: flex;\n  flex-direction: column;\n  gap: 14px;\n}\n#kb-app .kb-modal h3 {\n  font-size: 1.05rem;\n  font-weight: 700;\n}\n#kb-app .kb-modal label {\n  display: block;\n  font-size: 12px;\n  color: var(--text-muted);\n  margin-bottom: 4px;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: .05em;\n}\n#kb-app .kb-modal input[type=\"text\"],\n#kb-app .kb-modal textarea,\n#kb-app .kb-modal select {\n  width: 100%;\n  background: var(--surface2);\n  border: 1px solid var(--border);\n  border-radius: var(--radius);\n  color: var(--text);\n  font-size: 14px;\n  padding: 8px 10px;\n  font-family: inherit;\n  resize: vertical;\n}\n#kb-app .kb-modal input[type=\"text\"]:focus,\n#kb-app .kb-modal textarea:focus,\n#kb-app .kb-modal select:focus {\n  outline: none;\n  border-color: var(--accent);\n}\n#kb-app .kb-modal textarea { min-height: 80px; }\n#kb-app .kb-modal .kb-modal-actions {\n  display: flex;\n  gap: 8px;\n  justify-content: flex-end;\n  flex-wrap: wrap;\n}\n\n/* ── Label dots ── */\n#kb-app .kb-label-row {\n  display: flex;\n  gap: 8px;\n  align-items: center;\n}\n#kb-app .kb-label-dot {\n  width: 22px;\n  height: 22px;\n  border-radius: 50%;\n  cursor: pointer;\n  border: 2px solid transparent;\n  transition: border-color .1s, transform .1s;\n}\n#kb-app .kb-label-dot:hover { transform: scale(1.15); }\n#kb-app .kb-label-dot.selected { border-color: #fff; transform: scale(1.2); }\n#kb-app .kb-label-dot[data-v=\"none\"]   { background: var(--lbl-none); }\n#kb-app .kb-label-dot[data-v=\"red\"]    { background: var(--lbl-red); }\n#kb-app .kb-label-dot[data-v=\"orange\"] { background: var(--lbl-orange); }\n#kb-app .kb-label-dot[data-v=\"green\"]  { background: var(--lbl-green); }\n#kb-app .kb-label-dot[data-v=\"blue\"]   { background: var(--lbl-blue); }\n\n/* ── Move dropdown (mobile fallback) ── */\n#kb-app .kb-card select.move-select {\n  background: var(--surface);\n  border: 1px solid var(--border);\n  color: var(--text);\n  border-radius: 4px;\n  font-size: 12px;\n  padding: 2px 6px;\n  cursor: pointer;\n  max-width: 110px;\n}\n\n/* ── Toast ── */\n#kb-app .kb-toast {\n  position: fixed;\n  bottom: 24px;\n  left: 50%;\n  transform: translateX(-50%) translateY(80px);\n  background: var(--surface2);\n  color: var(--text);\n  border: 1px solid var(--border);\n  border-radius: var(--radius);\n  padding: 10px 20px;\n  font-size: 13px;\n  z-index: 10000;\n  transition: transform .3s ease;\n  white-space: nowrap;\n  pointer-events: none;\n}\n#kb-app .kb-toast.show { transform: translateX(-50%) translateY(0); }\n\n/* ── Responsive ── */\n@media (max-width: 600px) {\n  #kb-app .kb-col { min-width: 85vw; width: 85vw; }\n  #kb-app .kb-add-col { min-width: 70vw; width: 70vw; }\n}\n\u003c/style\u003e\n\u003c!-- Toolbar --\u003e\n\u003cdiv class=\"kb-toolbar\"\u003e\n  \u003ch2\u003eKanban Board\u003c/h2\u003e\n  \u003cbutton class=\"btn-ghost\" id=\"kb-btn-import\"\u003eImport JSON\u003c/button\u003e\n  \u003cbutton class=\"btn-ghost\" id=\"kb-btn-export\"\u003eExport JSON\u003c/button\u003e\n  \u003cbutton class=\"btn-danger\" id=\"kb-btn-clear\"\u003eClear Board\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Board container --\u003e\n\u003cdiv class=\"kb-board\" id=\"kb-board\"\u003e\u003c/div\u003e\n\u003c!-- Card modal --\u003e\n\u003cdiv class=\"kb-overlay hidden\" id=\"kb-modal-overlay\"\u003e\n  \u003cdiv class=\"kb-modal\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"kb-modal-title\"\u003e\n    \u003ch3 id=\"kb-modal-title\"\u003eAdd Card\u003c/h3\u003e\n    \u003cdiv\u003e\n      \u003clabel for=\"kb-input-title\"\u003eTitle\u003c/label\u003e\n      \u003cinput type=\"text\" id=\"kb-input-title\" placeholder=\"Card title…\" maxlength=\"120\"\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003clabel for=\"kb-input-desc\"\u003eDescription (optional)\u003c/label\u003e\n      \u003ctextarea id=\"kb-input-desc\" placeholder=\"Details, notes…\" maxlength=\"600\"\u003e\u003c/textarea\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003clabel\u003ePriority label\u003c/label\u003e\n      \u003cdiv class=\"kb-label-row\" id=\"kb-label-row\"\u003e\n        \u003cspan class=\"kb-label-dot selected\" data-v=\"none\"  title=\"None\"\u003e\u003c/span\u003e\n        \u003cspan class=\"kb-label-dot\"          data-v=\"red\"   title=\"High\"\u003e\u003c/span\u003e\n        \u003cspan class=\"kb-label-dot\"          data-v=\"orange\"title=\"Medium\"\u003e\u003c/span\u003e\n        \u003cspan class=\"kb-label-dot\"          data-v=\"green\" title=\"Low\"\u003e\u003c/span\u003e\n        \u003cspan class=\"kb-label-dot\"          data-v=\"blue\"  title=\"Info\"\u003e\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"kb-modal-actions\"\u003e\n      \u003cbutton class=\"btn-ghost\"   id=\"kb-modal-cancel\"\u003eCancel\u003c/button\u003e\n      \u003cbutton class=\"btn-danger hidden\" id=\"kb-modal-delete\"\u003eDelete Card\u003c/button\u003e\n      \u003cbutton class=\"btn-primary\" id=\"kb-modal-save\"\u003eSave\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Import file input (hidden) --\u003e\n\u003cinput type=\"file\" id=\"kb-file-input\" accept=\".json\" style=\"display:none\"\u003e\n\u003c!-- Toast --\u003e\n\u003cdiv class=\"kb-toast\" id=\"kb-toast\"\u003e\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  'use strict';\n\n  /* ── State ── */\n  const STORAGE_KEY = 'kb_board_v1';\n  let state = { columns: [], cards: [] };\n  let dragCardId = null;\n\n  /* modal edit state */\n  let modalMode  = 'add';   // 'add' | 'edit'\n  let modalColId = null;\n  let modalCardId= null;\n  let selectedLabel = 'none';\n\n  /* ── Utilities ── */\n  const uid = () =\u003e Math.random().toString(36).slice(2, 10) + Date.now().toString(36);\n\n  function save() {\n    try { localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); } catch(_) {}\n  }\n\n  function load() {\n    try {\n      const raw = localStorage.getItem(STORAGE_KEY);\n      if (raw) { const parsed = JSON.parse(raw); if (parsed \u0026\u0026 parsed.columns) state = parsed; }\n    } catch(_) {}\n  }\n\n  function showToast(msg) {\n    const t = document.getElementById('kb-toast');\n    t.textContent = msg;\n    t.classList.add('show');\n    setTimeout(() =\u003e t.classList.remove('show'), 2200);\n  }\n\n  /* ── Default data ── */\n  function initDefault() {\n    const colIds = [uid(), uid(), uid()];\n    state = {\n      columns: [\n        { id: colIds[0], title: 'To Do' },\n        { id: colIds[1], title: 'In Progress' },\n        { id: colIds[2], title: 'Done' },\n      ],\n      cards: [\n        { id: uid(), colId: colIds[0], title: 'Welcome to your Kanban Board', desc: 'Drag cards between columns, click to edit.', label: 'blue' },\n        { id: uid(), colId: colIds[0], title: 'Add a new card', desc: '', label: 'none' },\n        { id: uid(), colId: colIds[1], title: 'Review project scope', desc: '', label: 'orange' },\n        { id: uid(), colId: colIds[2], title: 'Set up board', desc: '', label: 'green' },\n      ]\n    };\n    save();\n  }\n\n  /* ── Render ── */\n  function cardsForCol(colId) {\n    return state.cards.filter(c =\u003e c.colId === colId);\n  }\n\n  function isMobile() {\n    return window.matchMedia('(max-width: 700px)').matches;\n  }\n\n  function renderBoard() {\n    const board = document.getElementById('kb-board');\n    board.innerHTML = '';\n\n    state.columns.forEach(col =\u003e {\n      const cards = cardsForCol(col.id);\n\n      const colEl = document.createElement('div');\n      colEl.className = 'kb-col';\n      colEl.dataset.colId = col.id;\n\n      /* ── header ── */\n      colEl.innerHTML = `\n        \u003cdiv class=\"kb-col-header\"\u003e\n          \u003cinput class=\"kb-col-title\" value=\"${escHtml(col.title)}\" aria-label=\"Column title\" maxlength=\"50\"\u003e\n          \u003cspan class=\"kb-col-count\"\u003e${cards.length}\u003c/span\u003e\n          \u003cbutton class=\"btn-icon kb-del-col\" title=\"Delete column\" aria-label=\"Delete column\"\u003e\u0026#x2715;\u003c/button\u003e\n        \u003c/div\u003e\n        \u003cdiv class=\"kb-cards\" data-col-id=\"${col.id}\"\u003e\u003c/div\u003e\n        \u003cdiv class=\"kb-col-footer\"\u003e\n          \u003cbutton class=\"btn-add-card\" data-col-id=\"${col.id}\"\u003e\u0026#xFF0B; Add card\u003c/button\u003e\n        \u003c/div\u003e`;\n\n      board.appendChild(colEl);\n\n      /* column title inline edit */\n      const titleInput = colEl.querySelector('.kb-col-title');\n      titleInput.addEventListener('change', () =\u003e {\n        col.title = titleInput.value.trim() || col.title;\n        titleInput.value = col.title;\n        save();\n        refreshCounts();\n        refreshMoveDropdowns();\n      });\n\n      /* delete column */\n      colEl.querySelector('.kb-del-col').addEventListener('click', () =\u003e {\n        if (!confirm(`Delete column \"${col.title}\" and all its cards?`)) return;\n        state.cards   = state.cards.filter(c =\u003e c.colId !== col.id);\n        state.columns = state.columns.filter(c =\u003e c.id !== col.id);\n        save();\n        renderBoard();\n      });\n\n      /* add card button */\n      colEl.querySelector('.btn-add-card').addEventListener('click', () =\u003e openModal('add', col.id));\n\n      /* ── cards area ── */\n      const cardsArea = colEl.querySelector('.kb-cards');\n      setupDropZone(cardsArea, col.id);\n\n      cards.forEach(card =\u003e {\n        const cardEl = createCardEl(card);\n        cardsArea.appendChild(cardEl);\n      });\n    });\n\n    /* ── Add column button ── */\n    const addColBtn = document.createElement('button');\n    addColBtn.className = 'kb-add-col';\n    addColBtn.textContent = '+ Add Column';\n    addColBtn.addEventListener('click', () =\u003e {\n      const title = prompt('Column name:');\n      if (!title || !title.trim()) return;\n      state.columns.push({ id: uid(), title: title.trim() });\n      save();\n      renderBoard();\n    });\n    board.appendChild(addColBtn);\n  }\n\n  function createCardEl(card) {\n    const el = document.createElement('div');\n    el.className = 'kb-card';\n    el.draggable = true;\n    el.dataset.cardId = card.id;\n    el.dataset.label  = card.label || 'none';\n\n    const moveOptions = state.columns\n      .filter(c =\u003e c.id !== card.colId)\n      .map(c =\u003e `\u003coption value=\"${c.id}\"\u003e${escHtml(c.title)}\u003c/option\u003e`)\n      .join('');\n\n    el.innerHTML = `\n      \u003cdiv class=\"kb-card-title\"\u003e${escHtml(card.title)}\u003c/div\u003e\n      ${card.desc ? `\u003cdiv class=\"kb-card-desc\"\u003e${escHtml(card.desc)}\u003c/div\u003e` : ''}\n      \u003cdiv class=\"kb-card-actions\"\u003e\n        ${moveOptions ? `\u003cselect class=\"move-select\" title=\"Move to column\" aria-label=\"Move card to column\"\u003e\n          \u003coption value=\"\"\u003eMove to…\u003c/option\u003e${moveOptions}\n        \u003c/select\u003e` : ''}\n        \u003cbutton class=\"btn-icon kb-edit-card\" title=\"Edit\" aria-label=\"Edit card\"\u003e\u0026#x270E;\u003c/button\u003e\n      \u003c/div\u003e`;\n\n    /* drag */\n    el.addEventListener('dragstart', e =\u003e {\n      dragCardId = card.id;\n      el.classList.add('dragging');\n      e.dataTransfer.effectAllowed = 'move';\n    });\n    el.addEventListener('dragend', () =\u003e {\n      dragCardId = null;\n      el.classList.remove('dragging');\n      document.querySelectorAll('#kb-board .kb-col').forEach(c =\u003e c.classList.remove('drag-over'));\n    });\n\n    /* touch support */\n    setupTouch(el, card.id);\n\n    /* edit button */\n    el.querySelector('.kb-edit-card').addEventListener('click', e =\u003e {\n      e.stopPropagation();\n      openModal('edit', card.colId, card.id);\n    });\n\n    /* move dropdown */\n    const sel = el.querySelector('.move-select');\n    if (sel) {\n      sel.addEventListener('change', () =\u003e {\n        const targetColId = sel.value;\n        if (!targetColId) return;\n        const c = state.cards.find(x =\u003e x.id === card.id);\n        if (c) { c.colId = targetColId; save(); renderBoard(); }\n      });\n    }\n\n    return el;\n  }\n\n  /* ── Drop zones ── */\n  function setupDropZone(area, colId) {\n    area.addEventListener('dragover', e =\u003e {\n      e.preventDefault();\n      e.dataTransfer.dropEffect = 'move';\n      area.closest('.kb-col').classList.add('drag-over');\n    });\n    area.addEventListener('dragleave', e =\u003e {\n      if (!area.contains(e.relatedTarget)) {\n        area.closest('.kb-col').classList.remove('drag-over');\n      }\n    });\n    area.addEventListener('drop', e =\u003e {\n      e.preventDefault();\n      area.closest('.kb-col').classList.remove('drag-over');\n      if (!dragCardId) return;\n      const card = state.cards.find(c =\u003e c.id === dragCardId);\n      if (card \u0026\u0026 card.colId !== colId) {\n        card.colId = colId;\n        save();\n        renderBoard();\n      }\n    });\n  }\n\n  /* ── Touch drag (simple touch-to-move via move dropdown, plus basic touch scroll) ── */\n  function setupTouch(el, cardId) {\n    let startX, startY, ghost = null, moved = false;\n    el.addEventListener('touchstart', e =\u003e {\n      const t = e.touches[0];\n      startX = t.clientX; startY = t.clientY;\n      moved = false;\n    }, { passive: true });\n\n    el.addEventListener('touchmove', e =\u003e {\n      const t = e.touches[0];\n      const dx = t.clientX - startX, dy = t.clientY - startY;\n      if (Math.abs(dx) \u003e 8 || Math.abs(dy) \u003e 8) moved = true;\n    }, { passive: true });\n  }\n\n  /* ── Counts \u0026 dropdown refresh (without full re-render) ── */\n  function refreshCounts() {\n    state.columns.forEach(col =\u003e {\n      const colEl = document.querySelector(`.kb-col[data-col-id=\"${col.id}\"]`);\n      if (!colEl) return;\n      const cnt = colEl.querySelector('.kb-col-count');\n      if (cnt) cnt.textContent = cardsForCol(col.id).length;\n    });\n  }\n\n  function refreshMoveDropdowns() {\n    /* cheapest: just re-render */\n    renderBoard();\n  }\n\n  /* ── Modal ── */\n  function openModal(mode, colId, cardId) {\n    modalMode   = mode;\n    modalColId  = colId;\n    modalCardId = cardId || null;\n    selectedLabel = 'none';\n\n    const overlay = document.getElementById('kb-modal-overlay');\n    const titleEl = document.getElementById('kb-modal-title');\n    const inputTitle = document.getElementById('kb-input-title');\n    const inputDesc  = document.getElementById('kb-input-desc');\n    const deleteBtn  = document.getElementById('kb-modal-delete');\n\n    if (mode === 'edit') {\n      const card = state.cards.find(c =\u003e c.id === cardId);\n      titleEl.textContent  = 'Edit Card';\n      inputTitle.value     = card.title;\n      inputDesc.value      = card.desc || '';\n      selectedLabel        = card.label || 'none';\n      deleteBtn.classList.remove('hidden');\n    } else {\n      titleEl.textContent  = 'Add Card';\n      inputTitle.value     = '';\n      inputDesc.value      = '';\n      selectedLabel        = 'none';\n      deleteBtn.classList.add('hidden');\n    }\n\n    /* sync label dots */\n    document.querySelectorAll('#kb-label-row .kb-label-dot').forEach(dot =\u003e {\n      dot.classList.toggle('selected', dot.dataset.v === selectedLabel);\n    });\n\n    overlay.classList.remove('hidden');\n    inputTitle.focus();\n  }\n\n  function closeModal() {\n    document.getElementById('kb-modal-overlay').classList.add('hidden');\n  }\n\n  /* ── Modal events ── */\n  document.getElementById('kb-modal-cancel').addEventListener('click', closeModal);\n  document.getElementById('kb-modal-overlay').addEventListener('click', e =\u003e {\n    if (e.target === document.getElementById('kb-modal-overlay')) closeModal();\n  });\n\n  document.querySelectorAll('#kb-label-row .kb-label-dot').forEach(dot =\u003e {\n    dot.addEventListener('click', () =\u003e {\n      selectedLabel = dot.dataset.v;\n      document.querySelectorAll('#kb-label-row .kb-label-dot').forEach(d =\u003e\n        d.classList.toggle('selected', d.dataset.v === selectedLabel));\n    });\n  });\n\n  document.getElementById('kb-modal-save').addEventListener('click', () =\u003e {\n    const title = document.getElementById('kb-input-title').value.trim();\n    if (!title) { document.getElementById('kb-input-title').focus(); return; }\n    const desc  = document.getElementById('kb-input-desc').value.trim();\n\n    if (modalMode === 'add') {\n      state.cards.push({ id: uid(), colId: modalColId, title, desc, label: selectedLabel });\n      showToast('Card added');\n    } else {\n      const card = state.cards.find(c =\u003e c.id === modalCardId);\n      if (card) { card.title = title; card.desc = desc; card.label = selectedLabel; }\n      showToast('Card updated');\n    }\n    save();\n    closeModal();\n    renderBoard();\n  });\n\n  /* Enter key in title saves */\n  document.getElementById('kb-input-title').addEventListener('keydown', e =\u003e {\n    if (e.key === 'Enter') { e.preventDefault(); document.getElementById('kb-modal-save').click(); }\n  });\n\n  document.getElementById('kb-modal-delete').addEventListener('click', () =\u003e {\n    if (!confirm('Delete this card?')) return;\n    state.cards = state.cards.filter(c =\u003e c.id !== modalCardId);\n    save();\n    closeModal();\n    renderBoard();\n    showToast('Card deleted');\n  });\n\n  /* ── Toolbar buttons ── */\n  document.getElementById('kb-btn-clear').addEventListener('click', () =\u003e {\n    if (!confirm('Clear all columns and cards? This cannot be undone.')) return;\n    state = { columns: [], cards: [] };\n    save();\n    renderBoard();\n    showToast('Board cleared');\n  });\n\n  document.getElementById('kb-btn-export').addEventListener('click', () =\u003e {\n    const json = JSON.stringify(state, null, 2);\n    const blob = new Blob([json], { type: 'application/json' });\n    const url  = URL.createObjectURL(blob);\n    const a    = document.createElement('a');\n    a.href     = url;\n    a.download = 'kanban-board.json';\n    a.click();\n    URL.revokeObjectURL(url);\n    showToast('Board exported');\n  });\n\n  document.getElementById('kb-btn-import').addEventListener('click', () =\u003e {\n    document.getElementById('kb-file-input').click();\n  });\n\n  document.getElementById('kb-file-input').addEventListener('change', e =\u003e {\n    const file = e.target.files[0];\n    if (!file) return;\n    const reader = new FileReader();\n    reader.onload = ev =\u003e {\n      try {\n        const parsed = JSON.parse(ev.target.result);\n        if (!parsed.columns || !parsed.cards) throw new Error('Invalid format');\n        state = parsed;\n        save();\n        renderBoard();\n        showToast('Board imported');\n      } catch (err) {\n        alert('Invalid JSON file. Please export from this tool first.');\n      }\n    };\n    reader.readAsText(file);\n    e.target.value = '';\n  });\n\n  /* ── Escape key closes modal ── */\n  document.addEventListener('keydown', e =\u003e {\n    if (e.key === 'Escape') closeModal();\n  });\n\n  /* ── HTML escape ── */\n  function escHtml(str) {\n    return String(str)\n      .replace(/\u0026/g, '\u0026amp;')\n      .replace(/\u003c/g, '\u0026lt;')\n      .replace(/\u003e/g, '\u0026gt;')\n      .replace(/\"/g, '\u0026quot;')\n      .replace(/'/g, '\u0026#39;');\n  }\n\n  /* ── Bootstrap ── */\n  load();\n  if (state.columns.length === 0) initDefault();\n  renderBoard();\n\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003eTrack habits alongside your tasks → \u003ca href=\"https://productivity-works.com/tools/habit-tracker/\"\u003eHabit Tracker\u003c/a\u003e\n\nFocus with timed sessions → \u003ca href=\"https://productivity-works.com/tools/pomodoro-timer/\"\u003ePomodoro Timer\u003c/a\u003e\n\nGenerate changelogs from your completed work → \u003ca href=\"https://productivity-works.com/tools/changelog-generator/\"\u003eChangelog Generator\u003c/a\u003e\n\u003c/p\u003e","title":"Kanban Board"},{"content":"Enter two strings and instantly calculate their edit distance — how many single-character insertions, deletions, or substitutions it takes to transform one into the other. Visualize the full DP matrix, trace the edit path, and run batch comparisons. Everything runs in your browser with no server, no sign-up, no tracking.\nSingle Pair Batch Mode String A String B Calculate \u0026#8646; Swap Clear Case-sensitive Show DP matrix — Edit Distance — Similarity 0 Insertions 0 Deletions 0 Substitutions Similarity Step-by-step Edit Operations Dynamic Programming Matrix (highlighted cells = optimal path) Batch Compare Enter one pair per line, separated by a comma: word1,word2\nCompare All Clear Case-sensitive What Is Levenshtein Distance? The Levenshtein distance (also called edit distance) between two strings is the minimum number of single-character edits — insertions, deletions, and substitutions — needed to transform one string into the other.\nThe classic example: transforming \u0026ldquo;kitten\u0026rdquo; into \u0026ldquo;sitting\u0026rdquo; requires 3 operations, so their Levenshtein distance is 3.\nOperation Example Cost Substitution k → s (kitten → sitten) 1 Substitution e → i (sitten → sittin) 1 Insertion sittin → sitting 1 Similarity % is computed as (1 − distance / max(len_a, len_b)) × 100.\nHow the DP Algorithm Works The calculator builds an (m+1) × (n+1) matrix where m and n are the lengths of the two strings. Each cell dp[i][j] stores the minimum edit distance between the first i characters of string A and the first j characters of string B. The recurrence is:\nIf A[i] == B[j]: dp[i][j] = dp[i-1][j-1] (no cost) Otherwise: dp[i][j] = 1 + min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) (substitute, delete, insert) Tracing back through the matrix from dp[m][n] reveals the specific sequence of operations shown in the step-by-step breakdown.\nCommon Use Cases Application How edit distance helps Spell checking Find the closest dictionary word DNA sequencing Measure genetic sequence similarity Fuzzy search Rank search results by closeness Plagiarism detection Detect near-duplicate passages Record linkage Merge duplicate database entries Autocorrect Suggest the most likely intended word NLP / ML Feature for text similarity models Tips Case-sensitive toggle — By default, comparison is case-insensitive. Enable case-sensitivity when comparing code identifiers or passwords. DP matrix — Enable \u0026ldquo;Show DP matrix\u0026rdquo; to see the full dynamic programming table with the optimal path highlighted. Limited to strings of 20 characters or fewer for readability. Batch mode — Paste a list of word1,word2 pairs to compare many strings at once — handy for evaluating fuzzy-match thresholds. Swap — Swap the two inputs instantly; Levenshtein distance is symmetric (d(A,B) == d(B,A)). Compare full text blocks line by line → Text Diff Checker Count words, characters, and sentences → Word Counter ","permalink":"https://productivity-works.com/tools/levenshtein-distance/","summary":"\u003cp\u003eEnter two strings and instantly calculate their edit distance — how many single-character insertions, deletions, or substitutions it takes to transform one into the other. Visualize the full DP matrix, trace the edit path, and run batch comparisons. Everything runs in your browser with no server, no sign-up, no tracking.\u003c/p\u003e\n\u003cdiv id=\"ld-app\"\u003e\n\u003cstyle\u003e\n#ld-app *,\n#ld-app *::before,\n#ld-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#ld-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 14px;\n  color: #1e293b;\n  line-height: 1.5;\n}\n#ld-app .ld-card {\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 18px 20px;\n  margin-bottom: 16px;\n}\n#ld-app .ld-row {\n  display: flex;\n  gap: 14px;\n  flex-wrap: wrap;\n}\n#ld-app .ld-col {\n  flex: 1;\n  min-width: 180px;\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n}\n#ld-app label.ld-label {\n  font-size: 12px;\n  font-weight: 600;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#ld-app input.ld-input {\n  width: 100%;\n  padding: 9px 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  font-size: 15px;\n  font-family: \"SF Mono\", \"Fira Code\", Consolas, monospace;\n  color: #1e293b;\n  background: #fff;\n  outline: none;\n  transition: border-color 0.15s, box-shadow 0.15s;\n}\n#ld-app input.ld-input:focus {\n  border-color: #6366f1;\n  box-shadow: 0 0 0 3px rgba(99,102,241,0.12);\n}\n#ld-app .ld-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  align-items: center;\n  margin-bottom: 0;\n}\n#ld-app .ld-btn {\n  padding: 7px 16px;\n  border-radius: 7px;\n  border: 1.5px solid #cbd5e1;\n  background: #f8fafc;\n  color: #334155;\n  font-size: 13px;\n  font-weight: 500;\n  cursor: pointer;\n  transition: background 0.15s, border-color 0.15s;\n  white-space: nowrap;\n}\n#ld-app .ld-btn:hover {\n  background: #f1f5f9;\n  border-color: #94a3b8;\n}\n#ld-app .ld-btn-primary {\n  background: #6366f1;\n  border-color: #6366f1;\n  color: #fff;\n  font-weight: 600;\n}\n#ld-app .ld-btn-primary:hover {\n  background: #4f46e5;\n  border-color: #4f46e5;\n}\n#ld-app label.ld-check {\n  display: flex;\n  align-items: center;\n  gap: 5px;\n  font-size: 13px;\n  color: #475569;\n  cursor: pointer;\n  user-select: none;\n}\n#ld-app label.ld-check input[type=\"checkbox\"] {\n  width: 15px;\n  height: 15px;\n  accent-color: #6366f1;\n  cursor: pointer;\n}\n#ld-app .ld-result-banner {\n  display: none;\n  gap: 20px;\n  flex-wrap: wrap;\n  background: linear-gradient(135deg, #eef2ff 0%, #f0fdf4 100%);\n  border: 1.5px solid #c7d2fe;\n  border-radius: 10px;\n  padding: 16px 20px;\n  margin-bottom: 16px;\n}\n#ld-app .ld-result-banner.show { display: flex; }\n#ld-app .ld-stat {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 2px;\n  min-width: 80px;\n}\n#ld-app .ld-stat-num {\n  font-size: 28px;\n  font-weight: 700;\n  color: #4f46e5;\n  line-height: 1;\n}\n#ld-app .ld-stat-label {\n  font-size: 11px;\n  color: #64748b;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  text-align: center;\n}\n#ld-app .ld-ops-row {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n  align-items: center;\n}\n#ld-app .ld-op-badge {\n  padding: 4px 10px;\n  border-radius: 20px;\n  font-size: 12px;\n  font-weight: 600;\n}\n#ld-app .ld-op-ins  { background: #dcfce7; color: #166534; }\n#ld-app .ld-op-del  { background: #fee2e2; color: #991b1b; }\n#ld-app .ld-op-sub  { background: #fef9c3; color: #854d0e; }\n#ld-app .ld-op-eq   { background: #f1f5f9; color: #475569; }\n#ld-app .ld-section-title {\n  font-size: 12px;\n  font-weight: 700;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 10px;\n}\n#ld-app .ld-steps {\n  display: none;\n  flex-direction: column;\n  gap: 6px;\n  max-height: 280px;\n  overflow-y: auto;\n  padding-right: 4px;\n}\n#ld-app .ld-steps.show { display: flex; }\n#ld-app .ld-step {\n  display: flex;\n  align-items: flex-start;\n  gap: 10px;\n  padding: 8px 12px;\n  border-radius: 7px;\n  border-left: 4px solid transparent;\n  background: #f8fafc;\n  font-size: 13px;\n}\n#ld-app .ld-step.ins { border-left-color: #22c55e; background: #f0fdf4; }\n#ld-app .ld-step.del { border-left-color: #ef4444; background: #fef2f2; }\n#ld-app .ld-step.sub { border-left-color: #f59e0b; background: #fffbeb; }\n#ld-app .ld-step.eq  { border-left-color: #cbd5e1; }\n#ld-app .ld-step-icon { font-weight: 700; font-size: 14px; min-width: 18px; }\n#ld-app .ld-step-text { color: #334155; line-height: 1.4; }\n#ld-app .ld-step-text code {\n  font-family: \"SF Mono\", \"Fira Code\", Consolas, monospace;\n  background: rgba(0,0,0,0.06);\n  padding: 1px 5px;\n  border-radius: 4px;\n  font-size: 12px;\n}\n/* DP Matrix */\n#ld-app .ld-matrix-wrap {\n  display: none;\n  overflow-x: auto;\n  margin-top: 4px;\n}\n#ld-app .ld-matrix-wrap.show { display: block; }\n#ld-app .ld-matrix-table {\n  border-collapse: collapse;\n  font-family: \"SF Mono\", \"Fira Code\", Consolas, monospace;\n  font-size: 12px;\n}\n#ld-app .ld-matrix-table th,\n#ld-app .ld-matrix-table td {\n  width: 32px;\n  height: 32px;\n  text-align: center;\n  vertical-align: middle;\n  border: 1px solid #e2e8f0;\n}\n#ld-app .ld-matrix-table th {\n  background: #f1f5f9;\n  color: #64748b;\n  font-weight: 700;\n  font-size: 11px;\n}\n#ld-app .ld-matrix-table td {\n  background: #fff;\n  color: #1e293b;\n}\n#ld-app .ld-matrix-table td.ld-path {\n  background: #eef2ff;\n  color: #4f46e5;\n  font-weight: 700;\n}\n#ld-app .ld-matrix-table td.ld-path-end {\n  background: #6366f1;\n  color: #fff;\n  font-weight: 700;\n}\n/* Similarity bar */\n#ld-app .ld-sim-bar-wrap {\n  margin-top: 8px;\n  background: #e2e8f0;\n  border-radius: 99px;\n  height: 8px;\n  overflow: hidden;\n}\n#ld-app .ld-sim-bar {\n  height: 100%;\n  border-radius: 99px;\n  background: linear-gradient(90deg, #6366f1, #22c55e);\n  transition: width 0.4s ease;\n  width: 0%;\n}\n/* Batch mode */\n#ld-app .ld-batch-area {\n  width: 100%;\n  min-height: 100px;\n  padding: 10px 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  font-family: \"SF Mono\", \"Fira Code\", Consolas, monospace;\n  font-size: 13px;\n  resize: vertical;\n  outline: none;\n  color: #1e293b;\n  background: #fff;\n  transition: border-color 0.15s, box-shadow 0.15s;\n}\n#ld-app .ld-batch-area:focus {\n  border-color: #6366f1;\n  box-shadow: 0 0 0 3px rgba(99,102,241,0.12);\n}\n#ld-app .ld-batch-results {\n  display: none;\n  flex-direction: column;\n  gap: 6px;\n  margin-top: 10px;\n}\n#ld-app .ld-batch-results.show { display: flex; }\n#ld-app .ld-batch-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  padding: 8px 12px;\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 7px;\n  font-size: 13px;\n  flex-wrap: wrap;\n}\n#ld-app .ld-batch-pair {\n  font-family: \"SF Mono\", \"Fira Code\", Consolas, monospace;\n  color: #475569;\n  flex: 1;\n  min-width: 120px;\n}\n#ld-app .ld-batch-dist {\n  font-weight: 700;\n  color: #4f46e5;\n  min-width: 80px;\n  text-align: right;\n}\n#ld-app .ld-batch-sim {\n  color: #16a34a;\n  font-weight: 600;\n  min-width: 60px;\n  text-align: right;\n}\n#ld-app .ld-tab-row {\n  display: flex;\n  gap: 4px;\n  margin-bottom: 14px;\n  border-bottom: 2px solid #e2e8f0;\n}\n#ld-app .ld-tab {\n  padding: 8px 16px;\n  font-size: 13px;\n  font-weight: 600;\n  color: #64748b;\n  cursor: pointer;\n  border: none;\n  background: none;\n  border-bottom: 3px solid transparent;\n  margin-bottom: -2px;\n  transition: color 0.15s, border-color 0.15s;\n}\n#ld-app .ld-tab.active {\n  color: #6366f1;\n  border-bottom-color: #6366f1;\n}\n#ld-app .ld-tab-panel { display: none; }\n#ld-app .ld-tab-panel.active { display: block; }\n#ld-app .ld-hint {\n  font-size: 12px;\n  color: #94a3b8;\n  margin-top: 5px;\n}\n#ld-app .ld-empty {\n  color: #94a3b8;\n  font-size: 13px;\n  text-align: center;\n  padding: 20px 0;\n}\n#ld-app .ld-error {\n  color: #dc2626;\n  font-size: 13px;\n  padding: 8px 12px;\n  background: #fef2f2;\n  border-radius: 7px;\n  border: 1px solid #fecaca;\n}\n\u003c/style\u003e\n\u003c!-- Tab navigation --\u003e\n\u003cdiv class=\"ld-tab-row\"\u003e\n  \u003cbutton class=\"ld-tab active\" data-tab=\"single\"\u003eSingle Pair\u003c/button\u003e\n  \u003cbutton class=\"ld-tab\" data-tab=\"batch\"\u003eBatch Mode\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- ===== SINGLE PAIR TAB ===== --\u003e\n\u003cdiv class=\"ld-tab-panel active\" id=\"ld-tab-single\"\u003e\n  \u003cdiv class=\"ld-card\"\u003e\n    \u003cdiv class=\"ld-row\" style=\"margin-bottom:14px;\"\u003e\n      \u003cdiv class=\"ld-col\"\u003e\n        \u003clabel class=\"ld-label\" for=\"ld-str1\"\u003eString A\u003c/label\u003e\n        \u003cinput class=\"ld-input\" id=\"ld-str1\" type=\"text\" placeholder=\"e.g. kitten\" autocomplete=\"off\" spellcheck=\"false\"\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"ld-col\"\u003e\n        \u003clabel class=\"ld-label\" for=\"ld-str2\"\u003eString B\u003c/label\u003e\n        \u003cinput class=\"ld-input\" id=\"ld-str2\" type=\"text\" placeholder=\"e.g. sitting\" autocomplete=\"off\" spellcheck=\"false\"\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ld-toolbar\"\u003e\n      \u003cbutton class=\"ld-btn ld-btn-primary\" id=\"ld-calc-btn\"\u003eCalculate\u003c/button\u003e\n      \u003cbutton class=\"ld-btn\" id=\"ld-swap-btn\"\u003e\u0026#8646; Swap\u003c/button\u003e\n      \u003cbutton class=\"ld-btn\" id=\"ld-clear-btn\"\u003eClear\u003c/button\u003e\n      \u003clabel class=\"ld-check\"\u003e\n        \u003cinput type=\"checkbox\" id=\"ld-case-toggle\"\u003e Case-sensitive\n      \u003c/label\u003e\n      \u003clabel class=\"ld-check\"\u003e\n        \u003cinput type=\"checkbox\" id=\"ld-matrix-toggle\"\u003e Show DP matrix\n      \u003c/label\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Result banner --\u003e\n  \u003cdiv class=\"ld-result-banner\" id=\"ld-banner\"\u003e\n    \u003cdiv class=\"ld-stat\"\u003e\n      \u003cspan class=\"ld-stat-num\" id=\"ld-dist-num\"\u003e—\u003c/span\u003e\n      \u003cspan class=\"ld-stat-label\"\u003eEdit Distance\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ld-stat\"\u003e\n      \u003cspan class=\"ld-stat-num\" id=\"ld-sim-num\"\u003e—\u003c/span\u003e\n      \u003cspan class=\"ld-stat-label\"\u003eSimilarity\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ld-stat\"\u003e\n      \u003cspan class=\"ld-stat-num\" id=\"ld-ins-num\" style=\"color:#16a34a;\"\u003e0\u003c/span\u003e\n      \u003cspan class=\"ld-stat-label\"\u003eInsertions\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ld-stat\"\u003e\n      \u003cspan class=\"ld-stat-num\" id=\"ld-del-num\" style=\"color:#dc2626;\"\u003e0\u003c/span\u003e\n      \u003cspan class=\"ld-stat-label\"\u003eDeletions\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ld-stat\"\u003e\n      \u003cspan class=\"ld-stat-num\" id=\"ld-sub-num\" style=\"color:#d97706;\"\u003e0\u003c/span\u003e\n      \u003cspan class=\"ld-stat-label\"\u003eSubstitutions\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv style=\"flex:1;min-width:140px;display:flex;flex-direction:column;justify-content:center;\"\u003e\n      \u003cdiv style=\"font-size:11px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.04em;margin-bottom:5px;\"\u003eSimilarity\u003c/div\u003e\n      \u003cdiv class=\"ld-sim-bar-wrap\"\u003e\u003cdiv class=\"ld-sim-bar\" id=\"ld-sim-bar\"\u003e\u003c/div\u003e\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Step-by-step explanation --\u003e\n  \u003cdiv class=\"ld-card\" id=\"ld-steps-card\" style=\"display:none;\"\u003e\n    \u003cdiv class=\"ld-section-title\"\u003eStep-by-step Edit Operations\u003c/div\u003e\n    \u003cdiv class=\"ld-steps show\" id=\"ld-steps\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- DP Matrix --\u003e\n  \u003cdiv class=\"ld-card\" id=\"ld-matrix-card\" style=\"display:none;\"\u003e\n    \u003cdiv class=\"ld-section-title\"\u003eDynamic Programming Matrix \u003cspan style=\"font-size:11px;font-weight:400;color:#94a3b8;\"\u003e(highlighted cells = optimal path)\u003c/span\u003e\u003c/div\u003e\n    \u003cdiv class=\"ld-matrix-wrap show\" id=\"ld-matrix\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ===== BATCH TAB ===== --\u003e\n\u003cdiv class=\"ld-tab-panel\" id=\"ld-tab-batch\"\u003e\n  \u003cdiv class=\"ld-card\"\u003e\n    \u003cdiv class=\"ld-section-title\" style=\"margin-bottom:8px;\"\u003eBatch Compare\u003c/div\u003e\n    \u003cp class=\"ld-hint\" style=\"margin-bottom:10px;\"\u003eEnter one pair per line, separated by a comma: \u003ccode style=\"font-family:monospace;background:#f1f5f9;padding:1px 5px;border-radius:3px;\"\u003eword1,word2\u003c/code\u003e\u003c/p\u003e","title":"Levenshtein Distance Calculator"},{"content":" Code Mode Language: JavaScript / CSS Python / Shell HTML SQL Clear Upload File No file chosen Supports: .txt, .js, .ts, .py, .html, .css, .sql, .sh, .md, .json, and more\n0 Total Lines 0 Non-Blank Lines 0 Blank Lines 0 Words 0 Characters 0 Chars (no spaces) 0 Paragraphs 0 Avg Line Length Code Analysis Code Lines0 Comment Lines0 Block Comment Lines0 Code / Comment Ratio— Longest line: 0 characters (line —)\nExport Statistics Copy Stats Also try: Word Counter \u0026nbsp;|\u0026nbsp; Reading Time Calculator How to Use the Line Counter Paste your text or code into the textarea, or click Upload File to load a local file. Statistics update instantly as you type. Enable Code Mode to see code vs comment breakdown, then pick your language. Use Export Statistics to download a .txt summary, or Copy Stats to copy to clipboard. What Each Statistic Means Stat Description Total Lines Every line including blank ones Non-Blank Lines Lines with at least one non-whitespace character Blank Lines Empty or whitespace-only lines Words Whitespace-delimited tokens Characters Total character count including spaces and newlines Chars (no spaces) Characters excluding all whitespace Paragraphs Blocks of text separated by blank lines Avg Line Length Mean characters per line Longest Line The single line with the most characters Code Lines Non-blank, non-comment lines (Code Mode) Comment Lines Lines starting with //, #, /*, *, or \u0026lt;!-- depending on language Comment Detection by Language Language Single-line Block JavaScript / CSS // /* … */ Python / Shell # \u0026quot;\u0026quot;\u0026quot;…\u0026quot;\u0026quot;\u0026quot; / '''…''' HTML — \u0026lt;!-- … --\u0026gt; SQL -- /* … */ Related Tools Word Counter — detailed word frequency and readability stats Reading Time Calculator — estimate how long your content takes to read ","permalink":"https://productivity-works.com/tools/line-counter/","summary":"\u003cdiv id=\"lc-app\"\u003e\n\u003cstyle\u003e\n#lc-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #1e293b;\n}\n#lc-app h2 {\n  font-size: 1.15rem;\n  font-weight: 700;\n  margin: 1.4rem 0 0.5rem;\n  color: #0f172a;\n}\n#lc-app .lc-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  align-items: center;\n  margin-bottom: 10px;\n}\n#lc-app .lc-toolbar label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #475569;\n}\n#lc-app select,\n#lc-app button {\n  font-size: 13px;\n  padding: 6px 14px;\n  border-radius: 7px;\n  border: 1.5px solid #cbd5e1;\n  background: #fff;\n  cursor: pointer;\n  color: #1e293b;\n  transition: border-color 0.15s, background 0.15s;\n}\n#lc-app select:focus,\n#lc-app button:focus {\n  outline: 2px solid #3b82f6;\n  outline-offset: 1px;\n}\n#lc-app button.lc-btn-primary {\n  background: #2563eb;\n  color: #fff;\n  border-color: #2563eb;\n  font-weight: 700;\n}\n#lc-app button.lc-btn-primary:hover {\n  background: #1d4ed8;\n  border-color: #1d4ed8;\n}\n#lc-app button.lc-btn-danger {\n  background: #fee2e2;\n  color: #b91c1c;\n  border-color: #fca5a5;\n  font-weight: 600;\n}\n#lc-app button.lc-btn-danger:hover {\n  background: #fecaca;\n}\n#lc-app button.lc-btn-secondary {\n  background: #f1f5f9;\n  color: #334155;\n  border-color: #cbd5e1;\n}\n#lc-app button.lc-btn-secondary:hover {\n  background: #e2e8f0;\n}\n#lc-app textarea#lc-input {\n  width: 100%;\n  min-height: 260px;\n  padding: 14px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 13.5px;\n  line-height: 1.6;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 10px;\n  resize: vertical;\n  box-sizing: border-box;\n  background: #f8fafc;\n  color: #0f172a;\n  transition: border-color 0.15s;\n}\n#lc-app textarea#lc-input:focus {\n  border-color: #3b82f6;\n  outline: none;\n  background: #fff;\n}\n#lc-app .lc-upload-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-top: 8px;\n  flex-wrap: wrap;\n}\n#lc-app .lc-upload-label {\n  display: inline-block;\n  padding: 6px 14px;\n  background: #f1f5f9;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  font-size: 13px;\n  font-weight: 600;\n  color: #334155;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n#lc-app .lc-upload-label:hover {\n  background: #e2e8f0;\n}\n#lc-app input[type=\"file\"] {\n  display: none;\n}\n#lc-app .lc-file-name {\n  font-size: 12px;\n  color: #64748b;\n}\n#lc-app .lc-stats-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));\n  gap: 12px;\n  margin-top: 18px;\n}\n#lc-app .lc-stat-card {\n  background: #f1f5f9;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 14px 16px;\n  text-align: center;\n}\n#lc-app .lc-stat-card.lc-highlight {\n  background: #eff6ff;\n  border-color: #bfdbfe;\n}\n#lc-app .lc-stat-value {\n  font-size: 2rem;\n  font-weight: 800;\n  color: #2563eb;\n  line-height: 1.1;\n}\n#lc-app .lc-stat-label {\n  font-size: 11.5px;\n  color: #64748b;\n  margin-top: 4px;\n  font-weight: 500;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#lc-app .lc-code-stats {\n  margin-top: 18px;\n  background: #0f172a;\n  border-radius: 10px;\n  padding: 16px 20px;\n  color: #e2e8f0;\n}\n#lc-app .lc-code-stats h3 {\n  font-size: 13px;\n  font-weight: 700;\n  color: #94a3b8;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin: 0 0 10px;\n}\n#lc-app .lc-code-row {\n  display: flex;\n  justify-content: space-between;\n  font-size: 13.5px;\n  padding: 4px 0;\n  border-bottom: 1px solid #1e293b;\n}\n#lc-app .lc-code-row:last-child {\n  border-bottom: none;\n}\n#lc-app .lc-code-key {\n  color: #7dd3fc;\n}\n#lc-app .lc-code-val {\n  font-weight: 700;\n  color: #86efac;\n}\n#lc-app .lc-longest-bar {\n  margin-top: 14px;\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 14px 16px;\n}\n#lc-app .lc-longest-bar p {\n  margin: 0;\n  font-size: 13px;\n  color: #475569;\n}\n#lc-app .lc-longest-content {\n  font-family: monospace;\n  font-size: 12px;\n  background: #1e293b;\n  color: #f1f5f9;\n  border-radius: 6px;\n  padding: 8px 12px;\n  margin-top: 8px;\n  word-break: break-all;\n  white-space: pre-wrap;\n}\n#lc-app .lc-mode-toggle {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n#lc-app .lc-switch {\n  position: relative;\n  display: inline-block;\n  width: 38px;\n  height: 22px;\n}\n#lc-app .lc-switch input {\n  opacity: 0;\n  width: 0;\n  height: 0;\n}\n#lc-app .lc-slider {\n  position: absolute;\n  cursor: pointer;\n  top: 0; left: 0; right: 0; bottom: 0;\n  background: #cbd5e1;\n  border-radius: 22px;\n  transition: 0.2s;\n}\n#lc-app .lc-slider:before {\n  position: absolute;\n  content: \"\";\n  height: 16px;\n  width: 16px;\n  left: 3px;\n  bottom: 3px;\n  background: white;\n  border-radius: 50%;\n  transition: 0.2s;\n}\n#lc-app input:checked + .lc-slider {\n  background: #2563eb;\n}\n#lc-app input:checked + .lc-slider:before {\n  transform: translateX(16px);\n}\n#lc-app .lc-switch-label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #334155;\n}\n#lc-app .lc-export-row {\n  margin-top: 14px;\n  display: flex;\n  gap: 8px;\n  flex-wrap: wrap;\n}\n#lc-app .lc-info {\n  font-size: 13px;\n  color: #64748b;\n  margin-top: 6px;\n}\n#lc-app .lc-divider {\n  border: none;\n  border-top: 1.5px solid #e2e8f0;\n  margin: 28px 0 20px;\n}\n#lc-app .lc-links {\n  font-size: 13.5px;\n  color: #475569;\n}\n#lc-app .lc-links a {\n  color: #2563eb;\n  text-decoration: none;\n  font-weight: 600;\n}\n#lc-app .lc-links a:hover {\n  text-decoration: underline;\n}\n@media (max-width: 540px) {\n  #lc-app .lc-stats-grid {\n    grid-template-columns: repeat(2, 1fr);\n  }\n}\n\u003c/style\u003e\n\u003c!-- Toolbar --\u003e\n\u003cdiv class=\"lc-toolbar\"\u003e\n  \u003cdiv class=\"lc-mode-toggle\"\u003e\n    \u003clabel class=\"lc-switch\"\u003e\n      \u003cinput type=\"checkbox\" id=\"lc-code-mode\"\u003e\n      \u003cspan class=\"lc-slider\"\u003e\u003c/span\u003e\n    \u003c/label\u003e\n    \u003cspan class=\"lc-switch-label\"\u003eCode Mode\u003c/span\u003e\n  \u003c/div\u003e\n  \u003clabel for=\"lc-lang\"\u003eLanguage:\u003c/label\u003e\n  \u003cselect id=\"lc-lang\"\u003e\n    \u003coption value=\"js\"\u003eJavaScript / CSS\u003c/option\u003e\n    \u003coption value=\"python\"\u003ePython / Shell\u003c/option\u003e\n    \u003coption value=\"html\"\u003eHTML\u003c/option\u003e\n    \u003coption value=\"sql\"\u003eSQL\u003c/option\u003e\n  \u003c/select\u003e\n  \u003cbutton class=\"lc-btn-danger\" id=\"lc-clear-btn\"\u003eClear\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Textarea --\u003e\n\u003ctextarea id=\"lc-input\" placeholder=\"Paste your text or code here, or upload a file below…\" spellcheck=\"false\"\u003e\u003c/textarea\u003e\n\u003c!-- File upload --\u003e\n\u003cdiv class=\"lc-upload-row\"\u003e\n  \u003clabel class=\"lc-upload-label\" for=\"lc-file-input\"\u003eUpload File\u003c/label\u003e\n  \u003cinput type=\"file\" id=\"lc-file-input\" accept=\".txt,.js,.ts,.py,.html,.htm,.css,.scss,.sql,.sh,.bash,.md,.json,.xml,.csv,.yaml,.yml,.rb,.java,.c,.cpp,.go,.rs,.php\"\u003e\n  \u003cspan class=\"lc-file-name\" id=\"lc-file-name\"\u003eNo file chosen\u003c/span\u003e\n\u003c/div\u003e\n\u003cp class=\"lc-info\"\u003eSupports: .txt, .js, .ts, .py, .html, .css, .sql, .sh, .md, .json, and more\u003c/p\u003e","title":"Line Counter \u0026 Code Statistics — Free Online Tool"},{"content":" Basic Calculator Extra Payments Compare Loans Loan Amount $ Annual Interest Rate % Loan Term Yrs Mo Start Date (optional) Calculate Monthly Payment\nMonthly Payment $0.00 \u0026lt;div class=\u0026quot;summary-grid\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;summary-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;s-label\u0026quot;\u0026gt;Total Payment\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;s-value\u0026quot; id=\u0026quot;r-total\u0026quot;\u0026gt;$0\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;s-sub\u0026quot;\u0026gt;principal + interest\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;summary-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;s-label\u0026quot;\u0026gt;Total Interest\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;s-value\u0026quot; id=\u0026quot;r-interest\u0026quot;\u0026gt;$0\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;s-sub\u0026quot; id=\u0026quot;r-interest-pct\u0026quot;\u0026gt;0% of loan\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;summary-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;s-label\u0026quot;\u0026gt;Payoff Date\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;s-value\u0026quot; id=\u0026quot;r-payoff\u0026quot; style=\u0026quot;font-size:16px;margin-top:4px;\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;s-sub\u0026quot; id=\u0026quot;r-payoff-sub\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Pie Chart --\u0026gt; \u0026lt;div class=\u0026quot;chart-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;pie-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;pie-chart\u0026quot; id=\u0026quot;pie-chart\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pie-center\u0026quot; id=\u0026quot;pie-center\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pie-legend\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;legend-item\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;legend-dot principal\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;legend-text\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;lt\u0026quot;\u0026gt;Principal\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;lv\u0026quot; id=\u0026quot;legend-principal\u0026quot;\u0026gt;$0\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;legend-item\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;legend-dot interest\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;legend-text\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;lt\u0026quot;\u0026gt;Total Interest\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;lv\u0026quot; id=\u0026quot;legend-interest\u0026quot;\u0026gt;$0\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Timeline Bar --\u0026gt; \u0026lt;div class=\u0026quot;timeline-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;timeline-title\u0026quot; id=\u0026quot;timeline-title\u0026quot;\u0026gt;Payoff Progress\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;timeline-bar-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;timeline-bar-fill\u0026quot; id=\u0026quot;timeline-bar\u0026quot; style=\u0026quot;width:0%\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;timeline-labels\u0026quot;\u0026gt; \u0026lt;span\u0026gt;Today\u0026lt;/span\u0026gt; \u0026lt;span id=\u0026quot;timeline-mid\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;span id=\u0026quot;timeline-end\u0026quot;\u0026gt;Payoff\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Amortization Table --\u0026gt; \u0026lt;div class=\u0026quot;amort-section\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Amortization Schedule\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;table-wrap\u0026quot;\u0026gt; \u0026lt;table class=\u0026quot;amort-table\u0026quot;\u0026gt; \u0026lt;thead\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;th\u0026gt;Month\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;Payment\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;Principal\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;Interest\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;Balance\u0026lt;/th\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;/thead\u0026gt; \u0026lt;tbody id=\u0026quot;amort-body\u0026quot;\u0026gt;\u0026lt;/tbody\u0026gt; \u0026lt;/table\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;amort-toggle-btn\u0026quot; id=\u0026quot;amort-toggle\u0026quot; onclick=\u0026quot;toggleAmort()\u0026quot;\u0026gt;Show Full Schedule\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; See how extra payments reduce your total interest and payoff time. Loan Amount $ Annual Interest Rate % Loan Term (years) yrs Extra Payments Extra Monthly Payment $ One-Time Extra Payment $ One-Time Payment at Month # Calculate Impact\nOriginal Monthly $0 New Monthly $0 \u0026lt;div class=\u0026quot;extra-impact\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;impact-box\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ib-label\u0026quot;\u0026gt;Time Saved\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ib-value\u0026quot; id=\u0026quot;e-time-saved\u0026quot;\u0026gt;0 months\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;impact-box\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ib-label\u0026quot;\u0026gt;Interest Saved\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ib-value\u0026quot; id=\u0026quot;e-interest-saved\u0026quot;\u0026gt;$0\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;impact-box\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ib-label\u0026quot;\u0026gt;Original Total Interest\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ib-value\u0026quot; id=\u0026quot;e-orig-interest\u0026quot;\u0026gt;$0\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;impact-box\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ib-label\u0026quot;\u0026gt;New Total Interest\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ib-value\u0026quot; id=\u0026quot;e-new-interest\u0026quot;\u0026gt;$0\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Enter two loan options to compare total cost, monthly payment, and interest side by side. Loan A Loan Amount $ Annual Interest Rate % Loan Term (years) yrs Monthly Payment $0 \u0026lt;!-- Loan B --\u0026gt; \u0026lt;div class=\u0026quot;compare-card\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Loan B\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;input-group\u0026quot; style=\u0026quot;margin-bottom:12px\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Loan Amount\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;input-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;input-prefix\u0026quot;\u0026gt;$\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;cb-amount\u0026quot; value=\u0026quot;20000\u0026quot; min=\u0026quot;0\u0026quot; step=\u0026quot;100\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;input-group\u0026quot; style=\u0026quot;margin-bottom:12px\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Annual Interest Rate\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;input-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;cb-rate\u0026quot; value=\u0026quot;8.0\u0026quot; min=\u0026quot;0\u0026quot; step=\u0026quot;0.01\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;input-suffix\u0026quot;\u0026gt;%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;input-group\u0026quot; style=\u0026quot;margin-bottom:12px\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Loan Term (years)\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;input-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;cb-term\u0026quot; value=\u0026quot;5\u0026quot; min=\u0026quot;1\u0026quot; step=\u0026quot;1\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;input-suffix\u0026quot;\u0026gt;yrs\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;compare-result\u0026quot; id=\u0026quot;cb-result\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;cr-label\u0026quot;\u0026gt;Monthly Payment\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cr-value\u0026quot; id=\u0026quot;cb-monthly\u0026quot;\u0026gt;$0\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;cr-sub\u0026quot; id=\u0026quot;cb-sub\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Compare Loans\nMetric Loan A Loan B How to Use This Loan Calculator Enter your loan details — input the loan amount, annual interest rate, and term in years or months. Click Calculate — your monthly payment appears instantly, along with total interest and payoff date. Review the amortization schedule — see exactly how each payment splits between principal and interest month by month. Try extra payments — switch to the Extra Payments tab and enter an additional monthly or one-time payment to see how much time and money you save. Compare two loans — use the Compare Loans tab to evaluate different rate or term options side by side before signing. Tips for Paying Off Loans Faster Add even a small extra monthly payment. An extra $50–$100/month on a 5-year loan can shave months off your payoff and save hundreds in interest. Make one extra payment per year. Apply a tax refund, bonus, or side income directly to your principal — this alone can cut a 30-year mortgage by several years. Choose the shortest term you can comfortably afford. A 3-year auto loan costs far less in total interest than a 6-year loan, even at the same rate. Refinance when rates drop. If your credit improves or market rates fall, refinancing at a lower rate reduces both your monthly payment and lifetime interest. Related Tools Calculate percentages and discounts → Percentage Calculator See how inflation affects your money → Inflation Calculator Track your subscription costs → Subscription Cost Calculator Related Articles How to Pay Off Debt Fast: Best Strategies 2026 ","permalink":"https://productivity-works.com/tools/loan-calculator/","summary":"\u003cdiv id=\"loan-app\"\u003e\n\u003cstyle\u003e\n#loan-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 960px;\n  margin: 0 auto;\n  color: #1f2937;\n}\n\n#loan-app * {\n  box-sizing: border-box;\n}\n\n/* Tabs */\n.loan-tabs {\n  display: flex;\n  gap: 8px;\n  margin-bottom: 24px;\n  flex-wrap: wrap;\n}\n\n.loan-tab {\n  padding: 10px 20px;\n  border: 2px solid #d1fae5;\n  border-radius: 8px;\n  background: #fff;\n  color: #059669;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.2s;\n}\n\n.loan-tab:hover {\n  background: #f0fdf4;\n}\n\n.loan-tab.active {\n  background: #059669;\n  color: #fff;\n  border-color: #059669;\n}\n\n/* Panels */\n.loan-panel {\n  display: none;\n}\n\n.loan-panel.active {\n  display: block;\n}\n\n/* Input Group */\n.input-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 16px;\n  margin-bottom: 16px;\n}\n\n@media (max-width: 600px) {\n  .input-grid {\n    grid-template-columns: 1fr;\n  }\n}\n\n.input-group {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n}\n\n.input-group label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #374151;\n}\n\n.input-row {\n  display: flex;\n  align-items: center;\n  border: 2px solid #d1fae5;\n  border-radius: 8px;\n  overflow: hidden;\n  background: #fff;\n  transition: border-color 0.2s;\n}\n\n.input-row:focus-within {\n  border-color: #059669;\n}\n\n.input-prefix, .input-suffix {\n  padding: 10px 12px;\n  background: #f0fdf4;\n  color: #059669;\n  font-weight: 700;\n  font-size: 15px;\n  border: none;\n  white-space: nowrap;\n}\n\n.input-row input[type=\"number\"],\n.input-row input[type=\"text\"] {\n  flex: 1;\n  border: none;\n  outline: none;\n  padding: 10px 12px;\n  font-size: 16px;\n  color: #1f2937;\n  background: transparent;\n  min-width: 0;\n}\n\n/* Term toggle */\n.term-row {\n  display: flex;\n  align-items: center;\n  border: 2px solid #d1fae5;\n  border-radius: 8px;\n  overflow: hidden;\n  background: #fff;\n  transition: border-color 0.2s;\n}\n\n.term-row:focus-within {\n  border-color: #059669;\n}\n\n.term-row input[type=\"number\"] {\n  flex: 1;\n  border: none;\n  outline: none;\n  padding: 10px 12px;\n  font-size: 16px;\n  color: #1f2937;\n  background: transparent;\n  min-width: 0;\n}\n\n.term-toggle {\n  display: flex;\n}\n\n.term-toggle button {\n  padding: 10px 12px;\n  border: none;\n  background: #f0fdf4;\n  color: #6b7280;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.2s;\n}\n\n.term-toggle button.active {\n  background: #059669;\n  color: #fff;\n}\n\n/* Calculate Button */\n.calc-btn {\n  width: 100%;\n  padding: 14px;\n  background: #059669;\n  color: #fff;\n  border: none;\n  border-radius: 10px;\n  font-size: 17px;\n  font-weight: 700;\n  cursor: pointer;\n  margin: 16px 0;\n  transition: background 0.2s;\n  letter-spacing: 0.3px;\n}\n\n.calc-btn:hover {\n  background: #047857;\n}\n\n/* Result Card */\n.result-hero {\n  background: linear-gradient(135deg, #059669, #047857);\n  border-radius: 14px;\n  padding: 28px;\n  text-align: center;\n  color: #fff;\n  margin-bottom: 20px;\n}\n\n.result-hero .label {\n  font-size: 14px;\n  opacity: 0.85;\n  margin-bottom: 6px;\n  font-weight: 500;\n}\n\n.result-hero .amount {\n  font-size: 52px;\n  font-weight: 800;\n  letter-spacing: -1px;\n  line-height: 1;\n}\n\n.result-hero .sublabel {\n  font-size: 13px;\n  opacity: 0.75;\n  margin-top: 6px;\n}\n\n/* Summary Grid */\n.summary-grid {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 12px;\n  margin-bottom: 20px;\n}\n\n@media (max-width: 600px) {\n  .summary-grid {\n    grid-template-columns: 1fr;\n  }\n}\n\n.summary-card {\n  background: #f0fdf4;\n  border: 1px solid #d1fae5;\n  border-radius: 10px;\n  padding: 16px;\n  text-align: center;\n}\n\n.summary-card .s-label {\n  font-size: 12px;\n  color: #6b7280;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.5px;\n  margin-bottom: 6px;\n}\n\n.summary-card .s-value {\n  font-size: 22px;\n  font-weight: 700;\n  color: #059669;\n}\n\n.summary-card .s-sub {\n  font-size: 12px;\n  color: #6b7280;\n  margin-top: 3px;\n}\n\n/* Pie Chart (CSS only) */\n.chart-section {\n  display: flex;\n  align-items: center;\n  gap: 32px;\n  background: #f9fafb;\n  border: 1px solid #e5e7eb;\n  border-radius: 12px;\n  padding: 24px;\n  margin-bottom: 20px;\n  flex-wrap: wrap;\n  justify-content: center;\n}\n\n.pie-wrap {\n  position: relative;\n  width: 140px;\n  height: 140px;\n  flex-shrink: 0;\n}\n\n.pie-chart {\n  width: 140px;\n  height: 140px;\n  border-radius: 50%;\n  background: conic-gradient(#059669 0deg 0deg, #6ee7b7 0deg 360deg);\n  transition: background 0.5s;\n}\n\n.pie-center {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  transform: translate(-50%, -50%);\n  text-align: center;\n  font-size: 11px;\n  font-weight: 700;\n  color: #374151;\n  pointer-events: none;\n}\n\n.pie-legend {\n  display: flex;\n  flex-direction: column;\n  gap: 12px;\n}\n\n.legend-item {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n\n.legend-dot {\n  width: 14px;\n  height: 14px;\n  border-radius: 50%;\n  flex-shrink: 0;\n}\n\n.legend-dot.principal { background: #059669; }\n.legend-dot.interest  { background: #6ee7b7; }\n\n.legend-text .lt {\n  font-size: 13px;\n  color: #6b7280;\n  font-weight: 500;\n}\n\n.legend-text .lv {\n  font-size: 16px;\n  font-weight: 700;\n  color: #1f2937;\n}\n\n/* Payoff Timeline Bar */\n.timeline-section {\n  margin-bottom: 20px;\n}\n\n.timeline-title {\n  font-size: 14px;\n  font-weight: 600;\n  color: #374151;\n  margin-bottom: 8px;\n}\n\n.timeline-bar-wrap {\n  background: #e5e7eb;\n  border-radius: 99px;\n  height: 22px;\n  overflow: hidden;\n  position: relative;\n}\n\n.timeline-bar-fill {\n  height: 100%;\n  background: linear-gradient(90deg, #059669, #34d399);\n  border-radius: 99px;\n  transition: width 0.6s ease;\n  min-width: 4px;\n}\n\n.timeline-labels {\n  display: flex;\n  justify-content: space-between;\n  font-size: 12px;\n  color: #6b7280;\n  margin-top: 5px;\n}\n\n/* Extra Payment Section */\n.section-card {\n  background: #fff;\n  border: 2px solid #d1fae5;\n  border-radius: 12px;\n  padding: 20px;\n  margin-bottom: 20px;\n}\n\n.section-card h3 {\n  margin: 0 0 16px 0;\n  font-size: 16px;\n  color: #059669;\n  font-weight: 700;\n}\n\n.extra-input-row {\n  display: flex;\n  gap: 12px;\n  flex-wrap: wrap;\n  margin-bottom: 14px;\n}\n\n.extra-input-row .input-group {\n  flex: 1;\n  min-width: 180px;\n}\n\n.extra-impact {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 12px;\n}\n\n@media (max-width: 500px) {\n  .extra-impact {\n    grid-template-columns: 1fr;\n  }\n}\n\n.impact-box {\n  background: #f0fdf4;\n  border: 1px solid #a7f3d0;\n  border-radius: 8px;\n  padding: 14px;\n  text-align: center;\n}\n\n.impact-box .ib-label {\n  font-size: 12px;\n  color: #6b7280;\n  font-weight: 600;\n  margin-bottom: 4px;\n}\n\n.impact-box .ib-value {\n  font-size: 20px;\n  font-weight: 800;\n  color: #059669;\n}\n\n/* Amortization Table */\n.amort-section {\n  margin-bottom: 20px;\n}\n\n.amort-section h3 {\n  font-size: 16px;\n  font-weight: 700;\n  color: #1f2937;\n  margin-bottom: 12px;\n}\n\n.table-wrap {\n  overflow-x: auto;\n  border-radius: 10px;\n  border: 1px solid #e5e7eb;\n}\n\n.amort-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 13px;\n}\n\n.amort-table th {\n  background: #059669;\n  color: #fff;\n  padding: 10px 12px;\n  text-align: right;\n  font-weight: 600;\n  white-space: nowrap;\n}\n\n.amort-table th:first-child {\n  text-align: left;\n}\n\n.amort-table td {\n  padding: 9px 12px;\n  text-align: right;\n  border-bottom: 1px solid #f3f4f6;\n  color: #374151;\n  white-space: nowrap;\n}\n\n.amort-table td:first-child {\n  text-align: left;\n  font-weight: 600;\n  color: #1f2937;\n}\n\n.amort-table tr:nth-child(even) td {\n  background: #f9fafb;\n}\n\n.amort-table tr:last-child td {\n  background: #f0fdf4;\n  font-weight: 700;\n  color: #059669;\n  border-bottom: none;\n}\n\n.amort-table tr:hover td {\n  background: #ecfdf5;\n}\n\n.amort-toggle-btn {\n  margin-top: 10px;\n  padding: 8px 18px;\n  background: #fff;\n  border: 2px solid #059669;\n  border-radius: 8px;\n  color: #059669;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.2s;\n}\n\n.amort-toggle-btn:hover {\n  background: #f0fdf4;\n}\n\n/* Comparison Panel */\n.compare-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 20px;\n  margin-bottom: 20px;\n}\n\n@media (max-width: 640px) {\n  .compare-grid {\n    grid-template-columns: 1fr;\n  }\n}\n\n.compare-card {\n  border: 2px solid #d1fae5;\n  border-radius: 12px;\n  padding: 18px;\n  background: #fff;\n}\n\n.compare-card h3 {\n  font-size: 15px;\n  font-weight: 700;\n  color: #059669;\n  margin: 0 0 14px 0;\n}\n\n.compare-result {\n  background: #f0fdf4;\n  border: 1px solid #a7f3d0;\n  border-radius: 10px;\n  padding: 16px;\n  text-align: center;\n  margin-top: 12px;\n}\n\n.compare-result .cr-label {\n  font-size: 12px;\n  color: #6b7280;\n  font-weight: 600;\n  margin-bottom: 4px;\n}\n\n.compare-result .cr-value {\n  font-size: 26px;\n  font-weight: 800;\n  color: #059669;\n}\n\n.compare-result .cr-sub {\n  font-size: 12px;\n  color: #6b7280;\n  margin-top: 6px;\n}\n\n.compare-winner {\n  border-color: #059669;\n  background: linear-gradient(135deg, #f0fdf4, #d1fae5);\n}\n\n.winner-badge {\n  display: inline-block;\n  background: #059669;\n  color: #fff;\n  font-size: 11px;\n  font-weight: 700;\n  padding: 3px 10px;\n  border-radius: 99px;\n  margin-bottom: 6px;\n  text-transform: uppercase;\n  letter-spacing: 0.5px;\n}\n\n.compare-summary-table {\n  width: 100%;\n  border-collapse: collapse;\n  margin-top: 16px;\n  font-size: 13px;\n}\n\n.compare-summary-table th {\n  background: #059669;\n  color: #fff;\n  padding: 9px 14px;\n  text-align: center;\n}\n\n.compare-summary-table td {\n  padding: 9px 14px;\n  text-align: center;\n  border-bottom: 1px solid #f3f4f6;\n}\n\n.compare-summary-table tr:nth-child(even) td {\n  background: #f9fafb;\n}\n\n.compare-summary-table .best {\n  color: #059669;\n  font-weight: 700;\n}\n\n/* Hidden */\n.hidden { display: none !important; }\n\n/* Alert / info */\n.info-box {\n  background: #f0fdf4;\n  border-left: 4px solid #059669;\n  padding: 12px 16px;\n  border-radius: 0 8px 8px 0;\n  font-size: 13px;\n  color: #374151;\n  margin-bottom: 16px;\n}\n\u003c/style\u003e\n\u003cdiv class=\"loan-tabs\"\u003e\n  \u003cbutton class=\"loan-tab active\" onclick=\"switchTab('basic')\"\u003eBasic Calculator\u003c/button\u003e\n  \u003cbutton class=\"loan-tab\" onclick=\"switchTab('extra')\"\u003eExtra Payments\u003c/button\u003e\n  \u003cbutton class=\"loan-tab\" onclick=\"switchTab('compare')\"\u003eCompare Loans\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- ===== BASIC PANEL ===== --\u003e\n\u003cdiv id=\"panel-basic\" class=\"loan-panel active\"\u003e\n  \u003cdiv class=\"input-grid\"\u003e\n    \u003cdiv class=\"input-group\"\u003e\n      \u003clabel\u003eLoan Amount\u003c/label\u003e\n      \u003cdiv class=\"input-row\"\u003e\n        \u003cspan class=\"input-prefix\"\u003e$\u003c/span\u003e\n        \u003cinput type=\"number\" id=\"b-amount\" value=\"20000\" min=\"0\" step=\"100\" placeholder=\"20000\"\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"input-group\"\u003e\n      \u003clabel\u003eAnnual Interest Rate\u003c/label\u003e\n      \u003cdiv class=\"input-row\"\u003e\n        \u003cinput type=\"number\" id=\"b-rate\" value=\"6.5\" min=\"0\" step=\"0.01\" placeholder=\"6.5\"\u003e\n        \u003cspan class=\"input-suffix\"\u003e%\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"input-group\"\u003e\n      \u003clabel\u003eLoan Term\u003c/label\u003e\n      \u003cdiv class=\"term-row\"\u003e\n        \u003cinput type=\"number\" id=\"b-term\" value=\"5\" min=\"1\" step=\"1\" placeholder=\"5\"\u003e\n        \u003cdiv class=\"term-toggle\"\u003e\n          \u003cbutton id=\"b-term-yr\" class=\"active\" onclick=\"setTermUnit('years')\"\u003eYrs\u003c/button\u003e\n          \u003cbutton id=\"b-term-mo\" onclick=\"setTermUnit('months')\"\u003eMo\u003c/button\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"input-group\"\u003e\n      \u003clabel\u003eStart Date (optional)\u003c/label\u003e\n      \u003cdiv class=\"input-row\"\u003e\n        \u003cinput type=\"month\" id=\"b-start\" style=\"padding:10px 12px;border:none;outline:none;font-size:15px;color:#1f2937;background:transparent;flex:1;min-width:0;\"\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003cp\u003e\u003cbutton class=\"calc-btn\" onclick=\"calcBasic()\"\u003eCalculate Monthly Payment\u003c/button\u003e\u003c/p\u003e","title":"Loan Calculator - Free Payment \u0026 Amortization Tool"},{"content":" Loan EMI → Loan EMI Calculator Savings goal → Savings Goal Calculator Loan Comparison Calculator Loan A Loan Name Principal ($) Annual Interest Rate (%) Term (Years) Loan B Loan Name Principal ($) Annual Interest Rate (%) Term (Years) Loan C (optional) Loan Name Principal ($) Annual Interest Rate (%) Term (Years) Calculate \u0026amp; Compare Reset Side-by-Side Comparison Cost Breakdown Chart Amortization Schedules Related Tools Debt Payoff Calculator Mortgage Affordability Calculator Mortgage Calculator ","permalink":"https://productivity-works.com/tools/loan-comparison/","summary":"\u003cblockquote\u003e\n\u003cp\u003eLoan EMI → \u003ca href=\"https://productivity-works.com/tools/loan-emi-calculator/\"\u003eLoan EMI Calculator\u003c/a\u003e\n\nSavings goal → \u003ca href=\"https://productivity-works.com/tools/savings-goal-calculator/\"\u003eSavings Goal Calculator\u003c/a\u003e\n\u003c/p\u003e\u003c/blockquote\u003e\n\u003cdiv id=\"lc-app\"\u003e\n\u003cstyle\u003e\n#lc-app *,#lc-app *::before,#lc-app *::after{box-sizing:border-box;margin:0;padding:0}\n#lc-app{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;font-size:15px;color:#1e293b;max-width:900px;margin:0 auto;padding:16px}\n#lc-app h2{font-size:1.25rem;font-weight:700;margin-bottom:16px;color:#0f172a}\n#lc-app h3{font-size:1rem;font-weight:700;margin-bottom:10px;color:#0f172a}\n#lc-app .lc-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px;margin-bottom:20px}\n#lc-app .lc-card{background:#f8fafc;border:1.5px solid #e2e8f0;border-radius:10px;padding:16px}\n#lc-app .lc-card.winner{border-color:#22c55e;background:#f0fdf4}\n#lc-app .lc-card-title{font-size:0.9rem;font-weight:700;margin-bottom:12px;color:#475569;display:flex;align-items:center;gap:6px}\n#lc-app .lc-badge{font-size:11px;padding:2px 8px;border-radius:20px;font-weight:700;background:#22c55e;color:#fff}\n#lc-app label{display:block;font-size:12px;font-weight:600;color:#64748b;margin-bottom:4px;margin-top:10px}\n#lc-app label:first-of-type{margin-top:0}\n#lc-app input[type=number],#lc-app input[type=text]{width:100%;padding:8px 10px;border:1.5px solid #cbd5e1;border-radius:7px;font-size:14px;color:#1e293b;background:#fff;transition:border-color .2s}\n#lc-app input[type=number]:focus,#lc-app input[type=text]:focus{outline:none;border-color:#3b82f6}\n#lc-app .lc-btn{display:inline-block;padding:10px 28px;background:#3b82f6;color:#fff;border:none;border-radius:8px;font-size:15px;font-weight:700;cursor:pointer;transition:background .2s;margin-right:8px}\n#lc-app .lc-btn:hover{background:#2563eb}\n#lc-app .lc-btn-reset{background:#94a3b8}\n#lc-app .lc-btn-reset:hover{background:#64748b}\n#lc-app .lc-actions{margin-bottom:24px}\n#lc-app .lc-results{margin-top:8px}\n#lc-app .lc-compare-table{width:100%;border-collapse:collapse;font-size:14px;margin-bottom:24px}\n#lc-app .lc-compare-table th{background:#1e293b;color:#fff;padding:10px 12px;text-align:left;font-weight:600;font-size:13px}\n#lc-app .lc-compare-table td{padding:9px 12px;border-bottom:1px solid #e2e8f0}\n#lc-app .lc-compare-table tr:nth-child(even) td{background:#f8fafc}\n#lc-app .lc-compare-table .winner-col{background:#f0fdf4!important;font-weight:700;color:#15803d}\n#lc-app .lc-chart-wrap{margin-bottom:24px}\n#lc-app canvas{display:block;max-width:100%;border-radius:10px;background:#f8fafc;border:1.5px solid #e2e8f0;padding:12px}\n#lc-app .lc-amort-wrap{margin-bottom:20px}\n#lc-app .lc-amort-wrap details{margin-bottom:10px}\n#lc-app .lc-amort-wrap summary{cursor:pointer;font-weight:700;font-size:13px;padding:8px 12px;background:#f1f5f9;border:1.5px solid #e2e8f0;border-radius:8px;list-style:none;display:flex;align-items:center;justify-content:space-between}\n#lc-app .lc-amort-wrap summary::-webkit-details-marker{display:none}\n#lc-app .lc-amort-wrap summary::after{content:'▼';font-size:11px;color:#94a3b8}\n#lc-app details[open] summary::after{content:'▲'}\n#lc-app .lc-amort-table{width:100%;border-collapse:collapse;font-size:13px;margin-top:8px}\n#lc-app .lc-amort-table th{background:#475569;color:#fff;padding:7px 10px;text-align:right;font-size:12px}\n#lc-app .lc-amort-table th:first-child{text-align:center}\n#lc-app .lc-amort-table td{padding:6px 10px;border-bottom:1px solid #f1f5f9;text-align:right}\n#lc-app .lc-amort-table td:first-child{text-align:center;font-weight:600;color:#64748b}\n#lc-app .lc-amort-table tr:nth-child(even) td{background:#f8fafc}\n#lc-app .lc-hidden{display:none}\n#lc-app .lc-loan-toggle{display:flex;align-items:center;gap:8px;margin-bottom:8px;font-size:13px;font-weight:600;color:#64748b}\n#lc-app .lc-loan-toggle input[type=checkbox]{width:16px;height:16px;cursor:pointer;accent-color:#3b82f6}\n#lc-app .lc-num{font-variant-numeric:tabular-nums}\n@media(max-width:600px){\n  #lc-app .lc-compare-table{font-size:12px}\n  #lc-app .lc-compare-table th,#lc-app .lc-compare-table td{padding:7px 8px}\n  #lc-app .lc-btn{padding:9px 18px;font-size:14px}\n}\n\u003c/style\u003e\n\u003ch2\u003eLoan Comparison Calculator\u003c/h2\u003e\n\u003cdiv class=\"lc-grid\" id=\"lc-inputs\"\u003e\n  \u003cdiv class=\"lc-card\" id=\"card-0\"\u003e\n    \u003cdiv class=\"lc-card-title\"\u003eLoan A\u003c/div\u003e\n    \u003clabel\u003eLoan Name\u003c/label\u003e\n    \u003cinput type=\"text\" class=\"lc-name\" data-i=\"0\" value=\"Loan A\" placeholder=\"e.g. Bank A\"\u003e\n    \u003clabel\u003ePrincipal ($)\u003c/label\u003e\n    \u003cinput type=\"number\" class=\"lc-principal\" data-i=\"0\" value=\"200000\" min=\"0\" step=\"1000\"\u003e\n    \u003clabel\u003eAnnual Interest Rate (%)\u003c/label\u003e\n    \u003cinput type=\"number\" class=\"lc-rate\" data-i=\"0\" value=\"5.5\" min=\"0\" step=\"0.01\"\u003e\n    \u003clabel\u003eTerm (Years)\u003c/label\u003e\n    \u003cinput type=\"number\" class=\"lc-term\" data-i=\"0\" value=\"30\" min=\"1\" max=\"50\"\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"lc-card\" id=\"card-1\"\u003e\n    \u003cdiv class=\"lc-card-title\"\u003eLoan B\u003c/div\u003e\n    \u003clabel\u003eLoan Name\u003c/label\u003e\n    \u003cinput type=\"text\" class=\"lc-name\" data-i=\"1\" value=\"Loan B\" placeholder=\"e.g. Bank B\"\u003e\n    \u003clabel\u003ePrincipal ($)\u003c/label\u003e\n    \u003cinput type=\"number\" class=\"lc-principal\" data-i=\"1\" value=\"200000\" min=\"0\" step=\"1000\"\u003e\n    \u003clabel\u003eAnnual Interest Rate (%)\u003c/label\u003e\n    \u003cinput type=\"number\" class=\"lc-rate\" data-i=\"1\" value=\"4.8\" min=\"0\" step=\"0.01\"\u003e\n    \u003clabel\u003eTerm (Years)\u003c/label\u003e\n    \u003cinput type=\"number\" class=\"lc-term\" data-i=\"1\" value=\"30\" min=\"1\" max=\"50\"\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"lc-card\" id=\"card-2\"\u003e\n    \u003cdiv class=\"lc-card-title\"\u003e\n      \u003clabel class=\"lc-loan-toggle\" style=\"margin:0\"\u003e\n        \u003cinput type=\"checkbox\" id=\"lc-enable2\"\u003e Loan C (optional)\n      \u003c/label\u003e\n    \u003c/div\u003e\n    \u003cdiv id=\"lc-inputs2\"\u003e\n      \u003clabel\u003eLoan Name\u003c/label\u003e\n      \u003cinput type=\"text\" class=\"lc-name\" data-i=\"2\" value=\"Loan C\" placeholder=\"e.g. Bank C\"\u003e\n      \u003clabel\u003ePrincipal ($)\u003c/label\u003e\n      \u003cinput type=\"number\" class=\"lc-principal\" data-i=\"2\" value=\"200000\" min=\"0\" step=\"1000\"\u003e\n      \u003clabel\u003eAnnual Interest Rate (%)\u003c/label\u003e\n      \u003cinput type=\"number\" class=\"lc-rate\" data-i=\"2\" value=\"5.0\" min=\"0\" step=\"0.01\"\u003e\n      \u003clabel\u003eTerm (Years)\u003c/label\u003e\n      \u003cinput type=\"number\" class=\"lc-term\" data-i=\"2\" value=\"25\" min=\"1\" max=\"50\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"lc-actions\"\u003e\n  \u003cbutton class=\"lc-btn\" id=\"lc-calc-btn\"\u003eCalculate \u0026amp; Compare\u003c/button\u003e\n  \u003cbutton class=\"lc-btn lc-btn-reset\" id=\"lc-reset-btn\"\u003eReset\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"lc-results lc-hidden\" id=\"lc-results\"\u003e\n  \u003ch3\u003eSide-by-Side Comparison\u003c/h3\u003e\n  \u003cdiv style=\"overflow-x:auto;margin-bottom:24px\"\u003e\n    \u003ctable class=\"lc-compare-table\" id=\"lc-compare-table\"\u003e\u003c/table\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"lc-chart-wrap\"\u003e\n    \u003ch3\u003eCost Breakdown Chart\u003c/h3\u003e\n    \u003ccanvas id=\"lc-chart\" height=\"260\"\u003e\u003c/canvas\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"lc-amort-wrap\" id=\"lc-amort-wrap\"\u003e\n    \u003ch3\u003eAmortization Schedules\u003c/h3\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n  var CURRENCY = '$';\n  var enable2 = document.getElementById('lc-enable2');\n  var inputs2 = document.getElementById('lc-inputs2');\n  inputs2.style.opacity = '0.4';\n  inputs2.style.pointerEvents = 'none';\n  enable2.addEventListener('change', function(){\n    inputs2.style.opacity = enable2.checked ? '1' : '0.4';\n    inputs2.style.pointerEvents = enable2.checked ? '' : 'none';\n  });\n\n  function fmt(n){\n    return CURRENCY + n.toLocaleString('en-US', {minimumFractionDigits:2, maximumFractionDigits:2});\n  }\n  function fmtN(n){\n    return n.toLocaleString('en-US', {minimumFractionDigits:2, maximumFractionDigits:2});\n  }\n\n  function calcLoan(principal, annualRate, years){\n    var n = years * 12;\n    var r = annualRate / 100 / 12;\n    var monthly;\n    if(r === 0){\n      monthly = principal / n;\n    } else {\n      monthly = principal * r * Math.pow(1+r, n) / (Math.pow(1+r, n) - 1);\n    }\n    var totalPaid = monthly * n;\n    var totalInterest = totalPaid - principal;\n    return {monthly: monthly, totalPaid: totalPaid, totalInterest: totalInterest, n: n, r: r, principal: principal};\n  }\n\n  function buildAmortization(loan, name){\n    var rows = [];\n    var balance = loan.principal;\n    var n = loan.n;\n    var r = loan.r;\n    var monthly = loan.monthly;\n    for(var i = 1; i \u003c= n; i++){\n      var interest = balance * r;\n      var principal = monthly - interest;\n      balance -= principal;\n      if(balance \u003c 0) balance = 0;\n      rows.push({month: i, payment: monthly, principal: principal, interest: interest, balance: balance});\n    }\n    return rows;\n  }\n\n  function renderAmortTable(rows, name, id){\n    var html = '\u003cdetails id=\"amort-'+id+'\"\u003e\u003csummary\u003e'+name+' — Amortization Schedule ('+rows.length+' payments)\u003c/summary\u003e';\n    html += '\u003cdiv style=\"overflow-x:auto\"\u003e\u003ctable class=\"lc-amort-table\"\u003e\u003cthead\u003e\u003ctr\u003e';\n    html += '\u003cth\u003eMonth\u003c/th\u003e\u003cth\u003ePayment\u003c/th\u003e\u003cth\u003ePrincipal\u003c/th\u003e\u003cth\u003eInterest\u003c/th\u003e\u003cth\u003eBalance\u003c/th\u003e';\n    html += '\u003c/tr\u003e\u003c/thead\u003e\u003ctbody\u003e';\n    var showAll = rows.length \u003c= 60;\n    var limit = showAll ? rows.length : 24;\n    for(var i = 0; i \u003c limit; i++){\n      var r = rows[i];\n      html += '\u003ctr\u003e\u003ctd\u003e'+r.month+'\u003c/td\u003e\u003ctd\u003e'+fmt(r.payment)+'\u003c/td\u003e\u003ctd\u003e'+fmt(r.principal)+'\u003c/td\u003e\u003ctd\u003e'+fmt(r.interest)+'\u003c/td\u003e\u003ctd\u003e'+fmt(r.balance)+'\u003c/td\u003e\u003c/tr\u003e';\n    }\n    if(!showAll){\n      html += '\u003ctr\u003e\u003ctd colspan=\"5\" style=\"text-align:center;color:#94a3b8;font-style:italic;padding:10px\"\u003e... '+(rows.length - 24)+' more rows (showing first 24 of '+rows.length+')\u003c/td\u003e\u003c/tr\u003e';\n      var last = rows[rows.length-1];\n      html += '\u003ctr\u003e\u003ctd\u003e'+last.month+'\u003c/td\u003e\u003ctd\u003e'+fmt(last.payment)+'\u003c/td\u003e\u003ctd\u003e'+fmt(last.principal)+'\u003c/td\u003e\u003ctd\u003e'+fmt(last.interest)+'\u003c/td\u003e\u003ctd\u003e'+fmt(last.balance)+'\u003c/td\u003e\u003c/tr\u003e';\n    }\n    html += '\u003c/tbody\u003e\u003c/table\u003e\u003c/div\u003e\u003c/details\u003e';\n    return html;\n  }\n\n  function drawChart(loans, results){\n    var canvas = document.getElementById('lc-chart');\n    var dpr = window.devicePixelRatio || 1;\n    var W = canvas.parentElement.clientWidth - 24;\n    var H = 260;\n    canvas.width = W * dpr;\n    canvas.height = H * dpr;\n    canvas.style.width = W + 'px';\n    canvas.style.height = H + 'px';\n    var ctx = canvas.getContext('2d');\n    ctx.scale(dpr, dpr);\n\n    var pad = {top: 30, right: 20, bottom: 50, left: 80};\n    var n = loans.length;\n    var maxVal = 0;\n    results.forEach(function(r){ if(r.totalPaid \u003e maxVal) maxVal = r.totalPaid; });\n    maxVal = maxVal * 1.1;\n\n    var colors = {principal: '#3b82f6', interest: '#f59e0b'};\n    var barW = Math.floor(((W - pad.left - pad.right) / n) * 0.55);\n    var gap = (W - pad.left - pad.right - barW * n) / (n + 1);\n    var chartH = H - pad.top - pad.bottom;\n\n    ctx.clearRect(0, 0, W, H);\n    ctx.fillStyle = '#f8fafc';\n    ctx.fillRect(0, 0, W, H);\n\n    // grid lines\n    var steps = 4;\n    ctx.strokeStyle = '#e2e8f0';\n    ctx.lineWidth = 1;\n    for(var s = 0; s \u003c= steps; s++){\n      var y = pad.top + chartH - (s / steps) * chartH;\n      ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(W - pad.right, y); ctx.stroke();\n      ctx.fillStyle = '#94a3b8';\n      ctx.font = '11px sans-serif';\n      ctx.textAlign = 'right';\n      var label = CURRENCY + ((maxVal * s / steps) / 1000).toFixed(0) + 'k';\n      ctx.fillText(label, pad.left - 6, y + 4);\n    }\n\n    // bars\n    results.forEach(function(r, i){\n      var x = pad.left + gap + i * (barW + gap);\n      var pxH = (r.principal / maxVal) * chartH;\n      var inH = (r.totalInterest / maxVal) * chartH;\n      var totalH = pxH + inH;\n      var baseY = pad.top + chartH;\n\n      // principal bar\n      ctx.fillStyle = colors.principal;\n      ctx.fillRect(x, baseY - pxH, barW, pxH);\n      // interest bar (stacked)\n      ctx.fillStyle = colors.interest;\n      ctx.fillRect(x, baseY - totalH, barW, inH);\n\n      // label\n      ctx.fillStyle = '#1e293b';\n      ctx.font = 'bold 12px sans-serif';\n      ctx.textAlign = 'center';\n      ctx.fillText(loans[i].name, x + barW/2, H - 10);\n    });\n\n    // legend\n    var lx = pad.left;\n    var ly = 12;\n    ctx.fillStyle = colors.principal; ctx.fillRect(lx, ly - 9, 14, 10);\n    ctx.fillStyle = '#475569'; ctx.font = '12px sans-serif'; ctx.textAlign = 'left';\n    ctx.fillText('Principal', lx + 18, ly);\n    ctx.fillStyle = colors.interest; ctx.fillRect(lx + 90, ly - 9, 14, 10);\n    ctx.fillStyle = '#475569';\n    ctx.fillText('Interest', lx + 108, ly);\n  }\n\n  document.getElementById('lc-calc-btn').addEventListener('click', calculate);\n  document.getElementById('lc-reset-btn').addEventListener('click', function(){\n    document.getElementById('lc-results').classList.add('lc-hidden');\n    document.querySelectorAll('#lc-inputs .lc-name').forEach(function(el,i){ el.value = ['Loan A','Loan B','Loan C'][i]; });\n    document.querySelectorAll('#lc-inputs .lc-principal').forEach(function(el){ el.value = 200000; });\n    document.querySelectorAll('#lc-inputs .lc-rate').forEach(function(el,i){ el.value = [5.5,4.8,5.0][i]; });\n    document.querySelectorAll('#lc-inputs .lc-term').forEach(function(el,i){ el.value = [30,30,25][i]; });\n    enable2.checked = false;\n    inputs2.style.opacity = '0.4';\n    inputs2.style.pointerEvents = 'none';\n  });\n\n  function getLoans(){\n    var loans = [];\n    for(var i = 0; i \u003c 3; i++){\n      if(i === 2 \u0026\u0026 !enable2.checked) continue;\n      var name = document.querySelector('.lc-name[data-i=\"'+i+'\"]').value.trim() || 'Loan '+(i+1);\n      var principal = parseFloat(document.querySelector('.lc-principal[data-i=\"'+i+'\"]').value) || 0;\n      var rate = parseFloat(document.querySelector('.lc-rate[data-i=\"'+i+'\"]').value) || 0;\n      var term = parseInt(document.querySelector('.lc-term[data-i=\"'+i+'\"]').value) || 1;\n      loans.push({name: name, principal: principal, rate: rate, term: term, idx: i});\n    }\n    return loans;\n  }\n\n  function calculate(){\n    var loans = getLoans();\n    if(!loans.length) return;\n\n    var results = loans.map(function(l){\n      return Object.assign({}, l, calcLoan(l.principal, l.rate, l.term));\n    });\n\n    // find winner (lowest total paid)\n    var winnerIdx = 0;\n    results.forEach(function(r, i){ if(r.totalPaid \u003c results[winnerIdx].totalPaid) winnerIdx = i; });\n\n    // comparison table\n    var rows = [\n      {label: 'Monthly Payment', key: 'monthly', format: fmt},\n      {label: 'Total Paid', key: 'totalPaid', format: fmt},\n      {label: 'Total Interest', key: 'totalInterest', format: fmt},\n      {label: 'Principal', key: 'principal', format: fmt},\n      {label: 'Annual Rate', fn: function(r){ return fmtN(r.rate) + '%'; }},\n      {label: 'Term', fn: function(r){ return r.term + ' yrs (' + r.n + ' mo)'; }},\n    ];\n\n    var thead = '\u003cthead\u003e\u003ctr\u003e\u003cth\u003eMetric\u003c/th\u003e';\n    results.forEach(function(r, i){ thead += '\u003cth\u003e'+(i === winnerIdx ? '\u0026#9733; ' : '')+r.name+(i === winnerIdx ? ' (Best)' : '')+'\u003c/th\u003e'; });\n    thead += '\u003c/tr\u003e\u003c/thead\u003e';\n\n    var tbody = '\u003ctbody\u003e';\n    rows.forEach(function(row){\n      tbody += '\u003ctr\u003e\u003ctd style=\"font-weight:600;color:#475569\"\u003e'+row.label+'\u003c/td\u003e';\n      results.forEach(function(r, i){\n        var val = row.fn ? row.fn(r) : row.format(r[row.key]);\n        tbody += '\u003ctd class=\"lc-num'+(i === winnerIdx ? ' winner-col' : '')+'\"\u003e'+val+'\u003c/td\u003e';\n      });\n      tbody += '\u003c/tr\u003e';\n    });\n    tbody += '\u003c/tbody\u003e';\n\n    document.getElementById('lc-compare-table').innerHTML = thead + tbody;\n\n    // amortization\n    var amortWrap = document.getElementById('lc-amort-wrap');\n    var amortHtml = '\u003ch3\u003eAmortization Schedules\u003c/h3\u003e';\n    results.forEach(function(r, i){\n      var amRows = buildAmortization(r, r.name);\n      amortHtml += renderAmortTable(amRows, r.name, i);\n    });\n    amortWrap.innerHTML = amortHtml;\n\n    document.getElementById('lc-results').classList.remove('lc-hidden');\n\n    setTimeout(function(){ drawChart(results, results); }, 50);\n\n    window.addEventListener('resize', function(){\n      drawChart(results, results);\n    });\n  }\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"https://productivity-works.com/tools/debt-payoff-calculator/\"\u003eDebt Payoff Calculator\u003c/a\u003e\n\n\u003ca href=\"https://productivity-works.com/tools/mortgage-affordability-calculator/\"\u003eMortgage Affordability Calculator\u003c/a\u003e\n\n\u003ca href=\"https://productivity-works.com/tools/mortgage-calculator/\"\u003eMortgage Calculator\u003c/a\u003e\n\u003c/p\u003e","title":"Loan Comparison Calculator"},{"content":"Enter your loan details below to instantly calculate your monthly EMI, total interest paid, and a full amortization schedule — no sign-up required, everything runs in your browser.\nCalculator Compare Loans Loan Details\n\u0026lt;div class=\u0026quot;form-group\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;emi-amount\u0026quot;\u0026gt;Loan Amount ($)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;emi-amount\u0026quot; placeholder=\u0026quot;e.g. 250000\u0026quot; min=\u0026quot;1\u0026quot; step=\u0026quot;1\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;form-group\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;emi-rate\u0026quot;\u0026gt;Annual Interest Rate (%)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;emi-rate\u0026quot; placeholder=\u0026quot;e.g. 6.5\u0026quot; min=\u0026quot;0.01\u0026quot; step=\u0026quot;0.01\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;form-group\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Loan Term\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;input-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;emi-term\u0026quot; placeholder=\u0026quot;e.g. 30\u0026quot; min=\u0026quot;1\u0026quot; step=\u0026quot;1\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;toggle-group\u0026quot; style=\u0026quot;flex-shrink:0;\u0026quot;\u0026gt; \u0026lt;button id=\u0026quot;emi-term-years\u0026quot; class=\u0026quot;active\u0026quot; onclick=\u0026quot;emiSetTermUnit('years')\u0026quot;\u0026gt;Yrs\u0026lt;/button\u0026gt; \u0026lt;button id=\u0026quot;emi-term-months\u0026quot; onclick=\u0026quot;emiSetTermUnit('months')\u0026quot;\u0026gt;Mo\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;form-group\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;emi-start\u0026quot;\u0026gt;Start Date\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;month\u0026quot; id=\u0026quot;emi-start\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;error-msg\u0026quot; id=\u0026quot;emi-error\u0026quot;\u0026gt;Please fill in all fields with valid values.\u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;btn-primary\u0026quot; onclick=\u0026quot;emiCalculate()\u0026quot;\u0026gt;Calculate EMI\u0026lt;/button\u0026gt; \u0026lt;!-- Summary cards --\u0026gt; \u0026lt;div class=\u0026quot;summary-grid\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;summary-card highlight\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;sc-label\u0026quot;\u0026gt;Monthly EMI\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sc-value\u0026quot; id=\u0026quot;res-emi\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sc-sub\u0026quot;\u0026gt;per month\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;summary-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;sc-label\u0026quot;\u0026gt;Total Interest\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sc-value\u0026quot; id=\u0026quot;res-interest\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sc-sub\u0026quot; id=\u0026quot;res-interest-pct\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;summary-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;sc-label\u0026quot;\u0026gt;Total Payment\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sc-value\u0026quot; id=\u0026quot;res-total\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sc-sub\u0026quot; id=\u0026quot;res-months-label\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Principal vs Interest ratio bar --\u0026gt; \u0026lt;div class=\u0026quot;ratio-bar-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;ratio-label\u0026quot;\u0026gt; \u0026lt;span id=\u0026quot;ratio-principal-label\u0026quot;\u0026gt;Principal\u0026lt;/span\u0026gt; \u0026lt;span id=\u0026quot;ratio-interest-label\u0026quot;\u0026gt;Interest\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;ratio-bar\u0026quot;\u0026gt;\u0026lt;div class=\u0026quot;ratio-bar-fill\u0026quot; id=\u0026quot;ratio-bar-fill\u0026quot; style=\u0026quot;width:50%\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Charts --\u0026gt; \u0026lt;div class=\u0026quot;charts-row\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;chart-box\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Breakdown\u0026lt;/h3\u0026gt; \u0026lt;canvas id=\u0026quot;donut-canvas\u0026quot; width=\u0026quot;180\u0026quot; height=\u0026quot;180\u0026quot;\u0026gt;\u0026lt;/canvas\u0026gt; \u0026lt;div class=\u0026quot;donut-legend\u0026quot;\u0026gt; \u0026lt;span\u0026gt;\u0026lt;span class=\u0026quot;legend-dot\u0026quot; style=\u0026quot;background:#3b82f6\u0026quot;\u0026gt;\u0026lt;/span\u0026gt;Principal\u0026lt;/span\u0026gt; \u0026lt;span\u0026gt;\u0026lt;span class=\u0026quot;legend-dot\u0026quot; style=\u0026quot;background:#f97316\u0026quot;\u0026gt;\u0026lt;/span\u0026gt;Interest\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;chart-box\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Balance Over Time\u0026lt;/h3\u0026gt; \u0026lt;canvas id=\u0026quot;line-canvas\u0026quot; width=\u0026quot;460\u0026quot; height=\u0026quot;220\u0026quot;\u0026gt;\u0026lt;/canvas\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Amortization table --\u0026gt; \u0026lt;div class=\u0026quot;table-section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;table-header\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Amortization Schedule\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;view-toggle\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;active\u0026quot; id=\u0026quot;view-monthly-btn\u0026quot; onclick=\u0026quot;emiShowView('monthly')\u0026quot;\u0026gt;Monthly\u0026lt;/button\u0026gt; \u0026lt;button id=\u0026quot;view-yearly-btn\u0026quot; onclick=\u0026quot;emiShowView('yearly')\u0026quot;\u0026gt;Yearly\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;table-scroll\u0026quot;\u0026gt; \u0026lt;table\u0026gt; \u0026lt;thead\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;th\u0026gt;Period\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;Payment\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;Principal\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;Interest\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;Balance\u0026lt;/th\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;/thead\u0026gt; \u0026lt;tbody id=\u0026quot;amort-tbody\u0026quot;\u0026gt;\u0026lt;/tbody\u0026gt; \u0026lt;/table\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Loan A\nLoan Amount ($) Annual Rate (%) Term (years) Loan B\nLoan Amount ($) Annual Rate (%) Term (years) Compare Loans Please fill in all fields for both loans. Related Tools\n\u0026rsaquo; Calculate compound interest \u0026rarr; Compound Interest Calculator\n\u0026rsaquo; Track ROI \u0026rarr; ROI Calculator\n\u0026rsaquo; Plan retirement \u0026rarr; Pension Simulator ","permalink":"https://productivity-works.com/tools/loan-emi-calculator/","summary":"\u003cp\u003eEnter your loan details below to instantly calculate your monthly EMI, total interest paid, and a full amortization schedule — no sign-up required, everything runs in your browser.\u003c/p\u003e\n\u003cdiv id=\"emi-app\"\u003e\n\u003cstyle\u003e\n  #emi-app {\n    font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n    color: #1e293b;\n    max-width: 960px;\n    margin: 0 auto;\n    padding: 0;\n  }\n  #emi-app *, #emi-app *::before, #emi-app *::after {\n    box-sizing: border-box;\n  }\n\u003cp\u003e/* ── Tabs ── */\n#emi-app .tab-bar {\ndisplay: flex;\ngap: 4px;\nmargin-bottom: 24px;\nborder-bottom: 2px solid #e2e8f0;\n}\n#emi-app .tab-btn {\nbackground: none;\nborder: none;\npadding: 10px 20px;\nfont-size: 0.9rem;\nfont-weight: 600;\ncolor: #64748b;\ncursor: pointer;\nborder-bottom: 3px solid transparent;\nmargin-bottom: -2px;\ntransition: color 0.15s, border-color 0.15s;\n}\n#emi-app .tab-btn.active {\ncolor: #0f172a;\nborder-bottom-color: #3b82f6;\n}\n#emi-app .tab-panel { display: none; }\n#emi-app .tab-panel.active { display: block; }\u003c/p\u003e","title":"Loan EMI Calculator"},{"content":" Generate Paragraphs Sentences Words Count (1–50) Text Style Classic Lorem Ipsum Hipster Ipsum Corporate Ipsum Generate Start with \"Lorem ipsum dolor sit amet...\" —Paragraphs —Sentences —Words —Characters Preview HTML Output \u0026#9998; Click Generate to create placeholder text \u0026#128203; Copy Plain Text \u0026#128203; Copy HTML Related: Count your real text with our Word Counter ","permalink":"https://productivity-works.com/tools/lorem-ipsum-generator/","summary":"\u003cdiv id=\"lorem-app\"\u003e\n\u003cstyle\u003e\n#lorem-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #1c1917;\n}\n\n#lorem-app * {\n  box-sizing: border-box;\n}\n\n/* Controls panel */\n#lorem-app .la-controls {\n  background: #fff7ed;\n  border: 2px solid #fed7aa;\n  border-radius: 14px;\n  padding: 24px;\n  margin-bottom: 24px;\n}\n\n#lorem-app .la-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 16px;\n  align-items: flex-end;\n  margin-bottom: 16px;\n}\n\n#lorem-app .la-row:last-child {\n  margin-bottom: 0;\n}\n\n#lorem-app .la-field {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n  flex: 1;\n  min-width: 150px;\n}\n\n#lorem-app .la-field label {\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #9a3412;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n\n#lorem-app select,\n#lorem-app input[type=\"number\"] {\n  height: 42px;\n  padding: 0 12px;\n  border: 2px solid #fed7aa;\n  border-radius: 8px;\n  font-size: 0.95rem;\n  background: #fff;\n  color: #1c1917;\n  outline: none;\n  transition: border-color 0.2s;\n}\n\n#lorem-app select:focus,\n#lorem-app input[type=\"number\"]:focus {\n  border-color: #ea580c;\n}\n\n#lorem-app input[type=\"number\"] {\n  width: 100px;\n}\n\n/* Checkbox row */\n#lorem-app .la-check-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  flex-wrap: wrap;\n}\n\n#lorem-app .la-check-row label {\n  font-size: 0.92rem;\n  color: #44403c;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n\n#lorem-app input[type=\"checkbox\"] {\n  width: 18px;\n  height: 18px;\n  accent-color: #ea580c;\n  cursor: pointer;\n}\n\n/* Generate button */\n#lorem-app .la-btn-generate {\n  background: #ea580c;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  padding: 0 28px;\n  height: 42px;\n  font-size: 1rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.2s, transform 0.1s;\n  white-space: nowrap;\n}\n\n#lorem-app .la-btn-generate:hover {\n  background: #c2410c;\n}\n\n#lorem-app .la-btn-generate:active {\n  transform: scale(0.97);\n}\n\n/* Stats bar */\n#lorem-app .la-stats {\n  display: flex;\n  gap: 12px;\n  flex-wrap: wrap;\n  margin-bottom: 16px;\n}\n\n#lorem-app .la-stat {\n  background: #fff7ed;\n  border: 1px solid #fed7aa;\n  border-radius: 8px;\n  padding: 8px 16px;\n  font-size: 0.88rem;\n  color: #7c2d12;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 2px;\n}\n\n#lorem-app .la-stat strong {\n  font-size: 1.3rem;\n  font-weight: 700;\n  color: #ea580c;\n}\n\n/* Output tabs */\n#lorem-app .la-tabs {\n  display: flex;\n  gap: 0;\n  margin-bottom: 0;\n  border-bottom: 2px solid #fed7aa;\n}\n\n#lorem-app .la-tab {\n  padding: 10px 20px;\n  font-size: 0.9rem;\n  font-weight: 600;\n  cursor: pointer;\n  border: 2px solid transparent;\n  border-bottom: none;\n  border-radius: 8px 8px 0 0;\n  background: #fff7ed;\n  color: #9a3412;\n  margin-bottom: -2px;\n  transition: background 0.15s;\n}\n\n#lorem-app .la-tab.active {\n  background: #fff;\n  border-color: #fed7aa;\n  border-bottom-color: #fff;\n  color: #ea580c;\n}\n\n/* Output panels */\n#lorem-app .la-output-wrap {\n  border: 2px solid #fed7aa;\n  border-top: none;\n  border-radius: 0 0 12px 12px;\n  background: #fff;\n  padding: 20px;\n  margin-bottom: 16px;\n}\n\n#lorem-app .la-panel {\n  display: none;\n}\n\n#lorem-app .la-panel.active {\n  display: block;\n}\n\n/* Preview paragraphs */\n#lorem-app .la-preview p {\n  margin: 0 0 1.1em 0;\n  line-height: 1.75;\n  color: #44403c;\n  font-size: 0.97rem;\n}\n\n#lorem-app .la-preview p:last-child {\n  margin-bottom: 0;\n}\n\n/* HTML code area */\n#lorem-app .la-code {\n  width: 100%;\n  min-height: 180px;\n  padding: 14px;\n  font-family: \"SF Mono\", \"Fira Code\", \"Consolas\", monospace;\n  font-size: 0.82rem;\n  line-height: 1.6;\n  border: 2px solid #e7e5e4;\n  border-radius: 8px;\n  background: #fafaf9;\n  color: #1c1917;\n  resize: vertical;\n  outline: none;\n  transition: border-color 0.2s;\n}\n\n#lorem-app .la-code:focus {\n  border-color: #ea580c;\n}\n\n/* Copy buttons */\n#lorem-app .la-copy-row {\n  display: flex;\n  justify-content: flex-end;\n  margin-top: 12px;\n}\n\n#lorem-app .la-btn-copy {\n  background: #fff;\n  color: #ea580c;\n  border: 2px solid #ea580c;\n  border-radius: 8px;\n  padding: 8px 20px;\n  font-size: 0.9rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s;\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n\n#lorem-app .la-btn-copy:hover {\n  background: #ea580c;\n  color: #fff;\n}\n\n#lorem-app .la-btn-copy.copied {\n  background: #16a34a;\n  border-color: #16a34a;\n  color: #fff;\n}\n\n/* Empty state */\n#lorem-app .la-empty {\n  text-align: center;\n  padding: 48px 20px;\n  color: #a8a29e;\n  font-size: 1rem;\n}\n\n#lorem-app .la-empty-icon {\n  font-size: 2.5rem;\n  margin-bottom: 12px;\n}\n\n/* Responsive */\n@media (max-width: 600px) {\n  #lorem-app .la-controls {\n    padding: 16px;\n  }\n  #lorem-app .la-row {\n    gap: 12px;\n  }\n  #lorem-app .la-field {\n    min-width: 120px;\n  }\n  #lorem-app input[type=\"number\"] {\n    width: 80px;\n  }\n  #lorem-app .la-tab {\n    padding: 8px 14px;\n    font-size: 0.82rem;\n  }\n}\n\u003c/style\u003e\n\u003c!-- Controls --\u003e\n\u003cdiv class=\"la-controls\"\u003e\n  \u003cdiv class=\"la-row\"\u003e\n    \u003cdiv class=\"la-field\"\u003e\n      \u003clabel for=\"la-type\"\u003eGenerate\u003c/label\u003e\n      \u003cselect id=\"la-type\"\u003e\n        \u003coption value=\"paragraphs\"\u003eParagraphs\u003c/option\u003e\n        \u003coption value=\"sentences\"\u003eSentences\u003c/option\u003e\n        \u003coption value=\"words\"\u003eWords\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"la-field\"\u003e\n      \u003clabel for=\"la-count\"\u003eCount (1–50)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"la-count\" value=\"3\" min=\"1\" max=\"50\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"la-field\"\u003e\n      \u003clabel for=\"la-style\"\u003eText Style\u003c/label\u003e\n      \u003cselect id=\"la-style\"\u003e\n        \u003coption value=\"classic\"\u003eClassic Lorem Ipsum\u003c/option\u003e\n        \u003coption value=\"hipster\"\u003eHipster Ipsum\u003c/option\u003e\n        \u003coption value=\"corporate\"\u003eCorporate Ipsum\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n    \u003cbutton class=\"la-btn-generate\" onclick=\"loremGenerate()\"\u003eGenerate\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"la-row\"\u003e\n    \u003cdiv class=\"la-check-row\"\u003e\n      \u003clabel\u003e\n        \u003cinput type=\"checkbox\" id=\"la-classic-start\" checked\u003e\n        Start with \"Lorem ipsum dolor sit amet...\"\n      \u003c/label\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Stats --\u003e\n\u003cdiv class=\"la-stats\"\u003e\n  \u003cdiv class=\"la-stat\"\u003e\u003cstrong id=\"la-stat-para\"\u003e—\u003c/strong\u003eParagraphs\u003c/div\u003e\n  \u003cdiv class=\"la-stat\"\u003e\u003cstrong id=\"la-stat-sent\"\u003e—\u003c/strong\u003eSentences\u003c/div\u003e\n  \u003cdiv class=\"la-stat\"\u003e\u003cstrong id=\"la-stat-words\"\u003e—\u003c/strong\u003eWords\u003c/div\u003e\n  \u003cdiv class=\"la-stat\"\u003e\u003cstrong id=\"la-stat-chars\"\u003e—\u003c/strong\u003eCharacters\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Output --\u003e\n\u003cdiv class=\"la-tabs\"\u003e\n  \u003cdiv class=\"la-tab active\" onclick=\"loremSwitchTab('preview', this)\"\u003ePreview\u003c/div\u003e\n  \u003cdiv class=\"la-tab\" onclick=\"loremSwitchTab('html', this)\"\u003eHTML Output\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"la-output-wrap\"\u003e\n  \u003cdiv id=\"la-panel-preview\" class=\"la-panel active\"\u003e\n    \u003cdiv class=\"la-preview\" id=\"la-preview-content\"\u003e\n      \u003cdiv class=\"la-empty\"\u003e\n        \u003cdiv class=\"la-empty-icon\"\u003e\u0026#9998;\u003c/div\u003e\n        \u003cdiv\u003eClick \u003cstrong\u003eGenerate\u003c/strong\u003e to create placeholder text\u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"la-copy-row\"\u003e\n      \u003cbutton class=\"la-btn-copy\" id=\"la-copy-plain\" onclick=\"loremCopyPlain()\"\u003e\n        \u0026#128203; Copy Plain Text\n      \u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv id=\"la-panel-html\" class=\"la-panel\"\u003e\n    \u003ctextarea class=\"la-code\" id=\"la-html-content\" readonly placeholder=\"HTML output will appear here...\"\u003e\u003c/textarea\u003e\n    \u003cdiv class=\"la-copy-row\"\u003e\n      \u003cbutton class=\"la-btn-copy\" id=\"la-copy-html\" onclick=\"loremCopyHtml()\"\u003e\n        \u0026#128203; Copy HTML\n      \u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n\n// ── DATA ──────────────────────────────────────────────────────────────────────\n\nconst DATA = {\n  classic: {\n    words: [\n      \"lorem\",\"ipsum\",\"dolor\",\"sit\",\"amet\",\"consectetur\",\"adipiscing\",\"elit\",\n      \"sed\",\"do\",\"eiusmod\",\"tempor\",\"incididunt\",\"ut\",\"labore\",\"et\",\"dolore\",\n      \"magna\",\"aliqua\",\"enim\",\"ad\",\"minim\",\"veniam\",\"quis\",\"nostrud\",\"exercitation\",\n      \"ullamco\",\"laboris\",\"nisi\",\"aliquip\",\"ex\",\"ea\",\"commodo\",\"consequat\",\"duis\",\n      \"aute\",\"irure\",\"in\",\"reprehenderit\",\"voluptate\",\"velit\",\"esse\",\"cillum\",\n      \"fugiat\",\"nulla\",\"pariatur\",\"excepteur\",\"sint\",\"occaecat\",\"cupidatat\",\"non\",\n      \"proident\",\"sunt\",\"culpa\",\"qui\",\"officia\",\"deserunt\",\"mollit\",\"anim\",\"id\",\"est\",\n      \"laborum\",\"perspiciatis\",\"unde\",\"omnis\",\"iste\",\"natus\",\"error\",\"accusantium\",\n      \"doloremque\",\"laudantium\",\"totam\",\"rem\",\"aperiam\",\"eaque\",\"ipsa\",\"quae\",\"ab\",\n      \"inventore\",\"veritatis\",\"quasi\",\"architecto\",\"beatae\",\"vitae\",\"dicta\",\"explicabo\",\n      \"nemo\",\"voluptatem\",\"quia\",\"voluptas\",\"aspernatur\",\"odit\",\"aut\",\"fugit\",\"magni\",\n      \"dolores\",\"ratione\",\"sequi\",\"nesciunt\",\"neque\",\"porro\",\"quisquam\",\"dolorem\"\n    ],\n    opener: \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\"\n  },\n  hipster: {\n    words: [\n      \"artisan\",\"craft\",\"aesthetic\",\"curated\",\"bespoke\",\"authentic\",\"sustainable\",\n      \"kombucha\",\"vinyl\",\"gastropub\",\"fixie\",\"letterpress\",\"kale\",\"synth\",\"flannel\",\n      \"activated\",\"charcoal\",\"ethical\",\"forage\",\"lo-fi\",\"normcore\",\"poke\",\"prism\",\n      \"raclette\",\"seitan\",\"shaman\",\"skateboard\",\"stumptown\",\"taiyaki\",\"taxidermy\",\n      \"tousled\",\"typewriter\",\"unicorn\",\"vaporwave\",\"venmo\",\"vexillologist\",\"wayfarers\",\n      \"williamsburg\",\"yuccie\",\"occupy\",\"heirloom\",\"gochujang\",\"gluten-free\",\"farm-to-table\",\n      \"direct\",\"trade\",\"cold-pressed\",\"single-origin\",\"pour-over\",\"cold-brew\",\"lomo\",\n      \"chicharrones\",\"microdosing\",\"pabst\",\"tattooed\",\"shoreditch\",\"poutine\",\"tbh\",\n      \"meggings\",\"ugh\",\"schlitz\",\"retro\",\"meh\",\"wolf\",\"cardigan\",\"trust-fund\",\"selfies\"\n    ],\n    opener: \"Artisan sustainable craft aesthetic curated bespoke kombucha vinyl gastropub fixie letterpress kale chips synth.\"\n  },\n  corporate: {\n    words: [\n      \"synergy\",\"leverage\",\"paradigm\",\"pivot\",\"disruptive\",\"scalable\",\"agile\",\"robust\",\n      \"innovative\",\"streamline\",\"holistic\",\"proactive\",\"actionable\",\"deliverable\",\n      \"bandwidth\",\"ecosystem\",\"stakeholder\",\"value-add\",\"end-to-end\",\"best-practice\",\n      \"solution\",\"core-competency\",\"drill-down\",\"empower\",\"granular\",\"ideation\",\n      \"incentivize\",\"low-hanging-fruit\",\"mindshare\",\"move-the-needle\",\"onboarding\",\n      \"repurpose\",\"rightsizing\",\"ROI\",\"run-it-up-the-flagpole\",\"seamless\",\"silo\",\n      \"takeaway\",\"think-outside-the-box\",\"touch-base\",\"utilize\",\"visibility\",\"win-win\",\n      \"circle-back\",\"boil-the-ocean\",\"deep-dive\",\"double-click\",\"growth-hacking\",\n      \"hyperlocal\",\"impactful\",\"in-the-weeds\",\"key-performance\",\"milestone\",\"north-star\",\n      \"organic-growth\",\"pain-point\",\"ramp-up\",\"resourceful\",\"robust-pipeline\",\"runway\"\n    ],\n    opener: \"Synergy leverage paradigm pivot disruptive scalable agile robust innovative streamline holistic proactive actionable deliverables.\"\n  }\n};\n\n// ── STATE ─────────────────────────────────────────────────────────────────────\n\nlet currentParagraphs = [];\n\n// ── HELPERS ───────────────────────────────────────────────────────────────────\n\nfunction randInt(min, max) {\n  return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n\nfunction pickRandom(arr) {\n  return arr[Math.floor(Math.random() * arr.length)];\n}\n\nfunction capitalize(str) {\n  return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\nfunction buildSentence(words, minW, maxW) {\n  const len = randInt(minW, maxW);\n  let parts = [];\n  for (let i = 0; i \u003c len; i++) {\n    parts.push(pickRandom(words));\n  }\n  return capitalize(parts.join(\" \")) + \".\";\n}\n\nfunction buildParagraph(words, sentMin, sentMax, wordMin, wordMax) {\n  const n = randInt(sentMin, sentMax);\n  let sentences = [];\n  for (let i = 0; i \u003c n; i++) {\n    sentences.push(buildSentence(words, wordMin, wordMax));\n  }\n  return sentences.join(\" \");\n}\n\n// ── GENERATION ────────────────────────────────────────────────────────────────\n\nfunction generateText(type, count, style, useOpener) {\n  const d = DATA[style];\n  const words = d.words;\n  let paragraphs = [];\n\n  if (type === \"paragraphs\") {\n    for (let i = 0; i \u003c count; i++) {\n      paragraphs.push(buildParagraph(words, 4, 8, 8, 18));\n    }\n  } else if (type === \"sentences\") {\n    let sentences = [];\n    for (let i = 0; i \u003c count; i++) {\n      sentences.push(buildSentence(words, 8, 18));\n    }\n    // Group into paragraphs of ~3 sentences\n    const perPara = 3;\n    for (let i = 0; i \u003c sentences.length; i += perPara) {\n      paragraphs.push(sentences.slice(i, i + perPara).join(\" \"));\n    }\n  } else if (type === \"words\") {\n    let chosen = [];\n    for (let i = 0; i \u003c count; i++) {\n      chosen.push(pickRandom(words));\n    }\n    paragraphs.push(capitalize(chosen.join(\" \")) + \".\");\n  }\n\n  if (useOpener \u0026\u0026 paragraphs.length \u003e 0) {\n    // Replace first sentence of first paragraph with the opener\n    paragraphs[0] = d.opener + \" \" + paragraphs[0];\n  }\n\n  return paragraphs;\n}\n\n// ── STATS ─────────────────────────────────────────────────────────────────────\n\nfunction updateStats(paragraphs) {\n  const fullText = paragraphs.join(\"\\n\\n\");\n  // Count sentences (rough: split on \". \" or \".\" at end)\n  const sentCount = (fullText.match(/[.!?]+/g) || []).length;\n  const wordCount = fullText.trim().split(/\\s+/).filter(Boolean).length;\n  const charCount = fullText.length;\n  const paraCount = paragraphs.length;\n\n  document.getElementById(\"la-stat-para\").textContent = paraCount;\n  document.getElementById(\"la-stat-sent\").textContent = sentCount;\n  document.getElementById(\"la-stat-words\").textContent = wordCount.toLocaleString();\n  document.getElementById(\"la-stat-chars\").textContent = charCount.toLocaleString();\n}\n\n// ── RENDER ────────────────────────────────────────────────────────────────────\n\nfunction renderPreview(paragraphs) {\n  const container = document.getElementById(\"la-preview-content\");\n  if (!paragraphs.length) {\n    container.innerHTML = '\u003cdiv class=\"la-empty\"\u003e\u003cdiv class=\"la-empty-icon\"\u003e\u0026#9998;\u003c/div\u003e\u003cdiv\u003eClick \u003cstrong\u003eGenerate\u003c/strong\u003e to create placeholder text\u003c/div\u003e\u003c/div\u003e';\n    return;\n  }\n  container.innerHTML = paragraphs\n    .map(p =\u003e `\u003cp\u003e${escapeHtml(p)}\u003c/p\u003e","title":"Lorem Ipsum Generator - Free Placeholder Text Tool"},{"content":" \u0026#9998; Markdown Editor Load Sample Clear \u0026#9790; Dark \u0026#x26F6; Fullscreen Copy HTML \u0026#11015; .md Export .html B I S H1 H2 H3 \u0026#128279; \u0026#128247; `c` ``` \u0026gt; \u0026#8226; ul 1. ol \u0026#9744; \u0026#9776; \u0026#8212; \u0026#9998; Editor 1 \u0026#128065; Preview Words: 0 Chars: 0 Lines: 0 Read: 0 min \u0026#10003; Autosaved Related Markdown Tools: Markdown Preview \u0026nbsp;\u0026middot;\u0026nbsp; Markdown Table Generator \u0026nbsp;\u0026middot;\u0026nbsp; Markdown TOC Generator Related Articles AI Writing Tools Comparison 2026: Best Options Ranked and Reviewed The Ultimate Notion Setup for Maximum Productivity Best Notion AI Templates for Productivity 2026 ","permalink":"https://productivity-works.com/tools/markdown-editor/","summary":"\u003cdiv id=\"me-app\"\u003e\n\u003cstyle\u003e\n/* ── Reset \u0026 Root ─────────────────────────────────────── */\n#me-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", sans-serif;\n  max-width: 1200px;\n  margin: 0 auto;\n  color: #1e1b4b;\n  --accent: #7c3aed;\n  --accent-dk: #6d28d9;\n  --accent-lt: #ede9fe;\n  --bg: #f8f7ff;\n  --surface: #ffffff;\n  --border: #d8d3f0;\n  --text: #1e1b4b;\n  --text-muted: #6b7280;\n  --code-bg: #f3f0ff;\n  --preview-bg: #ffffff;\n  --editor-bg: #fafafe;\n  --status-bg: #f3f0ff;\n  --btn-ghost: #ede9fe;\n  --btn-ghost-text: #5b21b6;\n  --toolbar-bg: #f5f3ff;\n  --shadow: 0 2px 12px rgba(124,58,237,0.10);\n  --radius: 10px;\n}\n#me-app * { box-sizing: border-box; }\n\n/* ── Dark theme ───────────────────────────────────────── */\n#me-app.me-dark {\n  --bg: #0f0e1a;\n  --surface: #1a1828;\n  --border: #3b3660;\n  --text: #e5e1ff;\n  --text-muted: #9d97c4;\n  --code-bg: #2a2540;\n  --preview-bg: #1a1828;\n  --editor-bg: #15132a;\n  --status-bg: #1a1828;\n  --btn-ghost: #2a2540;\n  --btn-ghost-text: #c4b5fd;\n  --toolbar-bg: #1a1828;\n  --shadow: 0 2px 12px rgba(0,0,0,0.40);\n  color: var(--text);\n}\n#me-app.me-dark #me-editor { color: #e5e1ff; background: var(--editor-bg); }\n#me-app.me-dark #me-fmt-toolbar { background: var(--toolbar-bg); border-color: var(--border); }\n\n/* ── Top Bar ──────────────────────────────────────────── */\n#me-topbar {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  flex-wrap: wrap;\n  gap: 8px;\n  background: var(--surface);\n  border: 1px solid var(--border);\n  border-radius: var(--radius) var(--radius) 0 0;\n  padding: 10px 14px;\n  box-shadow: var(--shadow);\n}\n#me-title {\n  font-weight: 700;\n  font-size: 1rem;\n  color: var(--accent);\n  letter-spacing: -0.01em;\n}\n#me-topbar-btns {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 6px;\n}\n\n/* ── Formatting Toolbar ───────────────────────────────── */\n#me-fmt-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  gap: 3px;\n  background: var(--toolbar-bg);\n  border-left: 1px solid var(--border);\n  border-right: 1px solid var(--border);\n  border-bottom: 1px solid var(--border);\n  padding: 7px 10px;\n}\n.me-sep {\n  width: 1px;\n  height: 20px;\n  background: var(--border);\n  margin: 0 4px;\n  flex-shrink: 0;\n}\n.me-fmt-btn {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  min-width: 30px;\n  height: 28px;\n  padding: 0 7px;\n  border: 1px solid var(--border);\n  border-radius: 5px;\n  background: var(--surface);\n  color: var(--text);\n  font-size: 0.8rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.12s, border-color 0.12s, transform 0.1s;\n  white-space: nowrap;\n  line-height: 1;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n}\n.me-fmt-btn:hover {\n  background: var(--accent-lt);\n  border-color: var(--accent);\n  color: var(--accent);\n}\n.me-fmt-btn:active { transform: scale(0.93); }\n\n/* ── Generic Buttons ──────────────────────────────────── */\n.me-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 5px;\n  padding: 6px 13px;\n  border: none;\n  border-radius: 6px;\n  font-size: 0.82rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s;\n  line-height: 1.2;\n  white-space: nowrap;\n}\n.me-btn:active { transform: scale(0.96); }\n.me-btn-primary {\n  background: var(--accent);\n  color: #fff;\n  box-shadow: 0 1px 4px rgba(124,58,237,0.25);\n}\n.me-btn-primary:hover { background: var(--accent-dk); }\n.me-btn-ghost {\n  background: var(--btn-ghost);\n  color: var(--btn-ghost-text);\n}\n.me-btn-ghost:hover { filter: brightness(0.95); }\n.me-btn-success {\n  background: #d1fae5;\n  color: #065f46;\n}\n.me-btn-success:hover { background: #a7f3d0; }\n.me-dark .me-btn-success { background: #064e3b; color: #6ee7b7; }\n\n/* ── Split Pane ───────────────────────────────────────── */\n#me-split {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  border-left: 1px solid var(--border);\n  border-right: 1px solid var(--border);\n  min-height: 500px;\n}\n@media (max-width: 680px) {\n  #me-split { grid-template-columns: 1fr; }\n}\n\n/* ── Pane Labels ──────────────────────────────────────── */\n.me-pane-label {\n  font-size: 0.7rem;\n  font-weight: 700;\n  letter-spacing: 0.09em;\n  text-transform: uppercase;\n  color: var(--text-muted);\n  padding: 5px 14px 4px;\n  background: var(--surface);\n  border-bottom: 1px solid var(--border);\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n#me-editor-pane {\n  border-right: 1px solid var(--border);\n  display: flex;\n  flex-direction: column;\n}\n#me-preview-pane {\n  display: flex;\n  flex-direction: column;\n  background: var(--preview-bg);\n}\n@media (max-width: 680px) {\n  #me-editor-pane { border-right: none; border-bottom: 1px solid var(--border); }\n}\n\n/* ── Editor ───────────────────────────────────────────── */\n#me-editor-wrap {\n  position: relative;\n  flex: 1;\n  display: flex;\n}\n#me-line-nums {\n  padding: 14px 8px 14px 10px;\n  font-size: 0.82rem;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  line-height: 1.65;\n  color: var(--text-muted);\n  background: var(--editor-bg);\n  border-right: 1px solid var(--border);\n  text-align: right;\n  user-select: none;\n  min-width: 36px;\n  overflow: hidden;\n  white-space: pre;\n}\n#me-editor {\n  flex: 1;\n  width: 100%;\n  padding: 14px 14px 14px 12px;\n  font-size: 0.9rem;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  line-height: 1.65;\n  border: none;\n  outline: none;\n  resize: none;\n  background: var(--editor-bg);\n  color: var(--text);\n  min-height: 460px;\n  tab-size: 2;\n}\n#me-editor::placeholder { color: var(--text-muted); opacity: 0.6; }\n\n/* ── Preview ──────────────────────────────────────────── */\n#me-preview {\n  flex: 1;\n  padding: 18px 20px;\n  overflow-y: auto;\n  background: var(--preview-bg);\n  color: var(--text);\n  font-size: 0.95rem;\n  line-height: 1.75;\n  min-height: 460px;\n}\n\n/* Rendered markdown styles */\n#me-preview h1, #me-preview h2, #me-preview h3,\n#me-preview h4, #me-preview h5, #me-preview h6 {\n  color: var(--accent);\n  margin: 1.1em 0 0.4em;\n  line-height: 1.25;\n  font-weight: 700;\n}\n#me-preview h1 { font-size: 1.65em; border-bottom: 2px solid var(--border); padding-bottom: 0.2em; }\n#me-preview h2 { font-size: 1.35em; border-bottom: 1px solid var(--border); padding-bottom: 0.15em; }\n#me-preview h3 { font-size: 1.15em; }\n#me-preview h4 { font-size: 1em; }\n#me-preview h5 { font-size: 0.9em; }\n#me-preview h6 { font-size: 0.85em; color: var(--text-muted); }\n#me-preview p { margin: 0.7em 0; }\n#me-preview a { color: var(--accent); text-decoration: underline; }\n#me-preview a:hover { color: var(--accent-dk); }\n#me-preview strong { font-weight: 700; }\n#me-preview em { font-style: italic; }\n#me-preview del { text-decoration: line-through; color: var(--text-muted); }\n#me-preview code {\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 0.88em;\n  background: var(--code-bg);\n  color: #9333ea;\n  padding: 0.15em 0.45em;\n  border-radius: 4px;\n  border: 1px solid var(--border);\n}\n#me-preview pre {\n  background: var(--code-bg);\n  border: 1px solid var(--border);\n  border-radius: 8px;\n  padding: 14px 16px;\n  overflow-x: auto;\n  margin: 1em 0;\n}\n#me-preview pre code {\n  background: none;\n  border: none;\n  padding: 0;\n  color: var(--text);\n  font-size: 0.87em;\n  line-height: 1.6;\n}\n#me-preview pre .kw { color: #9333ea; font-weight: 700; }\n#me-preview pre .str { color: #059669; }\n#me-preview pre .cm { color: #6b7280; font-style: italic; }\n#me-preview pre .nm { color: #b45309; }\n.me-dark #me-preview pre .kw { color: #c084fc; }\n.me-dark #me-preview pre .str { color: #34d399; }\n.me-dark #me-preview pre .cm { color: #9ca3af; }\n.me-dark #me-preview pre .nm { color: #fbbf24; }\n#me-preview blockquote {\n  border-left: 4px solid var(--accent);\n  margin: 1em 0;\n  padding: 8px 16px;\n  background: var(--code-bg);\n  border-radius: 0 6px 6px 0;\n  color: var(--text-muted);\n}\n#me-preview blockquote p { margin: 0; }\n#me-preview ul, #me-preview ol { padding-left: 1.6em; margin: 0.6em 0; }\n#me-preview li { margin: 0.25em 0; }\n#me-preview ul li { list-style-type: disc; }\n#me-preview ol li { list-style-type: decimal; }\n#me-preview hr { border: none; border-top: 2px solid var(--border); margin: 1.5em 0; }\n#me-preview img { max-width: 100%; border-radius: 6px; margin: 0.5em 0; }\n#me-preview table {\n  border-collapse: collapse;\n  width: 100%;\n  margin: 1em 0;\n  font-size: 0.92em;\n}\n#me-preview th {\n  background: var(--accent-lt);\n  color: var(--accent);\n  font-weight: 700;\n  padding: 8px 12px;\n  border: 1px solid var(--border);\n  text-align: left;\n}\n.me-dark #me-preview th { background: #2a2540; color: #c4b5fd; }\n#me-preview td { padding: 7px 12px; border: 1px solid var(--border); }\n#me-preview tr:nth-child(even) td { background: var(--code-bg); }\n\n/* ── Status Bar ───────────────────────────────────────── */\n#me-status {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  gap: 14px;\n  background: var(--status-bg);\n  border: 1px solid var(--border);\n  border-top: none;\n  border-radius: 0 0 var(--radius) var(--radius);\n  padding: 7px 16px;\n  font-size: 0.79rem;\n  color: var(--text-muted);\n}\n.me-stat { display: inline-flex; align-items: center; gap: 4px; }\n.me-stat-val { font-weight: 700; color: var(--accent); }\n#me-saved-msg {\n  margin-left: auto;\n  color: #059669;\n  font-weight: 600;\n  font-size: 0.77rem;\n  opacity: 0;\n  transition: opacity 0.3s;\n}\n#me-saved-msg.me-show { opacity: 1; }\n\n/* ── Fullscreen ───────────────────────────────────────── */\n#me-app.me-fullscreen {\n  position: fixed;\n  inset: 0;\n  z-index: 9999;\n  max-width: 100%;\n  background: var(--bg);\n  padding: 0;\n  overflow: auto;\n  border-radius: 0;\n}\n#me-app.me-fullscreen #me-split { min-height: calc(100vh - 160px); }\n\n/* ── Related Links ────────────────────────────────────── */\n#me-related {\n  margin-top: 20px;\n  padding: 12px 16px;\n  background: var(--status-bg);\n  border: 1px solid var(--border);\n  border-radius: 8px;\n  font-size: 0.88rem;\n  color: var(--text-muted);\n  line-height: 1.7;\n}\n#me-related strong { color: var(--text); }\n#me-related a { color: var(--accent); font-weight: 600; text-decoration: none; }\n#me-related a:hover { text-decoration: underline; }\n\u003c/style\u003e\n\u003c!-- Top Bar --\u003e\n\u003cdiv id=\"me-topbar\"\u003e\n  \u003cspan id=\"me-title\"\u003e\u0026#9998; Markdown Editor\u003c/span\u003e\n  \u003cdiv id=\"me-topbar-btns\"\u003e\n    \u003cbutton class=\"me-btn me-btn-ghost\" onclick=\"meLoadSample()\"\u003eLoad Sample\u003c/button\u003e\n    \u003cbutton class=\"me-btn me-btn-ghost\" onclick=\"meClear()\"\u003eClear\u003c/button\u003e\n    \u003cbutton class=\"me-btn me-btn-ghost\" onclick=\"meToggleTheme()\"\u003e\u0026#9790; Dark\u003c/button\u003e\n    \u003cbutton class=\"me-btn me-btn-ghost\" onclick=\"meToggleFullscreen()\"\u003e\u0026#x26F6; Fullscreen\u003c/button\u003e\n    \u003cbutton class=\"me-btn me-btn-ghost\" onclick=\"meCopyHTML()\"\u003eCopy HTML\u003c/button\u003e\n    \u003cbutton class=\"me-btn me-btn-success\" onclick=\"meDownloadMd()\"\u003e\u0026#11015; .md\u003c/button\u003e\n    \u003cbutton class=\"me-btn me-btn-primary\" onclick=\"meExportHTML()\"\u003eExport .html\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Formatting Toolbar --\u003e\n\u003cdiv id=\"me-fmt-toolbar\"\u003e\n  \u003cbutton class=\"me-fmt-btn\" title=\"Bold (Ctrl+B)\" onclick=\"meFmt('bold')\"\u003e\u003cstrong\u003eB\u003c/strong\u003e\u003c/button\u003e\n  \u003cbutton class=\"me-fmt-btn\" title=\"Italic (Ctrl+I)\" onclick=\"meFmt('italic')\"\u003e\u003cem\u003eI\u003c/em\u003e\u003c/button\u003e\n  \u003cbutton class=\"me-fmt-btn\" title=\"Strikethrough\" onclick=\"meFmt('strike')\"\u003e\u003cdel\u003eS\u003c/del\u003e\u003c/button\u003e\n  \u003cdiv class=\"me-sep\"\u003e\u003c/div\u003e\n  \u003cbutton class=\"me-fmt-btn\" title=\"Heading 1\" onclick=\"meFmt('h1')\"\u003eH1\u003c/button\u003e\n  \u003cbutton class=\"me-fmt-btn\" title=\"Heading 2\" onclick=\"meFmt('h2')\"\u003eH2\u003c/button\u003e\n  \u003cbutton class=\"me-fmt-btn\" title=\"Heading 3\" onclick=\"meFmt('h3')\"\u003eH3\u003c/button\u003e\n  \u003cdiv class=\"me-sep\"\u003e\u003c/div\u003e\n  \u003cbutton class=\"me-fmt-btn\" title=\"Link\" onclick=\"meFmt('link')\"\u003e\u0026#128279;\u003c/button\u003e\n  \u003cbutton class=\"me-fmt-btn\" title=\"Image\" onclick=\"meFmt('image')\"\u003e\u0026#128247;\u003c/button\u003e\n  \u003cdiv class=\"me-sep\"\u003e\u003c/div\u003e\n  \u003cbutton class=\"me-fmt-btn\" title=\"Inline Code\" onclick=\"meFmt('code')\"\u003e`c`\u003c/button\u003e\n  \u003cbutton class=\"me-fmt-btn\" title=\"Code Block\" onclick=\"meFmt('codeblock')\"\u003e```\u003c/button\u003e\n  \u003cbutton class=\"me-fmt-btn\" title=\"Blockquote\" onclick=\"meFmt('quote')\"\u003e\u0026gt;\u003c/button\u003e\n  \u003cdiv class=\"me-sep\"\u003e\u003c/div\u003e\n  \u003cbutton class=\"me-fmt-btn\" title=\"Unordered List\" onclick=\"meFmt('ul')\"\u003e\u0026#8226; ul\u003c/button\u003e\n  \u003cbutton class=\"me-fmt-btn\" title=\"Ordered List\" onclick=\"meFmt('ol')\"\u003e1. ol\u003c/button\u003e\n  \u003cbutton class=\"me-fmt-btn\" title=\"Task List\" onclick=\"meFmt('task')\"\u003e\u0026#9744;\u003c/button\u003e\n  \u003cdiv class=\"me-sep\"\u003e\u003c/div\u003e\n  \u003cbutton class=\"me-fmt-btn\" title=\"Table\" onclick=\"meFmt('table')\"\u003e\u0026#9776;\u003c/button\u003e\n  \u003cbutton class=\"me-fmt-btn\" title=\"Horizontal Rule\" onclick=\"meFmt('hr')\"\u003e\u0026#8212;\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Split Pane --\u003e\n\u003cdiv id=\"me-split\"\u003e\n  \u003cdiv id=\"me-editor-pane\"\u003e\n    \u003cdiv class=\"me-pane-label\"\u003e\u0026#9998; Editor\u003c/div\u003e\n    \u003cdiv id=\"me-editor-wrap\"\u003e\n      \u003cdiv id=\"me-line-nums\"\u003e1\u003c/div\u003e\n      \u003ctextarea id=\"me-editor\" placeholder=\"Start writing Markdown...\" spellcheck=\"false\"\u003e\u003c/textarea\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv id=\"me-preview-pane\"\u003e\n    \u003cdiv class=\"me-pane-label\"\u003e\u0026#128065; Preview\u003c/div\u003e\n    \u003cdiv id=\"me-preview\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Status Bar --\u003e\n\u003cdiv id=\"me-status\"\u003e\n  \u003cspan class=\"me-stat\"\u003eWords: \u003cspan class=\"me-stat-val\" id=\"me-stat-words\"\u003e0\u003c/span\u003e\u003c/span\u003e\n  \u003cspan class=\"me-stat\"\u003eChars: \u003cspan class=\"me-stat-val\" id=\"me-stat-chars\"\u003e0\u003c/span\u003e\u003c/span\u003e\n  \u003cspan class=\"me-stat\"\u003eLines: \u003cspan class=\"me-stat-val\" id=\"me-stat-lines\"\u003e0\u003c/span\u003e\u003c/span\u003e\n  \u003cspan class=\"me-stat\"\u003eRead: \u003cspan class=\"me-stat-val\" id=\"me-stat-read\"\u003e0 min\u003c/span\u003e\u003c/span\u003e\n  \u003cspan id=\"me-saved-msg\"\u003e\u0026#10003; Autosaved\u003c/span\u003e\n\u003c/div\u003e\n\u003c!-- Related Tools --\u003e\n\u003cdiv id=\"me-related\"\u003e\n  \u003cstrong\u003eRelated Markdown Tools:\u003c/strong\u003e\n  \u003ca href=\"/tools/markdown-preview/\"\u003eMarkdown Preview\u003c/a\u003e \u0026nbsp;\u0026middot;\u0026nbsp;\n  \u003ca href=\"/tools/markdown-table-generator/\"\u003eMarkdown Table Generator\u003c/a\u003e \u0026nbsp;\u0026middot;\u0026nbsp;\n  \u003ca href=\"/tools/markdown-toc-generator/\"\u003eMarkdown TOC Generator\u003c/a\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003c!-- /#me-app --\u003e\n\u003cscript\u003e\n(function() {\n  'use strict';\n\n  /* ── Sample Content ───────────────────────────────────── */\n  var SAMPLE = [\n    '# Welcome to Markdown Editor',\n    '',\n    'A **full-featured** split-pane editor with live preview. Use the toolbar or keyboard shortcuts to format your text.',\n    '',\n    '## Keyboard Shortcuts',\n    '',\n    '| Shortcut | Action |',\n    '|----------|--------|',\n    '| `Ctrl+B` | Bold |',\n    '| `Ctrl+I` | Italic |',\n    '| `Tab` | Indent |',\n    '',\n    '## Text Formatting',\n    '',\n    '**Bold text**, *italic text*, ~~strikethrough~~, and `inline code`.',\n    '',\n    '## Headings',\n    '',\n    '# H1 Heading',\n    '## H2 Heading',\n    '### H3 Heading',\n    '',\n    '## Lists',\n    '',\n    '- Unordered item one',\n    '- Unordered item two',\n    '  - Nested item',\n    '',\n    '1. First ordered item',\n    '2. Second ordered item',\n    '3. Third ordered item',\n    '',\n    '- [x] Completed task',\n    '- [ ] Pending task',\n    '',\n    '## Code',\n    '',\n    '```javascript',\n    'function greet(name) {',\n    '  // Say hello',\n    '  return `Hello, ${name}!`;',\n    '}',\n    'console.log(greet(\"World\"));',\n    '```',\n    '',\n    '## Blockquote',\n    '',\n    '\u003e \"The best writing tool is the one you actually use.\"',\n    '\u003e — Productivity Proverb',\n    '',\n    '## Link \u0026 Image',\n    '',\n    '[Visit Example](https://example.com)',\n    '',\n    '![Placeholder](https://via.placeholder.com/400x120?text=Markdown+Editor)',\n    '',\n    '## Horizontal Rule',\n    '',\n    '---',\n    '',\n    'Happy writing! Your work is **autosaved** to localStorage.'\n  ].join('\\n');\n\n  /* ── Markdown Parser ──────────────────────────────────── */\n  function parseMarkdown(md) {\n    var lines = md.split('\\n');\n    var out = '';\n    var i = 0;\n    var inFence = false;\n    var fenceLang = '';\n    var fenceLines = [];\n\n    while (i \u003c lines.length) {\n      var line = lines[i];\n\n      // Fenced code block\n      var fenceMatch = line.match(/^(`{3,}|~{3,})\\s*(\\w*)/);\n      if (fenceMatch \u0026\u0026 !inFence) {\n        inFence = true;\n        fenceLang = fenceMatch[2] || '';\n        fenceLines = [];\n        i++; continue;\n      }\n      if (inFence) {\n        if (/^(`{3,}|~{3,})/.test(line)) {\n          var cc = fenceLines.map(escapeHtml).join('\\n');\n          cc = highlightCode(cc, fenceLang);\n          out += '\u003cpre\u003e\u003ccode' + (fenceLang ? ' class=\"lang-' + escapeHtml(fenceLang) + '\"' : '') + '\u003e' + cc + '\u003c/code\u003e\u003c/pre\u003e\\n';\n\u003cpre\u003e\u003ccode\u003e      inFence = false; fenceLang = ''; fenceLines = [];\n    } else { fenceLines.push(line); }\n    i++; continue;\n  }\n\n  // Setext h1\n  if (i + 1 \u0026lt; lines.length \u0026amp;\u0026amp; /^=+\\s*$/.test(lines[i+1]) \u0026amp;\u0026amp; line.trim()) {\n    out += '\u0026lt;h1\u0026gt;' + inlineMd(line.trim()) + '\u0026lt;/h1\u0026gt;\\n'; i += 2; continue;\n  }\n  // Setext h2\n  if (i + 1 \u0026lt; lines.length \u0026amp;\u0026amp; /^-+\\s*$/.test(lines[i+1]) \u0026amp;\u0026amp; line.trim() \u0026amp;\u0026amp; !/^\\s*[-*+] /.test(line)) {\n    out += '\u0026lt;h2\u0026gt;' + inlineMd(line.trim()) + '\u0026lt;/h2\u0026gt;\\n'; i += 2; continue;\n  }\n\n  // ATX headings\n  var hm = line.match(/^(#{1,6})\\s+(.+?)(?:\\s+#+)?$/);\n  if (hm) {\n    var lvl = hm[1].length;\n    out += '\u0026lt;h' + lvl + '\u0026gt;' + inlineMd(hm[2].trim()) + '\u0026lt;/h' + lvl + '\u0026gt;\\n';\n    i++; continue;\n  }\n\n  // HR\n  if (/^(\\*{3,}|-{3,}|_{3,})\\s*$/.test(line.trim())) {\n    out += '\u0026lt;hr\u0026gt;\\n'; i++; continue;\n  }\n\n  // Blockquote\n  if (/^\u0026gt;\\s?/.test(line)) {\n    var bq = [];\n    while (i \u0026lt; lines.length \u0026amp;\u0026amp; /^\u0026gt;\\s?/.test(lines[i])) {\n      bq.push(lines[i].replace(/^\u0026gt;\\s?/, '')); i++;\n    }\n    out += '\u0026lt;blockquote\u0026gt;\u0026lt;p\u0026gt;' + inlineMd(bq.join('\\n')) + '\u0026lt;/p\u0026gt;\u0026lt;/blockquote\u0026gt;\\n';\n    continue;\n  }\n\n  // Table\n  if (/\\|/.test(line) \u0026amp;\u0026amp; i + 1 \u0026lt; lines.length \u0026amp;\u0026amp; /^\\|?[\\s:|-]+\\|/.test(lines[i+1])) {\n    var tbl = [];\n    while (i \u0026lt; lines.length \u0026amp;\u0026amp; /\\|/.test(lines[i])) { tbl.push(lines[i]); i++; }\n    out += parseTable(tbl); continue;\n  }\n\n  // Task list (before ul)\n  if (/^\\s*[-*+] \\[[ xX]\\]/.test(line)) {\n    var tl = [];\n    while (i \u0026lt; lines.length \u0026amp;\u0026amp; /^\\s*[-*+] \\[[ xX]\\]/.test(lines[i])) {\n      tl.push(lines[i]); i++;\n    }\n    out += '\u0026lt;ul class=\u0026quot;me-tasklist\u0026quot;\u0026gt;' + tl.map(function(l) {\n      var checked = /\\[x\\]/i.test(l);\n      var txt = l.replace(/^\\s*[-*+] \\[[ xX]\\]\\s*/, '');\n      return '\u0026lt;li\u0026gt;\u0026lt;input type=\u0026quot;checkbox\u0026quot; disabled' + (checked ? ' checked' : '') + '\u0026gt; ' + inlineMd(txt) + '\u0026lt;/li\u0026gt;';\n    }).join('') + '\u0026lt;/ul\u0026gt;\\n';\n    continue;\n  }\n\n  // UL\n  if (/^(\\s*)[-*+] /.test(line)) {\n    var ul = [];\n    while (i \u0026lt; lines.length \u0026amp;\u0026amp; /^(\\s*)[-*+] /.test(lines[i])) { ul.push(lines[i]); i++; }\n    out += '\u0026lt;ul\u0026gt;' + ul.map(function(l) {\n      return '\u0026lt;li\u0026gt;' + inlineMd(l.replace(/^\\s*[-*+] /, '')) + '\u0026lt;/li\u0026gt;';\n    }).join('') + '\u0026lt;/ul\u0026gt;\\n';\n    continue;\n  }\n\n  // OL\n  if (/^\\d+\\.\\s/.test(line)) {\n    var ol = [];\n    while (i \u0026lt; lines.length \u0026amp;\u0026amp; /^\\d+\\.\\s/.test(lines[i])) { ol.push(lines[i]); i++; }\n    out += '\u0026lt;ol\u0026gt;' + ol.map(function(l) {\n      return '\u0026lt;li\u0026gt;' + inlineMd(l.replace(/^\\d+\\.\\s/, '')) + '\u0026lt;/li\u0026gt;';\n    }).join('') + '\u0026lt;/ol\u0026gt;\\n';\n    continue;\n  }\n\n  // Blank\n  if (line.trim() === '') { out += '\\n'; i++; continue; }\n\n  // Paragraph\n  var pl = [];\n  while (i \u0026lt; lines.length \u0026amp;\u0026amp; lines[i].trim() !== ''\n    \u0026amp;\u0026amp; !/^#{1,6}\\s/.test(lines[i])\n    \u0026amp;\u0026amp; !/^(\\*{3,}|-{3,}|_{3,})\\s*$/.test(lines[i].trim())\n    \u0026amp;\u0026amp; !/^\u0026gt;\\s?/.test(lines[i])\n    \u0026amp;\u0026amp; !/^\\s*[-*+] /.test(lines[i])\n    \u0026amp;\u0026amp; !/^\\d+\\.\\s/.test(lines[i])\n    \u0026amp;\u0026amp; !/\\|/.test(lines[i])\n    \u0026amp;\u0026amp; !/^(`{3,}|~{3,})/.test(lines[i])) {\n    pl.push(lines[i]); i++;\n  }\n  if (pl.length) out += '\u0026lt;p\u0026gt;' + inlineMd(pl.join(' ')) + '\u0026lt;/p\u0026gt;\\n';\n}\n\nif (inFence \u0026amp;\u0026amp; fenceLines.length) {\n  out += '\u0026lt;pre\u0026gt;\u0026lt;code\u0026gt;' + fenceLines.map(escapeHtml).join('\\n') + '\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\\n';\n}\nreturn out;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e}\u003c/p\u003e","title":"Markdown Editor - Live Preview"},{"content":"Paste any Markdown text below to instantly extract every link, categorize it, detect broken formats, find duplicates, and export results as CSV or JSON.\nPaste Markdown Text Extract Links Clear # Link Text URL Type Flags Export CSV Export JSON Copy All URLs Related tools:\nMarkdown Preview Markdown Table Generator URL Encoder How to Use Paste any Markdown text into the box above. Click Extract Links (or press Ctrl+Enter / Cmd+Enter). Use the filter tabs to view specific link categories. Export the full list as CSV or JSON, or Copy All URLs to clipboard. What Gets Extracted Link type Markdown syntax Example External link [text](https://...) [Google](https://google.com) Internal link [text](/path) [About](/about) Anchor [text](#id) [Top](#top) Image ![alt](url) ![Logo](logo.png) Reference link [text][ref] [Docs][docs] Auto-link \u0026lt;https://...\u0026gt; \u0026lt;https://example.com\u0026gt; Broken Format Detection The checker flags links that have:\nMissing protocol — URLs starting with www. instead of https:// Spaces in URL — Whitespace inside a URL that will cause rendering failures Bare domains — Domain-like strings with no https:// prefix Related Tools Markdown Preview — Render Markdown as HTML in real time Markdown Table Generator — Build Markdown tables visually URL Encoder — Encode and decode URLs and query strings ","permalink":"https://productivity-works.com/tools/markdown-link-checker/","summary":"\u003cp\u003ePaste any Markdown text below to instantly extract every link, categorize it, detect broken formats, find duplicates, and export results as CSV or JSON.\u003c/p\u003e\n\u003cdiv id=\"mlc-app\"\u003e\n\u003cstyle\u003e\n#mlc-app *,\n#mlc-app *::before,\n#mlc-app *::after {\n  box-sizing: border-box;\n}\n\n#mlc-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n  font-size: 15px;\n  color: #1a1a2e;\n  max-width: 860px;\n  margin: 0 auto;\n  padding: 0;\n}\n\n#mlc-app h2.mlc-section-title {\n  font-size: 1.1rem;\n  font-weight: 700;\n  margin: 0 0 10px 0;\n  color: #0f3460;\n  letter-spacing: 0.02em;\n}\n\n/* Input area */\n#mlc-app .mlc-input-wrap {\n  background: #f8f9ff;\n  border: 1.5px solid #d0d7f7;\n  border-radius: 10px;\n  padding: 18px;\n  margin-bottom: 14px;\n}\n\n#mlc-app textarea#mlc-input {\n  width: 100%;\n  min-height: 180px;\n  border: 1.5px solid #c3cde8;\n  border-radius: 7px;\n  padding: 12px 14px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 13px;\n  line-height: 1.6;\n  resize: vertical;\n  outline: none;\n  background: #fff;\n  color: #1a1a2e;\n  transition: border-color 0.2s;\n}\n#mlc-app textarea#mlc-input:focus {\n  border-color: #4361ee;\n}\n#mlc-app textarea#mlc-input::placeholder {\n  color: #9aa5c4;\n}\n\n/* Buttons row */\n#mlc-app .mlc-btn-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-top: 12px;\n  align-items: center;\n}\n\n#mlc-app button.mlc-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 5px;\n  padding: 8px 18px;\n  border: none;\n  border-radius: 6px;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.18s, transform 0.1s;\n  white-space: nowrap;\n}\n#mlc-app button.mlc-btn:active {\n  transform: scale(0.97);\n}\n\n#mlc-app button#mlc-analyze-btn {\n  background: #4361ee;\n  color: #fff;\n}\n#mlc-app button#mlc-analyze-btn:hover {\n  background: #3451d1;\n}\n\n#mlc-app button#mlc-clear-btn {\n  background: #eef0fb;\n  color: #4361ee;\n  border: 1.5px solid #c3cde8;\n}\n#mlc-app button#mlc-clear-btn:hover {\n  background: #dde1f6;\n}\n\n#mlc-app button.mlc-btn-export {\n  background: #16213e;\n  color: #fff;\n}\n#mlc-app button.mlc-btn-export:hover {\n  background: #0f3460;\n}\n\n#mlc-app button#mlc-copy-urls-btn {\n  background: #e8f5e9;\n  color: #2e7d32;\n  border: 1.5px solid #a5d6a7;\n}\n#mlc-app button#mlc-copy-urls-btn:hover {\n  background: #c8e6c9;\n}\n\n/* Stats bar */\n#mlc-app .mlc-stats-bar {\n  display: none;\n  flex-wrap: wrap;\n  gap: 10px;\n  margin-bottom: 14px;\n}\n\n#mlc-app .mlc-stat-chip {\n  display: inline-flex;\n  align-items: center;\n  gap: 5px;\n  padding: 5px 13px;\n  border-radius: 20px;\n  font-size: 13px;\n  font-weight: 600;\n  background: #eef0fb;\n  color: #0f3460;\n  border: 1px solid #d0d7f7;\n}\n\n#mlc-app .mlc-stat-chip.chip-total  { background: #e8eaf6; border-color: #9fa8da; color: #283593; }\n#mlc-app .mlc-stat-chip.chip-ext    { background: #e3f2fd; border-color: #90caf9; color: #1565c0; }\n#mlc-app .mlc-stat-chip.chip-int    { background: #e8f5e9; border-color: #a5d6a7; color: #2e7d32; }\n#mlc-app .mlc-stat-chip.chip-img    { background: #fff3e0; border-color: #ffcc80; color: #e65100; }\n#mlc-app .mlc-stat-chip.chip-anchor { background: #f3e5f5; border-color: #ce93d8; color: #6a1b9a; }\n#mlc-app .mlc-stat-chip.chip-dup    { background: #fce4ec; border-color: #f48fb1; color: #880e4f; }\n#mlc-app .mlc-stat-chip.chip-broken { background: #ffebee; border-color: #ef9a9a; color: #b71c1c; }\n\n/* Filter tabs */\n#mlc-app .mlc-filter-row {\n  display: none;\n  flex-wrap: wrap;\n  gap: 6px;\n  margin-bottom: 12px;\n}\n\n#mlc-app button.mlc-filter-tab {\n  padding: 5px 14px;\n  border-radius: 20px;\n  border: 1.5px solid #c3cde8;\n  background: #fff;\n  color: #4361ee;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s;\n}\n#mlc-app button.mlc-filter-tab:hover,\n#mlc-app button.mlc-filter-tab.active {\n  background: #4361ee;\n  color: #fff;\n  border-color: #4361ee;\n}\n\n/* Results table */\n#mlc-app .mlc-results-wrap {\n  display: none;\n  overflow-x: auto;\n  border: 1.5px solid #d0d7f7;\n  border-radius: 10px;\n  margin-bottom: 14px;\n}\n\n#mlc-app table.mlc-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 13px;\n  min-width: 580px;\n}\n\n#mlc-app table.mlc-table thead tr {\n  background: #0f3460;\n  color: #fff;\n}\n\n#mlc-app table.mlc-table thead th {\n  padding: 10px 13px;\n  text-align: left;\n  font-weight: 700;\n  white-space: nowrap;\n}\n\n#mlc-app table.mlc-table tbody tr {\n  border-bottom: 1px solid #e8edf7;\n  transition: background 0.1s;\n}\n#mlc-app table.mlc-table tbody tr:last-child {\n  border-bottom: none;\n}\n#mlc-app table.mlc-table tbody tr:hover {\n  background: #f0f3ff;\n}\n\n#mlc-app table.mlc-table td {\n  padding: 9px 13px;\n  vertical-align: top;\n  word-break: break-word;\n}\n\n#mlc-app .mlc-badge {\n  display: inline-block;\n  padding: 2px 8px;\n  border-radius: 10px;\n  font-size: 11px;\n  font-weight: 700;\n  white-space: nowrap;\n  letter-spacing: 0.02em;\n}\n#mlc-app .badge-external { background: #e3f2fd; color: #1565c0; }\n#mlc-app .badge-internal { background: #e8f5e9; color: #2e7d32; }\n#mlc-app .badge-image    { background: #fff3e0; color: #e65100; }\n#mlc-app .badge-anchor   { background: #f3e5f5; color: #6a1b9a; }\n#mlc-app .badge-dup      { background: #fce4ec; color: #880e4f; margin-left: 4px; }\n#mlc-app .badge-broken   { background: #ffebee; color: #b71c1c; margin-left: 4px; }\n\n#mlc-app .mlc-url-cell {\n  max-width: 300px;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n#mlc-app .mlc-url-cell a {\n  color: #4361ee;\n  text-decoration: none;\n}\n#mlc-app .mlc-url-cell a:hover {\n  text-decoration: underline;\n}\n\n/* Toast / flash message */\n#mlc-app .mlc-toast {\n  display: none;\n  position: fixed;\n  bottom: 28px;\n  right: 28px;\n  background: #1a1a2e;\n  color: #fff;\n  padding: 10px 22px;\n  border-radius: 8px;\n  font-size: 14px;\n  font-weight: 600;\n  z-index: 9999;\n  box-shadow: 0 4px 16px rgba(0,0,0,0.25);\n  pointer-events: none;\n}\n\n/* Empty state */\n#mlc-app .mlc-empty {\n  text-align: center;\n  padding: 40px 20px;\n  color: #9aa5c4;\n  font-size: 14px;\n}\n\n/* Action row below table */\n#mlc-app .mlc-action-row {\n  display: none;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 18px;\n}\n\n/* Cross-link section */\n#mlc-app .mlc-crosslinks {\n  border-top: 1.5px solid #e0e4f7;\n  padding-top: 16px;\n  margin-top: 8px;\n}\n#mlc-app .mlc-crosslinks p {\n  margin: 0 0 8px 0;\n  font-size: 13px;\n  color: #555e7a;\n}\n#mlc-app .mlc-crosslinks a {\n  color: #4361ee;\n  text-decoration: none;\n  font-weight: 600;\n  margin-right: 10px;\n}\n#mlc-app .mlc-crosslinks a:hover {\n  text-decoration: underline;\n}\n\n/* Responsive */\n@media (max-width: 600px) {\n  #mlc-app .mlc-btn-row { flex-direction: column; }\n  #mlc-app button.mlc-btn { width: 100%; justify-content: center; }\n  #mlc-app .mlc-action-row { flex-direction: column; }\n  #mlc-app .mlc-url-cell { max-width: 160px; }\n}\n\u003c/style\u003e\n\u003c!-- Input --\u003e\n\u003cdiv class=\"mlc-input-wrap\"\u003e\n  \u003ch2 class=\"mlc-section-title\"\u003ePaste Markdown Text\u003c/h2\u003e\n  \u003ctextarea id=\"mlc-input\" placeholder=\"Paste your Markdown here...\u0026#10;\u0026#10;Example:\u0026#10;[Google](https://google.com)\u0026#10;![Logo](https://example.com/logo.png)\u0026#10;[About](#about)\u0026#10;[Internal Page](/blog/post)\"\u003e\u003c/textarea\u003e\n  \u003cdiv class=\"mlc-btn-row\"\u003e\n    \u003cbutton class=\"mlc-btn\" id=\"mlc-analyze-btn\" onclick=\"mlcAnalyze()\"\u003eExtract Links\u003c/button\u003e\n    \u003cbutton class=\"mlc-btn\" id=\"mlc-clear-btn\" onclick=\"mlcClear()\"\u003eClear\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Stats --\u003e\n\u003cdiv class=\"mlc-stats-bar\" id=\"mlc-stats-bar\"\u003e\u003c/div\u003e\n\u003c!-- Filter tabs --\u003e\n\u003cdiv class=\"mlc-filter-row\" id=\"mlc-filter-row\"\u003e\u003c/div\u003e\n\u003c!-- Table --\u003e\n\u003cdiv class=\"mlc-results-wrap\" id=\"mlc-results-wrap\"\u003e\n  \u003ctable class=\"mlc-table\" id=\"mlc-table\"\u003e\n    \u003cthead\u003e\n      \u003ctr\u003e\n        \u003cth\u003e#\u003c/th\u003e\n        \u003cth\u003eLink Text\u003c/th\u003e\n        \u003cth\u003eURL\u003c/th\u003e\n        \u003cth\u003eType\u003c/th\u003e\n        \u003cth\u003eFlags\u003c/th\u003e\n      \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody id=\"mlc-tbody\"\u003e\u003c/tbody\u003e\n  \u003c/table\u003e\n\u003c/div\u003e\n\u003c!-- Action row --\u003e\n\u003cdiv class=\"mlc-action-row\" id=\"mlc-action-row\"\u003e\n  \u003cbutton class=\"mlc-btn mlc-btn-export\" onclick=\"mlcExportCSV()\"\u003eExport CSV\u003c/button\u003e\n  \u003cbutton class=\"mlc-btn mlc-btn-export\" onclick=\"mlcExportJSON()\"\u003eExport JSON\u003c/button\u003e\n  \u003cbutton class=\"mlc-btn\" id=\"mlc-copy-urls-btn\" onclick=\"mlcCopyAllURLs()\"\u003eCopy All URLs\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Toast --\u003e\n\u003cdiv class=\"mlc-toast\" id=\"mlc-toast\"\u003e\u003c/div\u003e\n\u003c!-- Cross-links --\u003e\n\u003cdiv class=\"mlc-crosslinks\"\u003e\n  \u003cp\u003eRelated tools:\u003c/p\u003e","title":"Markdown Link Checker — Extract \u0026 Validate Links"},{"content":" \u0026#128196; Markdown Preview Load Sample Clear \u0026#9790; Dark Mode Copy HTML Export .html Markdown Preview Words: 0 Chars: 0 Lines: 0 Read time: 0 min Copied to clipboard! Related: Try our Word Counter for in-depth text analysis — word frequency, reading level, and more. Related Tools Html Beautifier Html Entity Converter Html Entity Encoder ","permalink":"https://productivity-works.com/tools/markdown-preview/","summary":"\u003cdiv id=\"md-app\"\u003e\n\u003cstyle\u003e\n/* ── Reset \u0026 Root ─────────────────────────────────────── */\n#md-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", sans-serif;\n  max-width: 1100px;\n  margin: 0 auto;\n  color: #1e1b4b;\n  --accent: #7c3aed;\n  --accent-dk: #6d28d9;\n  --accent-lt: #ede9fe;\n  --bg: #f8f7ff;\n  --surface: #ffffff;\n  --border: #d8d3f0;\n  --text: #1e1b4b;\n  --text-muted: #6b7280;\n  --code-bg: #f3f0ff;\n  --preview-bg: #ffffff;\n  --editor-bg: #faf9ff;\n  --status-bg: #f3f0ff;\n  --btn-ghost: #ede9fe;\n  --btn-ghost-text: #5b21b6;\n  --shadow: 0 2px 12px rgba(124,58,237,0.10);\n  --radius: 10px;\n}\n#md-app * { box-sizing: border-box; }\n\n/* Dark theme */\n#md-app.md-dark {\n  --bg: #0f0e1a;\n  --surface: #1a1828;\n  --border: #3b3660;\n  --text: #e5e1ff;\n  --text-muted: #9d97c4;\n  --code-bg: #2a2540;\n  --preview-bg: #1a1828;\n  --editor-bg: #15132a;\n  --status-bg: #1a1828;\n  --btn-ghost: #2a2540;\n  --btn-ghost-text: #c4b5fd;\n  --shadow: 0 2px 12px rgba(0,0,0,0.40);\n  color: var(--text);\n}\n\n#md-app.md-dark #md-editor { color: #e5e1ff; }\n\n/* ── Toolbar ──────────────────────────────────────────── */\n#md-toolbar {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  flex-wrap: wrap;\n  gap: 8px;\n  background: var(--surface);\n  border: 1px solid var(--border);\n  border-radius: var(--radius) var(--radius) 0 0;\n  padding: 10px 14px;\n  box-shadow: var(--shadow);\n}\n#md-toolbar-title {\n  font-weight: 700;\n  font-size: 1rem;\n  color: var(--accent);\n  letter-spacing: -0.01em;\n}\n#md-toolbar-btns {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 6px;\n}\n\n/* ── Buttons ──────────────────────────────────────────── */\n.md-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 5px;\n  padding: 6px 13px;\n  border: none;\n  border-radius: 6px;\n  font-size: 0.82rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s, box-shadow 0.15s;\n  line-height: 1.2;\n  white-space: nowrap;\n}\n.md-btn:active { transform: scale(0.96); }\n\n.md-btn-primary {\n  background: var(--accent);\n  color: #fff;\n  box-shadow: 0 1px 4px rgba(124,58,237,0.25);\n}\n.md-btn-primary:hover { background: var(--accent-dk); }\n\n.md-btn-ghost {\n  background: var(--btn-ghost);\n  color: var(--btn-ghost-text);\n}\n.md-btn-ghost:hover { filter: brightness(0.95); }\n\n.md-btn-success {\n  background: #d1fae5;\n  color: #065f46;\n}\n.md-btn-success:hover { background: #a7f3d0; }\n\n.md-dark .md-btn-success {\n  background: #064e3b;\n  color: #6ee7b7;\n}\n\n/* ── Split Pane Container ─────────────────────────────── */\n#md-split {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  border-left: 1px solid var(--border);\n  border-right: 1px solid var(--border);\n  min-height: 460px;\n}\n\n@media (max-width: 640px) {\n  #md-split {\n    grid-template-columns: 1fr;\n  }\n}\n\n/* ── Pane Labels ──────────────────────────────────────── */\n.md-pane-label {\n  font-size: 0.72rem;\n  font-weight: 700;\n  letter-spacing: 0.08em;\n  text-transform: uppercase;\n  color: var(--text-muted);\n  padding: 6px 14px 4px;\n  background: var(--surface);\n  border-bottom: 1px solid var(--border);\n}\n#md-editor-pane {\n  border-right: 1px solid var(--border);\n  display: flex;\n  flex-direction: column;\n}\n#md-preview-pane {\n  display: flex;\n  flex-direction: column;\n  background: var(--preview-bg);\n}\n\n@media (max-width: 640px) {\n  #md-editor-pane { border-right: none; border-bottom: 1px solid var(--border); }\n}\n\n/* ── Editor ───────────────────────────────────────────── */\n#md-editor {\n  flex: 1;\n  width: 100%;\n  padding: 16px;\n  font-size: 0.93rem;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  line-height: 1.65;\n  border: none;\n  outline: none;\n  resize: none;\n  background: var(--editor-bg);\n  color: var(--text);\n  min-height: 420px;\n}\n#md-editor::placeholder { color: var(--text-muted); opacity: 0.7; }\n\n/* ── Preview ──────────────────────────────────────────── */\n#md-preview {\n  flex: 1;\n  padding: 18px 20px;\n  overflow-y: auto;\n  background: var(--preview-bg);\n  color: var(--text);\n  font-size: 0.95rem;\n  line-height: 1.75;\n  min-height: 420px;\n}\n\n/* Rendered markdown styles inside preview */\n#md-preview h1, #md-preview h2, #md-preview h3,\n#md-preview h4, #md-preview h5, #md-preview h6 {\n  color: var(--accent);\n  margin: 1.1em 0 0.4em;\n  line-height: 1.25;\n  font-weight: 700;\n}\n#md-preview h1 { font-size: 1.65em; border-bottom: 2px solid var(--border); padding-bottom: 0.2em; }\n#md-preview h2 { font-size: 1.35em; border-bottom: 1px solid var(--border); padding-bottom: 0.15em; }\n#md-preview h3 { font-size: 1.15em; }\n#md-preview h4 { font-size: 1em; }\n#md-preview h5 { font-size: 0.9em; }\n#md-preview h6 { font-size: 0.85em; color: var(--text-muted); }\n\n#md-preview p { margin: 0.7em 0; }\n\n#md-preview a { color: var(--accent); text-decoration: underline; }\n#md-preview a:hover { color: var(--accent-dk); }\n\n#md-preview strong { font-weight: 700; color: inherit; }\n#md-preview em { font-style: italic; }\n#md-preview del { text-decoration: line-through; color: var(--text-muted); }\n\n#md-preview code {\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 0.88em;\n  background: var(--code-bg);\n  color: #9333ea;\n  padding: 0.15em 0.45em;\n  border-radius: 4px;\n  border: 1px solid var(--border);\n}\n\n#md-preview pre {\n  background: var(--code-bg);\n  border: 1px solid var(--border);\n  border-radius: 8px;\n  padding: 14px 16px;\n  overflow-x: auto;\n  margin: 1em 0;\n}\n#md-preview pre code {\n  background: none;\n  border: none;\n  padding: 0;\n  color: var(--text);\n  font-size: 0.87em;\n  line-height: 1.6;\n}\n\n/* Basic syntax highlight tokens (CSS-only, class-based) */\n#md-preview pre .kw { color: #9333ea; font-weight: 700; }\n#md-preview pre .str { color: #059669; }\n#md-preview pre .cm { color: #6b7280; font-style: italic; }\n#md-preview pre .nm { color: #b45309; }\n.md-dark #md-preview pre .kw { color: #c084fc; }\n.md-dark #md-preview pre .str { color: #34d399; }\n.md-dark #md-preview pre .cm { color: #9ca3af; }\n.md-dark #md-preview pre .nm { color: #fbbf24; }\n\n#md-preview blockquote {\n  border-left: 4px solid var(--accent);\n  margin: 1em 0;\n  padding: 8px 16px;\n  background: var(--code-bg);\n  border-radius: 0 6px 6px 0;\n  color: var(--text-muted);\n}\n#md-preview blockquote p { margin: 0; }\n\n#md-preview ul, #md-preview ol {\n  padding-left: 1.6em;\n  margin: 0.6em 0;\n}\n#md-preview li { margin: 0.25em 0; }\n#md-preview ul li { list-style-type: disc; }\n#md-preview ol li { list-style-type: decimal; }\n\n#md-preview hr {\n  border: none;\n  border-top: 2px solid var(--border);\n  margin: 1.5em 0;\n}\n\n#md-preview img {\n  max-width: 100%;\n  border-radius: 6px;\n  margin: 0.5em 0;\n}\n\n/* Tables */\n#md-preview table {\n  border-collapse: collapse;\n  width: 100%;\n  margin: 1em 0;\n  font-size: 0.92em;\n}\n#md-preview th {\n  background: var(--accent-lt);\n  color: var(--accent);\n  font-weight: 700;\n  padding: 8px 12px;\n  border: 1px solid var(--border);\n  text-align: left;\n}\n.md-dark #md-preview th {\n  background: #2a2540;\n  color: #c4b5fd;\n}\n#md-preview td {\n  padding: 7px 12px;\n  border: 1px solid var(--border);\n}\n#md-preview tr:nth-child(even) td { background: var(--code-bg); }\n\n/* ── Status Bar ───────────────────────────────────────── */\n#md-status {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  gap: 14px;\n  background: var(--status-bg);\n  border: 1px solid var(--border);\n  border-top: none;\n  border-radius: 0 0 var(--radius) var(--radius);\n  padding: 8px 16px;\n  font-size: 0.8rem;\n  color: var(--text-muted);\n}\n.md-stat {\n  display: inline-flex;\n  align-items: center;\n  gap: 4px;\n}\n.md-stat-val {\n  font-weight: 700;\n  color: var(--accent);\n}\n#md-copy-msg {\n  margin-left: auto;\n  color: #059669;\n  font-weight: 600;\n  font-size: 0.78rem;\n  opacity: 0;\n  transition: opacity 0.3s;\n}\n#md-copy-msg.md-show { opacity: 1; }\n\n/* ── Related ──────────────────────────────────────────── */\n#md-related {\n  margin-top: 20px;\n  padding: 12px 16px;\n  background: var(--status-bg);\n  border: 1px solid var(--border);\n  border-radius: 8px;\n  font-size: 0.88rem;\n  color: var(--text-muted);\n}\n#md-related a { color: var(--accent); font-weight: 600; }\n\u003c/style\u003e\n\u003c!-- Toolbar --\u003e\n\u003cdiv id=\"md-toolbar\"\u003e\n  \u003cspan id=\"md-toolbar-title\"\u003e\u0026#128196; Markdown Preview\u003c/span\u003e\n  \u003cdiv id=\"md-toolbar-btns\"\u003e\n    \u003cbutton class=\"md-btn md-btn-ghost\" id=\"md-btn-sample\" onclick=\"mdLoadSample()\"\u003eLoad Sample\u003c/button\u003e\n    \u003cbutton class=\"md-btn md-btn-ghost\" id=\"md-btn-clear\" onclick=\"mdClear()\"\u003eClear\u003c/button\u003e\n    \u003cbutton class=\"md-btn md-btn-ghost\" id=\"md-btn-theme\" onclick=\"mdToggleTheme()\"\u003e\u0026#9790; Dark Mode\u003c/button\u003e\n    \u003cbutton class=\"md-btn md-btn-ghost\" id=\"md-btn-copy\" onclick=\"mdCopyHTML()\"\u003eCopy HTML\u003c/button\u003e\n    \u003cbutton class=\"md-btn md-btn-primary\" id=\"md-btn-export\" onclick=\"mdExport()\"\u003eExport .html\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Split Pane --\u003e\n\u003cdiv id=\"md-split\"\u003e\n  \u003cdiv id=\"md-editor-pane\"\u003e\n    \u003cdiv class=\"md-pane-label\"\u003eMarkdown\u003c/div\u003e\n    \u003ctextarea id=\"md-editor\" placeholder=\"Type your markdown here...\" spellcheck=\"false\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n  \u003cdiv id=\"md-preview-pane\"\u003e\n    \u003cdiv class=\"md-pane-label\"\u003ePreview\u003c/div\u003e\n    \u003cdiv id=\"md-preview\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Status Bar --\u003e\n\u003cdiv id=\"md-status\"\u003e\n  \u003cspan class=\"md-stat\"\u003eWords: \u003cspan class=\"md-stat-val\" id=\"md-stat-words\"\u003e0\u003c/span\u003e\u003c/span\u003e\n  \u003cspan class=\"md-stat\"\u003eChars: \u003cspan class=\"md-stat-val\" id=\"md-stat-chars\"\u003e0\u003c/span\u003e\u003c/span\u003e\n  \u003cspan class=\"md-stat\"\u003eLines: \u003cspan class=\"md-stat-val\" id=\"md-stat-lines\"\u003e0\u003c/span\u003e\u003c/span\u003e\n  \u003cspan class=\"md-stat\"\u003eRead time: \u003cspan class=\"md-stat-val\" id=\"md-stat-read\"\u003e0 min\u003c/span\u003e\u003c/span\u003e\n  \u003cspan id=\"md-copy-msg\"\u003eCopied to clipboard!\u003c/span\u003e\n\u003c/div\u003e\n\u003cdiv id=\"md-related\"\u003e\n  Related: Try our \u003ca href=\"/tools/word-counter/\"\u003eWord Counter\u003c/a\u003e for in-depth text analysis — word frequency, reading level, and more.\n\u003c/div\u003e\n\u003c/div\u003e\u003c!-- /#md-app --\u003e\n\u003cscript\u003e\n(function() {\n  'use strict';\n\n  /* ── Sample content ──────────────────────────────────── */\n  var SAMPLE = [\n    '# Welcome to Markdown Preview',\n    '',\n    'This is a **live markdown editor** with real-time HTML preview. Edit the left pane and watch the right update instantly.',\n    '',\n    '## Features',\n    '',\n    '- Live side-by-side editing',\n    '- **Bold**, *italic*, ~~strikethrough~~',\n    '- [Links](https://example.com) and images',\n    '- Code blocks with syntax highlighting',\n    '- Tables, blockquotes, and more',\n    '',\n    '## Code Example',\n    '',\n    '```javascript',\n    'function greet(name) {',\n    '  // Say hello',\n    '  return `Hello, ${name}!`;',\n    '}',\n    'console.log(greet(\"World\"));',\n    '```',\n    '',\n    '## Inline Code',\n    '',\n    'Use `const` and `let` instead of `var` in modern JavaScript.',\n    '',\n    '## Blockquote',\n    '',\n    '\u003e \"The best tool is the one you actually use.\"',\n    '\u003e — Productivity Proverb',\n    '',\n    '## Table',\n    '',\n    '| Feature | Supported |',\n    '|---------|-----------|',\n    '| Headings | ✅ |',\n    '| Bold / Italic | ✅ |',\n    '| Code blocks | ✅ |',\n    '| Tables | ✅ |',\n    '| Blockquotes | ✅ |',\n    '| Images | ✅ |',\n    '',\n    '## Image',\n    '',\n    '![Placeholder image](https://via.placeholder.com/400x200?text=Markdown+Preview)',\n    '',\n    '## Horizontal Rule',\n    '',\n    '---',\n    '',\n    '### Ordered List',\n    '',\n    '1. Write your content in Markdown',\n    '2. See the live HTML preview',\n    '3. Copy or export the HTML',\n    '',\n    'Happy writing!'\n  ].join('\\n');\n\n  /* ── Markdown Parser ─────────────────────────────────── */\n  function parseMarkdown(md) {\n    // Escape HTML in code blocks first, then process\n    var lines = md.split('\\n');\n    var out = '';\n    var i = 0;\n    var inFence = false;\n    var fenceLang = '';\n    var fenceLines = [];\n\n    while (i \u003c lines.length) {\n      var line = lines[i];\n\n      // Fenced code block start/end\n      var fenceMatch = line.match(/^(`{3,}|~{3,})\\s*(\\w*)/);\n      if (fenceMatch \u0026\u0026 !inFence) {\n        inFence = true;\n        fenceLang = fenceMatch[2] || '';\n        fenceLines = [];\n        i++;\n        continue;\n      }\n      if (inFence) {\n        if (/^(`{3,}|~{3,})/.test(line)) {\n          var codeContent = fenceLines.map(escapeHtml).join('\\n');\n          codeContent = highlightCode(codeContent, fenceLang);\n          out += '\u003cpre\u003e\u003ccode' + (fenceLang ? ' class=\"lang-' + escapeHtml(fenceLang) + '\"' : '') + '\u003e' + codeContent + '\u003c/code\u003e\u003c/pre\u003e\\n';\n\u003cpre\u003e\u003ccode\u003e      inFence = false;\n      fenceLang = '';\n      fenceLines = [];\n    } else {\n      fenceLines.push(line);\n    }\n    i++;\n    continue;\n  }\n\n  // Setext headings (==== and ----)\n  if (i + 1 \u0026lt; lines.length) {\n    if (/^=+\\s*$/.test(lines[i+1]) \u0026amp;\u0026amp; line.trim()) {\n      out += '\u0026lt;h1\u0026gt;' + inlineMarkdown(line.trim()) + '\u0026lt;/h1\u0026gt;\\n';\n      i += 2;\n      continue;\n    }\n    if (/^-+\\s*$/.test(lines[i+1]) \u0026amp;\u0026amp; line.trim() \u0026amp;\u0026amp; !/^\\s*[-*+] /.test(line)) {\n      out += '\u0026lt;h2\u0026gt;' + inlineMarkdown(line.trim()) + '\u0026lt;/h2\u0026gt;\\n';\n      i += 2;\n      continue;\n    }\n  }\n\n  // ATX headings\n  var hMatch = line.match(/^(#{1,6})\\s+(.+?)(?:\\s+#+)?$/);\n  if (hMatch) {\n    var lvl = hMatch[1].length;\n    out += '\u0026lt;h' + lvl + '\u0026gt;' + inlineMarkdown(hMatch[2].trim()) + '\u0026lt;/h' + lvl + '\u0026gt;\\n';\n    i++;\n    continue;\n  }\n\n  // Horizontal rule\n  if (/^(\\*{3,}|-{3,}|_{3,})\\s*$/.test(line.trim())) {\n    out += '\u0026lt;hr\u0026gt;\\n';\n    i++;\n    continue;\n  }\n\n  // Blockquote\n  if (/^\u0026gt;\\s?/.test(line)) {\n    var bqLines = [];\n    while (i \u0026lt; lines.length \u0026amp;\u0026amp; /^\u0026gt;\\s?/.test(lines[i])) {\n      bqLines.push(lines[i].replace(/^\u0026gt;\\s?/, ''));\n      i++;\n    }\n    out += '\u0026lt;blockquote\u0026gt;' + parseParagraphs(bqLines.join('\\n')) + '\u0026lt;/blockquote\u0026gt;\\n';\n    continue;\n  }\n\n  // Table\n  if (/\\|/.test(line) \u0026amp;\u0026amp; i + 1 \u0026lt; lines.length \u0026amp;\u0026amp; /^\\|?[\\s:|-]+\\|/.test(lines[i+1])) {\n    var tableLines = [];\n    while (i \u0026lt; lines.length \u0026amp;\u0026amp; /\\|/.test(lines[i])) {\n      tableLines.push(lines[i]);\n      i++;\n    }\n    out += parseTable(tableLines);\n    continue;\n  }\n\n  // Unordered list\n  if (/^(\\s*)[-*+] /.test(line)) {\n    var ulLines = [];\n    while (i \u0026lt; lines.length \u0026amp;\u0026amp; /^(\\s*)[-*+] /.test(lines[i])) {\n      ulLines.push(lines[i]);\n      i++;\n    }\n    out += '\u0026lt;ul\u0026gt;' + ulLines.map(function(l) {\n      return '\u0026lt;li\u0026gt;' + inlineMarkdown(l.replace(/^\\s*[-*+] /, '')) + '\u0026lt;/li\u0026gt;';\n    }).join('') + '\u0026lt;/ul\u0026gt;\\n';\n    continue;\n  }\n\n  // Ordered list\n  if (/^\\d+\\.\\s/.test(line)) {\n    var olLines = [];\n    while (i \u0026lt; lines.length \u0026amp;\u0026amp; /^\\d+\\.\\s/.test(lines[i])) {\n      olLines.push(lines[i]);\n      i++;\n    }\n    out += '\u0026lt;ol\u0026gt;' + olLines.map(function(l) {\n      return '\u0026lt;li\u0026gt;' + inlineMarkdown(l.replace(/^\\d+\\.\\s/, '')) + '\u0026lt;/li\u0026gt;';\n    }).join('') + '\u0026lt;/ol\u0026gt;\\n';\n    continue;\n  }\n\n  // Paragraph / blank line\n  if (line.trim() === '') {\n    out += '\\n';\n    i++;\n    continue;\n  }\n\n  // Collect paragraph lines\n  var pLines = [];\n  while (i \u0026lt; lines.length \u0026amp;\u0026amp; lines[i].trim() !== ''\n    \u0026amp;\u0026amp; !/^#{1,6}\\s/.test(lines[i])\n    \u0026amp;\u0026amp; !/^(\\*{3,}|-{3,}|_{3,})\\s*$/.test(lines[i].trim())\n    \u0026amp;\u0026amp; !/^\u0026gt;\\s?/.test(lines[i])\n    \u0026amp;\u0026amp; !/^(\\s*)[-*+] /.test(lines[i])\n    \u0026amp;\u0026amp; !/^\\d+\\.\\s/.test(lines[i])\n    \u0026amp;\u0026amp; !/\\|/.test(lines[i])) {\n    pLines.push(lines[i]);\n    i++;\n  }\n  if (pLines.length) {\n    out += '\u0026lt;p\u0026gt;' + inlineMarkdown(pLines.join(' ')) + '\u0026lt;/p\u0026gt;\\n';\n  }\n}\n\n// Close any unclosed fence\nif (inFence \u0026amp;\u0026amp; fenceLines.length) {\n  out += '\u0026lt;pre\u0026gt;\u0026lt;code\u0026gt;' + fenceLines.map(escapeHtml).join('\\n') + '\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\\n';\n}\n\nreturn out;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e}\u003c/p\u003e","title":"Markdown Preview - Free Live Editor \u0026 HTML Preview Tool"},{"content":" Rows\u0026nbsp; Cols\u0026nbsp; Apply + Row + Col − Row − Col Markdown Output Copy Markdown Import CSV / TSV Paste CSV or TSV data below, or use the Paste from Clipboard button. The tool auto-detects the delimiter.\nImport Paste from Clipboard Related: Preview markdown \u0026rarr; Markdown Preview ","permalink":"https://productivity-works.com/tools/markdown-table-generator/","summary":"\u003cdiv id=\"mdtable-app\"\u003e\n\u003cstyle\u003e\n#mdtable-app *,\n#mdtable-app *::before,\n#mdtable-app *::after {\n  box-sizing: border-box;\n}\n#mdtable-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  color: #1e1e2e;\n  max-width: 900px;\n  margin: 0 auto;\n}\n#mdtable-app h2 {\n  font-size: 1.1rem;\n  font-weight: 700;\n  margin: 1.5rem 0 0.5rem;\n  color: #4f46e5;\n  letter-spacing: 0.01em;\n}\n#mdtable-app .toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem;\n  margin-bottom: 1rem;\n  align-items: center;\n}\n#mdtable-app .toolbar label {\n  font-size: 0.85rem;\n  color: #555;\n}\n#mdtable-app .toolbar input[type=\"number\"] {\n  width: 3.5rem;\n  padding: 0.3rem 0.4rem;\n  border: 1px solid #c7d2fe;\n  border-radius: 6px;\n  font-size: 0.9rem;\n  text-align: center;\n}\n#mdtable-app .btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 0.3rem;\n  padding: 0.4rem 0.85rem;\n  border: none;\n  border-radius: 7px;\n  font-size: 0.85rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s;\n}\n#mdtable-app .btn:active { transform: scale(0.97); }\n#mdtable-app .btn-primary {\n  background: #4f46e5;\n  color: #fff;\n}\n#mdtable-app .btn-primary:hover { background: #4338ca; }\n#mdtable-app .btn-secondary {\n  background: #e0e7ff;\n  color: #4f46e5;\n}\n#mdtable-app .btn-secondary:hover { background: #c7d2fe; }\n#mdtable-app .btn-danger {\n  background: #fee2e2;\n  color: #b91c1c;\n}\n#mdtable-app .btn-danger:hover { background: #fecaca; }\n#mdtable-app .btn-success {\n  background: #d1fae5;\n  color: #065f46;\n}\n#mdtable-app .btn-success:hover { background: #a7f3d0; }\n#mdtable-app .table-wrap {\n  overflow-x: auto;\n  border: 1px solid #e0e7ff;\n  border-radius: 10px;\n  margin-bottom: 1rem;\n}\n#mdtable-app table.editor-table {\n  border-collapse: collapse;\n  width: 100%;\n  min-width: 400px;\n}\n#mdtable-app table.editor-table th,\n#mdtable-app table.editor-table td {\n  border: 1px solid #c7d2fe;\n  padding: 0;\n}\n#mdtable-app table.editor-table th {\n  background: #eef2ff;\n}\n#mdtable-app .cell-input {\n  width: 100%;\n  min-width: 80px;\n  padding: 0.45rem 0.55rem;\n  border: none;\n  outline: none;\n  background: transparent;\n  font-size: 0.9rem;\n  font-family: inherit;\n  color: #1e1e2e;\n  resize: none;\n  overflow: hidden;\n  line-height: 1.4;\n}\n#mdtable-app .cell-input:focus {\n  background: #f5f3ff;\n}\n#mdtable-app .align-row th {\n  background: #f5f3ff;\n  padding: 4px 6px;\n  text-align: center;\n}\n#mdtable-app .align-select {\n  padding: 2px 4px;\n  border: 1px solid #c7d2fe;\n  border-radius: 5px;\n  font-size: 0.78rem;\n  background: #fff;\n  color: #4f46e5;\n  cursor: pointer;\n}\n#mdtable-app .col-ctrl-row th {\n  background: #f5f3ff;\n  padding: 4px 6px;\n  text-align: center;\n}\n#mdtable-app .output-area {\n  position: relative;\n}\n#mdtable-app textarea#md-output {\n  width: 100%;\n  min-height: 140px;\n  padding: 0.75rem 1rem;\n  border: 1px solid #c7d2fe;\n  border-radius: 10px;\n  font-family: \"Fira Mono\", \"Courier New\", monospace;\n  font-size: 0.88rem;\n  line-height: 1.6;\n  background: #f8f7ff;\n  color: #1e1e2e;\n  resize: vertical;\n  outline: none;\n}\n#mdtable-app .copy-btn-wrap {\n  display: flex;\n  justify-content: flex-end;\n  margin-top: 0.4rem;\n}\n#mdtable-app .import-area {\n  margin-top: 1rem;\n}\n#mdtable-app textarea#csv-input {\n  width: 100%;\n  min-height: 80px;\n  padding: 0.6rem 0.8rem;\n  border: 1px solid #c7d2fe;\n  border-radius: 8px;\n  font-family: \"Fira Mono\", \"Courier New\", monospace;\n  font-size: 0.85rem;\n  color: #333;\n  resize: vertical;\n  outline: none;\n}\n#mdtable-app .import-controls {\n  display: flex;\n  gap: 0.5rem;\n  margin-top: 0.4rem;\n  flex-wrap: wrap;\n  align-items: center;\n}\n#mdtable-app .status-msg {\n  font-size: 0.82rem;\n  color: #059669;\n  font-weight: 600;\n  min-height: 1.2em;\n}\n#mdtable-app .divider {\n  border: none;\n  border-top: 1px solid #e0e7ff;\n  margin: 1.5rem 0;\n}\n#mdtable-app .related-link {\n  margin-top: 1.8rem;\n  padding: 0.85rem 1.1rem;\n  background: #eef2ff;\n  border-left: 4px solid #4f46e5;\n  border-radius: 0 8px 8px 0;\n  font-size: 0.9rem;\n  color: #333;\n}\n#mdtable-app .related-link a {\n  color: #4f46e5;\n  font-weight: 600;\n  text-decoration: none;\n}\n#mdtable-app .related-link a:hover { text-decoration: underline; }\n\u003c/style\u003e\n\u003cdiv class=\"toolbar\"\u003e\n  \u003clabel\u003eRows\u0026nbsp;\u003cinput type=\"number\" id=\"row-count\" value=\"3\" min=\"1\" max=\"50\"\u003e\u003c/label\u003e\n  \u003clabel\u003eCols\u0026nbsp;\u003cinput type=\"number\" id=\"col-count\" value=\"3\" min=\"1\" max=\"20\"\u003e\u003c/label\u003e\n  \u003cbutton class=\"btn btn-primary\" onclick=\"mdtableApp.applyDimensions()\"\u003eApply\u003c/button\u003e\n  \u003cbutton class=\"btn btn-secondary\" onclick=\"mdtableApp.addRow()\"\u003e+ Row\u003c/button\u003e\n  \u003cbutton class=\"btn btn-secondary\" onclick=\"mdtableApp.addCol()\"\u003e+ Col\u003c/button\u003e\n  \u003cbutton class=\"btn btn-danger\" onclick=\"mdtableApp.removeRow()\"\u003e− Row\u003c/button\u003e\n  \u003cbutton class=\"btn btn-danger\" onclick=\"mdtableApp.removeCol()\"\u003e− Col\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"table-wrap\"\u003e\n  \u003ctable class=\"editor-table\" id=\"editor-table\"\u003e\n    \u003cthead id=\"editor-thead\"\u003e\u003c/thead\u003e\n    \u003ctbody id=\"editor-tbody\"\u003e\u003c/tbody\u003e\n  \u003c/table\u003e\n\u003c/div\u003e\n\u003ch2\u003eMarkdown Output\u003c/h2\u003e\n\u003cdiv class=\"output-area\"\u003e\n  \u003ctextarea id=\"md-output\" readonly\u003e\u003c/textarea\u003e\n  \u003cdiv class=\"copy-btn-wrap\"\u003e\n    \u003cbutton class=\"btn btn-success\" onclick=\"mdtableApp.copyOutput()\"\u003eCopy Markdown\u003c/button\u003e\n    \u003cspan class=\"status-msg\" id=\"copy-status\" style=\"margin-left:0.6rem;line-height:2.2;\"\u003e\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003chr class=\"divider\"\u003e\n\u003ch2\u003eImport CSV / TSV\u003c/h2\u003e\n\u003cp style=\"font-size:0.88rem;color:#555;margin:0 0 0.4rem;\"\u003ePaste CSV or TSV data below, or use the Paste from Clipboard button. The tool auto-detects the delimiter.\u003c/p\u003e","title":"Markdown Table Generator — Free Online Tool"},{"content":"Generate a clickable table of contents from any Markdown document. Paste your Markdown, configure the heading levels and list style, then copy the generated TOC right back into your document. Anchor links follow the GitHub-style slug format so they work on GitHub, GitLab, and most Markdown renderers.\nMin Heading Level H1 H2 H3 H4 Max Heading Level H2 H3 H4 H5 H6 List Style Bullet ( - ) Numbered ( 1. ) Indent 2 spaces 4 spaces Tab \u0026nbsp; Bold H1 entries \u0026nbsp; Add \"Table of Contents\" header \u0026#9654; Generate TOC Load Sample Clear \u0026#128203; Copy TOC Markdown Input Generated TOC TOC will appear here after generation... 0Headings found 0H1 0H2 0H3 0H4+ 0TOC lines Live Preview How to Use Paste your Markdown into the input box on the left Configure options — choose heading levels, list style, and indentation Click Generate TOC to extract all headings and build your table of contents Copy the TOC and paste it at the top of your Markdown document How Anchor Links Work This tool uses the GitHub-style slug algorithm to generate anchor links:\nText is lowercased Spaces become hyphens - Special characters (except hyphens) are removed Duplicate headings automatically get a numeric suffix: #installation, #installation-1, #installation-2 This format is compatible with GitHub, GitLab, Obsidian, VS Code Preview, and most Markdown renderers.\nRelated Tools Markdown Preview — Render Markdown in real time Markdown Link Checker — Validate all links in your Markdown HTML Table Generator — Build HTML tables visually ","permalink":"https://productivity-works.com/tools/markdown-toc-generator/","summary":"\u003cp\u003eGenerate a clickable table of contents from any Markdown document. Paste your Markdown, configure the heading levels and list style, then copy the generated TOC right back into your document. Anchor links follow the GitHub-style slug format so they work on GitHub, GitLab, and most Markdown renderers.\u003c/p\u003e\n\u003cdiv id=\"mt-app\"\u003e\n\u003cstyle\u003e\n  #mt-app {\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n    color: #1a1a2e;\n    max-width: 900px;\n    margin: 0 auto;\n    padding: 0;\n  }\n  #mt-app * { box-sizing: border-box; }\n\n  #mt-app .mt-grid {\n    display: grid;\n    grid-template-columns: 1fr 1fr;\n    gap: 16px;\n    margin-bottom: 16px;\n  }\n  @media (max-width: 640px) {\n    #mt-app .mt-grid { grid-template-columns: 1fr; }\n  }\n\n  #mt-app .mt-panel {\n    background: #fff;\n    border: 1px solid #e2e8f0;\n    border-radius: 10px;\n    overflow: hidden;\n  }\n  #mt-app .mt-panel-header {\n    background: #f8fafc;\n    border-bottom: 1px solid #e2e8f0;\n    padding: 10px 14px;\n    font-size: 13px;\n    font-weight: 600;\n    color: #475569;\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n  }\n  #mt-app textarea {\n    width: 100%;\n    border: none;\n    outline: none;\n    resize: vertical;\n    padding: 12px 14px;\n    font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n    font-size: 13px;\n    line-height: 1.6;\n    color: #1e293b;\n    background: #fff;\n    min-height: 260px;\n    display: block;\n  }\n  #mt-app .mt-output {\n    padding: 12px 14px;\n    font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n    font-size: 13px;\n    line-height: 1.6;\n    color: #1e293b;\n    min-height: 260px;\n    white-space: pre-wrap;\n    word-break: break-word;\n    background: #fff;\n  }\n  #mt-app .mt-output.placeholder {\n    color: #94a3b8;\n    font-style: italic;\n  }\n\n  #mt-app .mt-controls {\n    background: #fff;\n    border: 1px solid #e2e8f0;\n    border-radius: 10px;\n    padding: 14px 16px;\n    margin-bottom: 16px;\n    display: flex;\n    flex-wrap: wrap;\n    gap: 18px;\n    align-items: flex-end;\n  }\n  #mt-app .mt-control-group {\n    display: flex;\n    flex-direction: column;\n    gap: 5px;\n  }\n  #mt-app .mt-control-group label {\n    font-size: 12px;\n    font-weight: 600;\n    color: #64748b;\n    text-transform: uppercase;\n    letter-spacing: 0.04em;\n  }\n  #mt-app select,\n  #mt-app input[type=\"checkbox\"] {\n    cursor: pointer;\n  }\n  #mt-app select {\n    padding: 6px 10px;\n    border: 1px solid #cbd5e1;\n    border-radius: 6px;\n    font-size: 13px;\n    color: #1e293b;\n    background: #f8fafc;\n    outline: none;\n    min-width: 90px;\n  }\n  #mt-app select:focus {\n    border-color: #6366f1;\n    box-shadow: 0 0 0 2px rgba(99,102,241,0.15);\n  }\n  #mt-app .mt-checkbox-row {\n    display: flex;\n    align-items: center;\n    gap: 7px;\n    font-size: 13px;\n    color: #334155;\n    padding-top: 4px;\n  }\n  #mt-app input[type=\"checkbox\"] {\n    width: 15px;\n    height: 15px;\n    accent-color: #6366f1;\n  }\n\n  #mt-app .mt-btn-row {\n    display: flex;\n    gap: 10px;\n    flex-wrap: wrap;\n    margin-bottom: 16px;\n  }\n  #mt-app .mt-btn {\n    display: inline-flex;\n    align-items: center;\n    gap: 6px;\n    padding: 9px 18px;\n    border-radius: 7px;\n    font-size: 13px;\n    font-weight: 600;\n    cursor: pointer;\n    border: none;\n    transition: background 0.15s, transform 0.1s;\n  }\n  #mt-app .mt-btn:active { transform: scale(0.97); }\n  #mt-app .mt-btn-primary {\n    background: #6366f1;\n    color: #fff;\n  }\n  #mt-app .mt-btn-primary:hover { background: #4f46e5; }\n  #mt-app .mt-btn-secondary {\n    background: #f1f5f9;\n    color: #334155;\n    border: 1px solid #e2e8f0;\n  }\n  #mt-app .mt-btn-secondary:hover { background: #e2e8f0; }\n  #mt-app .mt-btn-copy {\n    background: #10b981;\n    color: #fff;\n  }\n  #mt-app .mt-btn-copy:hover { background: #059669; }\n  #mt-app .mt-btn-copy.copied { background: #475569; }\n\n  #mt-app .mt-stats {\n    font-size: 12px;\n    color: #64748b;\n    background: #f8fafc;\n    border: 1px solid #e2e8f0;\n    border-radius: 8px;\n    padding: 10px 14px;\n    margin-bottom: 16px;\n    display: flex;\n    gap: 20px;\n    flex-wrap: wrap;\n  }\n  #mt-app .mt-stat { display: flex; flex-direction: column; gap: 2px; }\n  #mt-app .mt-stat strong { font-size: 18px; color: #1e293b; font-weight: 700; }\n\n  #mt-app .mt-preview {\n    background: #fff;\n    border: 1px solid #e2e8f0;\n    border-radius: 10px;\n    overflow: hidden;\n    margin-bottom: 16px;\n  }\n  #mt-app .mt-preview-body {\n    padding: 14px 18px;\n    font-size: 14px;\n    line-height: 1.7;\n    color: #1e293b;\n  }\n  #mt-app .mt-preview-body ul,\n  #mt-app .mt-preview-body ol {\n    margin: 0;\n    padding-left: 20px;\n  }\n  #mt-app .mt-preview-body li { margin: 2px 0; }\n  #mt-app .mt-preview-body a {\n    color: #6366f1;\n    text-decoration: none;\n  }\n  #mt-app .mt-preview-body a:hover { text-decoration: underline; }\n  #mt-app .mt-empty-state {\n    padding: 32px;\n    text-align: center;\n    color: #94a3b8;\n    font-size: 13px;\n  }\n  #mt-app .mt-tabs {\n    display: flex;\n    border-bottom: 1px solid #e2e8f0;\n    padding: 0 14px;\n    background: #f8fafc;\n    gap: 2px;\n  }\n  #mt-app .mt-tab {\n    padding: 8px 14px;\n    font-size: 12px;\n    font-weight: 600;\n    color: #64748b;\n    cursor: pointer;\n    border-bottom: 2px solid transparent;\n    transition: color 0.15s, border-color 0.15s;\n    margin-bottom: -1px;\n    background: none;\n    border-top: none;\n    border-left: none;\n    border-right: none;\n    outline: none;\n  }\n  #mt-app .mt-tab.active {\n    color: #6366f1;\n    border-bottom-color: #6366f1;\n  }\n  #mt-app .mt-tab-content { display: none; }\n  #mt-app .mt-tab-content.active { display: block; }\n\u003c/style\u003e\n\u003cdiv class=\"mt-controls\"\u003e\n  \u003cdiv class=\"mt-control-group\"\u003e\n    \u003clabel\u003eMin Heading Level\u003c/label\u003e\n    \u003cselect id=\"mt-min-level\"\u003e\n      \u003coption value=\"1\"\u003eH1\u003c/option\u003e\n      \u003coption value=\"2\" selected\u003eH2\u003c/option\u003e\n      \u003coption value=\"3\"\u003eH3\u003c/option\u003e\n      \u003coption value=\"4\"\u003eH4\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"mt-control-group\"\u003e\n    \u003clabel\u003eMax Heading Level\u003c/label\u003e\n    \u003cselect id=\"mt-max-level\"\u003e\n      \u003coption value=\"2\"\u003eH2\u003c/option\u003e\n      \u003coption value=\"3\" selected\u003eH3\u003c/option\u003e\n      \u003coption value=\"4\"\u003eH4\u003c/option\u003e\n      \u003coption value=\"5\"\u003eH5\u003c/option\u003e\n      \u003coption value=\"6\"\u003eH6\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"mt-control-group\"\u003e\n    \u003clabel\u003eList Style\u003c/label\u003e\n    \u003cselect id=\"mt-list-style\"\u003e\n      \u003coption value=\"bullet\" selected\u003eBullet ( - )\u003c/option\u003e\n      \u003coption value=\"numbered\"\u003eNumbered ( 1. )\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"mt-control-group\"\u003e\n    \u003clabel\u003eIndent\u003c/label\u003e\n    \u003cselect id=\"mt-indent\"\u003e\n      \u003coption value=\"2\" selected\u003e2 spaces\u003c/option\u003e\n      \u003coption value=\"4\"\u003e4 spaces\u003c/option\u003e\n      \u003coption value=\"tab\"\u003eTab\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"mt-control-group\"\u003e\n    \u003clabel\u003e\u0026nbsp;\u003c/label\u003e\n    \u003cdiv class=\"mt-checkbox-row\"\u003e\n      \u003cinput type=\"checkbox\" id=\"mt-bold-h1\"\u003e\n      \u003cspan\u003eBold H1 entries\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"mt-control-group\"\u003e\n    \u003clabel\u003e\u0026nbsp;\u003c/label\u003e\n    \u003cdiv class=\"mt-checkbox-row\"\u003e\n      \u003cinput type=\"checkbox\" id=\"mt-add-title\" checked\u003e\n      \u003cspan\u003eAdd \"Table of Contents\" header\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"mt-btn-row\"\u003e\n  \u003cbutton class=\"mt-btn mt-btn-primary\" onclick=\"mtGenerate()\"\u003e\u0026#9654; Generate TOC\u003c/button\u003e\n  \u003cbutton class=\"mt-btn mt-btn-secondary\" onclick=\"mtLoadSample()\"\u003eLoad Sample\u003c/button\u003e\n  \u003cbutton class=\"mt-btn mt-btn-secondary\" onclick=\"mtClear()\"\u003eClear\u003c/button\u003e\n  \u003cbutton class=\"mt-btn mt-btn-copy\" id=\"mt-copy-btn\" onclick=\"mtCopy()\"\u003e\u0026#128203; Copy TOC\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"mt-grid\"\u003e\n  \u003cdiv class=\"mt-panel\"\u003e\n    \u003cdiv class=\"mt-panel-header\"\u003e\n      \u003cspan\u003eMarkdown Input\u003c/span\u003e\n      \u003cspan id=\"mt-line-count\" style=\"font-weight:400;color:#94a3b8;\"\u003e\u003c/span\u003e\n    \u003c/div\u003e\n    \u003ctextarea id=\"mt-input\" placeholder=\"Paste your Markdown here...\u0026#10;\u0026#10;# Introduction\u0026#10;## Getting Started\u0026#10;### Installation\u0026#10;## Usage\u0026#10;### Basic Example\u0026#10;## API Reference\" oninput=\"mtOnInput()\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"mt-panel\"\u003e\n    \u003cdiv class=\"mt-panel-header\"\u003e\n      \u003cspan\u003eGenerated TOC\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"mt-output placeholder\" id=\"mt-output\"\u003eTOC will appear here after generation...\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"mt-stats\" id=\"mt-stats\" style=\"display:none;\"\u003e\n  \u003cdiv class=\"mt-stat\"\u003e\u003cstrong id=\"stat-headings\"\u003e0\u003c/strong\u003e\u003cspan\u003eHeadings found\u003c/span\u003e\u003c/div\u003e\n  \u003cdiv class=\"mt-stat\"\u003e\u003cstrong id=\"stat-h1\"\u003e0\u003c/strong\u003e\u003cspan\u003eH1\u003c/span\u003e\u003c/div\u003e\n  \u003cdiv class=\"mt-stat\"\u003e\u003cstrong id=\"stat-h2\"\u003e0\u003c/strong\u003e\u003cspan\u003eH2\u003c/span\u003e\u003c/div\u003e\n  \u003cdiv class=\"mt-stat\"\u003e\u003cstrong id=\"stat-h3\"\u003e0\u003c/strong\u003e\u003cspan\u003eH3\u003c/span\u003e\u003c/div\u003e\n  \u003cdiv class=\"mt-stat\"\u003e\u003cstrong id=\"stat-h4plus\"\u003e0\u003c/strong\u003e\u003cspan\u003eH4+\u003c/span\u003e\u003c/div\u003e\n  \u003cdiv class=\"mt-stat\"\u003e\u003cstrong id=\"stat-toc-lines\"\u003e0\u003c/strong\u003e\u003cspan\u003eTOC lines\u003c/span\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"mt-preview\" id=\"mt-preview\" style=\"display:none;\"\u003e\n  \u003cdiv class=\"mt-panel-header\" style=\"background:#f8fafc;border-bottom:1px solid #e2e8f0;padding:10px 14px;font-size:13px;font-weight:600;color:#475569;\"\u003e\n    Live Preview\n  \u003c/div\u003e\n  \u003cdiv class=\"mt-preview-body\" id=\"mt-preview-body\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  // GitHub-style slug generation\n  function slugify(text) {\n    return text\n      .toLowerCase()\n      .replace(/[^\\w\\s\\-]/g, '')   // remove non-word chars except hyphens\n      .replace(/\\s+/g, '-')         // spaces to hyphens\n      .replace(/-+/g, '-')          // collapse multiple hyphens\n      .replace(/^-|-$/g, '');       // trim hyphens\n  }\n\n  function parseHeadings(markdown) {\n    const lines = markdown.split('\\n');\n    const headings = [];\n    let inFencedCode = false;\n    let fenceChar = '';\n\n    for (let i = 0; i \u003c lines.length; i++) {\n      const line = lines[i];\n\n      // Detect fenced code blocks\n      const fenceMatch = line.match(/^(`{3,}|~{3,})/);\n      if (fenceMatch) {\n        if (!inFencedCode) {\n          inFencedCode = true;\n          fenceChar = fenceMatch[1][0];\n        } else if (fenceChar === fenceMatch[1][0]) {\n          inFencedCode = false;\n          fenceChar = '';\n        }\n        continue;\n      }\n      if (inFencedCode) continue;\n\n      // Match ATX headings (# style)\n      const headingMatch = line.match(/^(#{1,6})\\s+(.+?)(?:\\s+#+\\s*)?$/);\n      if (headingMatch) {\n        const level = headingMatch[1].length;\n        const text = headingMatch[2].trim();\n        // Strip inline code, bold, italic from display text for slug\n        const plainText = text\n          .replace(/`[^`]+`/g, m =\u003e m.slice(1,-1))\n          .replace(/\\*\\*([^*]+)\\*\\*/g, '$1')\n          .replace(/__([^_]+)__/g, '$1')\n          .replace(/\\*([^*]+)\\*/g, '$1')\n          .replace(/_([^_]+)_/g, '$1')\n          .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1');\n        headings.push({ level, text, plainText });\n      }\n    }\n    return headings;\n  }\n\n  function buildToc(headings, opts) {\n    const { minLevel, maxLevel, listStyle, indent, boldH1, addTitle } = opts;\n\n    // Filter by level\n    const filtered = headings.filter(h =\u003e h.level \u003e= minLevel \u0026\u0026 h.level \u003c= maxLevel);\n    if (filtered.length === 0) return { toc: '', lines: 0 };\n\n    // Track duplicates for GitHub-style suffix\n    const slugCount = {};\n\n    // Numbered list counters per level\n    const counters = {};\n\n    const tocLines = [];\n    if (addTitle) tocLines.push('## Table of Contents\\n');\n\n    for (const h of filtered) {\n      // Build slug\n      let slug = slugify(h.plainText);\n      if (slugCount[slug] === undefined) {\n        slugCount[slug] = 0;\n      } else {\n        slugCount[slug]++;\n        slug = slug + '-' + slugCount[slug];\n      }\n\n      // Indentation: relative to minLevel\n      const depth = h.level - minLevel;\n      const indentStr = indent === 'tab'\n        ? '\\t'.repeat(depth)\n        : ' '.repeat(Number(indent) * depth);\n\n      let prefix;\n      if (listStyle === 'numbered') {\n        // Reset deeper levels when a shallower heading appears\n        for (const key of Object.keys(counters)) {\n          if (Number(key) \u003e h.level) delete counters[key];\n        }\n        counters[h.level] = (counters[h.level] || 0) + 1;\n        prefix = counters[h.level] + '.';\n      } else {\n        prefix = '-';\n      }\n\n      let displayText = h.text;\n      if (boldH1 \u0026\u0026 h.level === 1) displayText = '**' + displayText + '**';\n\n      tocLines.push(indentStr + prefix + ' [' + displayText + '](#' + slug + ')');\n    }\n\n    const toc = tocLines.join('\\n');\n    return { toc, lines: filtered.length };\n  }\n\n  function updateStats(headings, tocLineCount) {\n    const el = id =\u003e document.getElementById(id);\n    const counts = { 1:0, 2:0, 3:0, '4+':0 };\n    for (const h of headings) {\n      if (h.level \u003c= 3) counts[h.level]++;\n      else counts['4+']++;\n    }\n    el('stat-headings').textContent = headings.length;\n    el('stat-h1').textContent = counts[1];\n    el('stat-h2').textContent = counts[2];\n    el('stat-h3').textContent = counts[3];\n    el('stat-h4plus').textContent = counts['4+'];\n    el('stat-toc-lines').textContent = tocLineCount;\n    document.getElementById('mt-stats').style.display = 'flex';\n  }\n\n  function renderPreview(tocMd) {\n    // Simple Markdown list renderer for preview\n    const lines = tocMd.split('\\n');\n    let html = '';\n    let listStack = []; // stack of { type, depth }\n\n    // Check if numbered or bullet\n    const isNumbered = lines.some(l =\u003e /^\\s*\\d+\\./.test(l));\n\n    const getDepth = line =\u003e {\n      const m = line.match(/^(\\s*)/);\n      return m ? m[1].replace(/\\t/g, '  ').length : 0;\n    };\n\n    // Flush open lists\n    const flushTo = depth =\u003e {\n      while (listStack.length \u003e 0 \u0026\u0026 listStack[listStack.length-1].depth \u003e depth) {\n        const t = listStack.pop();\n        html += t.type === 'ol' ? '\u003c/ol\u003e' : '\u003c/ul\u003e';\n      }\n    };\n\n    for (const line of lines) {\n      if (!line.trim()) continue;\n      // Section header\n      if (line.startsWith('## ')) {\n        flushTo(-1);\n        html += '\u003cstrong\u003e' + esc(line.replace(/^## /, '')) + '\u003c/strong\u003e';\n        continue;\n      }\n      const itemMatch = line.match(/^(\\s*)(?:\\d+\\.|-)\\s+\\[(.+?)\\]\\(#([^)]+)\\)(.*)$/);\n      if (!itemMatch) continue;\n      const [, ws, text, anchor] = itemMatch;\n      const depth = ws.replace(/\\t/g, '  ').length;\n      const type = isNumbered \u0026\u0026 /^\\s*\\d+\\./.test(line) ? 'ol' : 'ul';\n\n      flushTo(depth - 1);\n\n      if (listStack.length === 0 || listStack[listStack.length-1].depth \u003c depth) {\n        html += type === 'ol' ? '\u003col\u003e' : '\u003cul\u003e';\n        listStack.push({ type, depth });\n      }\n\n      // Render bold if wrapped in **\n      let displayText = esc(text).replace(/\\*\\*(.+?)\\*\\*/g, '\u003cstrong\u003e$1\u003c/strong\u003e');\n      html += '\u003cli\u003e\u003ca href=\"#' + esc(anchor) + '\"\u003e' + displayText + '\u003c/a\u003e\u003c/li\u003e';\n    }\n    flushTo(-1);\n\n    document.getElementById('mt-preview-body').innerHTML = html || '\u003cem style=\"color:#94a3b8\"\u003eNothing to preview.\u003c/em\u003e';\n    document.getElementById('mt-preview').style.display = 'block';\n  }\n\n  function esc(str) {\n    return str.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;').replace(/\"/g,'\u0026quot;');\n  }\n\n  function getOpts() {\n    return {\n      minLevel: Number(document.getElementById('mt-min-level').value),\n      maxLevel: Number(document.getElementById('mt-max-level').value),\n      listStyle: document.getElementById('mt-list-style').value,\n      indent: document.getElementById('mt-indent').value,\n      boldH1: document.getElementById('mt-bold-h1').checked,\n      addTitle: document.getElementById('mt-add-title').checked,\n    };\n  }\n\n  window.mtGenerate = function() {\n    const input = document.getElementById('mt-input').value;\n    if (!input.trim()) {\n      setOutput('Please paste some Markdown content first.', true);\n      return;\n    }\n    const opts = getOpts();\n    const headings = parseHeadings(input);\n\n    if (headings.length === 0) {\n      setOutput('No headings found in the input. Make sure you have lines starting with #.', true);\n      document.getElementById('mt-stats').style.display = 'none';\n      document.getElementById('mt-preview').style.display = 'none';\n      return;\n    }\n\n    const { toc, lines } = buildToc(headings, opts);\n    if (!toc) {\n      setOutput('No headings found within the selected level range (H' + opts.minLevel + '–H' + opts.maxLevel + ').', true);\n      return;\n    }\n\n    setOutput(toc, false);\n    updateStats(headings, lines);\n    renderPreview(toc);\n  };\n\n  function setOutput(text, isPlaceholder) {\n    const el = document.getElementById('mt-output');\n    el.textContent = text;\n    el.classList.toggle('placeholder', isPlaceholder);\n  }\n\n  window.mtOnInput = function() {\n    const input = document.getElementById('mt-input').value;\n    const lines = input ? input.split('\\n').length : 0;\n    document.getElementById('mt-line-count').textContent = lines ? lines + ' lines' : '';\n  };\n\n  window.mtClear = function() {\n    document.getElementById('mt-input').value = '';\n    setOutput('TOC will appear here after generation...', true);\n    document.getElementById('mt-line-count').textContent = '';\n    document.getElementById('mt-stats').style.display = 'none';\n    document.getElementById('mt-preview').style.display = 'none';\n  };\n\n  window.mtLoadSample = function() {\n    document.getElementById('mt-input').value = `# My Project Documentation\n\n## Introduction\n\nWelcome to the project documentation.\n\n### What is This?\n\nAn overview of the tool.\n\n### Why Use It?\n\nKey benefits and use cases.\n\n## Getting Started\n\n### Prerequisites\n\n- Node.js 18+\n- npm or yarn\n\n### Installation\n\nStep-by-step installation guide.\n\n### Configuration\n\nHow to configure the tool.\n\n## Usage\n\n### Basic Example\n\nA simple usage example.\n\n### Advanced Options\n\nAdvanced configuration options.\n\n#### Option A\n\nDetails about option A.\n\n#### Option B\n\nDetails about option B.\n\n## API Reference\n\n### Methods\n\nList of available methods.\n\n### Events\n\nList of available events.\n\n## Contributing\n\nHow to contribute to this project.\n\n## License\n\nMIT License.`;\n    mtOnInput();\n    mtGenerate();\n  };\n\n  window.mtCopy = function() {\n    const output = document.getElementById('mt-output');\n    const text = output.textContent;\n    if (!text || output.classList.contains('placeholder')) return;\n    navigator.clipboard.writeText(text).then(() =\u003e {\n      const btn = document.getElementById('mt-copy-btn');\n      btn.textContent = 'Copied!';\n      btn.classList.add('copied');\n      setTimeout(() =\u003e {\n        btn.innerHTML = '\u0026#128203; Copy TOC';\n        btn.classList.remove('copied');\n      }, 2000);\n    });\n  };\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003ch2 id=\"how-to-use\"\u003eHow to Use\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003ePaste your Markdown\u003c/strong\u003e into the input box on the left\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eConfigure options\u003c/strong\u003e — choose heading levels, list style, and indentation\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eClick Generate TOC\u003c/strong\u003e to extract all headings and build your table of contents\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eCopy the TOC\u003c/strong\u003e and paste it at the top of your Markdown document\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"how-anchor-links-work\"\u003eHow Anchor Links Work\u003c/h2\u003e\n\u003cp\u003eThis tool uses the \u003cstrong\u003eGitHub-style slug algorithm\u003c/strong\u003e to generate anchor links:\u003c/p\u003e","title":"Markdown TOC Generator"},{"content":" Matrix Inputs Matrix A Size 2 × 2 3 × 3 4 × 4 5 × 5 Random Fill Clear A Matrix B Size 2 × 2 3 × 3 4 × 4 5 × 5 Random Fill Clear B Scalar (k): Used for scalar multiplication (k × A) Operations A + B A − B A × B det(A) A⁻¹ Aᵀ k × A Calculate Result Select an operation and click Calculate. Step-by-Step Related Tools Scientific Calculator Percentage Calculator How to Use the Matrix Calculator Set sizes — Choose grid size (2×2 to 5×5) independently for Matrix A and Matrix B. Enter values — Click each cell and type a number. Use Random Fill to populate with test values. Select operation — Click the operation button (A+B, A−B, A×B, det(A), A⁻¹, Aᵀ, or k×A). Calculate — Press the Calculate button to see the result and step-by-step breakdown. Supported Operations Operation Description Requires A + B Element-wise addition Same dimensions A − B Element-wise subtraction Same dimensions A × B Matrix multiplication A cols = B rows det(A) Determinant with cofactor steps Square matrix A A⁻¹ Inverse via Gauss-Jordan Square, non-singular A Aᵀ Transpose of A Any matrix A k × A Scalar multiplication Any matrix A What is a Matrix? A matrix is a rectangular array of numbers arranged in rows and columns. Matrices are fundamental to linear algebra, computer graphics, machine learning, physics simulations, and engineering.\nSquare matrix: same number of rows and columns (required for determinant/inverse) Determinant: a scalar value encoding geometric scaling; zero means the matrix is singular Inverse: A⁻¹ such that A × A⁻¹ = I (identity matrix) Transpose: rows become columns and vice versa ","permalink":"https://productivity-works.com/tools/matrix-calculator/","summary":"\u003cdiv id=\"mx-app\"\u003e\n\u003cstyle\u003e\n#mx-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#mx-app h2 {\n  font-size: 1.25rem;\n  font-weight: 700;\n  margin: 0 0 0.75rem 0;\n  color: #1a1a2e;\n}\n#mx-app .mx-card {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 1.25rem;\n  margin-bottom: 1rem;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.06);\n}\n#mx-app .mx-row {\n  display: flex;\n  gap: 1rem;\n  flex-wrap: wrap;\n}\n#mx-app .mx-col {\n  flex: 1;\n  min-width: 200px;\n}\n#mx-app .mx-label {\n  font-size: 0.8rem;\n  font-weight: 600;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 0.4rem;\n  display: block;\n}\n#mx-app select {\n  width: 100%;\n  padding: 0.45rem 0.6rem;\n  border: 1px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 0.9rem;\n  background: #f8fafc;\n  color: #1a1a2e;\n  cursor: pointer;\n  outline: none;\n  transition: border-color 0.2s;\n}\n#mx-app select:focus {\n  border-color: #6366f1;\n}\n#mx-app .mx-grid-wrap {\n  overflow-x: auto;\n}\n#mx-app .mx-grid {\n  display: inline-grid;\n  gap: 4px;\n  margin-top: 0.5rem;\n  padding: 0.5rem;\n  background: #f1f5f9;\n  border-radius: 8px;\n  border: 2px solid #e2e8f0;\n}\n#mx-app .mx-grid input {\n  width: 56px;\n  height: 44px;\n  text-align: center;\n  border: 1px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 0.95rem;\n  font-weight: 600;\n  color: #1a1a2e;\n  background: #fff;\n  outline: none;\n  transition: border-color 0.2s, box-shadow 0.2s;\n  -moz-appearance: textfield;\n}\n#mx-app .mx-grid input::-webkit-outer-spin-button,\n#mx-app .mx-grid input::-webkit-inner-spin-button {\n  -webkit-appearance: none;\n}\n#mx-app .mx-grid input:focus {\n  border-color: #6366f1;\n  box-shadow: 0 0 0 3px rgba(99,102,241,0.12);\n}\n#mx-app .mx-scalar-row {\n  display: flex;\n  align-items: center;\n  gap: 0.75rem;\n  margin-top: 0.75rem;\n  flex-wrap: wrap;\n}\n#mx-app .mx-scalar-row label {\n  font-size: 0.875rem;\n  font-weight: 600;\n  color: #475569;\n}\n#mx-app .mx-scalar-row input {\n  width: 80px;\n  padding: 0.4rem 0.5rem;\n  border: 1px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 0.95rem;\n  text-align: center;\n  outline: none;\n  -moz-appearance: textfield;\n}\n#mx-app .mx-scalar-row input::-webkit-outer-spin-button,\n#mx-app .mx-scalar-row input::-webkit-inner-spin-button {\n  -webkit-appearance: none;\n}\n#mx-app .mx-scalar-row input:focus {\n  border-color: #6366f1;\n  box-shadow: 0 0 0 3px rgba(99,102,241,0.12);\n}\n#mx-app .mx-btns {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem;\n  margin-top: 0.75rem;\n}\n#mx-app .mx-btn {\n  padding: 0.5rem 1rem;\n  border: none;\n  border-radius: 8px;\n  font-size: 0.875rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.18s, transform 0.12s, box-shadow 0.18s;\n  outline: none;\n}\n#mx-app .mx-btn:active {\n  transform: scale(0.97);\n}\n#mx-app .mx-btn-primary {\n  background: #6366f1;\n  color: #fff;\n  box-shadow: 0 2px 6px rgba(99,102,241,0.25);\n}\n#mx-app .mx-btn-primary:hover {\n  background: #4f46e5;\n  box-shadow: 0 4px 12px rgba(99,102,241,0.35);\n}\n#mx-app .mx-btn-secondary {\n  background: #f1f5f9;\n  color: #475569;\n  border: 1px solid #e2e8f0;\n}\n#mx-app .mx-btn-secondary:hover {\n  background: #e2e8f0;\n}\n#mx-app .mx-btn-danger {\n  background: #fef2f2;\n  color: #dc2626;\n  border: 1px solid #fecaca;\n}\n#mx-app .mx-btn-danger:hover {\n  background: #fee2e2;\n}\n#mx-app .mx-ops {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem;\n  margin-bottom: 0.75rem;\n}\n#mx-app .mx-op-btn {\n  padding: 0.5rem 0.9rem;\n  border: 2px solid #e2e8f0;\n  border-radius: 8px;\n  font-size: 0.875rem;\n  font-weight: 700;\n  cursor: pointer;\n  background: #fff;\n  color: #475569;\n  transition: all 0.18s;\n}\n#mx-app .mx-op-btn:hover {\n  border-color: #6366f1;\n  color: #6366f1;\n  background: #f5f3ff;\n}\n#mx-app .mx-op-btn.active {\n  background: #6366f1;\n  color: #fff;\n  border-color: #6366f1;\n  box-shadow: 0 2px 8px rgba(99,102,241,0.3);\n}\n#mx-app .mx-result-area {\n  background: #0f172a;\n  color: #e2e8f0;\n  border-radius: 10px;\n  padding: 1.25rem;\n  min-height: 80px;\n  font-family: \"JetBrains Mono\", \"Fira Code\", \"Courier New\", monospace;\n  font-size: 0.92rem;\n  line-height: 1.8;\n  white-space: pre;\n  overflow-x: auto;\n}\n#mx-app .mx-result-area.error {\n  background: #1c0a0a;\n  color: #f87171;\n}\n#mx-app .mx-result-area.success {\n  background: #0a1628;\n  color: #93c5fd;\n}\n#mx-app .mx-steps {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 1rem 1.25rem;\n  font-family: \"JetBrains Mono\", \"Fira Code\", \"Courier New\", monospace;\n  font-size: 0.82rem;\n  line-height: 1.8;\n  color: #475569;\n  white-space: pre-wrap;\n  overflow-x: auto;\n  margin-top: 0.75rem;\n}\n#mx-app .mx-badge {\n  display: inline-block;\n  font-size: 0.7rem;\n  font-weight: 700;\n  padding: 0.15rem 0.45rem;\n  border-radius: 999px;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n#mx-app .mx-badge-a { background: #dbeafe; color: #1d4ed8; }\n#mx-app .mx-badge-b { background: #dcfce7; color: #166534; }\n#mx-app .mx-info {\n  font-size: 0.82rem;\n  color: #94a3b8;\n  margin-top: 0.4rem;\n}\n#mx-app .mx-matrix-label {\n  font-size: 1.1rem;\n  font-weight: 800;\n  color: #6366f1;\n  margin-bottom: 0.25rem;\n}\n#mx-app .mx-related {\n  margin-top: 2rem;\n  padding-top: 1.25rem;\n  border-top: 1px solid #e2e8f0;\n}\n#mx-app .mx-related h3 {\n  font-size: 0.9rem;\n  font-weight: 700;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 0.6rem;\n}\n#mx-app .mx-related-links {\n  display: flex;\n  gap: 0.75rem;\n  flex-wrap: wrap;\n}\n#mx-app .mx-related-links a {\n  color: #6366f1;\n  text-decoration: none;\n  font-weight: 600;\n  font-size: 0.9rem;\n  padding: 0.35rem 0.75rem;\n  border: 1px solid #c7d2fe;\n  border-radius: 8px;\n  transition: all 0.18s;\n}\n#mx-app .mx-related-links a:hover {\n  background: #6366f1;\n  color: #fff;\n  border-color: #6366f1;\n}\n\u003c/style\u003e\n\u003cdiv class=\"mx-card\"\u003e\n  \u003ch2\u003eMatrix Inputs\u003c/h2\u003e\n  \u003cdiv class=\"mx-row\"\u003e\n    \u003cdiv class=\"mx-col\"\u003e\n      \u003cdiv class=\"mx-matrix-label\"\u003eMatrix A\u003c/div\u003e\n      \u003clabel class=\"mx-label\"\u003eSize\u003c/label\u003e\n      \u003cselect id=\"mx-size-a\"\u003e\n        \u003coption value=\"2\"\u003e2 × 2\u003c/option\u003e\n        \u003coption value=\"3\" selected\u003e3 × 3\u003c/option\u003e\n        \u003coption value=\"4\"\u003e4 × 4\u003c/option\u003e\n        \u003coption value=\"5\"\u003e5 × 5\u003c/option\u003e\n      \u003c/select\u003e\n      \u003cdiv class=\"mx-grid-wrap\"\u003e\n        \u003cdiv class=\"mx-grid\" id=\"mx-grid-a\"\u003e\u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"mx-btns\"\u003e\n        \u003cbutton class=\"mx-btn mx-btn-secondary\" id=\"mx-rand-a\"\u003eRandom Fill\u003c/button\u003e\n        \u003cbutton class=\"mx-btn mx-btn-danger\" id=\"mx-clear-a\"\u003eClear A\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"mx-col\"\u003e\n      \u003cdiv class=\"mx-matrix-label\"\u003eMatrix B\u003c/div\u003e\n      \u003clabel class=\"mx-label\"\u003eSize\u003c/label\u003e\n      \u003cselect id=\"mx-size-b\"\u003e\n        \u003coption value=\"2\"\u003e2 × 2\u003c/option\u003e\n        \u003coption value=\"3\" selected\u003e3 × 3\u003c/option\u003e\n        \u003coption value=\"4\"\u003e4 × 4\u003c/option\u003e\n        \u003coption value=\"5\"\u003e5 × 5\u003c/option\u003e\n      \u003c/select\u003e\n      \u003cdiv class=\"mx-grid-wrap\"\u003e\n        \u003cdiv class=\"mx-grid\" id=\"mx-grid-b\"\u003e\u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"mx-btns\"\u003e\n        \u003cbutton class=\"mx-btn mx-btn-secondary\" id=\"mx-rand-b\"\u003eRandom Fill\u003c/button\u003e\n        \u003cbutton class=\"mx-btn mx-btn-danger\" id=\"mx-clear-b\"\u003eClear B\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"mx-scalar-row\"\u003e\n    \u003clabel for=\"mx-scalar\"\u003eScalar (k):\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"mx-scalar\" value=\"2\" step=\"any\"\u003e\n    \u003cspan class=\"mx-info\"\u003eUsed for scalar multiplication (k × A)\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"mx-card\"\u003e\n  \u003ch2\u003eOperations\u003c/h2\u003e\n  \u003cdiv class=\"mx-ops\"\u003e\n    \u003cbutton class=\"mx-op-btn active\" data-op=\"add\"\u003eA + B\u003c/button\u003e\n    \u003cbutton class=\"mx-op-btn\" data-op=\"sub\"\u003eA − B\u003c/button\u003e\n    \u003cbutton class=\"mx-op-btn\" data-op=\"mul\"\u003eA × B\u003c/button\u003e\n    \u003cbutton class=\"mx-op-btn\" data-op=\"det\"\u003edet(A)\u003c/button\u003e\n    \u003cbutton class=\"mx-op-btn\" data-op=\"inv\"\u003eA⁻¹\u003c/button\u003e\n    \u003cbutton class=\"mx-op-btn\" data-op=\"tra\"\u003eAᵀ\u003c/button\u003e\n    \u003cbutton class=\"mx-op-btn\" data-op=\"scalar\"\u003ek × A\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"mx-btns\"\u003e\n    \u003cbutton class=\"mx-btn mx-btn-primary\" id=\"mx-calc\"\u003eCalculate\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"mx-card\"\u003e\n  \u003ch2\u003eResult\u003c/h2\u003e\n  \u003cdiv class=\"mx-result-area\" id=\"mx-result\"\u003eSelect an operation and click Calculate.\u003c/div\u003e\n  \u003cdiv id=\"mx-steps-wrap\" style=\"display:none\"\u003e\n    \u003ch2 style=\"margin-top:1rem\"\u003eStep-by-Step\u003c/h2\u003e\n    \u003cdiv class=\"mx-steps\" id=\"mx-steps\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"mx-related\"\u003e\n  \u003ch3\u003eRelated Tools\u003c/h3\u003e\n  \u003cdiv class=\"mx-related-links\"\u003e\n    \u003ca href=\"/tools/scientific-calculator/\"\u003eScientific Calculator\u003c/a\u003e\n    \u003ca href=\"/tools/percentage-calculator/\"\u003ePercentage Calculator\u003c/a\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  'use strict';\n\n  // --- State ---\n  var sizeA = 3, sizeB = 3;\n  var currentOp = 'add';\n\n  // --- Grid rendering ---\n  function buildGrid(containerId, size) {\n    var el = document.getElementById(containerId);\n    el.style.gridTemplateColumns = 'repeat(' + size + ', 56px)';\n    el.innerHTML = '';\n    for (var r = 0; r \u003c size; r++) {\n      for (var c = 0; c \u003c size; c++) {\n        var inp = document.createElement('input');\n        inp.type = 'number';\n        inp.step = 'any';\n        inp.value = '0';\n        inp.dataset.r = r;\n        inp.dataset.c = c;\n        el.appendChild(inp);\n      }\n    }\n  }\n\n  function rebuildA() {\n    sizeA = parseInt(document.getElementById('mx-size-a').value, 10);\n    buildGrid('mx-grid-a', sizeA);\n  }\n\n  function rebuildB() {\n    sizeB = parseInt(document.getElementById('mx-size-b').value, 10);\n    buildGrid('mx-grid-b', sizeB);\n  }\n\n  // --- Read matrix from grid ---\n  function readMatrix(gridId, size) {\n    var el = document.getElementById(gridId);\n    var inputs = el.querySelectorAll('input');\n    var M = [];\n    for (var r = 0; r \u003c size; r++) {\n      M[r] = [];\n      for (var c = 0; c \u003c size; c++) {\n        var v = parseFloat(inputs[r * size + c].value);\n        M[r][c] = isNaN(v) ? 0 : v;\n      }\n    }\n    return M;\n  }\n\n  // --- Random fill ---\n  function randomFill(gridId, size) {\n    var el = document.getElementById(gridId);\n    var inputs = el.querySelectorAll('input');\n    inputs.forEach(function(inp) {\n      inp.value = Math.floor(Math.random() * 19) - 9;\n    });\n  }\n\n  function clearGrid(gridId, size) {\n    var el = document.getElementById(gridId);\n    var inputs = el.querySelectorAll('input');\n    inputs.forEach(function(inp) { inp.value = '0'; });\n  }\n\n  // --- Matrix display ---\n  function fmt(v) {\n    if (Math.abs(v) \u003c 1e-9) return '0';\n    var r = Math.round(v * 1e8) / 1e8;\n    if (Number.isInteger(r)) return r.toString();\n    return parseFloat(r.toFixed(6)).toString();\n  }\n\n  function matrixToString(M, label) {\n    var n = M.length;\n    var cols = M[0].length;\n    var colWidths = [];\n    for (var c = 0; c \u003c cols; c++) {\n      var max = 0;\n      for (var r = 0; r \u003c n; r++) {\n        max = Math.max(max, fmt(M[r][c]).length);\n      }\n      colWidths[c] = max;\n    }\n    var lines = [];\n    if (label) lines.push(label + ':');\n    for (var r = 0; r \u003c n; r++) {\n      var row = M[r].map(function(v, c) {\n        var s = fmt(v);\n        return s.padStart(colWidths[c]);\n      }).join('  ');\n      var prefix = r === 0 ? '┌ ' : (r === n - 1 ? '└ ' : '│ ');\n      var suffix = r === 0 ? ' ┐' : (r === n - 1 ? ' ┘' : ' │');\n      lines.push(prefix + row + suffix);\n    }\n    return lines.join('\\n');\n  }\n\n  // --- Math ops ---\n  function matAdd(A, B) {\n    var n = A.length;\n    return A.map(function(row, r) {\n      return row.map(function(v, c) { return v + B[r][c]; });\n    });\n  }\n\n  function matSub(A, B) {\n    return A.map(function(row, r) {\n      return row.map(function(v, c) { return v - B[r][c]; });\n    });\n  }\n\n  function matMul(A, B) {\n    var n = A.length, m = B[0].length, p = B.length;\n    var C = [];\n    for (var r = 0; r \u003c n; r++) {\n      C[r] = [];\n      for (var c = 0; c \u003c m; c++) {\n        var s = 0;\n        for (var k = 0; k \u003c p; k++) s += A[r][k] * B[k][c];\n        C[r][c] = s;\n      }\n    }\n    return C;\n  }\n\n  function matTranspose(A) {\n    var n = A.length, m = A[0].length;\n    var T = [];\n    for (var c = 0; c \u003c m; c++) {\n      T[c] = [];\n      for (var r = 0; r \u003c n; r++) T[c][r] = A[r][c];\n    }\n    return T;\n  }\n\n  function matScalar(A, k) {\n    return A.map(function(row) { return row.map(function(v) { return v * k; }); });\n  }\n\n  // --- Determinant with cofactor expansion (for steps) ---\n  function getMinor(M, row, col) {\n    return M.filter(function(_, r) { return r !== row; }).map(function(row) {\n      return row.filter(function(_, c) { return c !== col; });\n    });\n  }\n\n  function detRecurse(M, steps, depth) {\n    var n = M.length;\n    if (n === 1) return M[0][0];\n    if (n === 2) {\n      var d = M[0][0] * M[1][1] - M[0][1] * M[1][0];\n      if (depth \u003c= 1 \u0026\u0026 steps) {\n        steps.push('det = ' + fmt(M[0][0]) + ' × ' + fmt(M[1][1]) + ' − ' + fmt(M[0][1]) + ' × ' + fmt(M[1][0]) + ' = ' + fmt(d));\n      }\n      return d;\n    }\n    var det = 0;\n    var expansion = [];\n    for (var c = 0; c \u003c n; c++) {\n      var sign = (c % 2 === 0) ? 1 : -1;\n      var minor = getMinor(M, 0, c);\n      var minorDet = detRecurse(minor, steps, depth + 1);\n      var term = sign * M[0][c] * minorDet;\n      det += term;\n      expansion.push((sign \u003e 0 ? '+' : '−') + fmt(Math.abs(M[0][c])) + '×M' + c);\n    }\n    if (depth === 0 \u0026\u0026 steps) {\n      steps.push('Cofactor expansion along row 1:');\n      var termStrs = [];\n      for (var c = 0; c \u003c n; c++) {\n        var sign = (c % 2 === 0) ? 1 : -1;\n        var minor = getMinor(M, 0, c);\n        var mdet = detRecurse(minor, null, 99);\n        var sStr = sign \u003e 0 ? '+' : '−';\n        termStrs.push('  ' + sStr + ' ' + fmt(Math.abs(M[0][c])) + ' × det(M' + c + ')  =  ' + sStr + ' ' + fmt(Math.abs(M[0][c])) + ' × ' + fmt(Math.abs(mdet)) + '  =  ' + fmt(sign * M[0][c] * mdet));\n      }\n      steps.push(termStrs.join('\\n'));\n      steps.push('det(A) = ' + fmt(det));\n    }\n    return det;\n  }\n\n  function matDet(M) {\n    var steps = [];\n    var d = detRecurse(M, steps, 0);\n    return { value: d, steps: steps };\n  }\n\n  // --- Inverse via Gauss-Jordan ---\n  function matInverse(M) {\n    var n = M.length;\n    // Augment [M | I]\n    var aug = M.map(function(row, r) {\n      var identity = new Array(n).fill(0);\n      identity[r] = 1;\n      return row.slice().concat(identity);\n    });\n\n    for (var col = 0; col \u003c n; col++) {\n      // Find pivot\n      var pivotRow = -1;\n      var maxVal = 0;\n      for (var r = col; r \u003c n; r++) {\n        if (Math.abs(aug[r][col]) \u003e maxVal) {\n          maxVal = Math.abs(aug[r][col]);\n          pivotRow = r;\n        }\n      }\n      if (maxVal \u003c 1e-12) return null; // singular\n\n      // Swap\n      var tmp = aug[col]; aug[col] = aug[pivotRow]; aug[pivotRow] = tmp;\n\n      // Scale pivot row\n      var scale = aug[col][col];\n      aug[col] = aug[col].map(function(v) { return v / scale; });\n\n      // Eliminate column\n      for (var r = 0; r \u003c n; r++) {\n        if (r === col) continue;\n        var factor = aug[r][col];\n        aug[r] = aug[r].map(function(v, c) { return v - factor * aug[col][c]; });\n      }\n    }\n\n    // Extract inverse\n    return aug.map(function(row) { return row.slice(n); });\n  }\n\n  // --- Result display ---\n  function showResult(text, type) {\n    var el = document.getElementById('mx-result');\n    el.textContent = text;\n    el.className = 'mx-result-area ' + (type || '');\n  }\n\n  function showSteps(text) {\n    var wrap = document.getElementById('mx-steps-wrap');\n    var el = document.getElementById('mx-steps');\n    if (text) {\n      wrap.style.display = 'block';\n      el.textContent = text;\n    } else {\n      wrap.style.display = 'none';\n      el.textContent = '';\n    }\n  }\n\n  // --- Calculate ---\n  function calculate() {\n    var A = readMatrix('mx-grid-a', sizeA);\n    var B = readMatrix('mx-grid-b', sizeB);\n    var k = parseFloat(document.getElementById('mx-scalar').value) || 0;\n\n    showSteps('');\n\n    try {\n      if (currentOp === 'add') {\n        if (sizeA !== sizeB) { showResult('Error: Matrices must be the same size for addition.', 'error'); return; }\n        var R = matAdd(A, B);\n        showResult(matrixToString(A, 'A') + '\\n\\n' + matrixToString(B, 'B') + '\\n\\nA + B =\\n' + matrixToString(R), 'success');\n      } else if (currentOp === 'sub') {\n        if (sizeA !== sizeB) { showResult('Error: Matrices must be the same size for subtraction.', 'error'); return; }\n        var R = matSub(A, B);\n        showResult(matrixToString(A, 'A') + '\\n\\n' + matrixToString(B, 'B') + '\\n\\nA − B =\\n' + matrixToString(R), 'success');\n      } else if (currentOp === 'mul') {\n        if (sizeA !== sizeB) { showResult('Error: For A×B, number of columns of A must equal number of rows of B.\\n(Currently both grids must be same size)', 'error'); return; }\n        var R = matMul(A, B);\n        showResult(matrixToString(A, 'A') + '\\n\\n' + matrixToString(B, 'B') + '\\n\\nA × B =\\n' + matrixToString(R), 'success');\n      } else if (currentOp === 'det') {\n        var res = matDet(A);\n        var stepText = res.steps.join('\\n');\n        showResult(matrixToString(A, 'A') + '\\n\\ndet(A) = ' + fmt(res.value), 'success');\n        showSteps('Determinant of A — Cofactor Expansion\\n\\n' + stepText);\n      } else if (currentOp === 'inv') {\n        var inv = matInverse(A);\n        if (!inv) {\n          showResult('Error: Matrix A is singular (determinant = 0). No inverse exists.', 'error');\n          return;\n        }\n        showResult(matrixToString(A, 'A') + '\\n\\nA⁻¹ =\\n' + matrixToString(inv), 'success');\n        showSteps('Method: Gauss-Jordan elimination\\nAugmented matrix [A|I] reduced to row echelon form → [I|A⁻¹]');\n      } else if (currentOp === 'tra') {\n        var T = matTranspose(A);\n        showResult(matrixToString(A, 'A') + '\\n\\nAᵀ =\\n' + matrixToString(T), 'success');\n      } else if (currentOp === 'scalar') {\n        var R = matScalar(A, k);\n        showResult(matrixToString(A, 'A') + '\\n\\n' + fmt(k) + ' × A =\\n' + matrixToString(R), 'success');\n      }\n    } catch(e) {\n      showResult('Unexpected error: ' + e.message, 'error');\n    }\n  }\n\n  // --- Event wiring ---\n  document.getElementById('mx-size-a').addEventListener('change', rebuildA);\n  document.getElementById('mx-size-b').addEventListener('change', rebuildB);\n  document.getElementById('mx-rand-a').addEventListener('click', function() { randomFill('mx-grid-a', sizeA); });\n  document.getElementById('mx-rand-b').addEventListener('click', function() { randomFill('mx-grid-b', sizeB); });\n  document.getElementById('mx-clear-a').addEventListener('click', function() { clearGrid('mx-grid-a', sizeA); });\n  document.getElementById('mx-clear-b').addEventListener('click', function() { clearGrid('mx-grid-b', sizeB); });\n  document.getElementById('mx-calc').addEventListener('click', calculate);\n\n  document.querySelectorAll('#mx-app .mx-op-btn').forEach(function(btn) {\n    btn.addEventListener('click', function() {\n      document.querySelectorAll('#mx-app .mx-op-btn').forEach(function(b) { b.classList.remove('active'); });\n      btn.classList.add('active');\n      currentOp = btn.dataset.op;\n    });\n  });\n\n  // --- Init ---\n  buildGrid('mx-grid-a', sizeA);\n  buildGrid('mx-grid-b', sizeB);\n\n})();\n\u003c/script\u003e\n\u003ch2 id=\"how-to-use-the-matrix-calculator\"\u003eHow to Use the Matrix Calculator\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eSet sizes\u003c/strong\u003e — Choose grid size (2×2 to 5×5) independently for Matrix A and Matrix B.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eEnter values\u003c/strong\u003e — Click each cell and type a number. Use \u003cstrong\u003eRandom Fill\u003c/strong\u003e to populate with test values.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSelect operation\u003c/strong\u003e — Click the operation button (A+B, A−B, A×B, det(A), A⁻¹, Aᵀ, or k×A).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eCalculate\u003c/strong\u003e — Press the Calculate button to see the result and step-by-step breakdown.\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"supported-operations\"\u003eSupported Operations\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eOperation\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n          \u003cth\u003eRequires\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003eA + B\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003eElement-wise addition\u003c/td\u003e\n          \u003ctd\u003eSame dimensions\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003eA − B\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003eElement-wise subtraction\u003c/td\u003e\n          \u003ctd\u003eSame dimensions\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003eA × B\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003eMatrix multiplication\u003c/td\u003e\n          \u003ctd\u003eA cols = B rows\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003edet(A)\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003eDeterminant with cofactor steps\u003c/td\u003e\n          \u003ctd\u003eSquare matrix A\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003eA⁻¹\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003eInverse via Gauss-Jordan\u003c/td\u003e\n          \u003ctd\u003eSquare, non-singular A\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003eAᵀ\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003eTranspose of A\u003c/td\u003e\n          \u003ctd\u003eAny matrix A\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003ek × A\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003eScalar multiplication\u003c/td\u003e\n          \u003ctd\u003eAny matrix A\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"what-is-a-matrix\"\u003eWhat is a Matrix?\u003c/h2\u003e\n\u003cp\u003eA \u003cstrong\u003ematrix\u003c/strong\u003e is a rectangular array of numbers arranged in rows and columns. Matrices are fundamental to linear algebra, computer graphics, machine learning, physics simulations, and engineering.\u003c/p\u003e","title":"Matrix Calculator"},{"content":" 👥 Attendees Role / Name Hourly Rate ($) Input type Hourly Annual Add + Add No attendees yet. Add roles above or use a preset. Total Attendees: 0 Combined Rate: $0.00/hr Per Minute: $0.00/min 📋 Pre-Meeting Estimate Planned Duration (minutes) Estimated cost for this meeting: $0.00 ⏱ Meeting Timer Idle 00:00 $0.00 real-time cost \u0026#9654; Start \u0026#9646;\u0026#9646; Pause \u0026#9632; Stop \u0026amp; Report \u0026#8635; Reset 📊 Meeting Report $0.00 Total Cost $0.00 Cost / Minute $0.00 Cost / Attendee What else could this buy? Focus with Pomodoro \u0026rarr; Pomodoro Timer Calculate salary \u0026rarr; Salary Calculator Track ROI \u0026rarr; ROI Calculator Related Articles Best Remote Work Tools in 2026: The Complete Stack ","permalink":"https://productivity-works.com/tools/meeting-cost-calculator/","summary":"\u003cdiv id=\"mc-app\"\u003e\n\u003cstyle\u003e\n#mc-app {\n  --bg: #0f172a;\n  --surface: #1e293b;\n  --surface2: #334155;\n  --border: #475569;\n  --accent: #38bdf8;\n  --accent-dark: #0284c7;\n  --accent-hover: #7dd3fc;\n  --success: #34d399;\n  --warning: #fbbf24;\n  --danger: #f87171;\n  --text: #f1f5f9;\n  --text-muted: #94a3b8;\n  --text-dim: #64748b;\n  --radius: 10px;\n  --radius-sm: 6px;\n  font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n  background: var(--bg);\n  color: var(--text);\n  padding: 1.5rem 1rem;\n  border-radius: 16px;\n  max-width: 820px;\n  margin: 0 auto;\n  box-sizing: border-box;\n}\n#mc-app *, #mc-app *::before, #mc-app *::after { box-sizing: border-box; }\n\n/* Typography */\n#mc-app h2 {\n  font-size: 1.1rem;\n  font-weight: 700;\n  color: var(--accent);\n  margin: 0 0 1rem 0;\n  letter-spacing: 0.04em;\n  text-transform: uppercase;\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n}\n#mc-app h2 .icon { font-size: 1.1em; }\n\n/* Cards */\n#mc-app .mc-card {\n  background: var(--surface);\n  border: 1px solid var(--border);\n  border-radius: var(--radius);\n  padding: 1.25rem 1.25rem;\n  margin-bottom: 1.25rem;\n}\n\n/* Grid */\n#mc-app .mc-grid-2 {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1.25rem;\n}\n@media (max-width: 600px) {\n  #mc-app .mc-grid-2 { grid-template-columns: 1fr; }\n}\n\n/* Buttons */\n#mc-app button {\n  cursor: pointer;\n  border: none;\n  border-radius: var(--radius-sm);\n  font-size: 0.875rem;\n  font-weight: 600;\n  padding: 0.45rem 0.9rem;\n  transition: background 0.15s, transform 0.1s, opacity 0.15s;\n}\n#mc-app button:active { transform: scale(0.97); }\n#mc-app button:disabled { opacity: 0.45; cursor: not-allowed; transform: none; }\n\n#mc-app .btn-primary {\n  background: var(--accent-dark);\n  color: #fff;\n}\n#mc-app .btn-primary:hover:not(:disabled) { background: var(--accent); color: #0f172a; }\n\n#mc-app .btn-success {\n  background: #065f46;\n  color: var(--success);\n  border: 1px solid var(--success);\n}\n#mc-app .btn-success:hover:not(:disabled) { background: var(--success); color: #022c22; }\n\n#mc-app .btn-warning {\n  background: #78350f;\n  color: var(--warning);\n  border: 1px solid var(--warning);\n}\n#mc-app .btn-warning:hover:not(:disabled) { background: var(--warning); color: #1c0a00; }\n\n#mc-app .btn-danger {\n  background: #450a0a;\n  color: var(--danger);\n  border: 1px solid var(--danger);\n}\n#mc-app .btn-danger:hover:not(:disabled) { background: var(--danger); color: #1c0000; }\n\n#mc-app .btn-ghost {\n  background: transparent;\n  color: var(--text-muted);\n  border: 1px solid var(--border);\n}\n#mc-app .btn-ghost:hover:not(:disabled) { background: var(--surface2); color: var(--text); }\n\n#mc-app .btn-sm {\n  font-size: 0.78rem;\n  padding: 0.3rem 0.65rem;\n}\n\n/* Form inputs */\n#mc-app input[type=\"text\"],\n#mc-app input[type=\"number\"] {\n  background: var(--bg);\n  border: 1px solid var(--border);\n  border-radius: var(--radius-sm);\n  color: var(--text);\n  font-size: 0.9rem;\n  padding: 0.45rem 0.7rem;\n  width: 100%;\n  outline: none;\n  transition: border-color 0.15s;\n}\n#mc-app input[type=\"text\"]:focus,\n#mc-app input[type=\"number\"]:focus {\n  border-color: var(--accent);\n}\n#mc-app input[type=\"radio\"] { accent-color: var(--accent); }\n\n#mc-app label {\n  font-size: 0.82rem;\n  color: var(--text-muted);\n  display: block;\n  margin-bottom: 0.3rem;\n}\n\n/* Presets */\n#mc-app .mc-presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem;\n  margin-bottom: 1rem;\n}\n#mc-app .mc-preset-btn {\n  background: var(--surface2);\n  color: var(--text);\n  border: 1px solid var(--border);\n  border-radius: 20px;\n  font-size: 0.8rem;\n  padding: 0.3rem 0.75rem;\n  cursor: pointer;\n  transition: background 0.15s, border-color 0.15s;\n}\n#mc-app .mc-preset-btn:hover { background: var(--accent-dark); border-color: var(--accent); color: #fff; }\n\n/* Attendee add form */\n#mc-app .mc-add-form {\n  display: grid;\n  grid-template-columns: 1fr auto auto auto;\n  gap: 0.6rem;\n  align-items: end;\n  margin-bottom: 1rem;\n}\n@media (max-width: 560px) {\n  #mc-app .mc-add-form {\n    grid-template-columns: 1fr 1fr;\n  }\n  #mc-app .mc-add-form .mc-add-btn { grid-column: 1 / -1; }\n}\n\n#mc-app .mc-rate-toggle {\n  display: flex;\n  gap: 0.4rem;\n  align-items: center;\n  font-size: 0.8rem;\n  color: var(--text-muted);\n  white-space: nowrap;\n}\n\n/* Attendee list */\n#mc-app .mc-attendee-list {\n  display: flex;\n  flex-direction: column;\n  gap: 0.5rem;\n}\n#mc-app .mc-attendee-item {\n  display: grid;\n  grid-template-columns: 1fr auto auto auto;\n  align-items: center;\n  gap: 0.6rem;\n  background: var(--surface2);\n  border: 1px solid var(--border);\n  border-radius: var(--radius-sm);\n  padding: 0.5rem 0.75rem;\n}\n@media (max-width: 480px) {\n  #mc-app .mc-attendee-item {\n    grid-template-columns: 1fr auto auto;\n  }\n}\n#mc-app .mc-attendee-name {\n  font-weight: 600;\n  font-size: 0.9rem;\n}\n#mc-app .mc-attendee-rate {\n  font-size: 0.82rem;\n  color: var(--text-muted);\n  white-space: nowrap;\n}\n#mc-app .mc-attendee-count {\n  display: flex;\n  align-items: center;\n  gap: 0.3rem;\n}\n#mc-app .mc-attendee-count button {\n  background: var(--bg);\n  border: 1px solid var(--border);\n  color: var(--text);\n  border-radius: 4px;\n  width: 26px;\n  height: 26px;\n  padding: 0;\n  font-size: 1rem;\n  line-height: 1;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n#mc-app .mc-attendee-count button:hover { background: var(--surface2); border-color: var(--accent); }\n#mc-app .mc-count-val {\n  min-width: 20px;\n  text-align: center;\n  font-weight: 700;\n  font-size: 0.9rem;\n}\n#mc-app .mc-remove-btn {\n  background: transparent;\n  border: none;\n  color: var(--text-dim);\n  font-size: 1.1rem;\n  padding: 0 0.2rem;\n  cursor: pointer;\n  transition: color 0.15s;\n}\n#mc-app .mc-remove-btn:hover { color: var(--danger); }\n\n#mc-app .mc-empty-msg {\n  text-align: center;\n  color: var(--text-dim);\n  font-size: 0.88rem;\n  padding: 1rem 0;\n}\n\n/* Summary bar */\n#mc-app .mc-summary-bar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 1rem;\n  background: var(--surface2);\n  border: 1px solid var(--border);\n  border-radius: var(--radius-sm);\n  padding: 0.75rem 1rem;\n  margin-top: 0.75rem;\n  font-size: 0.88rem;\n  color: var(--text-muted);\n}\n#mc-app .mc-summary-bar strong {\n  color: var(--accent);\n  font-size: 1rem;\n}\n\n/* Timer display */\n#mc-app .mc-timer-display {\n  text-align: center;\n  margin: 0.5rem 0 1.25rem;\n}\n#mc-app .mc-timer-time {\n  font-size: 3.5rem;\n  font-weight: 800;\n  letter-spacing: 0.08em;\n  color: var(--text);\n  font-variant-numeric: tabular-nums;\n  line-height: 1;\n}\n#mc-app .mc-timer-cost {\n  font-size: 2.2rem;\n  font-weight: 700;\n  color: var(--accent);\n  margin-top: 0.4rem;\n  font-variant-numeric: tabular-nums;\n}\n#mc-app .mc-timer-label {\n  font-size: 0.82rem;\n  color: var(--text-dim);\n  margin-top: 0.2rem;\n}\n#mc-app .mc-timer-running .mc-timer-cost { color: var(--warning); }\n\n#mc-app .mc-timer-buttons {\n  display: flex;\n  justify-content: center;\n  gap: 0.75rem;\n  flex-wrap: wrap;\n}\n#mc-app .mc-timer-buttons button {\n  min-width: 90px;\n  padding: 0.6rem 1.2rem;\n  font-size: 0.95rem;\n}\n\n/* Status badge */\n#mc-app .mc-status {\n  display: inline-flex;\n  align-items: center;\n  gap: 0.35rem;\n  font-size: 0.78rem;\n  font-weight: 600;\n  padding: 0.2rem 0.65rem;\n  border-radius: 20px;\n  margin-left: 0.5rem;\n  vertical-align: middle;\n}\n#mc-app .mc-status.idle { background: var(--surface2); color: var(--text-dim); }\n#mc-app .mc-status.running { background: #065f46; color: var(--success); }\n#mc-app .mc-status.paused { background: #78350f; color: var(--warning); }\n\n/* Estimate section */\n#mc-app .mc-estimate-row {\n  display: flex;\n  align-items: flex-end;\n  gap: 0.75rem;\n  flex-wrap: wrap;\n}\n#mc-app .mc-estimate-row \u003e div { flex: 1; min-width: 120px; }\n#mc-app .mc-estimate-result {\n  background: var(--surface2);\n  border: 1px solid var(--border);\n  border-radius: var(--radius-sm);\n  padding: 0.75rem 1rem;\n  margin-top: 0.75rem;\n  font-size: 0.9rem;\n  color: var(--text-muted);\n}\n#mc-app .mc-estimate-result .mc-big-num {\n  font-size: 1.8rem;\n  font-weight: 800;\n  color: var(--accent);\n}\n\n/* Results section */\n#mc-app .mc-results {\n  display: none;\n}\n#mc-app .mc-results.visible { display: block; }\n#mc-app .mc-results-grid {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 0.75rem;\n  margin-bottom: 1rem;\n}\n@media (max-width: 500px) {\n  #mc-app .mc-results-grid { grid-template-columns: 1fr 1fr; }\n}\n#mc-app .mc-result-box {\n  background: var(--surface2);\n  border: 1px solid var(--border);\n  border-radius: var(--radius-sm);\n  padding: 0.85rem 0.75rem;\n  text-align: center;\n}\n#mc-app .mc-result-box .mc-result-val {\n  font-size: 1.5rem;\n  font-weight: 800;\n  color: var(--accent);\n}\n#mc-app .mc-result-box .mc-result-lbl {\n  font-size: 0.75rem;\n  color: var(--text-dim);\n  margin-top: 0.2rem;\n}\n\n/* Fun comparisons */\n#mc-app .mc-comparisons {\n  background: var(--surface2);\n  border: 1px solid var(--border);\n  border-radius: var(--radius-sm);\n  padding: 1rem;\n}\n#mc-app .mc-comparisons h3 {\n  font-size: 0.88rem;\n  font-weight: 700;\n  color: var(--text-muted);\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin: 0 0 0.75rem 0;\n}\n#mc-app .mc-comp-list {\n  display: flex;\n  flex-direction: column;\n  gap: 0.45rem;\n}\n#mc-app .mc-comp-item {\n  display: flex;\n  align-items: center;\n  gap: 0.6rem;\n  font-size: 0.9rem;\n}\n#mc-app .mc-comp-icon { font-size: 1.2em; width: 1.4em; text-align: center; }\n#mc-app .mc-comp-text { color: var(--text); }\n#mc-app .mc-comp-text strong { color: var(--accent-hover); }\n\n/* Cross-links */\n#mc-app .mc-crosslinks {\n  background: var(--surface2);\n  border: 1px solid var(--border);\n  border-radius: var(--radius-sm);\n  padding: 0.85rem 1rem;\n  margin-top: 1.25rem;\n  font-size: 0.88rem;\n  color: var(--text-muted);\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem 1.5rem;\n}\n#mc-app .mc-crosslinks a {\n  color: var(--accent);\n  text-decoration: none;\n  font-weight: 500;\n}\n#mc-app .mc-crosslinks a:hover { text-decoration: underline; color: var(--accent-hover); }\n#mc-app .mc-crosslinks span { white-space: nowrap; }\n\n/* Divider */\n#mc-app .mc-divider {\n  height: 1px;\n  background: var(--border);\n  margin: 1rem 0;\n}\n\n/* Pulse animation for running cost */\n@keyframes mc-pulse {\n  0%   { opacity: 1; }\n  50%  { opacity: 0.7; }\n  100% { opacity: 1; }\n}\n#mc-app .mc-timer-running .mc-timer-cost {\n  animation: mc-pulse 1s infinite;\n}\n\u003c/style\u003e\n\u003c!-- ===== SECTION 1: ATTENDEES ===== --\u003e\n\u003cdiv class=\"mc-card\"\u003e\n  \u003ch2\u003e\u003cspan class=\"icon\"\u003e👥\u003c/span\u003e Attendees\u003c/h2\u003e\n  \u003cdiv class=\"mc-presets\" id=\"mc-presets\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"mc-add-form\" id=\"mc-add-form\"\u003e\n    \u003cdiv\u003e\n      \u003clabel for=\"mc-role-name\"\u003eRole / Name\u003c/label\u003e\n      \u003cinput type=\"text\" id=\"mc-role-name\" placeholder=\"e.g. Product Manager\" /\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003clabel id=\"mc-rate-label\" for=\"mc-rate-value\"\u003eHourly Rate ($)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"mc-rate-value\" min=\"0\" step=\"1\" placeholder=\"e.g. 80\" style=\"max-width:110px;\" /\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003clabel style=\"white-space:nowrap;\"\u003eInput type\u003c/label\u003e\n      \u003cdiv class=\"mc-rate-toggle\"\u003e\n        \u003clabel style=\"display:inline-flex;align-items:center;gap:0.3rem;color:var(--text);font-size:0.82rem;\"\u003e\n          \u003cinput type=\"radio\" name=\"mc-rate-type\" value=\"hourly\" checked /\u003e Hourly\n        \u003c/label\u003e\n        \u003clabel style=\"display:inline-flex;align-items:center;gap:0.3rem;color:var(--text);font-size:0.82rem;\"\u003e\n          \u003cinput type=\"radio\" name=\"mc-rate-type\" value=\"annual\" /\u003e Annual\n        \u003c/label\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"mc-add-btn\"\u003e\n      \u003clabel style=\"visibility:hidden;\"\u003eAdd\u003c/label\u003e\n      \u003cbutton class=\"btn-primary\" onclick=\"mcAddAttendee()\"\u003e+ Add\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"mc-attendee-list\" id=\"mc-attendee-list\"\u003e\n    \u003cdiv class=\"mc-empty-msg\" id=\"mc-empty-msg\"\u003eNo attendees yet. Add roles above or use a preset.\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"mc-summary-bar\" id=\"mc-summary-bar\" style=\"display:none;\"\u003e\n    \u003cspan\u003eTotal Attendees: \u003cstrong id=\"mc-total-attendees\"\u003e0\u003c/strong\u003e\u003c/span\u003e\n    \u003cspan\u003eCombined Rate: \u003cstrong id=\"mc-total-rate\"\u003e$0.00\u003c/strong\u003e/hr\u003c/span\u003e\n    \u003cspan\u003ePer Minute: \u003cstrong id=\"mc-per-minute\"\u003e$0.00\u003c/strong\u003e/min\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ===== SECTION 2: ESTIMATE ===== --\u003e\n\u003cdiv class=\"mc-card\"\u003e\n  \u003ch2\u003e\u003cspan class=\"icon\"\u003e📋\u003c/span\u003e Pre-Meeting Estimate\u003c/h2\u003e\n  \u003cdiv class=\"mc-estimate-row\"\u003e\n    \u003cdiv\u003e\n      \u003clabel for=\"mc-planned-mins\"\u003ePlanned Duration (minutes)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"mc-planned-mins\" min=\"1\" step=\"1\" placeholder=\"e.g. 60\" oninput=\"mcUpdateEstimate()\" /\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"mc-estimate-result\" id=\"mc-estimate-result\" style=\"display:none;\"\u003e\n    \u003cdiv\u003eEstimated cost for this meeting:\u003c/div\u003e\n    \u003cdiv class=\"mc-big-num\" id=\"mc-estimate-val\"\u003e$0.00\u003c/div\u003e\n    \u003cdiv style=\"font-size:0.8rem;color:var(--text-dim);margin-top:0.25rem;\" id=\"mc-estimate-detail\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ===== SECTION 3: TIMER ===== --\u003e\n\u003cdiv class=\"mc-card\"\u003e\n  \u003ch2\u003e\n    \u003cspan class=\"icon\"\u003e⏱\u003c/span\u003e Meeting Timer\n    \u003cspan class=\"mc-status idle\" id=\"mc-status-badge\"\u003eIdle\u003c/span\u003e\n  \u003c/h2\u003e\n  \u003cdiv class=\"mc-timer-display\" id=\"mc-timer-display\"\u003e\n    \u003cdiv class=\"mc-timer-time\" id=\"mc-timer-time\"\u003e00:00\u003c/div\u003e\n    \u003cdiv class=\"mc-timer-cost\" id=\"mc-timer-cost\"\u003e$0.00\u003c/div\u003e\n    \u003cdiv class=\"mc-timer-label\"\u003ereal-time cost\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"mc-timer-buttons\"\u003e\n    \u003cbutton class=\"btn-success\" id=\"mc-btn-start\" onclick=\"mcTimerStart()\"\u003e\u0026#9654; Start\u003c/button\u003e\n    \u003cbutton class=\"btn-warning\" id=\"mc-btn-pause\" onclick=\"mcTimerPause()\" disabled\u003e\u0026#9646;\u0026#9646; Pause\u003c/button\u003e\n    \u003cbutton class=\"btn-danger\" id=\"mc-btn-stop\" onclick=\"mcTimerStop()\" disabled\u003e\u0026#9632; Stop \u0026amp; Report\u003c/button\u003e\n    \u003cbutton class=\"btn-ghost\" id=\"mc-btn-reset\" onclick=\"mcTimerReset()\"\u003e\u0026#8635; Reset\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ===== SECTION 4: RESULTS ===== --\u003e\n\u003cdiv class=\"mc-card mc-results\" id=\"mc-results\"\u003e\n  \u003ch2\u003e\u003cspan class=\"icon\"\u003e📊\u003c/span\u003e Meeting Report\u003c/h2\u003e\n  \u003cdiv class=\"mc-results-grid\"\u003e\n    \u003cdiv class=\"mc-result-box\"\u003e\n      \u003cdiv class=\"mc-result-val\" id=\"mc-res-total\"\u003e$0.00\u003c/div\u003e\n      \u003cdiv class=\"mc-result-lbl\"\u003eTotal Cost\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"mc-result-box\"\u003e\n      \u003cdiv class=\"mc-result-val\" id=\"mc-res-per-min\"\u003e$0.00\u003c/div\u003e\n      \u003cdiv class=\"mc-result-lbl\"\u003eCost / Minute\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"mc-result-box\"\u003e\n      \u003cdiv class=\"mc-result-val\" id=\"mc-res-per-person\"\u003e$0.00\u003c/div\u003e\n      \u003cdiv class=\"mc-result-lbl\"\u003eCost / Attendee\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"mc-comparisons\"\u003e\n    \u003ch3\u003eWhat else could this buy?\u003c/h3\u003e\n    \u003cdiv class=\"mc-comp-list\" id=\"mc-comp-list\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ===== CROSS-LINKS ===== --\u003e\n\u003cdiv class=\"mc-crosslinks\"\u003e\n  \u003cspan\u003eFocus with Pomodoro \u0026rarr; \u003ca href=\"/tools/pomodoro-timer/\"\u003ePomodoro Timer\u003c/a\u003e\u003c/span\u003e\n  \u003cspan\u003eCalculate salary \u0026rarr; \u003ca href=\"/tools/salary-calculator/\"\u003eSalary Calculator\u003c/a\u003e\u003c/span\u003e\n  \u003cspan\u003eTrack ROI \u0026rarr; \u003ca href=\"/tools/roi-calculator/\"\u003eROI Calculator\u003c/a\u003e\u003c/span\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  /* ---- State ---- */\n  var attendees = [];\n  var timerInterval = null;\n  var timerStartTs = null;\n  var timerElapsed = 0; // ms accumulated before current start\n  var timerRunning = false;\n  var timerStopped = false;\n\n  /* ---- Presets ---- */\n  var PRESETS = [\n    { label: \"Junior\", rate: 30 },\n    { label: \"Mid\",    rate: 50 },\n    { label: \"Senior\", rate: 80 },\n    { label: \"Manager\",rate: 100 },\n    { label: \"Exec\",   rate: 150 },\n  ];\n\n  function buildPresets() {\n    var el = document.getElementById(\"mc-presets\");\n    el.innerHTML = \"\";\n    PRESETS.forEach(function(p) {\n      var btn = document.createElement(\"button\");\n      btn.className = \"mc-preset-btn\";\n      btn.textContent = p.label + \" ($\" + p.rate + \"/hr)\";\n      btn.onclick = function() { applyPreset(p); };\n      el.appendChild(btn);\n    });\n  }\n\n  function applyPreset(p) {\n    document.getElementById(\"mc-role-name\").value = p.label;\n    document.getElementById(\"mc-rate-value\").value = p.rate;\n    // set to hourly\n    document.querySelector('input[name=\"mc-rate-type\"][value=\"hourly\"]').checked = true;\n    updateRateLabel();\n    mcAddAttendee();\n  }\n\n  /* ---- Rate type toggle ---- */\n  document.querySelectorAll('input[name=\"mc-rate-type\"]').forEach(function(r) {\n    r.addEventListener(\"change\", updateRateLabel);\n  });\n  function updateRateLabel() {\n    var type = document.querySelector('input[name=\"mc-rate-type\"]:checked').value;\n    var lbl = document.getElementById(\"mc-rate-label\");\n    var inp = document.getElementById(\"mc-rate-value\");\n    if (type === \"annual\") {\n      lbl.textContent = \"Annual Salary ($)\";\n      inp.placeholder = \"e.g. 80000\";\n      inp.step = \"1000\";\n    } else {\n      lbl.textContent = \"Hourly Rate ($)\";\n      inp.placeholder = \"e.g. 80\";\n      inp.step = \"1\";\n    }\n  }\n\n  /* ---- Add Attendee ---- */\n  window.mcAddAttendee = function() {\n    var name = document.getElementById(\"mc-role-name\").value.trim();\n    var rateRaw = parseFloat(document.getElementById(\"mc-rate-value\").value);\n    var type = document.querySelector('input[name=\"mc-rate-type\"]:checked').value;\n\n    if (!name) { document.getElementById(\"mc-role-name\").focus(); return; }\n    if (isNaN(rateRaw) || rateRaw \u003c= 0) { document.getElementById(\"mc-rate-value\").focus(); return; }\n\n    var hourlyRate = (type === \"annual\") ? rateRaw / 2080 : rateRaw;\n\n    // Check if role already exists — increment count\n    var existing = attendees.find(function(a) { return a.name.toLowerCase() === name.toLowerCase(); });\n    if (existing) {\n      existing.count++;\n    } else {\n      attendees.push({ id: Date.now(), name: name, hourlyRate: hourlyRate, annualEquiv: hourlyRate * 2080, count: 1 });\n    }\n\n    document.getElementById(\"mc-role-name\").value = \"\";\n    document.getElementById(\"mc-rate-value\").value = \"\";\n    renderAttendees();\n    mcUpdateEstimate();\n  };\n\n  /* ---- Render Attendees ---- */\n  function renderAttendees() {\n    var list = document.getElementById(\"mc-attendee-list\");\n    var empty = document.getElementById(\"mc-empty-msg\");\n    var summaryBar = document.getElementById(\"mc-summary-bar\");\n\n    if (attendees.length === 0) {\n      list.innerHTML = \"\";\n      list.appendChild(empty);\n      empty.style.display = \"block\";\n      summaryBar.style.display = \"none\";\n      return;\n    }\n    empty.style.display = \"none\";\n    list.innerHTML = \"\";\n\n    attendees.forEach(function(a) {\n      var item = document.createElement(\"div\");\n      item.className = \"mc-attendee-item\";\n      item.innerHTML =\n        '\u003cdiv\u003e' +\n          '\u003cdiv class=\"mc-attendee-name\"\u003e' + escHtml(a.name) + '\u003c/div\u003e' +\n          '\u003cdiv class=\"mc-attendee-rate\"\u003e$' + a.hourlyRate.toFixed(2) + '/hr \u0026middot; ~$' + Math.round(a.annualEquiv).toLocaleString() + '/yr\u003c/div\u003e' +\n        '\u003c/div\u003e' +\n        '\u003cdiv class=\"mc-attendee-count\"\u003e' +\n          '\u003cbutton onclick=\"mcChangeCount(' + a.id + ',-1)\" title=\"Remove one\"\u003e\u0026#8722;\u003c/button\u003e' +\n          '\u003cspan class=\"mc-count-val\"\u003e' + a.count + '\u003c/span\u003e' +\n          '\u003cbutton onclick=\"mcChangeCount(' + a.id + ',1)\" title=\"Add one\"\u003e+\u003c/button\u003e' +\n        '\u003c/div\u003e' +\n        '\u003cdiv class=\"mc-attendee-rate\" style=\"white-space:nowrap;\"\u003eSubtotal: $' + (a.hourlyRate * a.count).toFixed(2) + '/hr\u003c/div\u003e' +\n        '\u003cbutton class=\"mc-remove-btn\" onclick=\"mcRemoveAttendee(' + a.id + ')\" title=\"Remove\"\u003e\u0026times;\u003c/button\u003e';\n      list.appendChild(item);\n    });\n\n    // Summary\n    var totalPeople = attendees.reduce(function(s, a) { return s + a.count; }, 0);\n    var totalRate = attendees.reduce(function(s, a) { return s + a.hourlyRate * a.count; }, 0);\n    document.getElementById(\"mc-total-attendees\").textContent = totalPeople;\n    document.getElementById(\"mc-total-rate\").textContent = \"$\" + totalRate.toFixed(2);\n    document.getElementById(\"mc-per-minute\").textContent = \"$\" + (totalRate / 60).toFixed(3);\n    summaryBar.style.display = \"flex\";\n  }\n\n  window.mcChangeCount = function(id, delta) {\n    var a = attendees.find(function(x) { return x.id === id; });\n    if (!a) return;\n    a.count = Math.max(1, a.count + delta);\n    renderAttendees();\n    mcUpdateEstimate();\n  };\n\n  window.mcRemoveAttendee = function(id) {\n    attendees = attendees.filter(function(a) { return a.id !== id; });\n    renderAttendees();\n    mcUpdateEstimate();\n  };\n\n  /* ---- Helpers ---- */\n  function getTotalHourlyRate() {\n    return attendees.reduce(function(s, a) { return s + a.hourlyRate * a.count; }, 0);\n  }\n  function getTotalAttendees() {\n    return attendees.reduce(function(s, a) { return s + a.count; }, 0);\n  }\n  function formatMoney(n) {\n    if (n \u003e= 10000) return \"$\" + Math.round(n).toLocaleString();\n    if (n \u003e= 100)   return \"$\" + n.toFixed(0);\n    return \"$\" + n.toFixed(2);\n  }\n  function formatTime(ms) {\n    var totalSec = Math.floor(ms / 1000);\n    var min = Math.floor(totalSec / 60);\n    var sec = totalSec % 60;\n    return pad2(min) + \":\" + pad2(sec);\n  }\n  function pad2(n) { return n \u003c 10 ? \"0\" + n : \"\" + n; }\n  function escHtml(s) {\n    return s.replace(/\u0026/g,\"\u0026amp;\").replace(/\u003c/g,\"\u0026lt;\").replace(/\u003e/g,\"\u0026gt;\").replace(/\"/g,\"\u0026quot;\");\n  }\n\n  /* ---- Estimate ---- */\n  window.mcUpdateEstimate = function() {\n    var mins = parseFloat(document.getElementById(\"mc-planned-mins\").value);\n    var resultEl = document.getElementById(\"mc-estimate-result\");\n    var valEl = document.getElementById(\"mc-estimate-val\");\n    var detailEl = document.getElementById(\"mc-estimate-detail\");\n    var rate = getTotalHourlyRate();\n\n    if (!mins || mins \u003c= 0 || rate \u003c= 0) {\n      resultEl.style.display = \"none\";\n      return;\n    }\n    var cost = rate * (mins / 60);\n    valEl.textContent = formatMoney(cost);\n    detailEl.textContent =\n      getTotalAttendees() + \" attendees \\u00d7 \" +\n      mins.toFixed(0) + \" min \\u00d7 $\" + rate.toFixed(2) + \"/hr combined\";\n    resultEl.style.display = \"block\";\n  };\n\n  /* ---- Timer ---- */\n  function getElapsedMs() {\n    if (timerRunning \u0026\u0026 timerStartTs) {\n      return timerElapsed + (Date.now() - timerStartTs);\n    }\n    return timerElapsed;\n  }\n\n  function updateTimerDisplay() {\n    var ms = getElapsedMs();\n    var rate = getTotalHourlyRate();\n    var cost = rate * (ms / 3600000);\n    document.getElementById(\"mc-timer-time\").textContent = formatTime(ms);\n    document.getElementById(\"mc-timer-cost\").textContent = formatMoney(cost);\n  }\n\n  window.mcTimerStart = function() {\n    if (timerRunning) return;\n    if (attendees.length === 0) {\n      alert(\"Please add at least one attendee before starting the timer.\");\n      return;\n    }\n    timerRunning = true;\n    timerStopped = false;\n    timerStartTs = Date.now();\n    timerInterval = setInterval(updateTimerDisplay, 1000);\n\n    document.getElementById(\"mc-btn-start\").disabled = true;\n    document.getElementById(\"mc-btn-pause\").disabled = false;\n    document.getElementById(\"mc-btn-stop\").disabled = false;\n    document.getElementById(\"mc-timer-display\").classList.add(\"mc-timer-running\");\n    setStatusBadge(\"running\");\n\n    // Hide previous results\n    document.getElementById(\"mc-results\").classList.remove(\"visible\");\n  };\n\n  window.mcTimerPause = function() {\n    if (!timerRunning) return;\n    timerElapsed = getElapsedMs();\n    timerRunning = false;\n    timerStartTs = null;\n    clearInterval(timerInterval);\n    timerInterval = null;\n\n    document.getElementById(\"mc-btn-start\").disabled = false;\n    document.getElementById(\"mc-btn-start\").textContent = \"\\u25b6 Resume\";\n    document.getElementById(\"mc-btn-pause\").disabled = true;\n    document.getElementById(\"mc-timer-display\").classList.remove(\"mc-timer-running\");\n    setStatusBadge(\"paused\");\n  };\n\n  window.mcTimerStop = function() {\n    if (!timerRunning \u0026\u0026 timerElapsed === 0) return;\n    // capture final\n    if (timerRunning) {\n      timerElapsed = getElapsedMs();\n      timerRunning = false;\n      clearInterval(timerInterval);\n      timerInterval = null;\n    }\n    timerStopped = true;\n    updateTimerDisplay();\n\n    document.getElementById(\"mc-btn-start\").disabled = true;\n    document.getElementById(\"mc-btn-pause\").disabled = true;\n    document.getElementById(\"mc-btn-stop\").disabled = true;\n    document.getElementById(\"mc-timer-display\").classList.remove(\"mc-timer-running\");\n    setStatusBadge(\"idle\");\n\n    showResults();\n  };\n\n  window.mcTimerReset = function() {\n    timerRunning = false;\n    timerStopped = false;\n    timerElapsed = 0;\n    timerStartTs = null;\n    clearInterval(timerInterval);\n    timerInterval = null;\n\n    document.getElementById(\"mc-timer-time\").textContent = \"00:00\";\n    document.getElementById(\"mc-timer-cost\").textContent = \"$0.00\";\n    document.getElementById(\"mc-btn-start\").disabled = false;\n    document.getElementById(\"mc-btn-start\").textContent = \"\\u25b6 Start\";\n    document.getElementById(\"mc-btn-pause\").disabled = true;\n    document.getElementById(\"mc-btn-stop\").disabled = true;\n    document.getElementById(\"mc-timer-display\").classList.remove(\"mc-timer-running\");\n    setStatusBadge(\"idle\");\n    document.getElementById(\"mc-results\").classList.remove(\"visible\");\n  };\n\n  function setStatusBadge(state) {\n    var badge = document.getElementById(\"mc-status-badge\");\n    badge.className = \"mc-status \" + state;\n    badge.textContent = state.charAt(0).toUpperCase() + state.slice(1);\n  }\n\n  /* ---- Results ---- */\n  var COMPARISONS = [\n    { icon: \"\\u2615\", name: \"cups of coffee\", price: 5 },\n    { icon: \"\\ud83c\\udfac\", name: \"months of Netflix\", price: 15.49 },\n    { icon: \"\\ud83c\\udf55\", name: \"large pizzas\", price: 18 },\n    { icon: \"\\ud83d\\udcda\", name: \"business books\", price: 20 },\n    { icon: \"\\ud83c\\udfce\\ufe0f\", name: \"Uber rides\", price: 22 },\n    { icon: \"\\ud83c\\udfb5\", name: \"Spotify months\", price: 10.99 },\n    { icon: \"\\ud83e\\udd50\", name: \"Starbucks lattes\", price: 6.5 },\n    { icon: \"\\ud83c\\udf89\", name: \"board game nights (rental)\", price: 12 },\n  ];\n\n  function showResults() {\n    var ms = timerElapsed;\n    var rate = getTotalHourlyRate();\n    var totalPeople = getTotalAttendees();\n    var totalMins = ms / 60000;\n    var totalCost = rate * (ms / 3600000);\n\n    document.getElementById(\"mc-res-total\").textContent = formatMoney(totalCost);\n    document.getElementById(\"mc-res-per-min\").textContent = totalMins \u003e 0 ? formatMoney(totalCost / totalMins) : \"$0.00\";\n    document.getElementById(\"mc-res-per-person\").textContent = (totalPeople \u003e 0 \u0026\u0026 totalCost \u003e 0) ? formatMoney(totalCost / totalPeople) : \"$0.00\";\n\n    // Comparisons\n    var compList = document.getElementById(\"mc-comp-list\");\n    compList.innerHTML = \"\";\n    COMPARISONS.forEach(function(c) {\n      var qty = Math.floor(totalCost / c.price);\n      if (qty \u003c 1) return;\n      var item = document.createElement(\"div\");\n      item.className = \"mc-comp-item\";\n      item.innerHTML =\n        '\u003cspan class=\"mc-comp-icon\"\u003e' + c.icon + '\u003c/span\u003e' +\n        '\u003cspan class=\"mc-comp-text\"\u003eThat\\'s \u003cstrong\u003e' + qty.toLocaleString() + ' ' + c.name + '\u003c/strong\u003e' +\n        ' ($' + c.price + ' each)\u003c/span\u003e';\n      compList.appendChild(item);\n    });\n    if (compList.children.length === 0) {\n      compList.innerHTML = '\u003cspan style=\"color:var(--text-dim);font-size:0.88rem;\"\u003eRun the meeting for a little longer to see comparisons!\u003c/span\u003e';\n    }\n\n    document.getElementById(\"mc-results\").classList.add(\"visible\");\n    document.getElementById(\"mc-results\").scrollIntoView({ behavior: \"smooth\", block: \"nearest\" });\n  }\n\n  /* ---- Init ---- */\n  buildPresets();\n  renderAttendees();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003ch2 id=\"related-articles\"\u003eRelated Articles\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://productivity-works.com/posts/best-remote-work-tools-2026/\"\u003eBest Remote Work Tools in 2026: The Complete Stack\u003c/a\u003e\n\u003c/li\u003e\n\u003c/ul\u003e","title":"Meeting Cost Calculator"},{"content":" Free Meme Generator Upload any image, add top and bottom text in classic meme style. Customize font, color, and outline. All processing happens in your browser — no uploads, no sign-up.\n🖼️ Drag \u0026 drop an image here JPG, PNG, GIF, WebP — up to 20 MB Choose File Preview 🎭 Upload an image to start creating your meme Meme Text Top Text Bottom Text Style Font Size 52px Outline Width 4px Text Color #ffffff Outline Color #000000 Alignment \u0026#9664; \u0026#9632; \u0026#9654; Download as PNG Reset How to Use STEP 1 — Upload an image: Drag and drop any image onto the drop zone, or click \u0026ldquo;Choose File.\u0026rdquo; JPG, PNG, GIF, and WebP files up to 20 MB are supported.\nSTEP 2 — Add text: Type your top and bottom text. Text is automatically uppercased to match classic meme style. It wraps automatically if it\u0026rsquo;s too long to fit.\nSTEP 3 — Customize style: Adjust font size (16–120 px), text color, outline color, and outline width. Classic meme style uses white text with a black outline — the defaults reflect this.\nSTEP 4 — Choose alignment: Toggle between left, center (default), and right alignment for both text blocks.\nSTEP 5 — Download: Click \u0026ldquo;Download as PNG\u0026rdquo; to save your meme. The canvas renders at full original image resolution.\nTips for Great Memes Keep text short. Classic memes use punchy phrases of 3–6 words per line. The generator wraps long text automatically, but shorter is almost always better.\nUse Impact font. The tool prioritizes Impact (the traditional meme font) and falls back to Arial Black. These wide, bold letterforms with a black outline are what define the classic meme aesthetic.\nOutline width matters. An outline width of 3–6 px works well for most images. Increase it for busy or dark backgrounds; decrease for light, clean backgrounds.\nFull resolution output. The PNG download is rendered at the original image\u0026rsquo;s pixel dimensions, so your meme stays sharp even on large displays.\nRelated Tools Resize or compress any image before adding text → Image Resizer Generate placeholder images for mockups or testing → Placeholder Image Generator ","permalink":"https://productivity-works.com/tools/meme-generator/","summary":"\u003cdiv id=\"mg-app\"\u003e\n\u003cstyle\u003e\n#mg-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  padding: 0 0 48px 0;\n  color: #1a202c;\n}\n\n#mg-app * {\n  box-sizing: border-box;\n}\n\n/* Hero */\n.mg-hero {\n  background: linear-gradient(135deg, #7c3aed 0%, #5b21b6 50%, #4c1d95 100%);\n  border-radius: 16px;\n  padding: 32px 28px;\n  margin-bottom: 24px;\n  color: #fff;\n}\n\n.mg-hero h2 {\n  margin: 0 0 6px 0;\n  font-size: 22px;\n  font-weight: 800;\n}\n\n.mg-hero p {\n  margin: 0;\n  font-size: 14px;\n  opacity: 0.88;\n  line-height: 1.7;\n}\n\n/* Drop Zone */\n.mg-dropzone {\n  border: 3px dashed #7c3aed;\n  border-radius: 14px;\n  padding: 44px 24px;\n  text-align: center;\n  background: #f5f3ff;\n  cursor: pointer;\n  transition: background 0.2s, border-color 0.2s;\n  margin-bottom: 20px;\n}\n\n.mg-dropzone.drag-over {\n  background: #ede9fe;\n  border-color: #5b21b6;\n}\n\n.mg-dropzone-icon {\n  font-size: 48px;\n  line-height: 1;\n  margin-bottom: 12px;\n  display: block;\n}\n\n.mg-dropzone-title {\n  font-size: 17px;\n  font-weight: 700;\n  color: #4c1d95;\n  margin-bottom: 6px;\n}\n\n.mg-dropzone-sub {\n  font-size: 13px;\n  color: #6b7280;\n  margin-bottom: 16px;\n}\n\n.mg-browse-btn {\n  display: inline-block;\n  padding: 10px 24px;\n  background: #7c3aed;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n\n.mg-browse-btn:hover {\n  background: #5b21b6;\n}\n\n#mg-file-input {\n  display: none;\n}\n\n/* Card */\n.mg-card {\n  background: #fff;\n  border-radius: 14px;\n  box-shadow: 0 2px 16px rgba(0,0,0,0.08);\n  padding: 22px 24px;\n  margin-bottom: 18px;\n}\n\n.mg-card h3 {\n  margin: 0 0 18px 0;\n  font-size: 13px;\n  font-weight: 700;\n  color: #374151;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  border-bottom: 2px solid #f5f3ff;\n  padding-bottom: 10px;\n}\n\n/* Canvas wrapper */\n.mg-canvas-wrap {\n  width: 100%;\n  overflow: hidden;\n  border-radius: 10px;\n  background: repeating-conic-gradient(#e5e7eb 0% 25%, #fff 0% 50%) 0 0 / 16px 16px;\n  border: 1px solid #e5e7eb;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  min-height: 200px;\n}\n\n#mg-canvas {\n  max-width: 100%;\n  max-height: 500px;\n  display: block;\n  object-fit: contain;\n}\n\n/* Text inputs */\n.mg-input-group {\n  display: flex;\n  flex-direction: column;\n  gap: 5px;\n  margin-bottom: 14px;\n}\n\n.mg-input-group label {\n  font-size: 12px;\n  font-weight: 700;\n  color: #374151;\n  letter-spacing: 0.02em;\n}\n\n.mg-text-input {\n  padding: 10px 14px;\n  border: 2px solid #e9d5ff;\n  border-radius: 8px;\n  font-size: 15px;\n  color: #1a202c;\n  transition: border-color 0.2s;\n  width: 100%;\n}\n\n.mg-text-input:focus {\n  outline: none;\n  border-color: #7c3aed;\n}\n\n/* Controls row */\n.mg-controls-row {\n  display: flex;\n  gap: 16px;\n  flex-wrap: wrap;\n  align-items: flex-start;\n}\n\n.mg-control-group {\n  flex: 1;\n  min-width: 140px;\n  display: flex;\n  flex-direction: column;\n  gap: 5px;\n}\n\n.mg-control-group label {\n  font-size: 12px;\n  font-weight: 700;\n  color: #374151;\n  letter-spacing: 0.02em;\n}\n\n.mg-control-input {\n  padding: 8px 12px;\n  border: 2px solid #e9d5ff;\n  border-radius: 8px;\n  font-size: 14px;\n  color: #1a202c;\n  width: 100%;\n  transition: border-color 0.2s;\n}\n\n.mg-control-input:focus {\n  outline: none;\n  border-color: #7c3aed;\n}\n\n/* Color picker inline */\n.mg-color-wrap {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n\n.mg-color-swatch {\n  width: 36px;\n  height: 36px;\n  border-radius: 6px;\n  border: 2px solid #e9d5ff;\n  padding: 2px;\n  cursor: pointer;\n  background: none;\n}\n\n.mg-color-label-val {\n  font-size: 13px;\n  font-weight: 600;\n  color: #374151;\n  font-family: monospace;\n}\n\n/* Alignment buttons */\n.mg-align-btns {\n  display: flex;\n  gap: 8px;\n}\n\n.mg-align-btn {\n  flex: 1;\n  padding: 8px 10px;\n  border: 2px solid #e9d5ff;\n  border-radius: 8px;\n  background: #fff;\n  font-size: 16px;\n  cursor: pointer;\n  transition: all 0.18s;\n  text-align: center;\n}\n\n.mg-align-btn:hover {\n  border-color: #7c3aed;\n  background: #f5f3ff;\n}\n\n.mg-align-btn.active {\n  border-color: #7c3aed;\n  background: #7c3aed;\n  color: #fff;\n}\n\n/* Slider */\n.mg-slider-wrap {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n\n.mg-slider {\n  flex: 1;\n  height: 6px;\n  border-radius: 3px;\n  background: linear-gradient(to right, #e9d5ff, #7c3aed);\n  -webkit-appearance: none;\n  appearance: none;\n  outline: none;\n  cursor: pointer;\n}\n\n.mg-slider::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 18px;\n  height: 18px;\n  border-radius: 50%;\n  background: #fff;\n  border: 2px solid #7c3aed;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.15);\n  cursor: pointer;\n}\n\n.mg-slider::-moz-range-thumb {\n  width: 18px;\n  height: 18px;\n  border-radius: 50%;\n  background: #fff;\n  border: 2px solid #7c3aed;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.15);\n  cursor: pointer;\n}\n\n.mg-slider-val {\n  font-size: 14px;\n  font-weight: 700;\n  color: #7c3aed;\n  min-width: 36px;\n  text-align: right;\n}\n\n/* Action buttons */\n.mg-actions {\n  display: flex;\n  gap: 12px;\n  flex-wrap: wrap;\n}\n\n.mg-download-btn {\n  flex: 1;\n  min-width: 160px;\n  padding: 14px 24px;\n  background: linear-gradient(135deg, #7c3aed, #5b21b6);\n  color: #fff;\n  border: none;\n  border-radius: 12px;\n  font-size: 16px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: opacity 0.2s, transform 0.1s;\n  text-align: center;\n}\n\n.mg-download-btn:hover:not(:disabled) { opacity: 0.92; }\n.mg-download-btn:active:not(:disabled) { transform: scale(0.98); }\n.mg-download-btn:disabled { background: #9ca3af; cursor: not-allowed; }\n\n.mg-reset-btn {\n  padding: 14px 24px;\n  background: #fff;\n  color: #7c3aed;\n  border: 2px solid #7c3aed;\n  border-radius: 12px;\n  font-size: 15px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.18s;\n}\n\n.mg-reset-btn:hover {\n  background: #f5f3ff;\n}\n\n/* Placeholder */\n.mg-placeholder {\n  text-align: center;\n  padding: 32px 16px;\n  color: #9ca3af;\n  font-size: 14px;\n}\n\n.mg-placeholder-icon {\n  font-size: 40px;\n  display: block;\n  margin-bottom: 8px;\n}\n\n/* Responsive */\n@media (max-width: 600px) {\n  .mg-hero { padding: 22px 16px; }\n  .mg-hero h2 { font-size: 18px; }\n  .mg-card { padding: 16px 14px; }\n  .mg-controls-row { gap: 10px; }\n  .mg-actions { flex-direction: column; }\n  .mg-reset-btn { width: 100%; text-align: center; }\n}\n\u003c/style\u003e\n\u003c!-- Hero --\u003e\n\u003cdiv class=\"mg-hero\"\u003e\n  \u003ch2\u003eFree Meme Generator\u003c/h2\u003e\n  \u003cp\u003eUpload any image, add top and bottom text in classic meme style. Customize font, color, and outline. All processing happens in your browser — no uploads, no sign-up.\u003c/p\u003e","title":"Meme Generator - Custom Meme Maker"},{"content":" Basic SEO Tags Page Title (recommended: 50–60 chars) 0 / 60 Meta Description (recommended: 120–160 chars) 0 / 160 Keywords (comma-separated) Author Indexing index noindex Link Following follow nofollow Open Graph Tags og:title og:type website article product video.movie music.song book og:description og:url (canonical URL) og:image URL Twitter Card Tags twitter:card summary_large_image summary app player twitter:title twitter:image URL twitter:description Google SERP Preview https://example.com/page Your Page Title Your meta description will appear here. Make it compelling and within 160 characters for best results. Social Share Preview Facebook / Open Graph og:image preview example.com Your OG Title og:description will appear here. Twitter / X Card twitter:image preview Your Twitter Title twitter:description will appear here. example.com Generated HTML Copy Related: Generate favicons \u0026rarr; Favicon Generator Related Tools Html Beautifier Html Entity Converter Html Entity Encoder Related Articles How to Use ChatGPT for Etsy Shop Descriptions (Complete 2026 Guide) Etsy SEO: How to Generate Perfect Tags with ChatGPT (Step-by-Step) Best Web Hosting 2026: Compared for Speed, Price \u0026amp; WordPress ","permalink":"https://productivity-works.com/tools/meta-tag-generator/","summary":"\u003cstyle\u003e\n#meta-app *,\n#meta-app *::before,\n#meta-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#meta-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  color: #1e293b;\n  max-width: 860px;\n  margin: 0 auto;\n  padding: 0 0 2rem;\n}\n#meta-app h2 {\n  font-size: 1.15rem;\n  font-weight: 700;\n  color: #0f172a;\n  margin: 1.75rem 0 1rem;\n  padding-bottom: 0.4rem;\n  border-bottom: 2px solid #e2e8f0;\n}\n#meta-app .field {\n  margin-bottom: 1.1rem;\n}\n#meta-app label {\n  display: block;\n  font-size: 0.85rem;\n  font-weight: 600;\n  color: #374151;\n  margin-bottom: 0.3rem;\n}\n#meta-app input[type=\"text\"],\n#meta-app input[type=\"url\"],\n#meta-app select,\n#meta-app textarea {\n  width: 100%;\n  padding: 0.55rem 0.75rem;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 0.93rem;\n  background: #fff;\n  color: #1e293b;\n  transition: border-color 0.18s;\n  outline: none;\n}\n#meta-app input[type=\"text\"]:focus,\n#meta-app input[type=\"url\"]:focus,\n#meta-app select:focus,\n#meta-app textarea:focus {\n  border-color: #2563eb;\n  box-shadow: 0 0 0 3px rgba(37,99,235,0.12);\n}\n#meta-app textarea {\n  resize: vertical;\n  min-height: 72px;\n}\n#meta-app .counter {\n  font-size: 0.78rem;\n  margin-top: 0.25rem;\n  font-weight: 500;\n  transition: color 0.18s;\n}\n#meta-app .counter.ok   { color: #16a34a; }\n#meta-app .counter.warn { color: #d97706; }\n#meta-app .counter.over { color: #dc2626; }\n#meta-app .row2 {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1rem;\n}\n#meta-app .row3 {\n  display: grid;\n  grid-template-columns: 1fr 1fr 1fr;\n  gap: 1rem;\n}\n@media (max-width: 580px) {\n  #meta-app .row2,\n  #meta-app .row3 { grid-template-columns: 1fr; }\n}\n\n/* SERP Preview */\n#meta-app .serp-box {\n  background: #fff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 1.1rem 1.3rem;\n  margin-top: 0.5rem;\n}\n#meta-app .serp-url {\n  font-size: 0.82rem;\n  color: #16a34a;\n  margin-bottom: 0.18rem;\n  word-break: break-all;\n}\n#meta-app .serp-title {\n  font-size: 1.12rem;\n  color: #2563eb;\n  font-weight: 600;\n  cursor: pointer;\n  line-height: 1.3;\n  margin-bottom: 0.22rem;\n  word-break: break-word;\n}\n#meta-app .serp-desc {\n  font-size: 0.88rem;\n  color: #4b5563;\n  line-height: 1.5;\n  word-break: break-word;\n}\n\n/* Social Previews */\n#meta-app .social-row {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1rem;\n  margin-top: 0.5rem;\n}\n@media (max-width: 580px) {\n  #meta-app .social-row { grid-template-columns: 1fr; }\n}\n#meta-app .social-card {\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  overflow: hidden;\n  background: #f8fafc;\n}\n#meta-app .social-card-header {\n  background: #e2e8f0;\n  padding: 0.45rem 0.8rem;\n  font-size: 0.78rem;\n  font-weight: 700;\n  color: #475569;\n  letter-spacing: 0.04em;\n  text-transform: uppercase;\n}\n#meta-app .social-img-placeholder {\n  background: #cbd5e1;\n  height: 130px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: #94a3b8;\n  font-size: 0.82rem;\n  position: relative;\n  overflow: hidden;\n}\n#meta-app .social-img-placeholder img {\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n  display: none;\n}\n#meta-app .social-img-placeholder .placeholder-text { position: absolute; }\n#meta-app .social-body {\n  padding: 0.6rem 0.8rem 0.7rem;\n}\n#meta-app .fb-card .social-domain {\n  font-size: 0.72rem;\n  color: #6b7280;\n  text-transform: uppercase;\n  margin-bottom: 0.2rem;\n}\n#meta-app .fb-card .social-title {\n  font-size: 0.9rem;\n  font-weight: 700;\n  color: #1e293b;\n  margin-bottom: 0.15rem;\n  line-height: 1.3;\n}\n#meta-app .fb-card .social-desc {\n  font-size: 0.78rem;\n  color: #6b7280;\n  line-height: 1.4;\n}\n#meta-app .tw-card-inner {\n  border-radius: 0 0 10px 10px;\n  overflow: hidden;\n}\n#meta-app .tw-card .social-body {\n  background: #fff;\n  border-top: 1px solid #e2e8f0;\n}\n#meta-app .tw-card .social-title {\n  font-size: 0.88rem;\n  font-weight: 700;\n  color: #0f172a;\n  margin-bottom: 0.15rem;\n}\n#meta-app .tw-card .social-desc {\n  font-size: 0.78rem;\n  color: #6b7280;\n  line-height: 1.4;\n}\n#meta-app .tw-card .social-domain {\n  font-size: 0.72rem;\n  color: #6b7280;\n  margin-top: 0.2rem;\n}\n\n/* Output */\n#meta-app .output-box {\n  position: relative;\n  margin-top: 0.5rem;\n}\n#meta-app pre {\n  background: #0f172a;\n  color: #e2e8f0;\n  padding: 1.1rem 1.2rem;\n  border-radius: 8px;\n  font-size: 0.82rem;\n  line-height: 1.65;\n  overflow-x: auto;\n  white-space: pre-wrap;\n  word-break: break-word;\n  font-family: \"Fira Code\", \"Cascadia Code\", \"Consolas\", monospace;\n}\n#meta-app .copy-btn {\n  position: absolute;\n  top: 0.6rem;\n  right: 0.6rem;\n  background: #2563eb;\n  color: #fff;\n  border: none;\n  padding: 0.35rem 0.85rem;\n  border-radius: 5px;\n  font-size: 0.8rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.16s;\n}\n#meta-app .copy-btn:hover { background: #1d4ed8; }\n#meta-app .copy-btn.copied { background: #16a34a; }\n\n/* Related */\n#meta-app .related-link {\n  margin-top: 2rem;\n  padding: 0.9rem 1.1rem;\n  background: #eff6ff;\n  border-left: 4px solid #2563eb;\n  border-radius: 0 6px 6px 0;\n  font-size: 0.92rem;\n  color: #1e293b;\n}\n#meta-app .related-link a {\n  color: #2563eb;\n  font-weight: 600;\n  text-decoration: none;\n}\n#meta-app .related-link a:hover { text-decoration: underline; }\n\u003c/style\u003e\n\u003cdiv id=\"meta-app\"\u003e\n\u003ch2\u003eBasic SEO Tags\u003c/h2\u003e\n\u003cdiv class=\"field\"\u003e\n  \u003clabel for=\"mg-title\"\u003ePage Title \u003cspan style=\"font-weight:400;color:#6b7280;\"\u003e(recommended: 50–60 chars)\u003c/span\u003e\u003c/label\u003e\n  \u003cinput type=\"text\" id=\"mg-title\" placeholder=\"My Awesome Page Title\" maxlength=\"100\" oninput=\"mgUpdate()\"\u003e\n  \u003cdiv class=\"counter ok\" id=\"cnt-title\"\u003e0 / 60\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"field\"\u003e\n  \u003clabel for=\"mg-desc\"\u003eMeta Description \u003cspan style=\"font-weight:400;color:#6b7280;\"\u003e(recommended: 120–160 chars)\u003c/span\u003e\u003c/label\u003e\n  \u003ctextarea id=\"mg-desc\" placeholder=\"A concise description of the page that appears in search results.\" maxlength=\"300\" oninput=\"mgUpdate()\"\u003e\u003c/textarea\u003e\n  \u003cdiv class=\"counter ok\" id=\"cnt-desc\"\u003e0 / 160\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"row2\"\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"mg-keywords\"\u003eKeywords \u003cspan style=\"font-weight:400;color:#6b7280;\"\u003e(comma-separated)\u003c/span\u003e\u003c/label\u003e\n    \u003cinput type=\"text\" id=\"mg-keywords\" placeholder=\"seo, meta tags, generator\" oninput=\"mgUpdate()\"\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"mg-author\"\u003eAuthor\u003c/label\u003e\n    \u003cinput type=\"text\" id=\"mg-author\" placeholder=\"Jane Doe\" oninput=\"mgUpdate()\"\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"row2\"\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"mg-robots-index\"\u003eIndexing\u003c/label\u003e\n    \u003cselect id=\"mg-robots-index\" onchange=\"mgUpdate()\"\u003e\n      \u003coption value=\"index\"\u003eindex\u003c/option\u003e\n      \u003coption value=\"noindex\"\u003enoindex\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"mg-robots-follow\"\u003eLink Following\u003c/label\u003e\n    \u003cselect id=\"mg-robots-follow\" onchange=\"mgUpdate()\"\u003e\n      \u003coption value=\"follow\"\u003efollow\u003c/option\u003e\n      \u003coption value=\"nofollow\"\u003enofollow\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003ch2\u003eOpen Graph Tags\u003c/h2\u003e\n\u003cdiv class=\"row2\"\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"mg-og-title\"\u003eog:title\u003c/label\u003e\n    \u003cinput type=\"text\" id=\"mg-og-title\" placeholder=\"Same as page title, or custom\" oninput=\"mgUpdate()\"\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"mg-og-type\"\u003eog:type\u003c/label\u003e\n    \u003cselect id=\"mg-og-type\" onchange=\"mgUpdate()\"\u003e\n      \u003coption value=\"website\"\u003ewebsite\u003c/option\u003e\n      \u003coption value=\"article\"\u003earticle\u003c/option\u003e\n      \u003coption value=\"product\"\u003eproduct\u003c/option\u003e\n      \u003coption value=\"video.movie\"\u003evideo.movie\u003c/option\u003e\n      \u003coption value=\"music.song\"\u003emusic.song\u003c/option\u003e\n      \u003coption value=\"book\"\u003ebook\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"field\"\u003e\n  \u003clabel for=\"mg-og-desc\"\u003eog:description\u003c/label\u003e\n  \u003ctextarea id=\"mg-og-desc\" placeholder=\"Description for social sharing.\" oninput=\"mgUpdate()\"\u003e\u003c/textarea\u003e\n\u003c/div\u003e\n\u003cdiv class=\"row2\"\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"mg-og-url\"\u003eog:url (canonical URL)\u003c/label\u003e\n    \u003cinput type=\"url\" id=\"mg-og-url\" placeholder=\"https://example.com/page\" oninput=\"mgUpdate()\"\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"mg-og-image\"\u003eog:image URL\u003c/label\u003e\n    \u003cinput type=\"url\" id=\"mg-og-image\" placeholder=\"https://example.com/image.jpg\" oninput=\"mgUpdate()\"\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003ch2\u003eTwitter Card Tags\u003c/h2\u003e\n\u003cdiv class=\"row3\"\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"mg-tw-card\"\u003etwitter:card\u003c/label\u003e\n    \u003cselect id=\"mg-tw-card\" onchange=\"mgUpdate()\"\u003e\n      \u003coption value=\"summary_large_image\"\u003esummary_large_image\u003c/option\u003e\n      \u003coption value=\"summary\"\u003esummary\u003c/option\u003e\n      \u003coption value=\"app\"\u003eapp\u003c/option\u003e\n      \u003coption value=\"player\"\u003eplayer\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"mg-tw-title\"\u003etwitter:title\u003c/label\u003e\n    \u003cinput type=\"text\" id=\"mg-tw-title\" placeholder=\"Twitter-specific title\" oninput=\"mgUpdate()\"\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"mg-tw-image\"\u003etwitter:image URL\u003c/label\u003e\n    \u003cinput type=\"url\" id=\"mg-tw-image\" placeholder=\"https://example.com/image.jpg\" oninput=\"mgUpdate()\"\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"field\"\u003e\n  \u003clabel for=\"mg-tw-desc\"\u003etwitter:description\u003c/label\u003e\n  \u003ctextarea id=\"mg-tw-desc\" placeholder=\"Description for Twitter sharing.\" oninput=\"mgUpdate()\" style=\"min-height:56px;\"\u003e\u003c/textarea\u003e\n\u003c/div\u003e\n\u003ch2\u003eGoogle SERP Preview\u003c/h2\u003e\n\u003cdiv class=\"serp-box\"\u003e\n  \u003cdiv class=\"serp-url\" id=\"prev-url\"\u003ehttps://example.com/page\u003c/div\u003e\n  \u003cdiv class=\"serp-title\" id=\"prev-title\"\u003eYour Page Title\u003c/div\u003e\n  \u003cdiv class=\"serp-desc\" id=\"prev-desc\"\u003eYour meta description will appear here. Make it compelling and within 160 characters for best results.\u003c/div\u003e\n\u003c/div\u003e\n\u003ch2\u003eSocial Share Preview\u003c/h2\u003e\n\u003cdiv class=\"social-row\"\u003e\n  \u003cdiv class=\"social-card fb-card\"\u003e\n    \u003cdiv class=\"social-card-header\"\u003eFacebook / Open Graph\u003c/div\u003e\n    \u003cdiv class=\"social-img-placeholder\" id=\"fb-img-wrap\"\u003e\n      \u003cimg id=\"fb-img\" src=\"\" alt=\"OG Image\"\u003e\n      \u003cspan class=\"placeholder-text\" id=\"fb-img-placeholder\"\u003eog:image preview\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"social-body\"\u003e\n      \u003cdiv class=\"social-domain\" id=\"fb-domain\"\u003eexample.com\u003c/div\u003e\n      \u003cdiv class=\"social-title\" id=\"fb-title\"\u003eYour OG Title\u003c/div\u003e\n      \u003cdiv class=\"social-desc\" id=\"fb-desc\"\u003eog:description will appear here.\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"social-card tw-card\"\u003e\n    \u003cdiv class=\"social-card-header\"\u003eTwitter / X Card\u003c/div\u003e\n    \u003cdiv class=\"tw-card-inner\"\u003e\n      \u003cdiv class=\"social-img-placeholder\" id=\"tw-img-wrap\"\u003e\n        \u003cimg id=\"tw-img\" src=\"\" alt=\"Twitter Image\"\u003e\n        \u003cspan class=\"placeholder-text\" id=\"tw-img-placeholder\"\u003etwitter:image preview\u003c/span\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"social-body\"\u003e\n        \u003cdiv class=\"social-title\" id=\"tw-title\"\u003eYour Twitter Title\u003c/div\u003e\n        \u003cdiv class=\"social-desc\" id=\"tw-desc\"\u003etwitter:description will appear here.\u003c/div\u003e\n        \u003cdiv class=\"social-domain\" id=\"tw-domain\"\u003eexample.com\u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003ch2\u003eGenerated HTML\u003c/h2\u003e\n\u003cdiv class=\"output-box\"\u003e\n  \u003cpre id=\"mg-output\"\u003e\u003c!-- Fill in the form above to generate tags --\u003e\u003c/pre\u003e\n  \u003cbutton class=\"copy-btn\" id=\"copy-btn\" onclick=\"mgCopy()\"\u003eCopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"related-link\"\u003e\n  Related: Generate favicons \u0026rarr; \u003ca href=\"/tools/favicon-generator/\"\u003eFavicon Generator\u003c/a\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  function v(id) { return document.getElementById(id); }\n\n  function setCounter(elId, current, max) {\n    var el = v(elId);\n    el.textContent = current + \" / \" + max;\n    el.className = \"counter\";\n    if (current \u003c= max * 0.8) el.classList.add(\"ok\");\n    else if (current \u003c= max) el.classList.add(\"warn\");\n    else el.classList.add(\"over\");\n  }\n\n  function esc(s) {\n    return s.replace(/\u0026/g,\"\u0026amp;\").replace(/\"/g,\"\u0026quot;\").replace(/\u003c/g,\"\u0026lt;\").replace(/\u003e/g,\"\u0026gt;\");\n  }\n\n  function getDomain(url) {\n    try { return new URL(url).hostname || \"example.com\"; }\n    catch(e) { return url.replace(/^https?:\\/\\//,\"\").split(\"/\")[0] || \"example.com\"; }\n  }\n\n  function updateImg(imgId, wrapId, phId, url) {\n    var img = v(imgId), wrap = v(wrapId), ph = v(phId);\n    if (url \u0026\u0026 url.trim()) {\n      img.src = url.trim();\n      img.style.display = \"block\";\n      ph.style.display = \"none\";\n    } else {\n      img.style.display = \"none\";\n      ph.style.display = \"block\";\n    }\n  }\n\n  window.mgUpdate = function () {\n    var title   = v(\"mg-title\").value;\n    var desc    = v(\"mg-desc\").value;\n    var kw      = v(\"mg-keywords\").value;\n    var author  = v(\"mg-author\").value;\n    var rIdx    = v(\"mg-robots-index\").value;\n    var rFol    = v(\"mg-robots-follow\").value;\n\n    var ogTitle = v(\"mg-og-title\").value || title;\n    var ogDesc  = v(\"mg-og-desc\").value || desc;\n    var ogType  = v(\"mg-og-type\").value;\n    var ogUrl   = v(\"mg-og-url\").value;\n    var ogImg   = v(\"mg-og-image\").value;\n\n    var twCard  = v(\"mg-tw-card\").value;\n    var twTitle = v(\"mg-tw-title\").value || ogTitle;\n    var twDesc  = v(\"mg-tw-desc\").value || ogDesc;\n    var twImg   = v(\"mg-tw-image\").value || ogImg;\n\n    // Counters\n    setCounter(\"cnt-title\", title.length, 60);\n    setCounter(\"cnt-desc\", desc.length, 160);\n\n    // SERP Preview\n    v(\"prev-title\").textContent = title || \"Your Page Title\";\n    v(\"prev-url\").textContent   = ogUrl || \"https://example.com/page\";\n    v(\"prev-desc\").textContent  = desc  || \"Your meta description will appear here.\";\n\n    // FB Preview\n    var domain = getDomain(ogUrl);\n    v(\"fb-domain\").textContent = domain;\n    v(\"fb-title\").textContent  = ogTitle || \"Your OG Title\";\n    v(\"fb-desc\").textContent   = ogDesc  || \"og:description will appear here.\";\n    updateImg(\"fb-img\",\"fb-img-wrap\",\"fb-img-placeholder\", ogImg);\n\n    // Twitter Preview\n    v(\"tw-domain\").textContent = domain;\n    v(\"tw-title\").textContent  = twTitle || \"Your Twitter Title\";\n    v(\"tw-desc\").textContent   = twDesc  || \"twitter:description will appear here.\";\n    updateImg(\"tw-img\",\"tw-img-wrap\",\"tw-img-placeholder\", twImg);\n\n    // Build output\n    var lines = [];\n    lines.push(\"\u003c!-- Primary Meta Tags --\u003e\");\n    if (title)  lines.push('\u003cmeta name=\"title\" content=\"' + esc(title) + '\"\u003e');\n    if (desc)   lines.push('\u003cmeta name=\"description\" content=\"' + esc(desc) + '\"\u003e');\n    if (kw)     lines.push('\u003cmeta name=\"keywords\" content=\"' + esc(kw) + '\"\u003e');\n    if (author) lines.push('\u003cmeta name=\"author\" content=\"' + esc(author) + '\"\u003e');\n    lines.push('\u003cmeta name=\"robots\" content=\"' + rIdx + \", \" + rFol + '\"\u003e');\n\n    lines.push(\"\");\n    lines.push(\"\u003c!-- Open Graph / Facebook --\u003e\");\n    lines.push('\u003cmeta property=\"og:type\" content=\"' + esc(ogType) + '\"\u003e');\n    if (ogUrl)   lines.push('\u003cmeta property=\"og:url\" content=\"' + esc(ogUrl) + '\"\u003e');\n    if (ogTitle) lines.push('\u003cmeta property=\"og:title\" content=\"' + esc(ogTitle) + '\"\u003e');\n    if (ogDesc)  lines.push('\u003cmeta property=\"og:description\" content=\"' + esc(ogDesc) + '\"\u003e');\n    if (ogImg)   lines.push('\u003cmeta property=\"og:image\" content=\"' + esc(ogImg) + '\"\u003e');\n\n    lines.push(\"\");\n    lines.push(\"\u003c!-- Twitter Card --\u003e\");\n    lines.push('\u003cmeta property=\"twitter:card\" content=\"' + esc(twCard) + '\"\u003e');\n    if (ogUrl)   lines.push('\u003cmeta property=\"twitter:url\" content=\"' + esc(ogUrl) + '\"\u003e');\n    if (twTitle) lines.push('\u003cmeta property=\"twitter:title\" content=\"' + esc(twTitle) + '\"\u003e');\n    if (twDesc)  lines.push('\u003cmeta property=\"twitter:description\" content=\"' + esc(twDesc) + '\"\u003e');\n    if (twImg)   lines.push('\u003cmeta property=\"twitter:image\" content=\"' + esc(twImg) + '\"\u003e');\n\n    v(\"mg-output\").textContent = lines.join(\"\\n\");\n  };\n\n  window.mgCopy = function () {\n    var text = v(\"mg-output\").textContent;\n    if (!text || text.trim() === \"\u003c!-- Fill in the form above to generate tags --\u003e\") return;\n    navigator.clipboard.writeText(text).then(function () {\n      var btn = v(\"copy-btn\");\n      btn.textContent = \"Copied!\";\n      btn.classList.add(\"copied\");\n      setTimeout(function () {\n        btn.textContent = \"Copy\";\n        btn.classList.remove(\"copied\");\n      }, 2000);\n    });\n  };\n\n  // Init\n  mgUpdate();\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"https://productivity-works.com/tools/html-beautifier/\"\u003eHtml Beautifier\u003c/a\u003e\n\n\u003ca href=\"https://productivity-works.com/tools/html-entity-converter/\"\u003eHtml Entity Converter\u003c/a\u003e\n\n\u003ca href=\"https://productivity-works.com/tools/html-entity-encoder/\"\u003eHtml Entity Encoder\u003c/a\u003e\n\u003c/p\u003e","title":"Meta Tag Generator — SEO \u0026 Open Graph Tags"},{"content":" Text → Morse Morse → Text Input Text Clear Morse Code Output Copy Copied! \u0026#9654; Play Audio \u0026#9646;\u0026#9646; Stop Speed (WPM): Ready Visual Display Reference Chart (click any row to insert) Encrypt text → Text Encryption Tool Generate barcodes → Barcode Generator ","permalink":"https://productivity-works.com/tools/morse-code/","summary":"\u003cdiv id=\"mc-app\"\u003e\n\u003cstyle\u003e\n#mc-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 820px;\n  margin: 0 auto;\n  color: #1e293b;\n}\n#mc-app * {\n  box-sizing: border-box;\n}\n#mc-app h2 {\n  font-size: 1.1rem;\n  font-weight: 700;\n  margin: 0 0 10px 0;\n  color: #0f172a;\n}\n#mc-app .mc-mode-bar {\n  display: flex;\n  gap: 8px;\n  margin-bottom: 18px;\n}\n#mc-app .mc-mode-btn {\n  padding: 9px 20px;\n  border: 2px solid #cbd5e1;\n  border-radius: 8px;\n  background: #fff;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  color: #475569;\n  transition: all 0.15s;\n}\n#mc-app .mc-mode-btn.active {\n  background: #0284c7;\n  border-color: #0284c7;\n  color: #fff;\n}\n#mc-app .mc-mode-btn:hover:not(.active) {\n  border-color: #0284c7;\n  color: #0284c7;\n}\n#mc-app .mc-panels {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 16px;\n  margin-bottom: 16px;\n}\n@media (max-width: 600px) {\n  #mc-app .mc-panels {\n    grid-template-columns: 1fr;\n  }\n}\n#mc-app .mc-panel {\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n}\n#mc-app .mc-panel label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #475569;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n#mc-app .mc-textarea {\n  width: 100%;\n  min-height: 140px;\n  padding: 12px 14px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 10px;\n  font-size: 15px;\n  font-family: inherit;\n  resize: vertical;\n  color: #1e293b;\n  background: #f8fafc;\n  transition: border-color 0.15s;\n  line-height: 1.6;\n}\n#mc-app .mc-textarea:focus {\n  outline: none;\n  border-color: #0284c7;\n  background: #fff;\n}\n#mc-app .mc-textarea[readonly] {\n  background: #f1f5f9;\n  color: #334155;\n}\n#mc-app .mc-output-box {\n  width: 100%;\n  min-height: 140px;\n  padding: 12px 14px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 10px;\n  font-size: 14px;\n  font-family: 'Courier New', monospace;\n  background: #f1f5f9;\n  color: #334155;\n  word-break: break-all;\n  white-space: pre-wrap;\n  line-height: 1.8;\n  min-height: 140px;\n}\n#mc-app .mc-actions {\n  display: flex;\n  gap: 8px;\n  flex-wrap: wrap;\n  align-items: center;\n}\n#mc-app .mc-btn {\n  padding: 8px 16px;\n  border-radius: 7px;\n  border: none;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#mc-app .mc-btn-primary {\n  background: #0284c7;\n  color: #fff;\n}\n#mc-app .mc-btn-primary:hover {\n  background: #0369a1;\n}\n#mc-app .mc-btn-secondary {\n  background: #e2e8f0;\n  color: #334155;\n}\n#mc-app .mc-btn-secondary:hover {\n  background: #cbd5e1;\n}\n#mc-app .mc-btn-danger {\n  background: #fee2e2;\n  color: #dc2626;\n}\n#mc-app .mc-btn-danger:hover {\n  background: #fecaca;\n}\n#mc-app .mc-controls {\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 14px 16px;\n  margin-bottom: 16px;\n  display: flex;\n  align-items: center;\n  gap: 20px;\n  flex-wrap: wrap;\n}\n#mc-app .mc-controls label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #475569;\n}\n#mc-app .mc-wpm-group {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n#mc-app .mc-wpm-input {\n  width: 70px;\n  padding: 6px 10px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  font-size: 14px;\n  font-weight: 600;\n  text-align: center;\n  color: #1e293b;\n}\n#mc-app .mc-wpm-input:focus {\n  outline: none;\n  border-color: #0284c7;\n}\n#mc-app .mc-status {\n  font-size: 12px;\n  color: #64748b;\n  margin-left: auto;\n}\n#mc-app .mc-visual {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 4px;\n  padding: 12px 14px;\n  background: #f1f5f9;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  margin-bottom: 16px;\n  min-height: 52px;\n  align-items: center;\n}\n#mc-app .mc-visual-dot {\n  display: inline-block;\n  width: 10px;\n  height: 10px;\n  border-radius: 50%;\n  background: #0284c7;\n}\n#mc-app .mc-visual-dash {\n  display: inline-block;\n  width: 28px;\n  height: 10px;\n  border-radius: 5px;\n  background: #0284c7;\n}\n#mc-app .mc-visual-space {\n  display: inline-block;\n  width: 20px;\n}\n#mc-app .mc-visual-letter-gap {\n  display: inline-block;\n  width: 8px;\n}\n#mc-app .mc-ref {\n  margin-top: 24px;\n}\n#mc-app .mc-ref-title {\n  font-size: 1rem;\n  font-weight: 700;\n  color: #0f172a;\n  margin-bottom: 12px;\n}\n#mc-app .mc-ref-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));\n  gap: 6px;\n}\n#mc-app .mc-ref-item {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 8px;\n  padding: 8px 10px;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  cursor: pointer;\n  transition: all 0.12s;\n}\n#mc-app .mc-ref-item:hover {\n  background: #e0f2fe;\n  border-color: #7dd3fc;\n}\n#mc-app .mc-ref-char {\n  font-size: 16px;\n  font-weight: 700;\n  color: #0284c7;\n  min-width: 20px;\n}\n#mc-app .mc-ref-morse {\n  font-size: 12px;\n  font-family: 'Courier New', monospace;\n  color: #475569;\n}\n#mc-app .mc-copy-toast {\n  display: none;\n  font-size: 12px;\n  color: #16a34a;\n  font-weight: 600;\n}\n#mc-app .mc-copy-toast.show {\n  display: inline;\n}\n#mc-app .mc-error {\n  color: #dc2626;\n  font-size: 13px;\n  margin-top: 4px;\n}\n\u003c/style\u003e\n\u003cdiv class=\"mc-mode-bar\"\u003e\n  \u003cbutton class=\"mc-mode-btn active\" id=\"mc-btn-t2m\" onclick=\"mcSetMode('t2m')\"\u003eText → Morse\u003c/button\u003e\n  \u003cbutton class=\"mc-mode-btn\" id=\"mc-btn-m2t\" onclick=\"mcSetMode('m2t')\"\u003eMorse → Text\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"mc-panels\"\u003e\n  \u003cdiv class=\"mc-panel\"\u003e\n    \u003clabel id=\"mc-input-label\"\u003eInput Text\u003c/label\u003e\n    \u003ctextarea class=\"mc-textarea\" id=\"mc-input\" placeholder=\"Type text here...\" oninput=\"mcConvert()\" autocomplete=\"off\" spellcheck=\"false\"\u003e\u003c/textarea\u003e\n    \u003cdiv class=\"mc-actions\"\u003e\n      \u003cbutton class=\"mc-btn mc-btn-danger\" onclick=\"mcClear()\"\u003eClear\u003c/button\u003e\n      \u003cspan class=\"mc-error\" id=\"mc-error\"\u003e\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"mc-panel\"\u003e\n    \u003clabel id=\"mc-output-label\"\u003eMorse Code Output\u003c/label\u003e\n    \u003cdiv class=\"mc-output-box\" id=\"mc-output\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"mc-actions\"\u003e\n      \u003cbutton class=\"mc-btn mc-btn-secondary\" onclick=\"mcCopy()\"\u003eCopy\u003c/button\u003e\n      \u003cspan class=\"mc-copy-toast\" id=\"mc-copy-toast\"\u003eCopied!\u003c/span\u003e\n      \u003cbutton class=\"mc-btn mc-btn-primary\" id=\"mc-play-btn\" onclick=\"mcPlay()\"\u003e\u0026#9654; Play Audio\u003c/button\u003e\n      \u003cbutton class=\"mc-btn mc-btn-danger\" id=\"mc-stop-btn\" onclick=\"mcStop()\" style=\"display:none;\"\u003e\u0026#9646;\u0026#9646; Stop\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"mc-controls\"\u003e\n  \u003cdiv class=\"mc-wpm-group\"\u003e\n    \u003clabel for=\"mc-wpm\"\u003eSpeed (WPM):\u003c/label\u003e\n    \u003cinput type=\"number\" class=\"mc-wpm-input\" id=\"mc-wpm\" value=\"15\" min=\"5\" max=\"40\" onchange=\"mcUpdateWpm()\"\u003e\n  \u003c/div\u003e\n  \u003cspan class=\"mc-status\" id=\"mc-status\"\u003eReady\u003c/span\u003e\n\u003c/div\u003e\n\u003cdiv id=\"mc-visual-area\"\u003e\n  \u003ch2\u003eVisual Display\u003c/h2\u003e\n  \u003cdiv class=\"mc-visual\" id=\"mc-visual\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"mc-ref\"\u003e\n  \u003cdiv class=\"mc-ref-title\"\u003eReference Chart \u003cspan style=\"font-size:12px;font-weight:400;color:#64748b;\"\u003e(click any row to insert)\u003c/span\u003e\u003c/div\u003e\n  \u003cdiv class=\"mc-ref-grid\" id=\"mc-ref-grid\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var CODE = {\n    'A':'.-','B':'-...','C':'-.-.','D':'-..','E':'.','F':'..-.','G':'--.','H':'....','I':'..','J':'.---',\n    'K':'-.-','L':'.-..','M':'--','N':'-.','O':'---','P':'.--.','Q':'--.-','R':'.-.','S':'...','T':'-',\n    'U':'..-','V':'...-','W':'.--','X':'-..-','Y':'-.--','Z':'--..',\n    '0':'-----','1':'.----','2':'..---','3':'...--','4':'....-','5':'.....','6':'-....','7':'--...','8':'---..','9':'----.',\n    '.':'.-.-.-',',':'--..--','?':'..--..','!':'-.-.--','/':'-..-.','(':'-.--.',')':'-.--.-',\n    '\u0026':'.-...',':':'---...',';':'-.-.-.','=':'-...-','+':'.-.-.', '-':'-....-','_':'..--.-',\n    '\"':'.-..-.','$':'...-..-','@':'.--.-.','\\'':'.----.'\n  };\n  var DECODE = {};\n  for (var k in CODE) DECODE[CODE[k]] = k;\n\n  var mode = 't2m';\n  var wpm = 15;\n  var audioCtx = null;\n  var playing = false;\n  var stopFlag = false;\n\n  function getDotDuration() {\n    // PARIS standard: 1 WPM = 1200ms / WPM dot duration\n    return 1200 / wpm;\n  }\n\n  window.mcSetMode = function(m) {\n    mode = m;\n    document.getElementById('mc-btn-t2m').classList.toggle('active', m === 't2m');\n    document.getElementById('mc-btn-m2t').classList.toggle('active', m === 'm2t');\n    var inp = document.getElementById('mc-input');\n    var inpLabel = document.getElementById('mc-input-label');\n    var outLabel = document.getElementById('mc-output-label');\n    inp.value = '';\n    document.getElementById('mc-output').textContent = '';\n    document.getElementById('mc-visual').innerHTML = '';\n    document.getElementById('mc-error').textContent = '';\n    if (m === 't2m') {\n      inp.placeholder = 'Type text here...';\n      inpLabel.textContent = 'Input Text';\n      outLabel.textContent = 'Morse Code Output';\n    } else {\n      inp.placeholder = 'Enter Morse code (dots and dashes, space between letters, / between words)';\n      inpLabel.textContent = 'Input Morse Code';\n      outLabel.textContent = 'Decoded Text';\n    }\n  };\n\n  window.mcConvert = function() {\n    var inp = document.getElementById('mc-input').value;\n    var out = '';\n    var errEl = document.getElementById('mc-error');\n    errEl.textContent = '';\n\n    if (mode === 't2m') {\n      var upper = inp.toUpperCase();\n      var words = upper.split(' ');\n      var morseWords = [];\n      for (var wi = 0; wi \u003c words.length; wi++) {\n        var word = words[wi];\n        var letters = [];\n        for (var li = 0; li \u003c word.length; li++) {\n          var ch = word[li];\n          if (CODE[ch]) {\n            letters.push(CODE[ch]);\n          } else if (ch !== '') {\n            // skip unknown silently\n          }\n        }\n        if (letters.length) morseWords.push(letters.join(' '));\n      }\n      out = morseWords.join(' / ');\n    } else {\n      // morse to text\n      var trimmed = inp.trim();\n      if (!trimmed) { document.getElementById('mc-output').textContent = ''; updateVisual(''); return; }\n      var wordGroups = trimmed.split(/\\s*\\/\\s*/);\n      var decoded = [];\n      var errors = [];\n      for (var wg = 0; wg \u003c wordGroups.length; wg++) {\n        var wgVal = wordGroups[wg].trim();\n        if (!wgVal) continue;\n        var tokens = wgVal.split(/\\s+/);\n        var word = '';\n        for (var t = 0; t \u003c tokens.length; t++) {\n          var tok = tokens[t];\n          if (!tok) continue;\n          if (DECODE[tok]) {\n            word += DECODE[tok];\n          } else {\n            word += '?';\n            errors.push(tok);\n          }\n        }\n        decoded.push(word);\n      }\n      out = decoded.join(' ');\n      if (errors.length) {\n        errEl.textContent = 'Unknown: ' + errors.slice(0,5).join(', ');\n      }\n    }\n\n    document.getElementById('mc-output').textContent = out;\n    updateVisual(mode === 't2m' ? out : '');\n  };\n\n  function updateVisual(morseStr) {\n    var vis = document.getElementById('mc-visual');\n    vis.innerHTML = '';\n    if (!morseStr) return;\n    var words = morseStr.split(' / ');\n    for (var wi = 0; wi \u003c words.length; wi++) {\n      if (wi \u003e 0) {\n        var sp = document.createElement('span');\n        sp.className = 'mc-visual-space';\n        vis.appendChild(sp);\n      }\n      var letters = words[wi].split(' ');\n      for (var li = 0; li \u003c letters.length; li++) {\n        if (li \u003e 0) {\n          var lg = document.createElement('span');\n          lg.className = 'mc-visual-letter-gap';\n          vis.appendChild(lg);\n        }\n        var sym = letters[li];\n        for (var si = 0; si \u003c sym.length; si++) {\n          var el = document.createElement('span');\n          if (sym[si] === '.') {\n            el.className = 'mc-visual-dot';\n          } else if (sym[si] === '-') {\n            el.className = 'mc-visual-dash';\n          }\n          vis.appendChild(el);\n        }\n      }\n    }\n  }\n\n  window.mcClear = function() {\n    mcStop();\n    document.getElementById('mc-input').value = '';\n    document.getElementById('mc-output').textContent = '';\n    document.getElementById('mc-visual').innerHTML = '';\n    document.getElementById('mc-error').textContent = '';\n    document.getElementById('mc-status').textContent = 'Ready';\n  };\n\n  window.mcCopy = function() {\n    var out = document.getElementById('mc-output').textContent;\n    if (!out) return;\n    navigator.clipboard.writeText(out).then(function() {\n      var t = document.getElementById('mc-copy-toast');\n      t.classList.add('show');\n      setTimeout(function() { t.classList.remove('show'); }, 2000);\n    }).catch(function() {\n      var ta = document.createElement('textarea');\n      ta.value = out;\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      var t = document.getElementById('mc-copy-toast');\n      t.classList.add('show');\n      setTimeout(function() { t.classList.remove('show'); }, 2000);\n    });\n  };\n\n  window.mcUpdateWpm = function() {\n    var v = parseInt(document.getElementById('mc-wpm').value, 10);\n    if (v \u003e= 5 \u0026\u0026 v \u003c= 40) wpm = v;\n  };\n\n  function getMorseToPlay() {\n    var out = document.getElementById('mc-output').textContent.trim();\n    if (mode === 'm2t') {\n      // play the input morse directly\n      out = document.getElementById('mc-input').value.trim();\n    }\n    return out;\n  }\n\n  window.mcPlay = function() {\n    if (playing) return;\n    var morse = getMorseToPlay();\n    if (!morse) {\n      document.getElementById('mc-status').textContent = 'Nothing to play.';\n      return;\n    }\n    stopFlag = false;\n    playing = true;\n    document.getElementById('mc-play-btn').style.display = 'none';\n    document.getElementById('mc-stop-btn').style.display = '';\n    document.getElementById('mc-status').textContent = 'Playing...';\n\n    if (!audioCtx) {\n      audioCtx = new (window.AudioContext || window.webkitAudioContext)();\n    }\n\n    playMorse(morse).then(function() {\n      playing = false;\n      stopFlag = false;\n      document.getElementById('mc-play-btn').style.display = '';\n      document.getElementById('mc-stop-btn').style.display = 'none';\n      document.getElementById('mc-status').textContent = 'Done';\n    });\n  };\n\n  window.mcStop = function() {\n    stopFlag = true;\n    playing = false;\n    document.getElementById('mc-play-btn').style.display = '';\n    document.getElementById('mc-stop-btn').style.display = 'none';\n    document.getElementById('mc-status').textContent = 'Stopped';\n  };\n\n  function sleep(ms) {\n    return new Promise(function(resolve) { setTimeout(resolve, ms); });\n  }\n\n  function beep(freq, duration) {\n    return new Promise(function(resolve) {\n      if (!audioCtx) { resolve(); return; }\n      var osc = audioCtx.createOscillator();\n      var gain = audioCtx.createGain();\n      osc.connect(gain);\n      gain.connect(audioCtx.destination);\n      osc.frequency.value = freq;\n      osc.type = 'sine';\n      gain.gain.setValueAtTime(0, audioCtx.currentTime);\n      gain.gain.linearRampToValueAtTime(0.4, audioCtx.currentTime + 0.005);\n      gain.gain.setValueAtTime(0.4, audioCtx.currentTime + duration / 1000 - 0.005);\n      gain.gain.linearRampToValueAtTime(0, audioCtx.currentTime + duration / 1000);\n      osc.start(audioCtx.currentTime);\n      osc.stop(audioCtx.currentTime + duration / 1000);\n      osc.onended = resolve;\n    });\n  }\n\n  async function playMorse(morse) {\n    var dot = getDotDuration();\n    var dash = dot * 3;\n    var symGap = dot;\n    var letterGap = dot * 3;\n    var wordGap = dot * 7;\n    var freq = 600;\n\n    var words = morse.split(/\\s*\\/\\s*/);\n    for (var wi = 0; wi \u003c words.length; wi++) {\n      if (stopFlag) return;\n      if (wi \u003e 0) await sleep(wordGap);\n      var letters = words[wi].trim().split(/\\s+/);\n      for (var li = 0; li \u003c letters.length; li++) {\n        if (stopFlag) return;\n        if (li \u003e 0) await sleep(letterGap);\n        var sym = letters[li];\n        for (var si = 0; si \u003c sym.length; si++) {\n          if (stopFlag) return;\n          if (si \u003e 0) await sleep(symGap);\n          if (sym[si] === '.') {\n            await beep(freq, dot);\n          } else if (sym[si] === '-') {\n            await beep(freq, dash);\n          }\n        }\n      }\n    }\n  }\n\n  function buildRefGrid() {\n    var grid = document.getElementById('mc-ref-grid');\n    var all = Object.keys(CODE);\n    for (var i = 0; i \u003c all.length; i++) {\n      var ch = all[i];\n      var item = document.createElement('div');\n      item.className = 'mc-ref-item';\n      item.innerHTML = '\u003cspan class=\"mc-ref-char\"\u003e' + ch + '\u003c/span\u003e\u003cspan class=\"mc-ref-morse\"\u003e' + CODE[ch] + '\u003c/span\u003e';\n      item.setAttribute('data-char', ch);\n      item.addEventListener('click', (function(c) {\n        return function() {\n          var inp = document.getElementById('mc-input');\n          if (mode === 't2m') {\n            inp.value += c;\n          } else {\n            var cur = inp.value;\n            var suffix = cur.length \u003e 0 \u0026\u0026 cur[cur.length-1] !== ' ' ? ' ' : '';\n            inp.value += suffix + CODE[c];\n          }\n          mcConvert();\n          inp.focus();\n        };\n      })(ch));\n      grid.appendChild(item);\n    }\n  }\n\n  buildRefGrid();\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003eEncrypt text → \u003ca href=\"https://productivity-works.com/tools/text-encryption/\"\u003eText Encryption Tool\u003c/a\u003e\n\nGenerate barcodes → \u003ca href=\"https://productivity-works.com/tools/barcode-generator/\"\u003eBarcode Generator\u003c/a\u003e\n\u003c/p\u003e","title":"Morse Code Translator"},{"content":" // MORSE CODE TRANSLATOR Text \u0026lt;--\u0026gt; Morse Code \u0026nbsp;|\u0026nbsp; Web Audio Playback \u0026nbsp;|\u0026nbsp; Auto-Detect Mode\nAuto-Detect Text → Morse Morse → Text Ready. Type in either box — Auto-Detect will figure out the direction. INPUT OUTPUT VISUAL DISPLAY dots and dashes will appear here... \u0026#9654; Play Audio \u0026#9632; Stop \u0026#10064; Copy Output \u0026#10005; Clear SPEED 15 WPM + Show Morse Reference Chart\nMorse Code Reference Letters Numbers Punctuation Copied! Related: Encode text with Base64 Encoder ","permalink":"https://productivity-works.com/tools/morse-code-translator/","summary":"\u003cdiv id=\"morse-app\"\u003e\n\u003cstyle\u003e\n#morse-app {\n  font-family: 'Courier New', 'Lucida Console', monospace;\n  background: #1a1a2e;\n  color: #e2e8f0;\n  border-radius: 14px;\n  padding: 28px;\n  max-width: 860px;\n  margin: 0 auto;\n  box-sizing: border-box;\n}\n#morse-app * { box-sizing: border-box; }\n\n#morse-app h2 {\n  color: #22c55e;\n  font-size: 1.2rem;\n  margin: 0 0 6px 0;\n  letter-spacing: 0.08em;\n  text-transform: uppercase;\n}\n\n.morse-subtitle {\n  color: #4ade80;\n  font-size: 0.78rem;\n  margin: 0 0 22px 0;\n  letter-spacing: 0.04em;\n  opacity: 0.75;\n}\n\n/* Mode selector */\n.morse-mode-bar {\n  display: flex;\n  gap: 8px;\n  margin-bottom: 20px;\n  flex-wrap: wrap;\n}\n.morse-mode-btn {\n  background: #16213e;\n  border: 1px solid #22c55e44;\n  color: #94a3b8;\n  padding: 7px 18px;\n  border-radius: 6px;\n  cursor: pointer;\n  font-family: inherit;\n  font-size: 0.82rem;\n  letter-spacing: 0.04em;\n  transition: all 0.18s;\n}\n.morse-mode-btn:hover { border-color: #22c55e; color: #22c55e; }\n.morse-mode-btn.active {\n  background: #22c55e;\n  border-color: #22c55e;\n  color: #1a1a2e;\n  font-weight: 700;\n}\n\n/* Panels */\n.morse-panels {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 16px;\n  margin-bottom: 16px;\n}\n@media (max-width: 640px) {\n  .morse-panels { grid-template-columns: 1fr; }\n}\n\n.morse-panel {\n  background: #16213e;\n  border: 1px solid #22c55e33;\n  border-radius: 10px;\n  padding: 16px;\n}\n.morse-panel-label {\n  font-size: 0.7rem;\n  color: #22c55e;\n  letter-spacing: 0.1em;\n  text-transform: uppercase;\n  margin-bottom: 10px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n\n.morse-textarea {\n  width: 100%;\n  min-height: 140px;\n  background: #0f172a;\n  border: 1px solid #22c55e22;\n  border-radius: 6px;\n  color: #e2e8f0;\n  font-family: 'Courier New', monospace;\n  font-size: 1rem;\n  padding: 12px;\n  resize: vertical;\n  outline: none;\n  transition: border-color 0.2s;\n  line-height: 1.6;\n}\n.morse-textarea:focus { border-color: #22c55e; }\n.morse-textarea[readonly] {\n  color: #4ade80;\n  font-size: 1.1rem;\n  letter-spacing: 0.06em;\n}\n\n/* Morse visual display */\n.morse-visual {\n  min-height: 60px;\n  background: #0f172a;\n  border: 1px solid #22c55e22;\n  border-radius: 6px;\n  padding: 12px;\n  display: flex;\n  flex-wrap: wrap;\n  gap: 4px;\n  align-items: center;\n  margin-top: 10px;\n}\n.morse-dot {\n  display: inline-block;\n  width: 8px;\n  height: 8px;\n  background: #22c55e;\n  border-radius: 50%;\n  box-shadow: 0 0 6px #22c55e88;\n}\n.morse-dash {\n  display: inline-block;\n  width: 24px;\n  height: 8px;\n  background: #22c55e;\n  border-radius: 4px;\n  box-shadow: 0 0 6px #22c55e88;\n}\n.morse-letter-gap {\n  display: inline-block;\n  width: 12px;\n}\n.morse-word-gap {\n  display: inline-block;\n  width: 28px;\n  border-left: 2px solid #22c55e22;\n  height: 20px;\n}\n\n/* Controls */\n.morse-controls {\n  display: flex;\n  gap: 12px;\n  margin-bottom: 16px;\n  flex-wrap: wrap;\n  align-items: center;\n}\n.morse-btn {\n  background: #22c55e;\n  border: none;\n  color: #1a1a2e;\n  padding: 9px 20px;\n  border-radius: 6px;\n  cursor: pointer;\n  font-family: inherit;\n  font-size: 0.85rem;\n  font-weight: 700;\n  letter-spacing: 0.06em;\n  transition: all 0.18s;\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n.morse-btn:hover { background: #4ade80; transform: translateY(-1px); }\n.morse-btn:active { transform: translateY(0); }\n.morse-btn.secondary {\n  background: #16213e;\n  border: 1px solid #22c55e55;\n  color: #22c55e;\n}\n.morse-btn.secondary:hover { background: #22c55e22; border-color: #22c55e; }\n.morse-btn:disabled {\n  opacity: 0.5;\n  cursor: not-allowed;\n  transform: none;\n}\n\n.morse-wpm-group {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin-left: auto;\n}\n.morse-wpm-label {\n  font-size: 0.75rem;\n  color: #64748b;\n  letter-spacing: 0.04em;\n}\n.morse-wpm-val {\n  color: #22c55e;\n  font-weight: 700;\n  min-width: 32px;\n}\n.morse-wpm-slider {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 120px;\n  height: 4px;\n  background: #22c55e33;\n  border-radius: 2px;\n  outline: none;\n}\n.morse-wpm-slider::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  width: 16px;\n  height: 16px;\n  background: #22c55e;\n  border-radius: 50%;\n  cursor: pointer;\n  box-shadow: 0 0 6px #22c55e88;\n}\n\n/* Status bar */\n.morse-status {\n  font-size: 0.75rem;\n  color: #4ade80;\n  min-height: 18px;\n  margin-bottom: 8px;\n  letter-spacing: 0.04em;\n}\n\n/* Reference chart */\n.morse-ref-toggle {\n  background: none;\n  border: 1px solid #22c55e33;\n  color: #64748b;\n  padding: 6px 14px;\n  border-radius: 6px;\n  cursor: pointer;\n  font-family: inherit;\n  font-size: 0.75rem;\n  letter-spacing: 0.05em;\n  transition: all 0.18s;\n  margin-bottom: 14px;\n}\n.morse-ref-toggle:hover { border-color: #22c55e; color: #22c55e; }\n\n.morse-ref {\n  display: none;\n  background: #16213e;\n  border: 1px solid #22c55e22;\n  border-radius: 10px;\n  padding: 20px;\n  margin-bottom: 10px;\n}\n.morse-ref.open { display: block; }\n\n.morse-ref h3 {\n  color: #22c55e;\n  font-size: 0.8rem;\n  letter-spacing: 0.1em;\n  text-transform: uppercase;\n  margin: 0 0 14px 0;\n}\n.morse-ref-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));\n  gap: 6px;\n}\n.morse-ref-item {\n  background: #0f172a;\n  border: 1px solid #22c55e22;\n  border-radius: 6px;\n  padding: 7px 10px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  font-size: 0.82rem;\n}\n.morse-ref-char {\n  color: #e2e8f0;\n  font-weight: 700;\n  font-size: 0.95rem;\n}\n.morse-ref-code {\n  color: #22c55e;\n  letter-spacing: 0.08em;\n}\n.morse-ref-section {\n  color: #64748b;\n  font-size: 0.7rem;\n  letter-spacing: 0.08em;\n  text-transform: uppercase;\n  margin: 14px 0 8px 0;\n}\n\n/* Copy toast */\n.morse-toast {\n  position: fixed;\n  bottom: 30px;\n  right: 30px;\n  background: #22c55e;\n  color: #1a1a2e;\n  padding: 10px 22px;\n  border-radius: 8px;\n  font-weight: 700;\n  font-size: 0.85rem;\n  opacity: 0;\n  pointer-events: none;\n  transition: opacity 0.3s;\n  z-index: 9999;\n  font-family: 'Courier New', monospace;\n}\n.morse-toast.show { opacity: 1; }\n\n/* Playing indicator */\n.morse-playing-bar {\n  height: 4px;\n  background: #22c55e22;\n  border-radius: 2px;\n  margin-bottom: 14px;\n  overflow: hidden;\n  display: none;\n}\n.morse-playing-bar.active { display: block; }\n.morse-playing-fill {\n  height: 100%;\n  background: #22c55e;\n  width: 0%;\n  transition: width 0.1s linear;\n  box-shadow: 0 0 8px #22c55e;\n}\n\u003c/style\u003e\n\u003ch2\u003e// MORSE CODE TRANSLATOR\u003c/h2\u003e\n\u003cp class=\"morse-subtitle\"\u003eText \u0026lt;--\u0026gt; Morse Code \u0026nbsp;|\u0026nbsp; Web Audio Playback \u0026nbsp;|\u0026nbsp; Auto-Detect Mode\u003c/p\u003e","title":"Morse Code Translator - Free Online Tool"},{"content":" Enter your financial details below to instantly find out the maximum home price you can afford, based on standard DTI (Debt-to-Income) lending guidelines used by most U.S. lenders. Your Financial Information Annual Gross Income Before taxes, all sources $ Total Monthly Debts Car, student loans, credit cards $ Down Payment Dollar amount $ Down Payment % Auto-syncs with amount above % Annual Interest Rate Current 30-yr avg ~6.8% % Loan Term Years 15 years 20 years 25 years 30 years Annual Property Tax Rate U.S. avg ~1.1% % Monthly Home Insurance Typical $100–$200/mo $ DTI Limit Guidelines Front-End DTI (Housing) — Limit: 28% Back-End DTI (All Debts) — Limit: 36% Calculate My Affordability Maximum Home Price You Can Afford $0 Based on DTI lending guidelines Max Monthly Payment — Loan Amount — Down Payment — Total Interest Paid — Total Cost of Loan — Interest Rate — Monthly Payment Breakdown Related Free Tools Compound Interest Calculator Monthly Budget Calculator Net Worth Calculator Savings Goal Calculator Loan Payoff Calculator This calculator is for educational purposes only and does not constitute financial advice.\nActual loan approval depends on credit score, lender criteria, and other factors. Consult a licensed mortgage professional. Related Tools Debt Payoff Calculator Loan Comparison Mortgage Calculator ","permalink":"https://productivity-works.com/tools/mortgage-affordability-calculator/","summary":"\u003cdiv id=\"ma-app\"\u003e\n\u003cstyle\u003e\n#ma-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #1a202c;\n  line-height: 1.6;\n}\n#ma-app h2 {\n  font-size: 1.5rem;\n  font-weight: 700;\n  margin: 0 0 1.25rem 0;\n  color: #1a202c;\n}\n#ma-app h3 {\n  font-size: 1.1rem;\n  font-weight: 600;\n  margin: 0 0 1rem 0;\n  color: #2d3748;\n}\n#ma-app .ma-intro {\n  background: #f0f7ff;\n  border-left: 4px solid #3182ce;\n  padding: 1rem 1.25rem;\n  border-radius: 0 8px 8px 0;\n  margin-bottom: 2rem;\n  font-size: 0.95rem;\n  color: #2c5282;\n}\n#ma-app .ma-card {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 1.75rem;\n  margin-bottom: 1.5rem;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.06);\n}\n#ma-app .ma-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1.25rem;\n}\n@media (max-width: 600px) {\n  #ma-app .ma-grid { grid-template-columns: 1fr; }\n}\n#ma-app .ma-field {\n  display: flex;\n  flex-direction: column;\n  gap: 0.4rem;\n}\n#ma-app .ma-field label {\n  font-size: 0.85rem;\n  font-weight: 600;\n  color: #4a5568;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#ma-app .ma-field .ma-hint {\n  font-size: 0.78rem;\n  color: #718096;\n  margin-top: -0.2rem;\n}\n#ma-app .ma-input-wrap {\n  position: relative;\n  display: flex;\n  align-items: center;\n}\n#ma-app .ma-input-wrap .ma-prefix,\n#ma-app .ma-input-wrap .ma-suffix {\n  position: absolute;\n  color: #718096;\n  font-size: 0.95rem;\n  font-weight: 500;\n  pointer-events: none;\n}\n#ma-app .ma-input-wrap .ma-prefix { left: 0.75rem; }\n#ma-app .ma-input-wrap .ma-suffix { right: 0.75rem; }\n#ma-app .ma-input-wrap input,\n#ma-app .ma-input-wrap select {\n  width: 100%;\n  border: 1.5px solid #cbd5e0;\n  border-radius: 8px;\n  padding: 0.6rem 0.85rem;\n  font-size: 1rem;\n  background: #fff;\n  color: #1a202c;\n  outline: none;\n  transition: border-color 0.2s;\n  box-sizing: border-box;\n}\n#ma-app .ma-input-wrap input:focus,\n#ma-app .ma-input-wrap select:focus {\n  border-color: #3182ce;\n  box-shadow: 0 0 0 3px rgba(49,130,206,0.15);\n}\n#ma-app .ma-input-wrap.has-prefix input { padding-left: 1.8rem; }\n#ma-app .ma-input-wrap.has-suffix input { padding-right: 2.2rem; }\n#ma-app .ma-dti-row {\n  display: flex;\n  gap: 1rem;\n  margin-top: 0.5rem;\n}\n#ma-app .ma-dti-chip {\n  flex: 1;\n  background: #ebf8ff;\n  border: 1px solid #bee3f8;\n  border-radius: 8px;\n  padding: 0.6rem 0.9rem;\n  text-align: center;\n}\n#ma-app .ma-dti-chip .ma-dti-label {\n  font-size: 0.75rem;\n  color: #2b6cb0;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#ma-app .ma-dti-chip .ma-dti-val {\n  font-size: 1.2rem;\n  font-weight: 700;\n  color: #2c5282;\n}\n#ma-app .ma-dti-chip.warn .ma-dti-val { color: #c05621; }\n#ma-app .ma-dti-chip.danger .ma-dti-val { color: #c53030; }\n#ma-app .ma-btn {\n  display: block;\n  width: 100%;\n  padding: 0.85rem 1.5rem;\n  background: linear-gradient(135deg, #3182ce, #2b6cb0);\n  color: #fff;\n  border: none;\n  border-radius: 10px;\n  font-size: 1.05rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: opacity 0.2s, transform 0.1s;\n  letter-spacing: 0.02em;\n}\n#ma-app .ma-btn:hover { opacity: 0.92; transform: translateY(-1px); }\n#ma-app .ma-btn:active { transform: translateY(0); }\n#ma-app .ma-results {\n  display: none;\n}\n#ma-app .ma-results.visible {\n  display: block;\n}\n#ma-app .ma-result-hero {\n  background: linear-gradient(135deg, #2b6cb0, #2c5282);\n  border-radius: 12px;\n  padding: 2rem;\n  text-align: center;\n  color: #fff;\n  margin-bottom: 1.5rem;\n}\n#ma-app .ma-result-hero .ma-hero-label {\n  font-size: 0.85rem;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  opacity: 0.85;\n  margin-bottom: 0.4rem;\n}\n#ma-app .ma-result-hero .ma-hero-price {\n  font-size: 2.8rem;\n  font-weight: 800;\n  line-height: 1.1;\n  margin-bottom: 0.75rem;\n}\n#ma-app .ma-result-hero .ma-hero-sub {\n  font-size: 1rem;\n  opacity: 0.9;\n}\n#ma-app .ma-stats-grid {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 1rem;\n  margin-bottom: 1.5rem;\n}\n@media (max-width: 600px) {\n  #ma-app .ma-stats-grid { grid-template-columns: 1fr 1fr; }\n}\n#ma-app .ma-stat {\n  background: #f7fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 1rem;\n  text-align: center;\n}\n#ma-app .ma-stat .ma-stat-label {\n  font-size: 0.75rem;\n  color: #718096;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  margin-bottom: 0.3rem;\n}\n#ma-app .ma-stat .ma-stat-value {\n  font-size: 1.25rem;\n  font-weight: 700;\n  color: #2d3748;\n}\n#ma-app .ma-stat .ma-stat-value.green { color: #276749; }\n#ma-app .ma-stat .ma-stat-value.orange { color: #c05621; }\n#ma-app .ma-chart-wrap {\n  display: flex;\n  gap: 2rem;\n  align-items: center;\n  flex-wrap: wrap;\n}\n#ma-app canvas#ma-pie {\n  width: 180px !important;\n  height: 180px !important;\n  flex-shrink: 0;\n}\n#ma-app .ma-legend {\n  flex: 1;\n  min-width: 180px;\n  display: flex;\n  flex-direction: column;\n  gap: 0.6rem;\n}\n#ma-app .ma-legend-item {\n  display: flex;\n  align-items: center;\n  gap: 0.65rem;\n  font-size: 0.9rem;\n}\n#ma-app .ma-legend-dot {\n  width: 14px;\n  height: 14px;\n  border-radius: 3px;\n  flex-shrink: 0;\n}\n#ma-app .ma-legend-name { color: #4a5568; flex: 1; }\n#ma-app .ma-legend-amt { font-weight: 700; color: #2d3748; }\n#ma-app .ma-alert {\n  border-radius: 8px;\n  padding: 0.85rem 1.1rem;\n  font-size: 0.88rem;\n  margin-top: 1rem;\n  display: flex;\n  align-items: flex-start;\n  gap: 0.6rem;\n}\n#ma-app .ma-alert.success { background: #f0fff4; border: 1px solid #9ae6b4; color: #276749; }\n#ma-app .ma-alert.warn { background: #fffbeb; border: 1px solid #fbd38d; color: #744210; }\n#ma-app .ma-alert.danger { background: #fff5f5; border: 1px solid #feb2b2; color: #742a2a; }\n#ma-app .ma-related {\n  background: #f7fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 1.5rem;\n  margin-top: 2rem;\n}\n#ma-app .ma-related h3 { margin-bottom: 0.75rem; }\n#ma-app .ma-related ul {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n  display: flex;\n  flex-direction: column;\n  gap: 0.5rem;\n}\n#ma-app .ma-related ul li a {\n  color: #3182ce;\n  text-decoration: none;\n  font-weight: 500;\n  font-size: 0.95rem;\n}\n#ma-app .ma-related ul li a:hover { text-decoration: underline; }\n#ma-app .ma-disclaimer {\n  font-size: 0.78rem;\n  color: #a0aec0;\n  margin-top: 1.5rem;\n  text-align: center;\n  line-height: 1.5;\n}\n\u003c/style\u003e\n\u003cdiv class=\"ma-intro\"\u003e\n  Enter your financial details below to instantly find out the maximum home price you can afford, based on standard DTI (Debt-to-Income) lending guidelines used by most U.S. lenders.\n\u003c/div\u003e\n\u003cdiv class=\"ma-card\"\u003e\n  \u003ch2\u003eYour Financial Information\u003c/h2\u003e\n  \u003cdiv class=\"ma-grid\"\u003e\n    \u003cdiv class=\"ma-field\"\u003e\n      \u003clabel\u003eAnnual Gross Income\u003c/label\u003e\n      \u003cspan class=\"ma-hint\"\u003eBefore taxes, all sources\u003c/span\u003e\n      \u003cdiv class=\"ma-input-wrap has-prefix\"\u003e\n        \u003cspan class=\"ma-prefix\"\u003e$\u003c/span\u003e\n        \u003cinput type=\"number\" id=\"ma-income\" value=\"90000\" min=\"0\" step=\"1000\" /\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ma-field\"\u003e\n      \u003clabel\u003eTotal Monthly Debts\u003c/label\u003e\n      \u003cspan class=\"ma-hint\"\u003eCar, student loans, credit cards\u003c/span\u003e\n      \u003cdiv class=\"ma-input-wrap has-prefix\"\u003e\n        \u003cspan class=\"ma-prefix\"\u003e$\u003c/span\u003e\n        \u003cinput type=\"number\" id=\"ma-debts\" value=\"500\" min=\"0\" step=\"50\" /\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ma-field\"\u003e\n      \u003clabel\u003eDown Payment\u003c/label\u003e\n      \u003cspan class=\"ma-hint\"\u003eDollar amount\u003c/span\u003e\n      \u003cdiv class=\"ma-input-wrap has-prefix\"\u003e\n        \u003cspan class=\"ma-prefix\"\u003e$\u003c/span\u003e\n        \u003cinput type=\"number\" id=\"ma-downpayment\" value=\"60000\" min=\"0\" step=\"1000\" /\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ma-field\"\u003e\n      \u003clabel\u003eDown Payment %\u003c/label\u003e\n      \u003cspan class=\"ma-hint\"\u003eAuto-syncs with amount above\u003c/span\u003e\n      \u003cdiv class=\"ma-input-wrap has-suffix\"\u003e\n        \u003cinput type=\"number\" id=\"ma-downpct\" value=\"\" min=\"0\" max=\"100\" step=\"0.5\" placeholder=\"e.g. 20\" /\u003e\n        \u003cspan class=\"ma-suffix\"\u003e%\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ma-field\"\u003e\n      \u003clabel\u003eAnnual Interest Rate\u003c/label\u003e\n      \u003cspan class=\"ma-hint\"\u003eCurrent 30-yr avg ~6.8%\u003c/span\u003e\n      \u003cdiv class=\"ma-input-wrap has-suffix\"\u003e\n        \u003cinput type=\"number\" id=\"ma-rate\" value=\"6.8\" min=\"0.1\" max=\"20\" step=\"0.05\" /\u003e\n        \u003cspan class=\"ma-suffix\"\u003e%\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ma-field\"\u003e\n      \u003clabel\u003eLoan Term\u003c/label\u003e\n      \u003cspan class=\"ma-hint\"\u003eYears\u003c/span\u003e\n      \u003cdiv class=\"ma-input-wrap\"\u003e\n        \u003cselect id=\"ma-term\"\u003e\n          \u003coption value=\"15\"\u003e15 years\u003c/option\u003e\n          \u003coption value=\"20\"\u003e20 years\u003c/option\u003e\n          \u003coption value=\"25\"\u003e25 years\u003c/option\u003e\n          \u003coption value=\"30\" selected\u003e30 years\u003c/option\u003e\n        \u003c/select\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ma-field\"\u003e\n      \u003clabel\u003eAnnual Property Tax Rate\u003c/label\u003e\n      \u003cspan class=\"ma-hint\"\u003eU.S. avg ~1.1%\u003c/span\u003e\n      \u003cdiv class=\"ma-input-wrap has-suffix\"\u003e\n        \u003cinput type=\"number\" id=\"ma-tax\" value=\"1.1\" min=\"0\" max=\"5\" step=\"0.05\" /\u003e\n        \u003cspan class=\"ma-suffix\"\u003e%\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ma-field\"\u003e\n      \u003clabel\u003eMonthly Home Insurance\u003c/label\u003e\n      \u003cspan class=\"ma-hint\"\u003eTypical $100–$200/mo\u003c/span\u003e\n      \u003cdiv class=\"ma-input-wrap has-prefix\"\u003e\n        \u003cspan class=\"ma-prefix\"\u003e$\u003c/span\u003e\n        \u003cinput type=\"number\" id=\"ma-insurance\" value=\"150\" min=\"0\" step=\"10\" /\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv style=\"margin-top:1.25rem;\"\u003e\n    \u003cdiv style=\"font-size:0.85rem;font-weight:600;color:#4a5568;text-transform:uppercase;letter-spacing:0.04em;margin-bottom:0.5rem;\"\u003eDTI Limit Guidelines\u003c/div\u003e\n    \u003cdiv class=\"ma-dti-row\"\u003e\n      \u003cdiv class=\"ma-dti-chip\" id=\"ma-frontend-chip\"\u003e\n        \u003cdiv class=\"ma-dti-label\"\u003eFront-End DTI (Housing)\u003c/div\u003e\n        \u003cdiv class=\"ma-dti-val\" id=\"ma-frontend-val\"\u003e—\u003c/div\u003e\n        \u003cdiv style=\"font-size:0.72rem;color:#4a5568;\"\u003eLimit: 28%\u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"ma-dti-chip\" id=\"ma-backend-chip\"\u003e\n        \u003cdiv class=\"ma-dti-label\"\u003eBack-End DTI (All Debts)\u003c/div\u003e\n        \u003cdiv class=\"ma-dti-val\" id=\"ma-backend-val\"\u003e—\u003c/div\u003e\n        \u003cdiv style=\"font-size:0.72rem;color:#4a5568;\"\u003eLimit: 36%\u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cbutton class=\"ma-btn\" id=\"ma-calc-btn\" style=\"margin-top:1.5rem;\" onclick=\"maCalculate()\"\u003e\n    Calculate My Affordability\n  \u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ma-results\" id=\"ma-results\"\u003e\n  \u003cdiv class=\"ma-result-hero\"\u003e\n    \u003cdiv class=\"ma-hero-label\"\u003eMaximum Home Price You Can Afford\u003c/div\u003e\n    \u003cdiv class=\"ma-hero-price\" id=\"ma-max-price\"\u003e$0\u003c/div\u003e\n    \u003cdiv class=\"ma-hero-sub\" id=\"ma-hero-sub\"\u003eBased on DTI lending guidelines\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ma-stats-grid\"\u003e\n    \u003cdiv class=\"ma-stat\"\u003e\n      \u003cdiv class=\"ma-stat-label\"\u003eMax Monthly Payment\u003c/div\u003e\n      \u003cdiv class=\"ma-stat-value\" id=\"ma-max-payment\"\u003e—\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ma-stat\"\u003e\n      \u003cdiv class=\"ma-stat-label\"\u003eLoan Amount\u003c/div\u003e\n      \u003cdiv class=\"ma-stat-value\" id=\"ma-loan-amt\"\u003e—\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ma-stat\"\u003e\n      \u003cdiv class=\"ma-stat-label\"\u003eDown Payment\u003c/div\u003e\n      \u003cdiv class=\"ma-stat-value green\" id=\"ma-dp-display\"\u003e—\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ma-stat\"\u003e\n      \u003cdiv class=\"ma-stat-label\"\u003eTotal Interest Paid\u003c/div\u003e\n      \u003cdiv class=\"ma-stat-value orange\" id=\"ma-total-interest\"\u003e—\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ma-stat\"\u003e\n      \u003cdiv class=\"ma-stat-label\"\u003eTotal Cost of Loan\u003c/div\u003e\n      \u003cdiv class=\"ma-stat-value\" id=\"ma-total-cost\"\u003e—\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ma-stat\"\u003e\n      \u003cdiv class=\"ma-stat-label\"\u003eInterest Rate\u003c/div\u003e\n      \u003cdiv class=\"ma-stat-value\" id=\"ma-rate-display\"\u003e—\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ma-card\"\u003e\n    \u003ch3\u003eMonthly Payment Breakdown\u003c/h3\u003e\n    \u003cdiv class=\"ma-chart-wrap\"\u003e\n      \u003ccanvas id=\"ma-pie\" width=\"180\" height=\"180\"\u003e\u003c/canvas\u003e\n      \u003cdiv class=\"ma-legend\" id=\"ma-legend\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv id=\"ma-dti-alert\" class=\"ma-alert\" style=\"display:none;\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ma-related\"\u003e\n    \u003ch3\u003eRelated Free Tools\u003c/h3\u003e\n    \u003cul\u003e\n      \u003cli\u003e\u003ca href=\"/tools/compound-interest-calculator/\"\u003eCompound Interest Calculator\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"/tools/budget-planner/\"\u003eMonthly Budget Calculator\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"/tools/net-worth-calculator/\"\u003eNet Worth Calculator\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"/tools/savings-goal-calculator/\"\u003eSavings Goal Calculator\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"/tools/debt-payoff-calculator/\"\u003eLoan Payoff Calculator\u003c/a\u003e\u003c/li\u003e\n    \u003c/ul\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cp class=\"ma-disclaimer\"\u003e\n  This calculator is for educational purposes only and does not constitute financial advice.\u003cbr\u003e\n  Actual loan approval depends on credit score, lender criteria, and other factors. Consult a licensed mortgage professional.\n\u003c/p\u003e","title":"Mortgage Affordability Calculator - How Much House Can You Afford?"},{"content":"Create soft UI shadow effects with full control over distance, blur, intensity, shape type, and border radius. Pick a background color and the generator automatically calculates the correct light and dark shadows for a perfect neumorphic look. Copy the CSS in one click.\nSoft Card Button Circle Input Field Toggle Switch Controls \u0026lt;label class=\u0026quot;nm-label\u0026quot;\u0026gt;Background Color\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;nm-color-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;nm-bg\u0026quot; value=\u0026quot;#e0e5ec\u0026quot; oninput=\u0026quot;nmUpdate()\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;nm-color-hex\u0026quot; id=\u0026quot;nm-bg-hex\u0026quot;\u0026gt;#e0e5ec\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;nm-divider\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;label class=\u0026quot;nm-label\u0026quot;\u0026gt;Shape Type\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;nm-seg\u0026quot; id=\u0026quot;nm-shape-seg\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;active\u0026quot; onclick=\u0026quot;nmShape('flat',this)\u0026quot;\u0026gt;Flat\u0026lt;/button\u0026gt; \u0026lt;button onclick=\u0026quot;nmShape('concave',this)\u0026quot;\u0026gt;Concave\u0026lt;/button\u0026gt; \u0026lt;button onclick=\u0026quot;nmShape('convex',this)\u0026quot;\u0026gt;Convex\u0026lt;/button\u0026gt; \u0026lt;button onclick=\u0026quot;nmShape('pressed',this)\u0026quot;\u0026gt;Pressed\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;label class=\u0026quot;nm-label\u0026quot;\u0026gt;Element Shape\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;nm-seg\u0026quot; id=\u0026quot;nm-el-seg\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;active\u0026quot; onclick=\u0026quot;nmElShape('card',this)\u0026quot;\u0026gt;Card\u0026lt;/button\u0026gt; \u0026lt;button onclick=\u0026quot;nmElShape('button',this)\u0026quot;\u0026gt;Button\u0026lt;/button\u0026gt; \u0026lt;button onclick=\u0026quot;nmElShape('circle',this)\u0026quot;\u0026gt;Circle\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;nm-divider\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;label class=\u0026quot;nm-label\u0026quot;\u0026gt;Size \u0026lt;span class=\u0026quot;nm-val\u0026quot; id=\u0026quot;v-size\u0026quot;\u0026gt;160px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;nm-size\u0026quot; min=\u0026quot;60\u0026quot; max=\u0026quot;300\u0026quot; value=\u0026quot;160\u0026quot; oninput=\u0026quot;nmUpdate()\u0026quot;\u0026gt; \u0026lt;label class=\u0026quot;nm-label\u0026quot;\u0026gt;Shadow Distance \u0026lt;span class=\u0026quot;nm-val\u0026quot; id=\u0026quot;v-dist\u0026quot;\u0026gt;10px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;nm-dist\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;50\u0026quot; value=\u0026quot;10\u0026quot; oninput=\u0026quot;nmUpdate()\u0026quot;\u0026gt; \u0026lt;label class=\u0026quot;nm-label\u0026quot;\u0026gt;Blur \u0026lt;span class=\u0026quot;nm-val\u0026quot; id=\u0026quot;v-blur\u0026quot;\u0026gt;20px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;nm-blur\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;100\u0026quot; value=\u0026quot;20\u0026quot; oninput=\u0026quot;nmUpdate()\u0026quot;\u0026gt; \u0026lt;label class=\u0026quot;nm-label\u0026quot;\u0026gt;Intensity \u0026lt;span class=\u0026quot;nm-val\u0026quot; id=\u0026quot;v-int\u0026quot;\u0026gt;0.25\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;nm-int\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;50\u0026quot; value=\u0026quot;25\u0026quot; oninput=\u0026quot;nmUpdate()\u0026quot;\u0026gt; \u0026lt;label class=\u0026quot;nm-label\u0026quot;\u0026gt;Border Radius \u0026lt;span class=\u0026quot;nm-val\u0026quot; id=\u0026quot;v-rad\u0026quot;\u0026gt;16px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;nm-rad\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;60\u0026quot; value=\u0026quot;16\u0026quot; oninput=\u0026quot;nmUpdate()\u0026quot;\u0026gt; PREVIEW Copy CSS How Neumorphism Works Neumorphism (soft UI) creates depth by casting two shadows from a single element — one light shadow in the top-left direction and one dark shadow in the bottom-right direction, both derived from the background color. The element itself must use the same color as its container background, which is why the background color picker is the most critical control.\nKey tips:\nKeep background colors in a mid-range lightness (not too dark, not too bright) Shadow intensity above 0.4 can look harsh — 0.15–0.30 is the sweet spot Use Pressed shape for active states like clicked buttons or focused inputs Concave and Convex use a gradient overlay to enhance the 3D illusion Neumorphism works best at larger sizes — tiny elements lose the effect Related Tools Glassmorphism effects → Glassmorphism Generator Box shadows → Box Shadow Generator CSS buttons → CSS Button Generator ","permalink":"https://productivity-works.com/tools/neumorphism-generator/","summary":"\u003cp\u003eCreate soft UI shadow effects with full control over distance, blur, intensity, shape type, and border radius. Pick a background color and the generator automatically calculates the correct light and dark shadows for a perfect neumorphic look. Copy the CSS in one click.\u003c/p\u003e\n\u003cdiv id=\"nm-app\" style=\"font-family:system-ui,-apple-system,sans-serif;max-width:900px;margin:0 auto;color:#333;\"\u003e\n\u003cstyle\u003e\n#nm-app *,#nm-app *::before,#nm-app *::after{box-sizing:border-box;}\n#nm-app .nm-layout{display:grid;grid-template-columns:1fr 1fr;gap:24px;margin-top:16px;}\n@media(max-width:660px){#nm-app .nm-layout{grid-template-columns:1fr;}}\n#nm-app .nm-panel{background:#f5f5f5;border-radius:14px;padding:20px;}\n#nm-app .nm-preview-wrap{display:flex;align-items:center;justify-content:center;border-radius:14px;min-height:220px;transition:background .3s;}\n#nm-app .nm-preview-el{transition:all .3s;cursor:default;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:600;color:#888;letter-spacing:.04em;}\n#nm-app label.nm-label{display:block;font-size:12px;font-weight:700;color:#555;margin-bottom:4px;margin-top:14px;text-transform:uppercase;letter-spacing:.06em;}\n#nm-app label.nm-label:first-child{margin-top:0;}\n#nm-app input[type=range]{width:100%;accent-color:#6366f1;height:4px;cursor:pointer;}\n#nm-app input[type=color]{width:48px;height:32px;border:none;border-radius:6px;cursor:pointer;padding:2px;background:none;}\n#nm-app .nm-row{display:flex;align-items:center;gap:10px;}\n#nm-app .nm-val{font-size:12px;color:#6366f1;font-weight:700;min-width:36px;text-align:right;}\n#nm-app .nm-seg{display:flex;gap:6px;flex-wrap:wrap;}\n#nm-app .nm-seg button{padding:5px 12px;border-radius:6px;border:1.5px solid #ddd;background:#fff;font-size:12px;cursor:pointer;font-weight:600;color:#555;transition:all .15s;}\n#nm-app .nm-seg button.active{background:#6366f1;color:#fff;border-color:#6366f1;}\n#nm-app .nm-presets{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px;}\n#nm-app .nm-preset-btn{padding:6px 14px;border-radius:20px;border:1.5px solid #c7d2fe;background:#eef2ff;font-size:12px;cursor:pointer;font-weight:600;color:#4338ca;transition:all .15s;}\n#nm-app .nm-preset-btn:hover{background:#6366f1;color:#fff;border-color:#6366f1;}\n#nm-app .nm-code-wrap{position:relative;margin-top:16px;}\n#nm-app .nm-code{background:#1e1e2e;color:#cdd6f4;border-radius:10px;padding:16px;font-size:12.5px;font-family:'Fira Mono','Courier New',monospace;line-height:1.7;overflow-x:auto;white-space:pre;tab-size:2;}\n#nm-app .nm-code .c-prop{color:#89b4fa;}\n#nm-app .nm-code .c-val{color:#a6e3a1;}\n#nm-app .nm-code .c-punc{color:#cdd6f4;}\n#nm-app .nm-copy-btn{position:absolute;top:10px;right:10px;padding:5px 14px;background:#6366f1;color:#fff;border:none;border-radius:6px;font-size:12px;font-weight:700;cursor:pointer;transition:background .15s;}\n#nm-app .nm-copy-btn:hover{background:#4f46e5;}\n#nm-app .nm-copy-btn.copied{background:#22c55e;}\n#nm-app h3.nm-section-title{margin:0 0 12px;font-size:14px;font-weight:800;color:#4338ca;text-transform:uppercase;letter-spacing:.08em;}\n#nm-app .nm-color-row{display:flex;align-items:center;gap:10px;}\n#nm-app .nm-color-hex{font-size:13px;font-weight:700;color:#555;font-family:monospace;}\n#nm-app .nm-divider{height:1px;background:#e5e7eb;margin:14px 0;}\n\u003c/style\u003e\n\u003cdiv class=\"nm-presets\"\u003e\n  \u003cbutton class=\"nm-preset-btn\" onclick=\"nmLoadPreset('softcard')\"\u003eSoft Card\u003c/button\u003e\n  \u003cbutton class=\"nm-preset-btn\" onclick=\"nmLoadPreset('button')\"\u003eButton\u003c/button\u003e\n  \u003cbutton class=\"nm-preset-btn\" onclick=\"nmLoadPreset('circle')\"\u003eCircle\u003c/button\u003e\n  \u003cbutton class=\"nm-preset-btn\" onclick=\"nmLoadPreset('input')\"\u003eInput Field\u003c/button\u003e\n  \u003cbutton class=\"nm-preset-btn\" onclick=\"nmLoadPreset('toggle')\"\u003eToggle Switch\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"nm-layout\"\u003e\n  \u003c!-- Left: Controls --\u003e\n  \u003cdiv class=\"nm-panel\"\u003e\n    \u003ch3 class=\"nm-section-title\"\u003eControls\u003c/h3\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;label class=\u0026quot;nm-label\u0026quot;\u0026gt;Background Color\u0026lt;/label\u0026gt;\n\u0026lt;div class=\u0026quot;nm-color-row\u0026quot;\u0026gt;\n  \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;nm-bg\u0026quot; value=\u0026quot;#e0e5ec\u0026quot; oninput=\u0026quot;nmUpdate()\u0026quot;\u0026gt;\n  \u0026lt;span class=\u0026quot;nm-color-hex\u0026quot; id=\u0026quot;nm-bg-hex\u0026quot;\u0026gt;#e0e5ec\u0026lt;/span\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;nm-divider\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n\n\u0026lt;label class=\u0026quot;nm-label\u0026quot;\u0026gt;Shape Type\u0026lt;/label\u0026gt;\n\u0026lt;div class=\u0026quot;nm-seg\u0026quot; id=\u0026quot;nm-shape-seg\u0026quot;\u0026gt;\n  \u0026lt;button class=\u0026quot;active\u0026quot; onclick=\u0026quot;nmShape('flat',this)\u0026quot;\u0026gt;Flat\u0026lt;/button\u0026gt;\n  \u0026lt;button onclick=\u0026quot;nmShape('concave',this)\u0026quot;\u0026gt;Concave\u0026lt;/button\u0026gt;\n  \u0026lt;button onclick=\u0026quot;nmShape('convex',this)\u0026quot;\u0026gt;Convex\u0026lt;/button\u0026gt;\n  \u0026lt;button onclick=\u0026quot;nmShape('pressed',this)\u0026quot;\u0026gt;Pressed\u0026lt;/button\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;label class=\u0026quot;nm-label\u0026quot;\u0026gt;Element Shape\u0026lt;/label\u0026gt;\n\u0026lt;div class=\u0026quot;nm-seg\u0026quot; id=\u0026quot;nm-el-seg\u0026quot;\u0026gt;\n  \u0026lt;button class=\u0026quot;active\u0026quot; onclick=\u0026quot;nmElShape('card',this)\u0026quot;\u0026gt;Card\u0026lt;/button\u0026gt;\n  \u0026lt;button onclick=\u0026quot;nmElShape('button',this)\u0026quot;\u0026gt;Button\u0026lt;/button\u0026gt;\n  \u0026lt;button onclick=\u0026quot;nmElShape('circle',this)\u0026quot;\u0026gt;Circle\u0026lt;/button\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;nm-divider\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n\n\u0026lt;label class=\u0026quot;nm-label\u0026quot;\u0026gt;Size \u0026lt;span class=\u0026quot;nm-val\u0026quot; id=\u0026quot;v-size\u0026quot;\u0026gt;160px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt;\n\u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;nm-size\u0026quot; min=\u0026quot;60\u0026quot; max=\u0026quot;300\u0026quot; value=\u0026quot;160\u0026quot; oninput=\u0026quot;nmUpdate()\u0026quot;\u0026gt;\n\n\u0026lt;label class=\u0026quot;nm-label\u0026quot;\u0026gt;Shadow Distance \u0026lt;span class=\u0026quot;nm-val\u0026quot; id=\u0026quot;v-dist\u0026quot;\u0026gt;10px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt;\n\u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;nm-dist\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;50\u0026quot; value=\u0026quot;10\u0026quot; oninput=\u0026quot;nmUpdate()\u0026quot;\u0026gt;\n\n\u0026lt;label class=\u0026quot;nm-label\u0026quot;\u0026gt;Blur \u0026lt;span class=\u0026quot;nm-val\u0026quot; id=\u0026quot;v-blur\u0026quot;\u0026gt;20px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt;\n\u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;nm-blur\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;100\u0026quot; value=\u0026quot;20\u0026quot; oninput=\u0026quot;nmUpdate()\u0026quot;\u0026gt;\n\n\u0026lt;label class=\u0026quot;nm-label\u0026quot;\u0026gt;Intensity \u0026lt;span class=\u0026quot;nm-val\u0026quot; id=\u0026quot;v-int\u0026quot;\u0026gt;0.25\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt;\n\u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;nm-int\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;50\u0026quot; value=\u0026quot;25\u0026quot; oninput=\u0026quot;nmUpdate()\u0026quot;\u0026gt;\n\n\u0026lt;label class=\u0026quot;nm-label\u0026quot;\u0026gt;Border Radius \u0026lt;span class=\u0026quot;nm-val\u0026quot; id=\u0026quot;v-rad\u0026quot;\u0026gt;16px\u0026lt;/span\u0026gt;\u0026lt;/label\u0026gt;\n\u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;nm-rad\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;60\u0026quot; value=\u0026quot;16\u0026quot; oninput=\u0026quot;nmUpdate()\u0026quot;\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n  \u003c!-- Right: Preview --\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"nm-panel\" style=\"padding:0;overflow:hidden;\"\u003e\n      \u003cdiv class=\"nm-preview-wrap\" id=\"nm-preview-wrap\"\u003e\n        \u003cdiv class=\"nm-preview-el\" id=\"nm-preview-el\"\u003ePREVIEW\u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"nm-code-wrap\"\u003e\n      \u003cpre class=\"nm-code\" id=\"nm-code-out\"\u003e\u003c/pre\u003e\n      \u003cbutton class=\"nm-copy-btn\" id=\"nm-copy-btn\" onclick=\"nmCopy()\"\u003eCopy CSS\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n  var state = {\n    bg: '#e0e5ec',\n    dist: 10,\n    blur: 20,\n    intensity: 0.25,\n    radius: 16,\n    size: 160,\n    shape: 'flat',\n    elShape: 'card'\n  };\n\n  function hexToRgb(hex) {\n    hex = hex.replace('#','');\n    if(hex.length===3) hex=hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];\n    var n=parseInt(hex,16);\n    return {r:(n\u003e\u003e16)\u0026255,g:(n\u003e\u003e8)\u0026255,b:n\u0026255};\n  }\n\n  function rgbToHex(r,g,b){\n    return '#'+[r,g,b].map(function(v){\n      v=Math.max(0,Math.min(255,Math.round(v)));\n      return v.toString(16).padStart(2,'0');\n    }).join('');\n  }\n\n  function calcShadows(hex, dist, blur, intensity) {\n    var c = hexToRgb(hex);\n    var lighten = intensity * 255;\n    var darken = intensity * 255;\n    var light = rgbToHex(c.r+lighten, c.g+lighten, c.b+lighten);\n    var dark  = rgbToHex(c.r-darken,  c.g-darken,  c.b-darken);\n    return {light:light, dark:dark};\n  }\n\n  function buildBoxShadow(dist, blur, sh, shape, inset) {\n    var prefix = inset ? 'inset ' : '';\n    var base = prefix+(-dist)+'px '+(-dist)+'px '+blur+'px '+sh.light+', '+\n               prefix+dist+'px '+dist+'px '+blur+'px '+sh.dark;\n    return base;\n  }\n\n  function buildBackground(bg, shape) {\n    if(shape==='flat') return bg;\n    var c = hexToRgb(bg);\n    if(shape==='concave'){\n      var d=20;\n      var c1=rgbToHex(c.r-d,c.g-d,c.b-d);\n      var c2=rgbToHex(c.r+d,c.g+d,c.b+d);\n      return 'linear-gradient(145deg,'+c1+','+c2+')';\n    }\n    if(shape==='convex'){\n      var d=20;\n      var c1=rgbToHex(c.r+d,c.g+d,c.b+d);\n      var c2=rgbToHex(c.r-d,c.g-d,c.b-d);\n      return 'linear-gradient(145deg,'+c1+','+c2+')';\n    }\n    return bg;\n  }\n\n  function getRadiusForElShape(elShape, radius) {\n    if(elShape==='circle') return '50%';\n    if(elShape==='button') return Math.min(radius,30)+'px';\n    return radius+'px';\n  }\n\n  function getSizeStyle(elShape, size) {\n    if(elShape==='circle') return {width:size+'px',height:size+'px'};\n    if(elShape==='button') return {width:(size*1.6)+'px',height:(size*0.5)+'px'};\n    return {width:size+'px',height:size+'px'};\n  }\n\n  function nmUpdate() {\n    state.bg      = document.getElementById('nm-bg').value;\n    state.dist    = parseInt(document.getElementById('nm-dist').value);\n    state.blur    = parseInt(document.getElementById('nm-blur').value);\n    state.intensity = parseInt(document.getElementById('nm-int').value)/100;\n    state.radius  = parseInt(document.getElementById('nm-rad').value);\n    state.size    = parseInt(document.getElementById('nm-size').value);\n\n    document.getElementById('nm-bg-hex').textContent = state.bg;\n    document.getElementById('v-dist').textContent = state.dist+'px';\n    document.getElementById('v-blur').textContent = state.blur+'px';\n    document.getElementById('v-int').textContent  = state.intensity.toFixed(2);\n    document.getElementById('v-rad').textContent  = state.radius+'px';\n    document.getElementById('v-size').textContent = state.size+'px';\n\n    var sh = calcShadows(state.bg, state.dist, state.blur, state.intensity);\n    var isPressed = state.shape==='pressed';\n    var boxShadow = buildBoxShadow(state.dist, state.blur, sh, state.shape, isPressed);\n    var bg = buildBackground(state.bg, state.shape);\n    var borderRadius = getRadiusForElShape(state.elShape, state.radius);\n    var sz = getSizeStyle(state.elShape, state.size);\n\n    // Update preview wrap\n    var wrap = document.getElementById('nm-preview-wrap');\n    wrap.style.background = state.bg;\n    wrap.style.padding = '40px';\n\n    // Update preview element\n    var el = document.getElementById('nm-preview-el');\n    el.style.width = sz.width;\n    el.style.height = sz.height;\n    el.style.borderRadius = borderRadius;\n    el.style.background = bg;\n    el.style.boxShadow = boxShadow;\n\n    // Generate CSS\n    var radiusVal = borderRadius;\n    var bgVal = bg;\n    var lines = [];\n    lines.push('.nm-element {');\n    lines.push('  background: '+bgVal+';');\n    lines.push('  border-radius: '+radiusVal+';');\n    lines.push('  box-shadow: '+boxShadow+';');\n    if(sz.width===sz.height \u0026\u0026 state.elShape!=='button') {\n      lines.push('  width: '+sz.width+';');\n      lines.push('  height: '+sz.height+';');\n    } else {\n      lines.push('  width: '+sz.width+';');\n      lines.push('  height: '+sz.height+';');\n    }\n    lines.push('}');\n\n    renderCode(lines.join('\\n'), sh);\n  }\n\n  function renderCode(css, sh) {\n    // Simple syntax highlighting\n    var html = css\n      .replace(/\u0026/g,'\u0026amp;')\n      .replace(/\u003c/g,'\u0026lt;')\n      .replace(/\u003e/g,'\u0026gt;')\n      .replace(/([\\w-]+)(?=\\s*:)/g,'\u003cspan class=\"c-prop\"\u003e$1\u003c/span\u003e')\n      .replace(/:\\s*([^;{}\\n]+)/g,function(m,v){\n        return ': \u003cspan class=\"c-val\"\u003e'+v+'\u003c/span\u003e';\n      })\n      .replace(/[{}]/g,'\u003cspan class=\"c-punc\"\u003e$\u0026\u003c/span\u003e');\n    document.getElementById('nm-code-out').innerHTML = html;\n  }\n\n  window.nmUpdate = nmUpdate;\n\n  window.nmShape = function(s, btn) {\n    state.shape = s;\n    document.querySelectorAll('#nm-shape-seg button').forEach(function(b){b.classList.remove('active');});\n    btn.classList.add('active');\n    nmUpdate();\n  };\n\n  window.nmElShape = function(s, btn) {\n    state.elShape = s;\n    document.querySelectorAll('#nm-el-seg button').forEach(function(b){b.classList.remove('active');});\n    btn.classList.add('active');\n    nmUpdate();\n  };\n\n  window.nmCopy = function() {\n    var code = document.getElementById('nm-code-out').textContent;\n    navigator.clipboard.writeText(code).then(function(){\n      var btn = document.getElementById('nm-copy-btn');\n      btn.textContent = 'Copied!';\n      btn.classList.add('copied');\n      setTimeout(function(){btn.textContent='Copy CSS';btn.classList.remove('copied');},1800);\n    });\n  };\n\n  var presets = {\n    softcard:  {bg:'#e0e5ec',dist:10,blur:20,int:25,rad:16,size:160,shape:'flat',elShape:'card'},\n    button:    {bg:'#e8ecf0',dist:6, blur:12,int:20,rad:10,size:130,shape:'convex',elShape:'button'},\n    circle:    {bg:'#dde1e7',dist:8, blur:16,int:22,rad:50,size:140,shape:'flat',elShape:'circle'},\n    input:     {bg:'#eaeef3',dist:5, blur:10,int:18,rad:8, size:160,shape:'pressed',elShape:'button'},\n    toggle:    {bg:'#e0e5ec',dist:6, blur:12,int:20,rad:30,size:100,shape:'flat',elShape:'button'}\n  };\n\n  window.nmLoadPreset = function(name) {\n    var p = presets[name];\n    if(!p) return;\n    document.getElementById('nm-bg').value = p.bg;\n    document.getElementById('nm-dist').value = p.dist;\n    document.getElementById('nm-blur').value = p.blur;\n    document.getElementById('nm-int').value = p.int;\n    document.getElementById('nm-rad').value = p.rad;\n    document.getElementById('nm-size').value = p.size;\n    state.shape = p.shape;\n    state.elShape = p.elShape;\n    // Update shape seg buttons\n    document.querySelectorAll('#nm-shape-seg button').forEach(function(b){\n      b.classList.toggle('active', b.textContent.toLowerCase()===p.shape||(p.shape==='pressed'\u0026\u0026b.textContent==='Pressed'));\n    });\n    document.querySelectorAll('#nm-el-seg button').forEach(function(b){\n      var label = b.textContent.toLowerCase();\n      b.classList.toggle('active', label===p.elShape);\n    });\n    nmUpdate();\n  };\n\n  // Init\n  nmUpdate();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch3 id=\"how-neumorphism-works\"\u003eHow Neumorphism Works\u003c/h3\u003e\n\u003cp\u003eNeumorphism (soft UI) creates depth by casting two shadows from a single element — one light shadow in the top-left direction and one dark shadow in the bottom-right direction, both derived from the background color. The element itself must use the same color as its container background, which is why the background color picker is the most critical control.\u003c/p\u003e","title":"Neumorphism Generator"},{"content":" Number Base Converter Input Number Input Base Base 2 (Binary) Base 8 (Octal) Base 10 (Decimal) Base 16 (Hex) Conversions Binary (Base 2) — \u0026#x2398; Octal (Base 8) — \u0026#x2398; Decimal (Base 10) — \u0026#x2398; Hexadecimal (Base 16) — \u0026#x2398; Custom Base (2–36) Base 12 — \u0026#x2398; Bit View 8-bit 16-bit 32-bit Two's Complement (negative representation) ASCII Bitwise Operations Operand B (decimal) AND A AND B — — \u0026#x2398; OR A OR B — — \u0026#x2398; XOR A XOR B — — \u0026#x2398; NOT NOT A (32-bit) — — \u0026#x2398; Shift A by bits: \u0026#x00AB; Left (SHL) Right (SHR) \u0026#x00BB; SHL Shift Left Result — — \u0026#x2398; SHR Shift Right Result — — \u0026#x2398; Scientific math → Scientific Calculator Matrix math → Matrix Calculator ","permalink":"https://productivity-works.com/tools/number-base-converter/","summary":"\u003cdiv id=\"nb-app\"\u003e\n\u003cstyle\u003e\n#nb-app *, #nb-app *::before, #nb-app *::after { box-sizing: border-box; margin: 0; padding: 0; }\n#nb-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n  max-width: 720px;\n  margin: 0 auto;\n  padding: 12px;\n  color: #1e293b;\n}\n#nb-app h2.nb-title {\n  font-size: 18px;\n  font-weight: 700;\n  color: #0f172a;\n  margin-bottom: 14px;\n  letter-spacing: -0.3px;\n}\n#nb-app .nb-card {\n  background: #fff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 18px;\n  margin-bottom: 14px;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.05);\n}\n#nb-app .nb-input-row {\n  display: flex;\n  gap: 10px;\n  align-items: flex-end;\n  flex-wrap: wrap;\n}\n#nb-app .nb-field {\n  display: flex;\n  flex-direction: column;\n  gap: 5px;\n  flex: 1;\n  min-width: 160px;\n}\n#nb-app .nb-field label {\n  font-size: 12px;\n  font-weight: 600;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.5px;\n}\n#nb-app .nb-input {\n  padding: 10px 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 16px;\n  font-family: 'Courier New', Courier, monospace;\n  font-weight: 600;\n  color: #0f172a;\n  background: #f8fafc;\n  transition: border-color 0.2s;\n  width: 100%;\n}\n#nb-app .nb-input:focus {\n  outline: none;\n  border-color: #6366f1;\n  background: #fff;\n}\n#nb-app .nb-input.nb-error {\n  border-color: #f43f5e;\n  background: #fff1f2;\n}\n#nb-app select.nb-select {\n  padding: 10px 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 14px;\n  color: #0f172a;\n  background: #f8fafc;\n  cursor: pointer;\n  min-width: 120px;\n}\n#nb-app select.nb-select:focus {\n  outline: none;\n  border-color: #6366f1;\n}\n#nb-app .nb-error-msg {\n  font-size: 12px;\n  color: #f43f5e;\n  min-height: 16px;\n  margin-top: 4px;\n}\n#nb-app .nb-results-grid {\n  display: grid;\n  grid-template-columns: repeat(2, 1fr);\n  gap: 10px;\n  margin-bottom: 10px;\n}\n#nb-app .nb-result-item {\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 12px 14px;\n  position: relative;\n}\n#nb-app .nb-result-item.nb-active {\n  border-color: #6366f1;\n  background: #eef2ff;\n}\n#nb-app .nb-result-label {\n  font-size: 11px;\n  font-weight: 700;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.6px;\n  margin-bottom: 4px;\n}\n#nb-app .nb-result-value {\n  font-family: 'Courier New', Courier, monospace;\n  font-size: 15px;\n  font-weight: 700;\n  color: #0f172a;\n  word-break: break-all;\n  line-height: 1.4;\n  padding-right: 28px;\n}\n#nb-app .nb-copy-btn {\n  position: absolute;\n  top: 10px;\n  right: 10px;\n  background: none;\n  border: none;\n  cursor: pointer;\n  color: #94a3b8;\n  font-size: 14px;\n  padding: 2px 4px;\n  border-radius: 4px;\n  transition: color 0.2s, background 0.2s;\n  line-height: 1;\n}\n#nb-app .nb-copy-btn:hover { color: #6366f1; background: #e0e7ff; }\n#nb-app .nb-copy-btn.nb-copied { color: #22c55e; }\n#nb-app .nb-custom-row {\n  display: flex;\n  gap: 10px;\n  align-items: flex-end;\n  flex-wrap: wrap;\n  margin-top: 10px;\n}\n#nb-app .nb-custom-result {\n  background: #f0fdf4;\n  border: 1.5px solid #bbf7d0;\n  border-radius: 10px;\n  padding: 12px 14px;\n  flex: 1;\n  min-width: 200px;\n  position: relative;\n  display: flex;\n  flex-direction: column;\n  gap: 3px;\n}\n#nb-app .nb-custom-result .nb-result-label { color: #16a34a; }\n#nb-app .nb-custom-result .nb-result-value { padding-right: 28px; }\n#nb-app .nb-section-title {\n  font-size: 13px;\n  font-weight: 700;\n  color: #475569;\n  margin-bottom: 10px;\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n#nb-app .nb-bit-tabs {\n  display: flex;\n  gap: 6px;\n  margin-bottom: 10px;\n  flex-wrap: wrap;\n}\n#nb-app .nb-tab-btn {\n  padding: 5px 14px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 6px;\n  background: #f8fafc;\n  font-size: 12px;\n  font-weight: 600;\n  color: #475569;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#nb-app .nb-tab-btn.nb-active-tab {\n  background: #6366f1;\n  border-color: #6366f1;\n  color: #fff;\n}\n#nb-app .nb-bit-display {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 4px;\n  font-family: 'Courier New', Courier, monospace;\n}\n#nb-app .nb-bit-group {\n  display: flex;\n  gap: 2px;\n}\n#nb-app .nb-bit {\n  width: 24px;\n  height: 30px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 13px;\n  font-weight: 700;\n  border-radius: 5px;\n  border: 1.5px solid #e2e8f0;\n  background: #f8fafc;\n  color: #64748b;\n  transition: all 0.1s;\n}\n#nb-app .nb-bit.nb-one {\n  background: #6366f1;\n  border-color: #6366f1;\n  color: #fff;\n}\n#nb-app .nb-bit.nb-msb {\n  border-color: #f43f5e;\n}\n#nb-app .nb-bit-indices {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 4px;\n  margin-top: 2px;\n}\n#nb-app .nb-bit-idx-group {\n  display: flex;\n  gap: 2px;\n}\n#nb-app .nb-bit-idx {\n  width: 24px;\n  font-size: 9px;\n  color: #94a3b8;\n  text-align: center;\n}\n#nb-app .nb-twos-row {\n  margin-top: 10px;\n  background: #fef3c7;\n  border: 1.5px solid #fde68a;\n  border-radius: 8px;\n  padding: 10px 14px;\n}\n#nb-app .nb-twos-row .nb-result-label { color: #92400e; margin-bottom: 4px; }\n#nb-app .nb-twos-value {\n  font-family: 'Courier New', Courier, monospace;\n  font-size: 14px;\n  font-weight: 700;\n  color: #7c3aed;\n  word-break: break-all;\n}\n#nb-app .nb-ascii-row {\n  margin-top: 8px;\n  background: #f0fdf4;\n  border: 1.5px solid #bbf7d0;\n  border-radius: 8px;\n  padding: 10px 14px;\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n#nb-app .nb-ascii-label {\n  font-size: 11px;\n  font-weight: 700;\n  color: #15803d;\n  text-transform: uppercase;\n  letter-spacing: 0.5px;\n  white-space: nowrap;\n}\n#nb-app .nb-ascii-char {\n  font-family: 'Courier New', Courier, monospace;\n  font-size: 22px;\n  font-weight: 700;\n  color: #065f46;\n  background: #dcfce7;\n  border-radius: 6px;\n  width: 36px;\n  height: 36px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n#nb-app .nb-ascii-info {\n  font-size: 12px;\n  color: #166534;\n}\n#nb-app .nb-bitwise-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 10px;\n}\n#nb-app .nb-bitwise-result {\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 8px;\n  padding: 10px 12px;\n  position: relative;\n}\n#nb-app .nb-bitwise-result .nb-result-label { margin-bottom: 4px; }\n#nb-app .nb-bitwise-result .nb-result-value { font-size: 13px; }\n#nb-app .nb-op-label {\n  font-size: 11px;\n  font-weight: 800;\n  color: #6366f1;\n  background: #e0e7ff;\n  border-radius: 4px;\n  padding: 1px 6px;\n  display: inline-block;\n  margin-bottom: 4px;\n}\n#nb-app .nb-shift-controls {\n  display: flex;\n  gap: 8px;\n  align-items: center;\n  margin-top: 8px;\n  flex-wrap: wrap;\n}\n#nb-app .nb-shift-btn {\n  padding: 6px 14px;\n  border: 1.5px solid #6366f1;\n  border-radius: 7px;\n  background: #fff;\n  color: #6366f1;\n  font-size: 13px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#nb-app .nb-shift-btn:hover { background: #6366f1; color: #fff; }\n#nb-app .nb-shift-input {\n  width: 56px;\n  padding: 6px 8px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  font-size: 13px;\n  text-align: center;\n}\n#nb-app .nb-shift-input:focus { outline: none; border-color: #6366f1; }\n#nb-app .nb-b-label {\n  font-size: 12px;\n  color: #64748b;\n  font-weight: 600;\n}\n#nb-app .nb-b-input {\n  padding: 8px 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 15px;\n  font-family: 'Courier New', Courier, monospace;\n  font-weight: 600;\n  width: 100%;\n  background: #f8fafc;\n}\n#nb-app .nb-b-input:focus { outline: none; border-color: #6366f1; background: #fff; }\n#nb-app .nb-b-input.nb-error { border-color: #f43f5e; background: #fff1f2; }\n@media (max-width: 520px) {\n  #nb-app .nb-results-grid { grid-template-columns: 1fr; }\n  #nb-app .nb-bitwise-grid { grid-template-columns: 1fr; }\n  #nb-app .nb-bit { width: 20px; height: 26px; font-size: 11px; }\n  #nb-app .nb-bit-idx { width: 20px; }\n}\n\u003c/style\u003e\n\u003ch2 class=\"nb-title\"\u003eNumber Base Converter\u003c/h2\u003e\n\u003cdiv class=\"nb-card\"\u003e\n  \u003cdiv class=\"nb-section-title\"\u003eInput\u003c/div\u003e\n  \u003cdiv class=\"nb-input-row\"\u003e\n    \u003cdiv class=\"nb-field\" style=\"flex:2;min-width:200px;\"\u003e\n      \u003clabel for=\"nb-num-input\"\u003eNumber\u003c/label\u003e\n      \u003cinput id=\"nb-num-input\" class=\"nb-input\" type=\"text\" placeholder=\"Enter a number...\" autocomplete=\"off\" spellcheck=\"false\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"nb-field\" style=\"flex:1;min-width:140px;\"\u003e\n      \u003clabel for=\"nb-base-select\"\u003eInput Base\u003c/label\u003e\n      \u003cselect id=\"nb-base-select\" class=\"nb-select\"\u003e\n        \u003coption value=\"2\"\u003eBase 2 (Binary)\u003c/option\u003e\n        \u003coption value=\"8\"\u003eBase 8 (Octal)\u003c/option\u003e\n        \u003coption value=\"10\" selected\u003eBase 10 (Decimal)\u003c/option\u003e\n        \u003coption value=\"16\"\u003eBase 16 (Hex)\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"nb-error-msg\" id=\"nb-error\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"nb-card\"\u003e\n  \u003cdiv class=\"nb-section-title\"\u003eConversions\u003c/div\u003e\n  \u003cdiv class=\"nb-results-grid\"\u003e\n    \u003cdiv class=\"nb-result-item\" id=\"nb-r-bin\"\u003e\n      \u003cdiv class=\"nb-result-label\"\u003eBinary (Base 2)\u003c/div\u003e\n      \u003cdiv class=\"nb-result-value\" id=\"nb-v-bin\"\u003e—\u003c/div\u003e\n      \u003cbutton class=\"nb-copy-btn\" onclick=\"nbCopy('nb-v-bin',this)\" title=\"Copy\"\u003e\u0026#x2398;\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"nb-result-item\" id=\"nb-r-oct\"\u003e\n      \u003cdiv class=\"nb-result-label\"\u003eOctal (Base 8)\u003c/div\u003e\n      \u003cdiv class=\"nb-result-value\" id=\"nb-v-oct\"\u003e—\u003c/div\u003e\n      \u003cbutton class=\"nb-copy-btn\" onclick=\"nbCopy('nb-v-oct',this)\" title=\"Copy\"\u003e\u0026#x2398;\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"nb-result-item\" id=\"nb-r-dec\"\u003e\n      \u003cdiv class=\"nb-result-label\"\u003eDecimal (Base 10)\u003c/div\u003e\n      \u003cdiv class=\"nb-result-value\" id=\"nb-v-dec\"\u003e—\u003c/div\u003e\n      \u003cbutton class=\"nb-copy-btn\" onclick=\"nbCopy('nb-v-dec',this)\" title=\"Copy\"\u003e\u0026#x2398;\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"nb-result-item\" id=\"nb-r-hex\"\u003e\n      \u003cdiv class=\"nb-result-label\"\u003eHexadecimal (Base 16)\u003c/div\u003e\n      \u003cdiv class=\"nb-result-value\" id=\"nb-v-hex\"\u003e—\u003c/div\u003e\n      \u003cbutton class=\"nb-copy-btn\" onclick=\"nbCopy('nb-v-hex',this)\" title=\"Copy\"\u003e\u0026#x2398;\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"nb-custom-row\"\u003e\n    \u003cdiv class=\"nb-field\" style=\"min-width:120px;max-width:160px;\"\u003e\n      \u003clabel for=\"nb-custom-base\"\u003eCustom Base (2–36)\u003c/label\u003e\n      \u003cinput id=\"nb-custom-base\" class=\"nb-input\" type=\"number\" min=\"2\" max=\"36\" value=\"12\" style=\"font-size:14px;\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"nb-custom-result\"\u003e\n      \u003cdiv class=\"nb-result-label\" id=\"nb-custom-label\"\u003eBase 12\u003c/div\u003e\n      \u003cdiv class=\"nb-result-value\" id=\"nb-v-custom\"\u003e—\u003c/div\u003e\n      \u003cbutton class=\"nb-copy-btn\" onclick=\"nbCopy('nb-v-custom',this)\" title=\"Copy\"\u003e\u0026#x2398;\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"nb-card\"\u003e\n  \u003cdiv class=\"nb-section-title\"\u003eBit View\u003c/div\u003e\n  \u003cdiv class=\"nb-bit-tabs\"\u003e\n    \u003cbutton class=\"nb-tab-btn nb-active-tab\" onclick=\"nbSetBits(8,this)\"\u003e8-bit\u003c/button\u003e\n    \u003cbutton class=\"nb-tab-btn\" onclick=\"nbSetBits(16,this)\"\u003e16-bit\u003c/button\u003e\n    \u003cbutton class=\"nb-tab-btn\" onclick=\"nbSetBits(32,this)\"\u003e32-bit\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"nb-bit-display\" id=\"nb-bit-display\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"nb-bit-indices\" id=\"nb-bit-indices\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"nb-twos-row\" id=\"nb-twos-wrap\" style=\"display:none;\"\u003e\n    \u003cdiv class=\"nb-result-label\"\u003eTwo's Complement (negative representation)\u003c/div\u003e\n    \u003cdiv class=\"nb-twos-value\" id=\"nb-twos-value\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"nb-ascii-row\" id=\"nb-ascii-wrap\" style=\"display:none;\"\u003e\n    \u003cdiv class=\"nb-ascii-label\"\u003eASCII\u003c/div\u003e\n    \u003cdiv class=\"nb-ascii-char\" id=\"nb-ascii-char\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"nb-ascii-info\" id=\"nb-ascii-info\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"nb-card\"\u003e\n  \u003cdiv class=\"nb-section-title\"\u003eBitwise Operations\u003c/div\u003e\n  \u003cdiv style=\"margin-bottom:10px;\"\u003e\n    \u003cdiv class=\"nb-b-label\" style=\"margin-bottom:4px;\"\u003eOperand B (decimal)\u003c/div\u003e\n    \u003cinput id=\"nb-b-input\" class=\"nb-b-input\" type=\"text\" placeholder=\"Enter second number (decimal)...\" autocomplete=\"off\"\u003e\n    \u003cdiv class=\"nb-error-msg\" id=\"nb-b-error\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"nb-bitwise-grid\" id=\"nb-bitwise-grid\"\u003e\n    \u003cdiv class=\"nb-bitwise-result\"\u003e\n      \u003cdiv class=\"nb-op-label\"\u003eAND\u003c/div\u003e\n      \u003cdiv class=\"nb-result-label\"\u003eA AND B\u003c/div\u003e\n      \u003cdiv class=\"nb-result-value\" id=\"nb-and-dec\"\u003e—\u003c/div\u003e\n      \u003cdiv class=\"nb-result-value\" style=\"font-size:11px;color:#64748b;\" id=\"nb-and-bin\"\u003e—\u003c/div\u003e\n      \u003cbutton class=\"nb-copy-btn\" onclick=\"nbCopy('nb-and-dec',this)\" title=\"Copy\"\u003e\u0026#x2398;\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"nb-bitwise-result\"\u003e\n      \u003cdiv class=\"nb-op-label\"\u003eOR\u003c/div\u003e\n      \u003cdiv class=\"nb-result-label\"\u003eA OR B\u003c/div\u003e\n      \u003cdiv class=\"nb-result-value\" id=\"nb-or-dec\"\u003e—\u003c/div\u003e\n      \u003cdiv class=\"nb-result-value\" style=\"font-size:11px;color:#64748b;\" id=\"nb-or-bin\"\u003e—\u003c/div\u003e\n      \u003cbutton class=\"nb-copy-btn\" onclick=\"nbCopy('nb-or-dec',this)\" title=\"Copy\"\u003e\u0026#x2398;\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"nb-bitwise-result\"\u003e\n      \u003cdiv class=\"nb-op-label\"\u003eXOR\u003c/div\u003e\n      \u003cdiv class=\"nb-result-label\"\u003eA XOR B\u003c/div\u003e\n      \u003cdiv class=\"nb-result-value\" id=\"nb-xor-dec\"\u003e—\u003c/div\u003e\n      \u003cdiv class=\"nb-result-value\" style=\"font-size:11px;color:#64748b;\" id=\"nb-xor-bin\"\u003e—\u003c/div\u003e\n      \u003cbutton class=\"nb-copy-btn\" onclick=\"nbCopy('nb-xor-dec',this)\" title=\"Copy\"\u003e\u0026#x2398;\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"nb-bitwise-result\"\u003e\n      \u003cdiv class=\"nb-op-label\"\u003eNOT\u003c/div\u003e\n      \u003cdiv class=\"nb-result-label\"\u003eNOT A (32-bit)\u003c/div\u003e\n      \u003cdiv class=\"nb-result-value\" id=\"nb-not-dec\"\u003e—\u003c/div\u003e\n      \u003cdiv class=\"nb-result-value\" style=\"font-size:11px;color:#64748b;\" id=\"nb-not-bin\"\u003e—\u003c/div\u003e\n      \u003cbutton class=\"nb-copy-btn\" onclick=\"nbCopy('nb-not-dec',this)\" title=\"Copy\"\u003e\u0026#x2398;\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"nb-shift-controls\"\u003e\n    \u003cspan class=\"nb-b-label\"\u003eShift A by\u003c/span\u003e\n    \u003cinput id=\"nb-shift-n\" class=\"nb-shift-input\" type=\"number\" value=\"1\" min=\"1\" max=\"31\"\u003e\n    \u003cspan class=\"nb-b-label\"\u003ebits:\u003c/span\u003e\n    \u003cbutton class=\"nb-shift-btn\" onclick=\"nbShift('left')\"\u003e\u0026#x00AB; Left (SHL)\u003c/button\u003e\n    \u003cbutton class=\"nb-shift-btn\" onclick=\"nbShift('right')\"\u003eRight (SHR) \u0026#x00BB;\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv style=\"margin-top:8px;display:flex;gap:10px;flex-wrap:wrap;\"\u003e\n    \u003cdiv class=\"nb-bitwise-result\" style=\"flex:1;min-width:180px;\"\u003e\n      \u003cdiv class=\"nb-op-label\"\u003eSHL\u003c/div\u003e\n      \u003cdiv class=\"nb-result-label\"\u003eShift Left Result\u003c/div\u003e\n      \u003cdiv class=\"nb-result-value\" id=\"nb-shl-dec\"\u003e—\u003c/div\u003e\n      \u003cdiv class=\"nb-result-value\" style=\"font-size:11px;color:#64748b;\" id=\"nb-shl-bin\"\u003e—\u003c/div\u003e\n      \u003cbutton class=\"nb-copy-btn\" onclick=\"nbCopy('nb-shl-dec',this)\" title=\"Copy\"\u003e\u0026#x2398;\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"nb-bitwise-result\" style=\"flex:1;min-width:180px;\"\u003e\n      \u003cdiv class=\"nb-op-label\"\u003eSHR\u003c/div\u003e\n      \u003cdiv class=\"nb-result-label\"\u003eShift Right Result\u003c/div\u003e\n      \u003cdiv class=\"nb-result-value\" id=\"nb-shr-dec\"\u003e—\u003c/div\u003e\n      \u003cdiv class=\"nb-result-value\" style=\"font-size:11px;color:#64748b;\" id=\"nb-shr-bin\"\u003e—\u003c/div\u003e\n      \u003cbutton class=\"nb-copy-btn\" onclick=\"nbCopy('nb-shr-dec',this)\" title=\"Copy\"\u003e\u0026#x2398;\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n  var nbBitWidth = 8;\n  var nbCurrentDec = null;\n\n  function nbGetInput() {\n    return document.getElementById('nb-num-input').value.trim();\n  }\n  function nbGetBase() {\n    return parseInt(document.getElementById('nb-base-select').value, 10);\n  }\n  function nbSetError(msg) {\n    document.getElementById('nb-error').textContent = msg || '';\n  }\n\n  function nbParseToBigInt(str, base) {\n    str = str.trim().toUpperCase();\n    var negative = false;\n    if (str.startsWith('-')) { negative = true; str = str.slice(1); }\n    if (!str) return null;\n    var digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';\n    var val = BigInt(0);\n    var b = BigInt(base);\n    for (var i = 0; i \u003c str.length; i++) {\n      var d = digits.indexOf(str[i]);\n      if (d \u003c 0 || d \u003e= base) return null;\n      val = val * b + BigInt(d);\n    }\n    return negative ? -val : val;\n  }\n\n  function nbConvert() {\n    var str = nbGetInput();\n    var base = nbGetBase();\n    var ids = ['nb-r-bin','nb-r-oct','nb-r-dec','nb-r-hex'];\n    ids.forEach(function(id){ document.getElementById(id).classList.remove('nb-active'); });\n    var activeMap = {2:'nb-r-bin',8:'nb-r-oct',10:'nb-r-dec',16:'nb-r-hex'};\n    if (activeMap[base]) document.getElementById(activeMap[base]).classList.add('nb-active');\n\n    if (!str) {\n      nbSetError('');\n      ['nb-v-bin','nb-v-oct','nb-v-dec','nb-v-hex','nb-v-custom'].forEach(function(id){\n        document.getElementById(id).textContent = '—';\n      });\n      nbCurrentDec = null;\n      nbRenderBits(null);\n      nbClearBitwise();\n      return;\n    }\n\n    var val = nbParseToBigInt(str, base);\n    if (val === null) {\n      nbSetError('Invalid characters for base ' + base);\n      ['nb-v-bin','nb-v-oct','nb-v-dec','nb-v-hex','nb-v-custom'].forEach(function(id){\n        document.getElementById(id).textContent = '—';\n      });\n      document.getElementById('nb-num-input').classList.add('nb-error');\n      nbCurrentDec = null;\n      nbRenderBits(null);\n      nbClearBitwise();\n      return;\n    }\n    document.getElementById('nb-num-input').classList.remove('nb-error');\n    nbSetError('');\n    nbCurrentDec = val;\n\n    var prefix = val \u003c 0n ? '-' : '';\n    var absVal = val \u003c 0n ? -val : val;\n\n    document.getElementById('nb-v-bin').textContent = prefix + absVal.toString(2);\n    document.getElementById('nb-v-oct').textContent = prefix + absVal.toString(8);\n    document.getElementById('nb-v-dec').textContent = val.toString(10);\n    document.getElementById('nb-v-hex').textContent = (prefix + absVal.toString(16)).toUpperCase();\n\n    var cb = parseInt(document.getElementById('nb-custom-base').value, 10);\n    nbUpdateCustom(val, cb);\n\n    nbRenderBits(val);\n    nbUpdateBitwise();\n    nbUpdateAscii(val);\n  }\n\n  function nbUpdateCustom(val, cb) {\n    if (isNaN(cb) || cb \u003c 2 || cb \u003e 36) {\n      document.getElementById('nb-v-custom').textContent = 'Invalid base';\n      document.getElementById('nb-custom-label').textContent = 'Custom Base';\n      return;\n    }\n    document.getElementById('nb-custom-label').textContent = 'Base ' + cb;\n    if (val === null) { document.getElementById('nb-v-custom').textContent = '—'; return; }\n    var prefix = val \u003c 0n ? '-' : '';\n    var absVal = val \u003c 0n ? -val : val;\n    document.getElementById('nb-v-custom').textContent = (prefix + absVal.toString(cb)).toUpperCase();\n  }\n\n  function nbRenderBits(val) {\n    var display = document.getElementById('nb-bit-display');\n    var indices = document.getElementById('nb-bit-indices');\n    display.innerHTML = '';\n    indices.innerHTML = '';\n\n    var bits = nbBitWidth;\n    var bitsArr = [];\n    if (val !== null) {\n      var n = val;\n      if (n \u003c 0n) {\n        // Two's complement\n        var mask = (1n \u003c\u003c BigInt(bits)) - 1n;\n        n = (n \u0026 mask);\n      }\n      var binStr = n.toString(2);\n      binStr = binStr.slice(-bits).padStart(bits, '0');\n      for (var i = 0; i \u003c bits; i++) bitsArr.push(binStr[i] === '1' ? 1 : 0);\n    } else {\n      for (var i = 0; i \u003c bits; i++) bitsArr.push(0);\n    }\n\n    // Render in groups of 4\n    var groupSize = 4;\n    var groups = Math.ceil(bits / groupSize);\n    for (var g = 0; g \u003c groups; g++) {\n      var grpEl = document.createElement('div');\n      grpEl.className = 'nb-bit-group';\n      var start = g * groupSize;\n      var end = Math.min(start + groupSize, bits);\n      for (var b = start; b \u003c end; b++) {\n        var bitEl = document.createElement('div');\n        bitEl.className = 'nb-bit' + (bitsArr[b] ? ' nb-one' : '') + (b === 0 ? ' nb-msb' : '');\n        bitEl.textContent = bitsArr[b];\n        grpEl.appendChild(bitEl);\n      }\n      display.appendChild(grpEl);\n    }\n\n    // Render indices\n    for (var g = 0; g \u003c groups; g++) {\n      var idxGrp = document.createElement('div');\n      idxGrp.className = 'nb-bit-idx-group';\n      var start = g * groupSize;\n      var end = Math.min(start + groupSize, bits);\n      for (var b = start; b \u003c end; b++) {\n        var idxEl = document.createElement('div');\n        idxEl.className = 'nb-bit-idx';\n        idxEl.textContent = bits - 1 - b;\n        idxGrp.appendChild(idxEl);\n      }\n      indices.appendChild(idxGrp);\n    }\n\n    // Two's complement display for negative values\n    var twosWrap = document.getElementById('nb-twos-wrap');\n    if (val !== null \u0026\u0026 val \u003c 0n) {\n      twosWrap.style.display = '';\n      var mask = (1n \u003c\u003c BigInt(bits)) - 1n;\n      var tc = (val \u0026 mask);\n      document.getElementById('nb-twos-value').textContent =\n        tc.toString(2).padStart(bits, '0') + ' (binary) = ' + tc.toString(10) + ' (unsigned decimal) = 0x' + tc.toString(16).toUpperCase();\n    } else {\n      twosWrap.style.display = 'none';\n    }\n  }\n\n  function nbUpdateAscii(val) {\n    var wrap = document.getElementById('nb-ascii-wrap');\n    if (val === null || val \u003c 0n || val \u003e 127n) {\n      wrap.style.display = 'none';\n      return;\n    }\n    var code = Number(val);\n    wrap.style.display = '';\n    var charEl = document.getElementById('nb-ascii-char');\n    var infoEl = document.getElementById('nb-ascii-info');\n    var controlNames = {0:'NUL',1:'SOH',2:'STX',3:'ETX',4:'EOT',5:'ENQ',6:'ACK',7:'BEL',8:'BS',9:'HT',10:'LF',11:'VT',12:'FF',13:'CR',14:'SO',15:'SI',16:'DLE',17:'DC1',18:'DC2',19:'DC3',20:'DC4',21:'NAK',22:'SYN',23:'ETB',24:'CAN',25:'EM',26:'SUB',27:'ESC',28:'FS',29:'GS',30:'RS',31:'US',127:'DEL'};\n    if (controlNames[code] !== undefined) {\n      charEl.textContent = controlNames[code];\n      charEl.style.fontSize = '11px';\n      infoEl.textContent = 'Control character (non-printable)';\n    } else {\n      charEl.textContent = String.fromCharCode(code);\n      charEl.style.fontSize = '22px';\n      infoEl.textContent = 'ASCII code ' + code + ' — printable character';\n    }\n  }\n\n  function nbGetBVal() {\n    var bStr = document.getElementById('nb-b-input').value.trim();\n    document.getElementById('nb-b-error').textContent = '';\n    document.getElementById('nb-b-input').classList.remove('nb-error');\n    if (!bStr) return null;\n    var bv = nbParseToBigInt(bStr, 10);\n    if (bv === null) {\n      document.getElementById('nb-b-error').textContent = 'Invalid decimal number';\n      document.getElementById('nb-b-input').classList.add('nb-error');\n      return null;\n    }\n    return bv;\n  }\n\n  function nbFmtBitwise(dec, prefix) {\n    if (dec === null) return;\n    var absVal = dec \u003c 0n ? -dec : dec;\n    var sign = dec \u003c 0n ? '-' : '';\n    var binStr = sign + absVal.toString(2);\n    return { dec: dec.toString(10), bin: binStr };\n  }\n\n  function nbUpdateBitwise() {\n    var a = nbCurrentDec;\n    var b = nbGetBVal();\n\n    // NOT is unary — always compute from A\n    if (a !== null) {\n      var notVal = ~BigInt(Number(a));\n      document.getElementById('nb-not-dec').textContent = notVal.toString(10);\n      var notAbs = notVal \u003c 0n ? -notVal : notVal;\n      document.getElementById('nb-not-bin').textContent = (notVal \u003c 0n ? '-' : '') + notAbs.toString(2);\n    } else {\n      document.getElementById('nb-not-dec').textContent = '—';\n      document.getElementById('nb-not-bin').textContent = '—';\n    }\n\n    if (a === null || b === null) {\n      ['and','or','xor'].forEach(function(op){\n        document.getElementById('nb-'+op+'-dec').textContent = '—';\n        document.getElementById('nb-'+op+'-bin').textContent = '—';\n      });\n      return;\n    }\n\n    var an = BigInt(Number(a));\n    var bn = BigInt(Number(b));\n\n    var ops = {and: an \u0026 bn, or: an | bn, xor: an ^ bn};\n    Object.keys(ops).forEach(function(op){\n      var v = ops[op];\n      var abs = v \u003c 0n ? -v : v;\n      var sign = v \u003c 0n ? '-' : '';\n      document.getElementById('nb-'+op+'-dec').textContent = v.toString(10);\n      document.getElementById('nb-'+op+'-bin').textContent = sign + abs.toString(2);\n    });\n  }\n\n  function nbShift(dir) {\n    if (nbCurrentDec === null) return;\n    var n = parseInt(document.getElementById('nb-shift-n').value, 10);\n    if (isNaN(n) || n \u003c 1) n = 1;\n    var a = BigInt(Number(nbCurrentDec));\n    var shlVal = a \u003c\u003c BigInt(n);\n    var shrVal = a \u003e\u003e BigInt(n);\n\n    var shlAbs = shlVal \u003c 0n ? -shlVal : shlVal;\n    var shrAbs = shrVal \u003c 0n ? -shrVal : shrVal;\n    document.getElementById('nb-shl-dec').textContent = shlVal.toString(10);\n    document.getElementById('nb-shl-bin').textContent = (shlVal \u003c 0n ? '-' : '') + shlAbs.toString(2);\n    document.getElementById('nb-shr-dec').textContent = shrVal.toString(10);\n    document.getElementById('nb-shr-bin').textContent = (shrVal \u003c 0n ? '-' : '') + shrAbs.toString(2);\n  }\n\n  function nbClearBitwise() {\n    ['nb-and-dec','nb-and-bin','nb-or-dec','nb-or-bin','nb-xor-dec','nb-xor-bin',\n     'nb-not-dec','nb-not-bin','nb-shl-dec','nb-shl-bin','nb-shr-dec','nb-shr-bin'].forEach(function(id){\n      document.getElementById(id).textContent = '—';\n    });\n  }\n\n  window.nbCopy = function(id, btn) {\n    var txt = document.getElementById(id).textContent;\n    if (!txt || txt === '—') return;\n    navigator.clipboard.writeText(txt).then(function(){\n      btn.classList.add('nb-copied');\n      btn.textContent = '\\u2713';\n      setTimeout(function(){ btn.classList.remove('nb-copied'); btn.textContent = '\\u2398'; }, 1200);\n    });\n  };\n\n  window.nbSetBits = function(n, el) {\n    nbBitWidth = n;\n    document.querySelectorAll('#nb-app .nb-tab-btn').forEach(function(b){ b.classList.remove('nb-active-tab'); });\n    el.classList.add('nb-active-tab');\n    nbRenderBits(nbCurrentDec);\n  };\n\n  window.nbShift = nbShift;\n\n  document.getElementById('nb-num-input').addEventListener('input', nbConvert);\n  document.getElementById('nb-base-select').addEventListener('change', nbConvert);\n  document.getElementById('nb-custom-base').addEventListener('input', function(){\n    var cb = parseInt(this.value, 10);\n    nbUpdateCustom(nbCurrentDec, cb);\n  });\n  document.getElementById('nb-b-input').addEventListener('input', nbUpdateBitwise);\n\n  nbRenderBits(null);\n})();\n\u003c/script\u003e\n\u003cblockquote\u003e\n\u003cp\u003eScientific math → \u003ca href=\"https://productivity-works.com/tools/scientific-calculator/\"\u003eScientific Calculator\u003c/a\u003e\n\nMatrix math → \u003ca href=\"https://productivity-works.com/tools/matrix-calculator/\"\u003eMatrix Calculator\u003c/a\u003e\n\u003c/p\u003e","title":"Number Base Converter"},{"content":" Ascending ↑ Descending ↓ Switch to Text Mode Remove Duplicates Input — one per line or comma-separated Sorted Output NUMBER MODE Copy Count — Min — Max — Sum — Average — Median — Related Tools Generate random numbers → Random Number Generator ","permalink":"https://productivity-works.com/tools/number-sorter/","summary":"\u003cdiv id=\"ns-app\"\u003e\n\u003cstyle\u003e\n#ns-app {\n  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;\n  background: #0f0f13;\n  color: #e2e8f0;\n  border-radius: 12px;\n  padding: 24px;\n  margin: 0 auto;\n  max-width: 900px;\n  box-sizing: border-box;\n}\n\n#ns-app * {\n  box-sizing: border-box;\n}\n\n#ns-app h2 {\n  font-size: 1.05rem;\n  font-weight: 600;\n  color: #f1f5f9;\n  margin: 0 0 10px 0;\n}\n\n#ns-app .ns-row {\n  display: flex;\n  gap: 16px;\n  margin-bottom: 20px;\n}\n\n@media (max-width: 640px) {\n  #ns-app .ns-row {\n    flex-direction: column;\n  }\n}\n\n#ns-app .ns-col {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n}\n\n#ns-app textarea {\n  width: 100%;\n  flex: 1;\n  min-height: 200px;\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  color: #e2e8f0;\n  font-size: 0.95rem;\n  padding: 12px 14px;\n  resize: vertical;\n  font-family: 'Fira Mono', 'Consolas', monospace;\n  transition: border-color 0.2s;\n  outline: none;\n  line-height: 1.6;\n}\n\n#ns-app textarea:focus {\n  border-color: #6366f1;\n}\n\n#ns-app .ns-label {\n  font-size: 0.75rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #6366f1;\n  margin-bottom: 6px;\n}\n\n#ns-app .ns-controls {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 16px;\n  align-items: center;\n}\n\n#ns-app .ns-btn {\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 6px;\n  color: #cbd5e1;\n  font-size: 0.82rem;\n  padding: 6px 14px;\n  cursor: pointer;\n  transition: all 0.18s;\n  font-family: inherit;\n  font-weight: 500;\n}\n\n#ns-app .ns-btn:hover {\n  background: #6366f1;\n  border-color: #6366f1;\n  color: #fff;\n}\n\n#ns-app .ns-btn.active {\n  background: #6366f1;\n  border-color: #6366f1;\n  color: #fff;\n}\n\n#ns-app .ns-btn.success {\n  background: #16a34a;\n  border-color: #16a34a;\n  color: #fff;\n}\n\n#ns-app .ns-separator {\n  width: 1px;\n  height: 28px;\n  background: #2d2d3d;\n  margin: 0 2px;\n}\n\n#ns-app .ns-toggle {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 0.82rem;\n  color: #94a3b8;\n  cursor: pointer;\n  user-select: none;\n  padding: 6px 10px;\n  border: 1px solid #2d2d3d;\n  border-radius: 6px;\n  background: #1a1a24;\n  transition: all 0.18s;\n}\n\n#ns-app .ns-toggle:hover {\n  border-color: #6366f1;\n  color: #e2e8f0;\n}\n\n#ns-app .ns-toggle input[type=\"checkbox\"] {\n  accent-color: #6366f1;\n  width: 14px;\n  height: 14px;\n  cursor: pointer;\n}\n\n#ns-app .ns-stats {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));\n  gap: 8px;\n  margin-bottom: 16px;\n}\n\n#ns-app .ns-stat-card {\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  padding: 10px 14px;\n  text-align: center;\n}\n\n#ns-app .ns-stat-label {\n  font-size: 0.7rem;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  color: #64748b;\n  margin-bottom: 4px;\n}\n\n#ns-app .ns-stat-val {\n  font-size: 1.05rem;\n  font-weight: 700;\n  color: #6366f1;\n  font-family: 'Fira Mono', 'Consolas', monospace;\n  word-break: break-all;\n}\n\n#ns-app .ns-output-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin-bottom: 6px;\n}\n\n#ns-app .ns-mode-badge {\n  font-size: 0.7rem;\n  background: #1e1e2e;\n  border: 1px solid #2d2d3d;\n  border-radius: 4px;\n  padding: 2px 8px;\n  color: #64748b;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n}\n\n#ns-app .ns-placeholder {\n  color: #4a5568;\n  font-style: italic;\n}\n\n#ns-app .ns-error {\n  color: #f87171;\n  font-size: 0.82rem;\n  margin-top: 6px;\n  min-height: 1.2em;\n}\n\u003c/style\u003e\n\u003cdiv class=\"ns-controls\"\u003e\n  \u003c!-- Sort order --\u003e\n  \u003cbutton class=\"ns-btn active\" id=\"ns-asc\" onclick=\"nsSetOrder('asc')\"\u003eAscending ↑\u003c/button\u003e\n  \u003cbutton class=\"ns-btn\" id=\"ns-desc\" onclick=\"nsSetOrder('desc')\"\u003eDescending ↓\u003c/button\u003e\n  \u003cdiv class=\"ns-separator\"\u003e\u003c/div\u003e\n  \u003c!-- Mode toggle --\u003e\n  \u003cbutton class=\"ns-btn\" id=\"ns-mode-btn\" onclick=\"nsToggleMode()\"\u003eSwitch to Text Mode\u003c/button\u003e\n  \u003cdiv class=\"ns-separator\"\u003e\u003c/div\u003e\n  \u003c!-- Options --\u003e\n  \u003clabel class=\"ns-toggle\"\u003e\n    \u003cinput type=\"checkbox\" id=\"ns-dedup\" onchange=\"nsProcess()\"\u003e Remove Duplicates\n  \u003c/label\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ns-row\"\u003e\n  \u003cdiv class=\"ns-col\"\u003e\n    \u003cdiv class=\"ns-label\"\u003eInput — one per line or comma-separated\u003c/div\u003e\n    \u003ctextarea id=\"ns-input\" placeholder=\"Enter numbers here\u0026#10;e.g.\u0026#10;42\u0026#10;7\u0026#10;3.14\u0026#10;-5\u0026#10;100\u0026#10;\u0026#10;or: 5, 2, 8, 1, 9\" oninput=\"nsProcess()\"\u003e\u003c/textarea\u003e\n    \u003cdiv class=\"ns-error\" id=\"ns-error\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ns-col\"\u003e\n    \u003cdiv class=\"ns-output-header\"\u003e\n      \u003cdiv class=\"ns-label\"\u003eSorted Output\u003c/div\u003e\n      \u003cdiv style=\"display:flex;gap:6px;align-items:center;\"\u003e\n        \u003cspan class=\"ns-mode-badge\" id=\"ns-mode-badge\"\u003eNUMBER MODE\u003c/span\u003e\n        \u003cbutton class=\"ns-btn\" id=\"ns-copy-btn\" onclick=\"nsCopy()\"\u003eCopy\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003ctextarea id=\"ns-output\" readonly placeholder=\"Sorted result will appear here...\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ns-stats\" id=\"ns-stats\" style=\"display:none;\"\u003e\n  \u003cdiv class=\"ns-stat-card\"\u003e\n    \u003cdiv class=\"ns-stat-label\"\u003eCount\u003c/div\u003e\n    \u003cdiv class=\"ns-stat-val\" id=\"ns-s-count\"\u003e—\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ns-stat-card\"\u003e\n    \u003cdiv class=\"ns-stat-label\"\u003eMin\u003c/div\u003e\n    \u003cdiv class=\"ns-stat-val\" id=\"ns-s-min\"\u003e—\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ns-stat-card\"\u003e\n    \u003cdiv class=\"ns-stat-label\"\u003eMax\u003c/div\u003e\n    \u003cdiv class=\"ns-stat-val\" id=\"ns-s-max\"\u003e—\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ns-stat-card\"\u003e\n    \u003cdiv class=\"ns-stat-label\"\u003eSum\u003c/div\u003e\n    \u003cdiv class=\"ns-stat-val\" id=\"ns-s-sum\"\u003e—\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ns-stat-card\"\u003e\n    \u003cdiv class=\"ns-stat-label\"\u003eAverage\u003c/div\u003e\n    \u003cdiv class=\"ns-stat-val\" id=\"ns-s-avg\"\u003e—\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ns-stat-card\"\u003e\n    \u003cdiv class=\"ns-stat-label\"\u003eMedian\u003c/div\u003e\n    \u003cdiv class=\"ns-stat-val\" id=\"ns-s-med\"\u003e—\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var nsMode = 'number'; // 'number' | 'text'\n  var nsOrder = 'asc';\n\n  function nsGetItems() {\n    var raw = document.getElementById('ns-input').value;\n    if (!raw.trim()) return [];\n    // Support comma-separated or newline-separated\n    var parts;\n    if (raw.indexOf(',') !== -1) {\n      parts = raw.split(',');\n    } else {\n      parts = raw.split('\\n');\n    }\n    return parts.map(function(p) { return p.trim(); }).filter(function(p) { return p !== ''; });\n  }\n\n  function nsFmt(n) {\n    // Format number: avoid floating point noise\n    var s = String(n);\n    if (s.indexOf('.') !== -1) {\n      // trim trailing zeros up to reasonable precision\n      s = parseFloat(n.toFixed(10)).toString();\n    }\n    return s;\n  }\n\n  window.nsSetOrder = function(order) {\n    nsOrder = order;\n    document.getElementById('ns-asc').classList.toggle('active', order === 'asc');\n    document.getElementById('ns-desc').classList.toggle('active', order === 'desc');\n    nsProcess();\n  };\n\n  window.nsToggleMode = function() {\n    nsMode = nsMode === 'number' ? 'text' : 'number';\n    document.getElementById('ns-mode-btn').textContent = nsMode === 'number' ? 'Switch to Text Mode' : 'Switch to Number Mode';\n    document.getElementById('ns-mode-badge').textContent = nsMode === 'number' ? 'NUMBER MODE' : 'TEXT MODE';\n    document.getElementById('ns-error').textContent = '';\n    nsProcess();\n  };\n\n  window.nsProcess = function() {\n    var items = nsGetItems();\n    var dedup = document.getElementById('ns-dedup').checked;\n    var errorEl = document.getElementById('ns-error');\n    var statsEl = document.getElementById('ns-stats');\n    var outputEl = document.getElementById('ns-output');\n\n    errorEl.textContent = '';\n\n    if (!items.length) {\n      outputEl.value = '';\n      statsEl.style.display = 'none';\n      return;\n    }\n\n    if (nsMode === 'number') {\n      var nums = [];\n      var bad = [];\n      items.forEach(function(item) {\n        var n = Number(item);\n        if (item === '' || isNaN(n)) {\n          bad.push(item);\n        } else {\n          nums.push(n);\n        }\n      });\n\n      if (bad.length) {\n        errorEl.textContent = 'Non-numeric values ignored: ' + bad.slice(0, 5).join(', ') + (bad.length \u003e 5 ? '...' : '');\n      }\n\n      if (dedup) {\n        nums = nums.filter(function(v, i, a) { return a.indexOf(v) === i; });\n      }\n\n      nums.sort(function(a, b) { return nsOrder === 'asc' ? a - b : b - a; });\n\n      outputEl.value = nums.map(nsFmt).join('\\n');\n\n      if (nums.length) {\n        var sum = nums.reduce(function(a, b) { return a + b; }, 0);\n        var avg = sum / nums.length;\n        var sorted = nums.slice().sort(function(a, b) { return a - b; });\n        var mid = Math.floor(sorted.length / 2);\n        var median = sorted.length % 2 === 0\n          ? (sorted[mid - 1] + sorted[mid]) / 2\n          : sorted[mid];\n\n        document.getElementById('ns-s-count').textContent = nums.length;\n        document.getElementById('ns-s-min').textContent = nsFmt(nums[nsOrder === 'asc' ? 0 : nums.length - 1]);\n        document.getElementById('ns-s-max').textContent = nsFmt(nums[nsOrder === 'asc' ? nums.length - 1 : 0]);\n        document.getElementById('ns-s-sum').textContent = nsFmt(sum);\n        document.getElementById('ns-s-avg').textContent = nsFmt(Math.round(avg * 1e10) / 1e10);\n        document.getElementById('ns-s-med').textContent = nsFmt(median);\n        statsEl.style.display = 'grid';\n      } else {\n        statsEl.style.display = 'none';\n      }\n\n    } else {\n      // Text mode — alphabetical sort\n      var texts = items.slice();\n      if (dedup) {\n        texts = texts.filter(function(v, i, a) { return a.indexOf(v) === i; });\n      }\n      texts.sort(function(a, b) {\n        var cmp = a.localeCompare(b, undefined, { sensitivity: 'base' });\n        return nsOrder === 'asc' ? cmp : -cmp;\n      });\n      outputEl.value = texts.join('\\n');\n\n      document.getElementById('ns-s-count').textContent = texts.length;\n      document.getElementById('ns-s-min').textContent = texts[0] || '—';\n      document.getElementById('ns-s-max').textContent = texts[texts.length - 1] || '—';\n      document.getElementById('ns-s-sum').textContent = '—';\n      document.getElementById('ns-s-avg').textContent = '—';\n      document.getElementById('ns-s-med').textContent = '—';\n      statsEl.style.display = 'grid';\n    }\n  };\n\n  window.nsCopy = function() {\n    var text = document.getElementById('ns-output').value;\n    if (!text) return;\n    var btn = document.getElementById('ns-copy-btn');\n    var done = function() {\n      btn.textContent = 'Copied!';\n      btn.classList.add('success');\n      setTimeout(function() {\n        btn.textContent = 'Copy';\n        btn.classList.remove('success');\n      }, 1800);\n    };\n    if (navigator.clipboard) {\n      navigator.clipboard.writeText(text).then(done).catch(function() {\n        fallbackCopy(text, done);\n      });\n    } else {\n      fallbackCopy(text, done);\n    }\n  };\n\n  function fallbackCopy(text, cb) {\n    var ta = document.createElement('textarea');\n    ta.value = text;\n    ta.style.position = 'fixed';\n    ta.style.opacity = '0';\n    document.body.appendChild(ta);\n    ta.select();\n    document.execCommand('copy');\n    document.body.removeChild(ta);\n    cb();\n  }\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003eGenerate random numbers → \u003ca href=\"https://productivity-works.com/tools/random-number-generator/\"\u003eRandom Number Generator\u003c/a\u003e\n\u003c/p\u003e","title":"Number Sorter - Sort Numbers Online, Free"},{"content":"Disclosure: This page may contain affiliate links. If you make a purchase through these links, we may earn a commission at no extra cost to you. We only recommend tools and services we genuinely find useful.\nNumber to Words Converter Need to write a check, draft a legal document, or just double-check how to spell a large number in English? This free tool converts any number — up to the trillions — into plain English words instantly. Switch to Currency mode to spell out dollar amounts with cents, or Ordinal mode to get \u0026ldquo;first,\u0026rdquo; \u0026ldquo;second,\u0026rdquo; \u0026ldquo;twenty-third,\u0026rdquo; and so on.\nNumber to Words Converter Standard Currency ($) Ordinal Your result will appear here Copy Supports integers up to 999 trillion. Currency mode supports two decimal places. Try: 42 1,234 1,000,000 1,234,567,890 999 trillion How to Use Choose a mode — Standard, Currency ($), or Ordinal Type your number in the input field Read the result instantly below Click Copy to copy the words to your clipboard Mode Guide Mode Input Output Standard 1,234,567 one million two hundred thirty-four thousand five hundred sixty-seven Currency 1234.56 one thousand two hundred thirty-four dollars and fifty-six cents Ordinal 21 twenty-first Common Use Cases Writing checks: Banks often require the dollar amount spelled out in words Legal documents: Contracts frequently spell out key figures to prevent alteration Academic writing: Some style guides require numbers under a certain threshold to be written out Learning English: See exactly how large numbers are spoken aloud Proofreading: Verify you\u0026rsquo;re spelling a number correctly in formal correspondence Numbers Up to Trillion This converter handles numbers from negative 999 trillion to positive 999 trillion — more than enough for any real-world use case including national budgets, astronomical distances, and financial reports.\nRelated: Calculate percentages with our Percentage Calculator ","permalink":"https://productivity-works.com/tools/number-to-words/","summary":"\u003cp\u003e\u003cem\u003eDisclosure: This page may contain affiliate links. If you make a purchase through these links, we may earn a commission at no extra cost to you. We only recommend tools and services we genuinely find useful.\u003c/em\u003e\u003c/p\u003e\n\u003ch1 id=\"number-to-words-converter\"\u003eNumber to Words Converter\u003c/h1\u003e\n\u003cp\u003eNeed to write a check, draft a legal document, or just double-check how to spell a large number in English? This free tool converts any number — up to the trillions — into plain English words instantly. Switch to Currency mode to spell out dollar amounts with cents, or Ordinal mode to get \u0026ldquo;first,\u0026rdquo; \u0026ldquo;second,\u0026rdquo; \u0026ldquo;twenty-third,\u0026rdquo; and so on.\u003c/p\u003e","title":"Number to Words Converter | Convert Numbers to English Words Free"},{"content":" Manual Input \u0026mdash; Paste Your OG Values og:title 0 / 60 \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-label\u0026quot;\u0026gt; \u0026lt;span\u0026gt;og:description\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;og-counter\u0026quot; id=\u0026quot;og-desc-count\u0026quot;\u0026gt;0 / 155\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;textarea id=\u0026quot;og-desc\u0026quot; rows=\u0026quot;3\u0026quot; placeholder=\u0026quot;A short description of this page for social sharing…\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-label\u0026quot;\u0026gt;\u0026lt;span\u0026gt;og:url\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;url\u0026quot; id=\u0026quot;og-url\u0026quot; placeholder=\u0026quot;https://example.com/my-page\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-col\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-label\u0026quot;\u0026gt;\u0026lt;span\u0026gt;og:image URL\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;url\u0026quot; id=\u0026quot;og-image\u0026quot; placeholder=\u0026quot;https://example.com/og-image.png\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-dim-result\u0026quot; id=\u0026quot;og-dim-result\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-label\u0026quot;\u0026gt;\u0026lt;span\u0026gt;og:type\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;select id=\u0026quot;og-type\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;website\u0026quot;\u0026gt;website\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;article\u0026quot;\u0026gt;article\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;product\u0026quot;\u0026gt;product\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;profile\u0026quot;\u0026gt;profile\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;video.other\u0026quot;\u0026gt;video.other\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-label\u0026quot;\u0026gt;\u0026lt;span\u0026gt;twitter:card\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;select id=\u0026quot;og-twcard\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;summary_large_image\u0026quot;\u0026gt;summary_large_image\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;summary\u0026quot;\u0026gt;summary\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;app\u0026quot;\u0026gt;app\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;player\u0026quot;\u0026gt;player\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-label\u0026quot;\u0026gt;\u0026lt;span\u0026gt;twitter:site (optional)\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;og-twsite\u0026quot; placeholder=\u0026quot;@yourusername\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Update Preview Reset Analysis Detected OG Tags Platform Previews Twitter / X Facebook LinkedIn Discord No image provided example.com Page Title Page description will appear here. Twitter/X uses summary_large_image card with 1200\u0026times;630px image.\nNo image provided example.com Page Title Page description will appear here. Facebook recommends 1200\u0026times;630px. Minimum: 200\u0026times;200px.\nNo image provided Page Title example.com LinkedIn recommends 1200\u0026times;627px. Title only (no description shown in card).\nexample.com Page Title Page description will appear here. No image provided Discord reads og:image and og:description. Max embed width ~400px.\nHTML Meta Tags Copy HTML Related Tools Generate meta tags → Meta Tag Generator Create placeholder images → Placeholder Image Generator ","permalink":"https://productivity-works.com/tools/og-image-tester/","summary":"\u003cdiv id=\"og-app\"\u003e\n\u003cstyle\u003e\n#og-app *,\n#og-app *::before,\n#og-app *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n#og-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 14px;\n  color: #1e293b;\n  line-height: 1.5;\n}\n\n#og-app h2 {\n  font-size: 15px;\n  font-weight: 700;\n  color: #0f172a;\n  margin-bottom: 12px;\n  padding-bottom: 6px;\n  border-bottom: 2px solid #e2e8f0;\n}\n\n#og-app h3 {\n  font-size: 13px;\n  font-weight: 700;\n  color: #0f172a;\n  margin-bottom: 10px;\n}\n\n#og-app .og-card {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 18px 20px;\n  margin-bottom: 20px;\n}\n\n#og-app .og-field {\n  margin-bottom: 14px;\n}\n\n#og-app .og-label {\n  display: flex;\n  justify-content: space-between;\n  align-items: baseline;\n  font-size: 12px;\n  font-weight: 600;\n  color: #475569;\n  margin-bottom: 5px;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n\n#og-app .og-counter {\n  font-size: 11px;\n  font-weight: 400;\n  color: #94a3b8;\n}\n\n#og-app .og-counter.warn { color: #f59e0b; }\n#og-app .og-counter.over { color: #ef4444; }\n\n#og-app input[type=\"text\"],\n#og-app input[type=\"url\"],\n#og-app select,\n#og-app textarea {\n  width: 100%;\n  border: 1px solid #cbd5e1;\n  border-radius: 6px;\n  padding: 8px 10px;\n  font-size: 13px;\n  color: #1e293b;\n  background: #fff;\n  outline: none;\n  transition: border-color 0.15s;\n  font-family: inherit;\n}\n\n#og-app input[type=\"text\"]:focus,\n#og-app input[type=\"url\"]:focus,\n#og-app select:focus,\n#og-app textarea:focus {\n  border-color: #6366f1;\n  box-shadow: 0 0 0 3px rgba(99,102,241,0.08);\n}\n\n#og-app textarea { resize: vertical; min-height: 60px; }\n\n#og-app .og-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  padding: 9px 18px;\n  border-radius: 7px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  border: none;\n  transition: background 0.15s, transform 0.1s;\n  font-family: inherit;\n}\n\n#og-app .og-btn:active { transform: scale(0.97); }\n\n#og-app .og-btn-primary {\n  background: #6366f1;\n  color: #fff;\n}\n#og-app .og-btn-primary:hover { background: #4f46e5; }\n\n#og-app .og-btn-secondary {\n  background: #e2e8f0;\n  color: #334155;\n}\n#og-app .og-btn-secondary:hover { background: #cbd5e1; }\n\n#og-app .og-btn-copy {\n  background: #0ea5e9;\n  color: #fff;\n  font-size: 12px;\n  padding: 6px 14px;\n}\n#og-app .og-btn-copy:hover { background: #0284c7; }\n#og-app .og-btn-copy.copied { background: #22c55e; }\n\n#og-app .og-row {\n  display: flex;\n  gap: 16px;\n  flex-wrap: wrap;\n}\n\n#og-app .og-col {\n  flex: 1;\n  min-width: 260px;\n}\n\n/* Tag grid */\n#og-app .og-tags-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));\n  gap: 8px;\n  margin-bottom: 12px;\n}\n\n#og-app .og-tag-item {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 7px;\n  padding: 8px 10px;\n  font-size: 12px;\n}\n\n#og-app .og-tag-name {\n  font-weight: 600;\n  color: #6366f1;\n  font-family: monospace;\n  font-size: 11px;\n  margin-bottom: 2px;\n}\n\n#og-app .og-tag-value {\n  color: #334155;\n  word-break: break-all;\n}\n\n#og-app .og-tag-item.missing {\n  border-color: #fca5a5;\n  background: #fff5f5;\n}\n#og-app .og-tag-item.missing .og-tag-name { color: #ef4444; }\n#og-app .og-tag-item.missing .og-tag-value { color: #ef4444; font-style: italic; }\n\n/* Warnings */\n#og-app .og-warnings {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n  margin-bottom: 14px;\n}\n\n#og-app .og-warning {\n  display: flex;\n  align-items: flex-start;\n  gap: 8px;\n  padding: 8px 12px;\n  border-radius: 7px;\n  font-size: 12px;\n}\n\n#og-app .og-warning.warn { background: #fffbeb; border: 1px solid #fde68a; color: #92400e; }\n#og-app .og-warning.ok { background: #f0fdf4; border: 1px solid #bbf7d0; color: #166534; }\n#og-app .og-warning.err { background: #fef2f2; border: 1px solid #fecaca; color: #991b1b; }\n\n#og-app .og-warning-icon { font-size: 14px; flex-shrink: 0; margin-top: 1px; }\n\n/* Platform tabs */\n#og-app .og-tabs {\n  display: flex;\n  gap: 4px;\n  flex-wrap: wrap;\n  margin-bottom: 16px;\n}\n\n#og-app .og-tab {\n  padding: 7px 16px;\n  border-radius: 6px;\n  border: 1.5px solid #e2e8f0;\n  background: #fff;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  color: #64748b;\n  transition: all 0.15s;\n  font-family: inherit;\n}\n\n#og-app .og-tab:hover { border-color: #6366f1; color: #6366f1; }\n#og-app .og-tab.active { background: #6366f1; border-color: #6366f1; color: #fff; }\n\n/* Platform previews */\n#og-app .og-platform-preview { display: none; }\n#og-app .og-platform-preview.active { display: block; }\n\n/* ---- Twitter / X ---- */\n#og-app .og-twitter-wrap {\n  background: #000;\n  border-radius: 12px;\n  padding: 20px;\n  max-width: 500px;\n}\n\n#og-app .og-twitter-card {\n  border: 1px solid #2f3336;\n  border-radius: 12px;\n  overflow: hidden;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  background: #000;\n  cursor: pointer;\n}\n\n#og-app .og-twitter-img-wrap {\n  width: 100%;\n  padding-bottom: 52.5%; /* 1200x630 ratio */\n  position: relative;\n  background: #2f3336;\n  overflow: hidden;\n}\n\n#og-app .og-twitter-img-wrap img {\n  position: absolute;\n  top: 0; left: 0;\n  width: 100%; height: 100%;\n  object-fit: cover;\n}\n\n#og-app .og-twitter-img-placeholder {\n  position: absolute;\n  top: 0; left: 0; right: 0; bottom: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: #536471;\n  font-size: 13px;\n  flex-direction: column;\n  gap: 8px;\n}\n\n#og-app .og-twitter-img-placeholder svg { opacity: 0.4; }\n\n#og-app .og-twitter-meta {\n  padding: 10px 14px 12px;\n  background: #000;\n  border-top: 1px solid #2f3336;\n}\n\n#og-app .og-twitter-domain {\n  font-size: 13px;\n  color: #536471;\n  margin-bottom: 2px;\n}\n\n#og-app .og-twitter-title {\n  font-size: 15px;\n  font-weight: 700;\n  color: #e7e9ea;\n  line-height: 1.3;\n  margin-bottom: 2px;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n#og-app .og-twitter-desc {\n  font-size: 13px;\n  color: #536471;\n  display: -webkit-box;\n  -webkit-line-clamp: 2;\n  -webkit-box-orient: vertical;\n  overflow: hidden;\n}\n\n/* ---- Facebook ---- */\n#og-app .og-fb-wrap {\n  background: #f0f2f5;\n  border-radius: 8px;\n  padding: 20px;\n  max-width: 500px;\n}\n\n#og-app .og-fb-card {\n  border: 1px solid #dddfe2;\n  border-radius: 8px;\n  overflow: hidden;\n  background: #fff;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n}\n\n#og-app .og-fb-img-wrap {\n  width: 100%;\n  padding-bottom: 52.5%;\n  position: relative;\n  background: #e4e6ea;\n  overflow: hidden;\n}\n\n#og-app .og-fb-img-wrap img {\n  position: absolute;\n  top: 0; left: 0;\n  width: 100%; height: 100%;\n  object-fit: cover;\n}\n\n#og-app .og-fb-img-placeholder {\n  position: absolute;\n  top: 0; left: 0; right: 0; bottom: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: #bcc0c4;\n  font-size: 13px;\n  flex-direction: column;\n  gap: 8px;\n}\n\n#og-app .og-fb-meta {\n  padding: 10px 12px 12px;\n  border-top: 1px solid #dddfe2;\n  background: #f2f3f5;\n}\n\n#og-app .og-fb-domain {\n  font-size: 11px;\n  color: #606770;\n  text-transform: uppercase;\n  letter-spacing: 0.03em;\n  margin-bottom: 2px;\n}\n\n#og-app .og-fb-title {\n  font-size: 16px;\n  font-weight: 700;\n  color: #1c1e21;\n  line-height: 1.3;\n  margin-bottom: 3px;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n#og-app .og-fb-desc {\n  font-size: 14px;\n  color: #606770;\n  display: -webkit-box;\n  -webkit-line-clamp: 2;\n  -webkit-box-orient: vertical;\n  overflow: hidden;\n}\n\n/* ---- LinkedIn ---- */\n#og-app .og-li-wrap {\n  background: #f3f2ef;\n  border-radius: 8px;\n  padding: 20px;\n  max-width: 500px;\n}\n\n#og-app .og-li-card {\n  border: 1px solid #e0dfdb;\n  border-radius: 2px;\n  overflow: hidden;\n  background: #fff;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n}\n\n#og-app .og-li-img-wrap {\n  width: 100%;\n  padding-bottom: 52.5%;\n  position: relative;\n  background: #e8e6e1;\n  overflow: hidden;\n}\n\n#og-app .og-li-img-wrap img {\n  position: absolute;\n  top: 0; left: 0;\n  width: 100%; height: 100%;\n  object-fit: cover;\n}\n\n#og-app .og-li-img-placeholder {\n  position: absolute;\n  top: 0; left: 0; right: 0; bottom: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: #b0aba2;\n  font-size: 13px;\n  flex-direction: column;\n  gap: 8px;\n}\n\n#og-app .og-li-meta {\n  padding: 8px 12px 12px;\n  border-top: 1px solid #e0dfdb;\n  background: #eef3f8;\n}\n\n#og-app .og-li-title {\n  font-size: 14px;\n  font-weight: 600;\n  color: rgba(0,0,0,0.9);\n  line-height: 1.3;\n  margin-bottom: 3px;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n#og-app .og-li-domain {\n  font-size: 12px;\n  color: rgba(0,0,0,0.6);\n}\n\n/* ---- Discord ---- */\n#og-app .og-dc-wrap {\n  background: #36393f;\n  border-radius: 8px;\n  padding: 20px;\n  max-width: 500px;\n}\n\n#og-app .og-dc-embed {\n  border-left: 4px solid #5865f2;\n  background: #2f3136;\n  border-radius: 4px;\n  padding: 12px 16px;\n  font-family: \"Whitney\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n}\n\n#og-app .og-dc-site {\n  font-size: 12px;\n  font-weight: 600;\n  color: #fff;\n  margin-bottom: 4px;\n}\n\n#og-app .og-dc-title {\n  font-size: 15px;\n  font-weight: 600;\n  color: #00aff4;\n  margin-bottom: 4px;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n#og-app .og-dc-desc {\n  font-size: 13px;\n  color: #dcddde;\n  margin-bottom: 12px;\n  display: -webkit-box;\n  -webkit-line-clamp: 3;\n  -webkit-box-orient: vertical;\n  overflow: hidden;\n  line-height: 1.4;\n}\n\n#og-app .og-dc-img {\n  width: 100%;\n  border-radius: 4px;\n  display: block;\n  max-height: 300px;\n  object-fit: cover;\n}\n\n#og-app .og-dc-img-placeholder {\n  width: 100%;\n  height: 180px;\n  background: #40444b;\n  border-radius: 4px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: #72767d;\n  font-size: 13px;\n  flex-direction: column;\n  gap: 8px;\n}\n\n/* Image dimension checker */\n#og-app .og-dim-result {\n  margin-top: 10px;\n  padding: 10px 14px;\n  border-radius: 7px;\n  font-size: 12px;\n  display: none;\n}\n\n#og-app .og-dim-result.show { display: block; }\n#og-app .og-dim-result.ok { background: #f0fdf4; border: 1px solid #bbf7d0; color: #166534; }\n#og-app .og-dim-result.warn { background: #fffbeb; border: 1px solid #fde68a; color: #92400e; }\n#og-app .og-dim-result.err { background: #fef2f2; border: 1px solid #fecaca; color: #991b1b; }\n\n/* Output code */\n#og-app .og-code-block {\n  background: #0f172a;\n  border-radius: 8px;\n  padding: 14px 16px;\n  font-family: \"Fira Code\", \"Cascadia Code\", monospace;\n  font-size: 12px;\n  color: #94a3b8;\n  overflow-x: auto;\n  white-space: pre;\n  line-height: 1.6;\n  margin-bottom: 10px;\n}\n\n#og-app .og-code-block .tok-tag { color: #f472b6; }\n#og-app .og-code-block .tok-attr { color: #7dd3fc; }\n#og-app .og-code-block .tok-val { color: #86efac; }\n\n/* Responsive */\n@media (max-width: 600px) {\n  #og-app .og-row { flex-direction: column; }\n  #og-app .og-col { min-width: 0; }\n  #og-app .og-tabs { gap: 3px; }\n  #og-app .og-tab { padding: 6px 12px; font-size: 11px; }\n}\n\u003c/style\u003e\n\u003c!-- ===== MANUAL INPUT ===== --\u003e\n\u003cdiv class=\"og-card\"\u003e\n  \u003ch2\u003eManual Input \u0026mdash; Paste Your OG Values\u003c/h2\u003e\n  \u003cdiv class=\"og-row\"\u003e\n    \u003cdiv class=\"og-col\"\u003e\n      \u003cdiv class=\"og-field\"\u003e\n        \u003cdiv class=\"og-label\"\u003e\n          \u003cspan\u003eog:title\u003c/span\u003e\n          \u003cspan class=\"og-counter\" id=\"og-title-count\"\u003e0 / 60\u003c/span\u003e\n        \u003c/div\u003e\n        \u003cinput type=\"text\" id=\"og-title\" placeholder=\"My Awesome Page Title\" maxlength=\"200\"\u003e\n      \u003c/div\u003e\n\u003cpre\u003e\u003ccode\u003e  \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;og-label\u0026quot;\u0026gt;\n      \u0026lt;span\u0026gt;og:description\u0026lt;/span\u0026gt;\n      \u0026lt;span class=\u0026quot;og-counter\u0026quot; id=\u0026quot;og-desc-count\u0026quot;\u0026gt;0 / 155\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;textarea id=\u0026quot;og-desc\u0026quot; rows=\u0026quot;3\u0026quot; placeholder=\u0026quot;A short description of this page for social sharing…\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;og-label\u0026quot;\u0026gt;\u0026lt;span\u0026gt;og:url\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;input type=\u0026quot;url\u0026quot; id=\u0026quot;og-url\u0026quot; placeholder=\u0026quot;https://example.com/my-page\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;og-col\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;og-label\u0026quot;\u0026gt;\u0026lt;span\u0026gt;og:image URL\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;input type=\u0026quot;url\u0026quot; id=\u0026quot;og-image\u0026quot; placeholder=\u0026quot;https://example.com/og-image.png\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;og-dim-result\u0026quot; id=\u0026quot;og-dim-result\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;og-label\u0026quot;\u0026gt;\u0026lt;span\u0026gt;og:type\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;select id=\u0026quot;og-type\u0026quot;\u0026gt;\n      \u0026lt;option value=\u0026quot;website\u0026quot;\u0026gt;website\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;article\u0026quot;\u0026gt;article\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;product\u0026quot;\u0026gt;product\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;profile\u0026quot;\u0026gt;profile\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;video.other\u0026quot;\u0026gt;video.other\u0026lt;/option\u0026gt;\n    \u0026lt;/select\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;og-label\u0026quot;\u0026gt;\u0026lt;span\u0026gt;twitter:card\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;select id=\u0026quot;og-twcard\u0026quot;\u0026gt;\n      \u0026lt;option value=\u0026quot;summary_large_image\u0026quot;\u0026gt;summary_large_image\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;summary\u0026quot;\u0026gt;summary\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;app\u0026quot;\u0026gt;app\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;player\u0026quot;\u0026gt;player\u0026lt;/option\u0026gt;\n    \u0026lt;/select\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;og-label\u0026quot;\u0026gt;\u0026lt;span\u0026gt;twitter:site (optional)\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;og-twsite\u0026quot; placeholder=\u0026quot;@yourusername\u0026quot;\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n  \u003cdiv style=\"display:flex;gap:10px;flex-wrap:wrap;margin-top:4px;\"\u003e\n    \u003cbutton class=\"og-btn og-btn-primary\" onclick=\"ogUpdate()\"\u003eUpdate Preview\u003c/button\u003e\n    \u003cbutton class=\"og-btn og-btn-secondary\" onclick=\"ogReset()\"\u003eReset\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ===== WARNINGS / ANALYSIS ===== --\u003e\n\u003cdiv class=\"og-card\" id=\"og-analysis-card\" style=\"display:none;\"\u003e\n  \u003ch2\u003eAnalysis\u003c/h2\u003e\n  \u003cdiv class=\"og-warnings\" id=\"og-warnings\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ===== DETECTED TAGS ===== --\u003e\n\u003cdiv class=\"og-card\" id=\"og-tags-card\" style=\"display:none;\"\u003e\n  \u003ch2\u003eDetected OG Tags\u003c/h2\u003e\n  \u003cdiv class=\"og-tags-grid\" id=\"og-tags-grid\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ===== PLATFORM PREVIEWS ===== --\u003e\n\u003cdiv class=\"og-card\" id=\"og-preview-card\" style=\"display:none;\"\u003e\n  \u003ch2\u003ePlatform Previews\u003c/h2\u003e\n  \u003cdiv class=\"og-tabs\"\u003e\n    \u003cbutton class=\"og-tab active\" onclick=\"ogTab(this,'twitter')\"\u003eTwitter / X\u003c/button\u003e\n    \u003cbutton class=\"og-tab\" onclick=\"ogTab(this,'facebook')\"\u003eFacebook\u003c/button\u003e\n    \u003cbutton class=\"og-tab\" onclick=\"ogTab(this,'linkedin')\"\u003eLinkedIn\u003c/button\u003e\n    \u003cbutton class=\"og-tab\" onclick=\"ogTab(this,'discord')\"\u003eDiscord\u003c/button\u003e\n  \u003c/div\u003e\n  \u003c!-- Twitter --\u003e\n  \u003cdiv class=\"og-platform-preview active\" id=\"prev-twitter\"\u003e\n    \u003cdiv class=\"og-twitter-wrap\"\u003e\n      \u003cdiv class=\"og-twitter-card\"\u003e\n        \u003cdiv class=\"og-twitter-img-wrap\" id=\"tw-img-wrap\"\u003e\n          \u003cdiv class=\"og-twitter-img-placeholder\" id=\"tw-img-ph\"\u003e\n            \u003csvg width=\"40\" height=\"40\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\u003e\u003crect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/\u003e\u003ccircle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/\u003e\u003cpath d=\"M21 15l-5-5L5 21\"/\u003e\u003c/svg\u003e\n            No image provided\n          \u003c/div\u003e\n          \u003cimg id=\"tw-img\" src=\"\" alt=\"\" style=\"display:none;\"\u003e\n        \u003c/div\u003e\n        \u003cdiv class=\"og-twitter-meta\"\u003e\n          \u003cdiv class=\"og-twitter-domain\" id=\"tw-domain\"\u003eexample.com\u003c/div\u003e\n          \u003cdiv class=\"og-twitter-title\" id=\"tw-title\"\u003ePage Title\u003c/div\u003e\n          \u003cdiv class=\"og-twitter-desc\" id=\"tw-desc\"\u003ePage description will appear here.\u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cp style=\"font-size:11px;color:#94a3b8;margin-top:8px;\"\u003eTwitter/X uses \u003ccode\u003esummary_large_image\u003c/code\u003e card with 1200\u0026times;630px image.\u003c/p\u003e","title":"OG Image Tester"},{"content":" Online Metronome 120 BPM Moderato Tempo Start Tap Tempo Time Signature 2/4 4/4 3/4 6/8 Volume 80% Tempo Reference Largo40 – 59 BPMVery slow, broad Andante60 – 85 BPMWalking pace Moderato86 – 140 BPMModerate Allegro141 – 180 BPMFast, lively Presto181 – 220 BPMVery fast Related Tools Pomodoro Timer Stopwatch Countdown Timer Focus Noise Generator Related Tools Noise Generator ","permalink":"https://productivity-works.com/tools/metronome/","summary":"\u003cdiv id=\"met-app\"\u003e\n\u003cstyle\u003e\n#met-app {\n  font-family: 'Segoe UI', system-ui, sans-serif;\n  max-width: 540px;\n  margin: 0 auto;\n  padding: 24px 20px;\n  color: #1a1a2e;\n}\n#met-app * { box-sizing: border-box; }\n\n#met-app h2 {\n  text-align: center;\n  font-size: 1.4rem;\n  margin: 0 0 24px;\n  color: #16213e;\n}\n\n/* BPM Display */\n#met-app .bpm-display {\n  text-align: center;\n  margin-bottom: 20px;\n}\n#met-app .bpm-number {\n  font-size: 5rem;\n  font-weight: 800;\n  line-height: 1;\n  color: #0f3460;\n  letter-spacing: -2px;\n}\n#met-app .bpm-label {\n  font-size: 0.85rem;\n  letter-spacing: 3px;\n  text-transform: uppercase;\n  color: #888;\n  margin-top: 4px;\n}\n\n/* Tempo marking */\n#met-app .tempo-marking {\n  text-align: center;\n  font-size: 1.05rem;\n  font-style: italic;\n  color: #e94560;\n  margin-bottom: 20px;\n  min-height: 1.5em;\n}\n\n/* Beat indicator */\n#met-app .beat-row {\n  display: flex;\n  justify-content: center;\n  gap: 10px;\n  margin-bottom: 24px;\n}\n#met-app .beat-dot {\n  width: 36px;\n  height: 36px;\n  border-radius: 50%;\n  background: #dde3f0;\n  transition: background 0.05s, transform 0.05s;\n}\n#met-app .beat-dot.accent {\n  background: #c0c8e0;\n}\n#met-app .beat-dot.active {\n  background: #e94560;\n  transform: scale(1.18);\n  box-shadow: 0 0 12px rgba(233,69,96,0.5);\n}\n#met-app .beat-dot.accent.active {\n  background: #c0392b;\n  box-shadow: 0 0 16px rgba(192,57,43,0.6);\n}\n\n/* Slider */\n#met-app .slider-wrap {\n  margin-bottom: 20px;\n}\n#met-app .slider-wrap label {\n  display: block;\n  font-size: 0.8rem;\n  font-weight: 600;\n  letter-spacing: 1px;\n  text-transform: uppercase;\n  color: #555;\n  margin-bottom: 8px;\n}\n#met-app input[type=\"range\"] {\n  width: 100%;\n  height: 6px;\n  accent-color: #0f3460;\n  cursor: pointer;\n}\n\n/* Buttons */\n#met-app .btn-row {\n  display: flex;\n  gap: 12px;\n  margin-bottom: 20px;\n}\n#met-app .btn {\n  flex: 1;\n  padding: 14px;\n  border: none;\n  border-radius: 10px;\n  font-size: 1rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s;\n}\n#met-app .btn:active { transform: scale(0.97); }\n#met-app .btn-start {\n  background: #0f3460;\n  color: #fff;\n}\n#met-app .btn-start.running {\n  background: #e94560;\n}\n#met-app .btn-tap {\n  background: #f0f3fa;\n  color: #0f3460;\n  border: 2px solid #ccd3e8;\n}\n#met-app .btn-tap:active {\n  background: #dde3f0;\n}\n\n/* Time signature */\n#met-app .timesig-wrap {\n  margin-bottom: 20px;\n}\n#met-app .timesig-wrap label {\n  display: block;\n  font-size: 0.8rem;\n  font-weight: 600;\n  letter-spacing: 1px;\n  text-transform: uppercase;\n  color: #555;\n  margin-bottom: 8px;\n}\n#met-app .timesig-grid {\n  display: flex;\n  gap: 8px;\n}\n#met-app .timesig-btn {\n  flex: 1;\n  padding: 10px 4px;\n  border: 2px solid #ccd3e8;\n  border-radius: 8px;\n  background: #f0f3fa;\n  color: #333;\n  font-size: 0.9rem;\n  font-weight: 600;\n  cursor: pointer;\n  text-align: center;\n  transition: all 0.15s;\n}\n#met-app .timesig-btn.selected {\n  border-color: #0f3460;\n  background: #0f3460;\n  color: #fff;\n}\n\n/* Volume */\n#met-app .vol-wrap {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  margin-bottom: 24px;\n}\n#met-app .vol-wrap label {\n  font-size: 0.8rem;\n  font-weight: 600;\n  letter-spacing: 1px;\n  text-transform: uppercase;\n  color: #555;\n  white-space: nowrap;\n}\n#met-app .vol-wrap input[type=\"range\"] {\n  flex: 1;\n}\n#met-app .vol-pct {\n  font-size: 0.85rem;\n  color: #555;\n  width: 36px;\n  text-align: right;\n}\n\n/* Tempo reference */\n#met-app .tempo-ref {\n  background: #f7f9ff;\n  border: 1px solid #dde3f0;\n  border-radius: 10px;\n  padding: 16px;\n  margin-bottom: 24px;\n}\n#met-app .tempo-ref h3 {\n  font-size: 0.8rem;\n  font-weight: 700;\n  letter-spacing: 1px;\n  text-transform: uppercase;\n  color: #888;\n  margin: 0 0 12px;\n}\n#met-app .tempo-ref table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.88rem;\n}\n#met-app .tempo-ref td {\n  padding: 5px 8px;\n  color: #333;\n}\n#met-app .tempo-ref td:first-child {\n  font-style: italic;\n  font-weight: 600;\n  color: #0f3460;\n  width: 38%;\n}\n#met-app .tempo-ref tr:nth-child(even) td {\n  background: #eef1fa;\n}\n#met-app .tempo-ref .current-row td {\n  background: #ffeef1;\n  color: #c0392b;\n  font-weight: 700;\n}\n\n/* Related tools */\n#met-app .related {\n  border-top: 1px solid #dde3f0;\n  padding-top: 20px;\n}\n#met-app .related h3 {\n  font-size: 0.8rem;\n  font-weight: 700;\n  letter-spacing: 1px;\n  text-transform: uppercase;\n  color: #888;\n  margin: 0 0 10px;\n}\n#met-app .related ul {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n}\n#met-app .related a {\n  display: inline-block;\n  padding: 6px 14px;\n  background: #f0f3fa;\n  border: 1px solid #ccd3e8;\n  border-radius: 20px;\n  font-size: 0.85rem;\n  color: #0f3460;\n  text-decoration: none;\n  transition: background 0.15s;\n}\n#met-app .related a:hover {\n  background: #dde3f0;\n}\n\u003c/style\u003e\n\u003ch2\u003eOnline Metronome\u003c/h2\u003e\n\u003c!-- Beat dots --\u003e\n\u003cdiv class=\"beat-row\" id=\"met-beat-row\"\u003e\u003c/div\u003e\n\u003c!-- BPM display --\u003e\n\u003cdiv class=\"bpm-display\"\u003e\n  \u003cdiv class=\"bpm-number\" id=\"met-bpm-num\"\u003e120\u003c/div\u003e\n  \u003cdiv class=\"bpm-label\"\u003eBPM\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Tempo marking --\u003e\n\u003cdiv class=\"tempo-marking\" id=\"met-tempo-name\"\u003eModerato\u003c/div\u003e\n\u003c!-- BPM slider --\u003e\n\u003cdiv class=\"slider-wrap\"\u003e\n  \u003clabel for=\"met-bpm-slider\"\u003eTempo\u003c/label\u003e\n  \u003cinput type=\"range\" id=\"met-bpm-slider\" min=\"40\" max=\"220\" value=\"120\"\u003e\n\u003c/div\u003e\n\u003c!-- Start / Tap --\u003e\n\u003cdiv class=\"btn-row\"\u003e\n  \u003cbutton class=\"btn btn-start\" id=\"met-start-btn\"\u003eStart\u003c/button\u003e\n  \u003cbutton class=\"btn btn-tap\" id=\"met-tap-btn\"\u003eTap Tempo\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Time signature --\u003e\n\u003cdiv class=\"timesig-wrap\"\u003e\n  \u003clabel\u003eTime Signature\u003c/label\u003e\n  \u003cdiv class=\"timesig-grid\" id=\"met-timesig-grid\"\u003e\n    \u003cbutton class=\"timesig-btn\" data-beats=\"2\" data-label=\"2/4\"\u003e2/4\u003c/button\u003e\n    \u003cbutton class=\"timesig-btn selected\" data-beats=\"4\" data-label=\"4/4\"\u003e4/4\u003c/button\u003e\n    \u003cbutton class=\"timesig-btn\" data-beats=\"3\" data-label=\"3/4\"\u003e3/4\u003c/button\u003e\n    \u003cbutton class=\"timesig-btn\" data-beats=\"6\" data-label=\"6/8\"\u003e6/8\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Volume --\u003e\n\u003cdiv class=\"vol-wrap\"\u003e\n  \u003clabel for=\"met-vol\"\u003eVolume\u003c/label\u003e\n  \u003cinput type=\"range\" id=\"met-vol\" min=\"0\" max=\"100\" value=\"80\"\u003e\n  \u003cspan class=\"vol-pct\" id=\"met-vol-pct\"\u003e80%\u003c/span\u003e\n\u003c/div\u003e\n\u003c!-- Tempo reference table --\u003e\n\u003cdiv class=\"tempo-ref\"\u003e\n  \u003ch3\u003eTempo Reference\u003c/h3\u003e\n  \u003ctable id=\"met-tempo-table\"\u003e\n    \u003ctr data-min=\"40\" data-max=\"59\"\u003e\u003ctd\u003eLargo\u003c/td\u003e\u003ctd\u003e40 – 59 BPM\u003c/td\u003e\u003ctd\u003eVery slow, broad\u003c/td\u003e\u003c/tr\u003e\n    \u003ctr data-min=\"60\" data-max=\"85\"\u003e\u003ctd\u003eAndante\u003c/td\u003e\u003ctd\u003e60 – 85 BPM\u003c/td\u003e\u003ctd\u003eWalking pace\u003c/td\u003e\u003c/tr\u003e\n    \u003ctr data-min=\"86\" data-max=\"140\"\u003e\u003ctd\u003eModerato\u003c/td\u003e\u003ctd\u003e86 – 140 BPM\u003c/td\u003e\u003ctd\u003eModerate\u003c/td\u003e\u003c/tr\u003e\n    \u003ctr data-min=\"141\" data-max=\"180\"\u003e\u003ctd\u003eAllegro\u003c/td\u003e\u003ctd\u003e141 – 180 BPM\u003c/td\u003e\u003ctd\u003eFast, lively\u003c/td\u003e\u003c/tr\u003e\n    \u003ctr data-min=\"181\" data-max=\"220\"\u003e\u003ctd\u003ePresto\u003c/td\u003e\u003ctd\u003e181 – 220 BPM\u003c/td\u003e\u003ctd\u003eVery fast\u003c/td\u003e\u003c/tr\u003e\n  \u003c/table\u003e\n\u003c/div\u003e\n\u003c!-- Related tools --\u003e\n\u003cdiv class=\"related\"\u003e\n  \u003ch3\u003eRelated Tools\u003c/h3\u003e\n  \u003cul\u003e\n    \u003cli\u003e\u003ca href=\"/tools/pomodoro-timer/\"\u003ePomodoro Timer\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/stopwatch/\"\u003eStopwatch\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/countdown-timer/\"\u003eCountdown Timer\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/noise-generator/\"\u003eFocus Noise Generator\u003c/a\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  'use strict';\n\n  // --- State ---\n  var bpm = 120;\n  var beatsPerMeasure = 4;\n  var currentBeat = -1;\n  var isRunning = false;\n  var volume = 0.8;\n  var audioCtx = null;\n  var nextBeatTime = 0;\n  var scheduleAheadTime = 0.1;\n  var lookahead = 25.0;\n  var schedulerTimer = null;\n\n  // Tap tempo state\n  var tapTimes = [];\n\n  // Tempo markings\n  var tempoMarkings = [\n    { name: 'Largo',    min: 40,  max: 59  },\n    { name: 'Andante',  min: 60,  max: 85  },\n    { name: 'Moderato', min: 86,  max: 140 },\n    { name: 'Allegro',  min: 141, max: 180 },\n    { name: 'Presto',   min: 181, max: 220 }\n  ];\n\n  // --- DOM refs ---\n  var bpmSlider  = document.getElementById('met-bpm-slider');\n  var bpmNumEl   = document.getElementById('met-bpm-num');\n  var tempoName  = document.getElementById('met-tempo-name');\n  var startBtn   = document.getElementById('met-start-btn');\n  var tapBtn     = document.getElementById('met-tap-btn');\n  var beatRow    = document.getElementById('met-beat-row');\n  var timesigGrid= document.getElementById('met-timesig-grid');\n  var volSlider  = document.getElementById('met-vol');\n  var volPct     = document.getElementById('met-vol-pct');\n  var tempoTable = document.getElementById('met-tempo-table');\n\n  // --- Audio ---\n  function getAudioCtx() {\n    if (!audioCtx) {\n      audioCtx = new (window.AudioContext || window.webkitAudioContext)();\n    }\n    return audioCtx;\n  }\n\n  function scheduleClick(time, isAccent) {\n    var ctx = getAudioCtx();\n    var osc = ctx.createOscillator();\n    var env = ctx.createGain();\n\n    osc.connect(env);\n    env.connect(ctx.destination);\n\n    if (isAccent) {\n      osc.frequency.value = 1100;\n      env.gain.setValueAtTime(volume, time);\n      env.gain.exponentialRampToValueAtTime(0.001, time + 0.06);\n    } else {\n      osc.frequency.value = 880;\n      env.gain.setValueAtTime(volume * 0.65, time);\n      env.gain.exponentialRampToValueAtTime(0.001, time + 0.04);\n    }\n\n    osc.start(time);\n    osc.stop(time + 0.08);\n  }\n\n  // --- Beat dots ---\n  function renderBeatDots() {\n    beatRow.innerHTML = '';\n    for (var i = 0; i \u003c beatsPerMeasure; i++) {\n      var d = document.createElement('div');\n      d.className = 'beat-dot' + (i === 0 ? ' accent' : '');\n      d.id = 'met-dot-' + i;\n      beatRow.appendChild(d);\n    }\n  }\n\n  function flashBeat(beatIndex) {\n    for (var i = 0; i \u003c beatsPerMeasure; i++) {\n      var d = document.getElementById('met-dot-' + i);\n      if (d) d.classList.remove('active');\n    }\n    var active = document.getElementById('met-dot-' + beatIndex);\n    if (active) active.classList.add('active');\n  }\n\n  function clearBeats() {\n    for (var i = 0; i \u003c beatsPerMeasure; i++) {\n      var d = document.getElementById('met-dot-' + i);\n      if (d) d.classList.remove('active');\n    }\n  }\n\n  // --- Scheduler ---\n  var pendingFlashes = [];\n\n  function scheduler() {\n    var ctx = getAudioCtx();\n    while (nextBeatTime \u003c ctx.currentTime + scheduleAheadTime) {\n      var isAccent = (currentBeat === 0);\n      scheduleClick(nextBeatTime, isAccent);\n\n      // Schedule visual flash\n      var beatToFlash = currentBeat;\n      var flashAt = nextBeatTime;\n      pendingFlashes.push({ beat: beatToFlash, time: flashAt });\n\n      currentBeat = (currentBeat + 1) % beatsPerMeasure;\n      nextBeatTime += 60.0 / bpm;\n    }\n  }\n\n  function visualLoop() {\n    if (!isRunning) return;\n    var ctx = getAudioCtx();\n    var now = ctx.currentTime;\n    while (pendingFlashes.length \u0026\u0026 pendingFlashes[0].time \u003c= now + 0.015) {\n      flashBeat(pendingFlashes.shift().beat);\n    }\n    requestAnimationFrame(visualLoop);\n  }\n\n  function startMetronome() {\n    var ctx = getAudioCtx();\n    if (ctx.state === 'suspended') ctx.resume();\n    currentBeat = 0;\n    pendingFlashes = [];\n    nextBeatTime = ctx.currentTime + 0.05;\n    schedulerTimer = setInterval(scheduler, lookahead);\n    visualLoop();\n  }\n\n  function stopMetronome() {\n    clearInterval(schedulerTimer);\n    schedulerTimer = null;\n    pendingFlashes = [];\n    clearBeats();\n  }\n\n  // --- BPM helpers ---\n  function getTempoName(b) {\n    for (var i = 0; i \u003c tempoMarkings.length; i++) {\n      if (b \u003e= tempoMarkings[i].min \u0026\u0026 b \u003c= tempoMarkings[i].max) return tempoMarkings[i].name;\n    }\n    return '';\n  }\n\n  function updateBPM(val) {\n    bpm = Math.min(220, Math.max(40, val));\n    bpmSlider.value = bpm;\n    bpmNumEl.textContent = bpm;\n    tempoName.textContent = getTempoName(bpm);\n    highlightTempoRow(bpm);\n  }\n\n  function highlightTempoRow(b) {\n    var rows = tempoTable.querySelectorAll('tr');\n    rows.forEach(function(row) {\n      var mn = parseInt(row.getAttribute('data-min'));\n      var mx = parseInt(row.getAttribute('data-max'));\n      if (b \u003e= mn \u0026\u0026 b \u003c= mx) {\n        row.classList.add('current-row');\n      } else {\n        row.classList.remove('current-row');\n      }\n    });\n  }\n\n  // --- Events ---\n  bpmSlider.addEventListener('input', function() {\n    updateBPM(parseInt(this.value));\n  });\n\n  startBtn.addEventListener('click', function() {\n    isRunning = !isRunning;\n    if (isRunning) {\n      startBtn.textContent = 'Stop';\n      startBtn.classList.add('running');\n      startMetronome();\n    } else {\n      startBtn.textContent = 'Start';\n      startBtn.classList.remove('running');\n      stopMetronome();\n    }\n  });\n\n  // Tap tempo\n  tapBtn.addEventListener('click', function() {\n    var now = Date.now();\n    tapTimes.push(now);\n    // Keep only taps within last 3 seconds\n    tapTimes = tapTimes.filter(function(t) { return now - t \u003c 3000; });\n    if (tapTimes.length \u003e= 2) {\n      var intervals = [];\n      for (var i = 1; i \u003c tapTimes.length; i++) {\n        intervals.push(tapTimes[i] - tapTimes[i-1]);\n      }\n      var avg = intervals.reduce(function(a,b){return a+b;},0) / intervals.length;\n      var tappedBPM = Math.round(60000 / avg);\n      updateBPM(tappedBPM);\n      // Restart if running to apply new BPM immediately\n      if (isRunning) {\n        stopMetronome();\n        startMetronome();\n      }\n    }\n    // Visual feedback\n    tapBtn.style.background = '#0f3460';\n    tapBtn.style.color = '#fff';\n    setTimeout(function() {\n      tapBtn.style.background = '';\n      tapBtn.style.color = '';\n    }, 100);\n  });\n\n  // Time signature buttons\n  timesigGrid.querySelectorAll('.timesig-btn').forEach(function(btn) {\n    btn.addEventListener('click', function() {\n      timesigGrid.querySelectorAll('.timesig-btn').forEach(function(b){ b.classList.remove('selected'); });\n      this.classList.add('selected');\n      beatsPerMeasure = parseInt(this.getAttribute('data-beats'));\n      currentBeat = 0;\n      pendingFlashes = [];\n      renderBeatDots();\n      if (!isRunning) clearBeats();\n    });\n  });\n\n  // Volume\n  volSlider.addEventListener('input', function() {\n    volume = parseInt(this.value) / 100;\n    volPct.textContent = this.value + '%';\n  });\n\n  // BPM input via click on number\n  bpmNumEl.style.cursor = 'pointer';\n  bpmNumEl.title = 'Click to type BPM';\n  bpmNumEl.addEventListener('click', function() {\n    var val = prompt('Enter BPM (40–220):', bpm);\n    if (val !== null) {\n      var n = parseInt(val);\n      if (!isNaN(n)) updateBPM(n);\n    }\n  });\n\n  // --- Init ---\n  renderBeatDots();\n  updateBPM(120);\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"https://productivity-works.com/tools/noise-generator/\"\u003eNoise Generator\u003c/a\u003e\n\u003c/p\u003e","title":"Online Metronome - Free BPM Timer"},{"content":"Paste your URL or fill in the fields below to instantly preview how your page will appear when shared on Facebook, Twitter, LinkedIn, and in Google search results.\nOpen Graph Properties \u0026lt;div class=\u0026quot;og-section-label\u0026quot;\u0026gt;Core Tags\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;og-title\u0026quot;\u0026gt;og:title\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;og-title\u0026quot; placeholder=\u0026quot;Your page title\u0026quot; maxlength=\u0026quot;200\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-char-info\u0026quot; id=\u0026quot;og-title-count\u0026quot;\u0026gt;0 / 60\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;og-description\u0026quot;\u0026gt;og:description\u0026lt;/label\u0026gt; \u0026lt;textarea id=\u0026quot;og-description\u0026quot; placeholder=\u0026quot;A concise description of your page content...\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;div class=\u0026quot;og-char-info\u0026quot; id=\u0026quot;og-desc-count\u0026quot;\u0026gt;0 / 160\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;og-url\u0026quot;\u0026gt;og:url\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;url\u0026quot; id=\u0026quot;og-url\u0026quot; placeholder=\u0026quot;https://example.com/page\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;og-image\u0026quot;\u0026gt;og:image (URL)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;url\u0026quot; id=\u0026quot;og-image\u0026quot; placeholder=\u0026quot;https://example.com/image.jpg\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-img-hint\u0026quot;\u0026gt;Recommended: \u0026lt;span\u0026gt;1200 × 630 px\u0026lt;/span\u0026gt; (1.91:1) · Min 600 × 315 px · Max 8 MB\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;og-type\u0026quot;\u0026gt;og:type\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;og-type\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;website\u0026quot;\u0026gt;website\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;article\u0026quot;\u0026gt;article\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;product\u0026quot;\u0026gt;product\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;video.other\u0026quot;\u0026gt;video.other\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;music.song\u0026quot;\u0026gt;music.song\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;book\u0026quot;\u0026gt;book\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;profile\u0026quot;\u0026gt;profile\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;og-site-name\u0026quot;\u0026gt;og:site_name\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;og-site-name\u0026quot; placeholder=\u0026quot;Your Site Name\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-section-label\u0026quot; style=\u0026quot;margin-top:22px;\u0026quot;\u0026gt;Twitter Card\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;tw-card\u0026quot;\u0026gt;twitter:card\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;tw-card\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;summary_large_image\u0026quot;\u0026gt;summary_large_image (large image)\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;summary\u0026quot;\u0026gt;summary (small square image)\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;tw-title\u0026quot;\u0026gt;twitter:title \u0026lt;em style=\u0026quot;font-weight:400;color:#9ca3af;\u0026quot;\u0026gt;(leave blank to use og:title)\u0026lt;/em\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;tw-title\u0026quot; placeholder=\u0026quot;Twitter-specific title (optional)\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-char-info\u0026quot; id=\u0026quot;tw-title-count\u0026quot;\u0026gt;0 / 70\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;tw-description\u0026quot;\u0026gt;twitter:description \u0026lt;em style=\u0026quot;font-weight:400;color:#9ca3af;\u0026quot;\u0026gt;(leave blank to use og:description)\u0026lt;/em\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;textarea id=\u0026quot;tw-description\u0026quot; placeholder=\u0026quot;Twitter-specific description (optional)\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;div class=\u0026quot;og-char-info\u0026quot; id=\u0026quot;tw-desc-count\u0026quot;\u0026gt;0 / 200\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt; \u0026lt;label for=\u0026quot;tw-image\u0026quot;\u0026gt;twitter:image \u0026lt;em style=\u0026quot;font-weight:400;color:#9ca3af;\u0026quot;\u0026gt;(leave blank to use og:image)\u0026lt;/em\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;url\u0026quot; id=\u0026quot;tw-image\u0026quot; placeholder=\u0026quot;https://example.com/twitter-image.jpg\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-img-hint\u0026quot;\u0026gt;summary_large_image: \u0026lt;span\u0026gt;1200 × 628 px\u0026lt;/span\u0026gt; · summary: \u0026lt;span\u0026gt;144 × 144 px\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-btn-row\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;og-btn og-btn-primary\u0026quot; id=\u0026quot;og-generate-btn\u0026quot;\u0026gt;Generate HTML Meta Tags\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;og-btn og-btn-secondary\u0026quot; id=\u0026quot;og-clear-btn\u0026quot;\u0026gt;Clear All\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; Image Dimension Guide \u0026lt;table class=\u0026quot;og-dim-table\u0026quot;\u0026gt; \u0026lt;thead\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;th\u0026gt;Platform\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;Recommended Size\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;Ratio\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;Max Size\u0026lt;/th\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;/thead\u0026gt; \u0026lt;tbody\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;td\u0026gt;\u0026lt;strong\u0026gt;Facebook\u0026lt;/strong\u0026gt;\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;1200 × 630 px\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;1.91:1\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;8 MB\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;td\u0026gt;\u0026lt;strong\u0026gt;Twitter Large\u0026lt;/strong\u0026gt;\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;1200 × 628 px\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;~1.91:1\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;5 MB\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;td\u0026gt;\u0026lt;strong\u0026gt;Twitter Summary\u0026lt;/strong\u0026gt;\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;144 × 144 px\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;1:1\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;5 MB\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;td\u0026gt;\u0026lt;strong\u0026gt;LinkedIn\u0026lt;/strong\u0026gt;\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;1200 × 627 px\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;1.91:1\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;5 MB\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;td\u0026gt;\u0026lt;strong\u0026gt;Google (SERP)\u0026lt;/strong\u0026gt;\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;N/A (uses text)\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;—\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;—\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;/tbody\u0026gt; \u0026lt;/table\u0026gt; \u0026lt;div style=\u0026quot;margin-top:20px;\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-section-label\u0026quot;\u0026gt;Title Character Limits\u0026lt;/div\u0026gt; \u0026lt;table class=\u0026quot;og-dim-table\u0026quot;\u0026gt; \u0026lt;thead\u0026gt; \u0026lt;tr\u0026gt;\u0026lt;th\u0026gt;Platform\u0026lt;/th\u0026gt;\u0026lt;th\u0026gt;Title\u0026lt;/th\u0026gt;\u0026lt;th\u0026gt;Description\u0026lt;/th\u0026gt;\u0026lt;/tr\u0026gt; \u0026lt;/thead\u0026gt; \u0026lt;tbody\u0026gt; \u0026lt;tr\u0026gt;\u0026lt;td\u0026gt;\u0026lt;strong\u0026gt;Facebook OG\u0026lt;/strong\u0026gt;\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;~60 chars\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;~160 chars\u0026lt;/td\u0026gt;\u0026lt;/tr\u0026gt; \u0026lt;tr\u0026gt;\u0026lt;td\u0026gt;\u0026lt;strong\u0026gt;Twitter\u0026lt;/strong\u0026gt;\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;~70 chars\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;~200 chars\u0026lt;/td\u0026gt;\u0026lt;/tr\u0026gt; \u0026lt;tr\u0026gt;\u0026lt;td\u0026gt;\u0026lt;strong\u0026gt;LinkedIn\u0026lt;/strong\u0026gt;\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;~119 chars\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;~300 chars\u0026lt;/td\u0026gt;\u0026lt;/tr\u0026gt; \u0026lt;tr\u0026gt;\u0026lt;td\u0026gt;\u0026lt;strong\u0026gt;Google SERP\u0026lt;/strong\u0026gt;\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;~60 chars\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;~160 chars\u0026lt;/td\u0026gt;\u0026lt;/tr\u0026gt; \u0026lt;/tbody\u0026gt; \u0026lt;/table\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div style=\u0026quot;margin-top:20px;\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-section-label\u0026quot;\u0026gt;Tips\u0026lt;/div\u0026gt; \u0026lt;ul style=\u0026quot;font-size:12.5px;color:#374151;padding-left:18px;line-height:1.8;\u0026quot;\u0026gt; \u0026lt;li\u0026gt;Use HTTPS for all image URLs (HTTP may be blocked).\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;Facebook caches OG data — use the \u0026lt;a href=\u0026quot;https://developers.facebook.com/tools/json-formatter/\u0026quot; target=\u0026quot;_blank\u0026quot; rel=\u0026quot;noopener\u0026quot; style=\u0026quot;color:#4f6ef7;\u0026quot;\u0026gt;Sharing Debugger\u0026lt;/a\u0026gt; to refresh.\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;Twitter crawls images on first share. Images smaller than 144×144 px are ignored.\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;LinkedIn has a 7-day cache — post a new URL to trigger a fresh crawl.\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;Keep titles under 60 characters to avoid truncation in Google search.\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;Avoid duplicate og:title and \u0026amp;lt;title\u0026amp;gt; tag values where possible.\u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; \u0026lt;/div\u0026gt; Live Previews \u0026lt;!-- Facebook --\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div class=\u0026quot;og-card-label\u0026quot;\u0026gt;Facebook\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-card-fb\u0026quot; id=\u0026quot;prev-fb\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-card-img\u0026quot; id=\u0026quot;prev-fb-img-wrap\u0026quot;\u0026gt; \u0026lt;span\u0026gt;No image URL provided\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-card-body\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-card-domain\u0026quot; id=\u0026quot;prev-fb-domain\u0026quot;\u0026gt;example.com\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-card-title\u0026quot; id=\u0026quot;prev-fb-title\u0026quot;\u0026gt;Your page title will appear here\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-card-desc\u0026quot; id=\u0026quot;prev-fb-desc\u0026quot;\u0026gt;Your page description will appear here.\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Twitter --\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div class=\u0026quot;og-card-label\u0026quot;\u0026gt;Twitter / X\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-card-tw summary-large\u0026quot; id=\u0026quot;prev-tw\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-tw-inner\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-card-img-tw\u0026quot; id=\u0026quot;prev-tw-img-wrap\u0026quot; style=\u0026quot;aspect-ratio:2/1;\u0026quot;\u0026gt; \u0026lt;span\u0026gt;No image URL provided\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-card-body-tw\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-card-domain-tw\u0026quot; id=\u0026quot;prev-tw-domain\u0026quot;\u0026gt;example.com\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-card-title-tw\u0026quot; id=\u0026quot;prev-tw-title\u0026quot;\u0026gt;Your page title will appear here\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-card-desc-tw\u0026quot; id=\u0026quot;prev-tw-desc\u0026quot;\u0026gt;Your page description will appear here.\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- LinkedIn --\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div class=\u0026quot;og-card-label\u0026quot;\u0026gt;LinkedIn\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-card-li\u0026quot; id=\u0026quot;prev-li\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-card-img-li\u0026quot; id=\u0026quot;prev-li-img-wrap\u0026quot;\u0026gt; \u0026lt;span\u0026gt;No image URL provided\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-card-body-li\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-card-title-li\u0026quot; id=\u0026quot;prev-li-title\u0026quot;\u0026gt;Your page title will appear here\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-card-domain-li\u0026quot; id=\u0026quot;prev-li-domain\u0026quot;\u0026gt;example.com\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Google SERP --\u0026gt; \u0026lt;div\u0026gt; \u0026lt;div class=\u0026quot;og-card-label\u0026quot;\u0026gt;Google Search Result\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-card-google\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;og-serp-url\u0026quot;\u0026gt; \u0026lt;span id=\u0026quot;prev-g-sitename\u0026quot;\u0026gt;Your Site\u0026lt;/span\u0026gt; \u0026amp;rsaquo; \u0026lt;span class=\u0026quot;og-serp-breadcrumb\u0026quot; id=\u0026quot;prev-g-breadcrumb\u0026quot;\u0026gt;example.com\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-serp-title\u0026quot; id=\u0026quot;prev-g-title\u0026quot;\u0026gt;Your page title will appear here\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;og-serp-desc\u0026quot; id=\u0026quot;prev-g-desc\u0026quot;\u0026gt;Your page description will appear here. Google may override this with page content it deems more relevant to the query.\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Generated HTML Meta Tags Copy to Clipboard Copied to clipboard! Related Tools Generate meta tags → Meta Tag Generator Create favicons → Favicon Generator Generate placeholder images → Placeholder Image Generator ","permalink":"https://productivity-works.com/tools/og-preview/","summary":"\u003cp\u003ePaste your URL or fill in the fields below to instantly preview how your page will appear when shared on Facebook, Twitter, LinkedIn, and in Google search results.\u003c/p\u003e\n\u003cdiv id=\"og-app\"\u003e\n\u003cstyle\u003e\n/* ── Reset \u0026 base ── */\n#og-app *,\n#og-app *::before,\n#og-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#og-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n  font-size: 15px;\n  color: #1a1a2e;\n  line-height: 1.5;\n}\n\n/* ── Layout ── */\n#og-app .og-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 24px;\n  margin-top: 20px;\n}\n@media (max-width: 760px) {\n  #og-app .og-grid {\n    grid-template-columns: 1fr;\n  }\n}\n\n/* ── Panel ── */\n#og-app .og-panel {\n  background: #f8f9fc;\n  border: 1px solid #dde1ea;\n  border-radius: 10px;\n  padding: 22px 20px;\n}\n#og-app .og-panel h2 {\n  font-size: 15px;\n  font-weight: 700;\n  color: #2c3e6b;\n  margin-bottom: 16px;\n  padding-bottom: 10px;\n  border-bottom: 2px solid #dde1ea;\n  letter-spacing: 0.02em;\n  text-transform: uppercase;\n}\n\n/* ── Form fields ── */\n#og-app .og-field {\n  margin-bottom: 14px;\n}\n#og-app .og-field label {\n  display: block;\n  font-size: 12px;\n  font-weight: 600;\n  color: #4a5378;\n  margin-bottom: 5px;\n  letter-spacing: 0.03em;\n}\n#og-app .og-field input,\n#og-app .og-field select,\n#og-app .og-field textarea {\n  width: 100%;\n  padding: 8px 11px;\n  border: 1px solid #c8cdd8;\n  border-radius: 6px;\n  font-size: 13px;\n  color: #1a1a2e;\n  background: #fff;\n  transition: border-color 0.18s;\n  outline: none;\n  font-family: inherit;\n}\n#og-app .og-field input:focus,\n#og-app .og-field select:focus,\n#og-app .og-field textarea:focus {\n  border-color: #4f6ef7;\n  box-shadow: 0 0 0 3px rgba(79,110,247,0.12);\n}\n#og-app .og-field textarea {\n  resize: vertical;\n  min-height: 64px;\n}\n\n/* ── Character counter ── */\n#og-app .og-char-info {\n  display: flex;\n  justify-content: flex-end;\n  font-size: 11px;\n  margin-top: 4px;\n  color: #888;\n  transition: color 0.2s;\n}\n#og-app .og-char-info.warn {\n  color: #d97706;\n  font-weight: 600;\n}\n#og-app .og-char-info.over {\n  color: #dc2626;\n  font-weight: 700;\n}\n\n/* ── Image hint ── */\n#og-app .og-img-hint {\n  font-size: 11px;\n  color: #6b7280;\n  margin-top: 5px;\n  line-height: 1.4;\n}\n#og-app .og-img-hint span {\n  display: inline-block;\n  background: #e8eeff;\n  color: #3b5bdb;\n  border-radius: 4px;\n  padding: 1px 6px;\n  font-weight: 600;\n  font-size: 10.5px;\n  margin-right: 4px;\n}\n\n/* ── Buttons ── */\n#og-app .og-btn-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  margin-top: 18px;\n}\n#og-app .og-btn {\n  padding: 9px 18px;\n  border-radius: 7px;\n  border: none;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.18s, transform 0.1s;\n  font-family: inherit;\n}\n#og-app .og-btn:active {\n  transform: scale(0.97);\n}\n#og-app .og-btn-primary {\n  background: #4f6ef7;\n  color: #fff;\n}\n#og-app .og-btn-primary:hover {\n  background: #3a56d4;\n}\n#og-app .og-btn-secondary {\n  background: #e8eeff;\n  color: #3b5bdb;\n  border: 1px solid #c5d0ff;\n}\n#og-app .og-btn-secondary:hover {\n  background: #d5dfff;\n}\n#og-app .og-btn-success {\n  background: #059669;\n  color: #fff;\n}\n#og-app .og-btn-success:hover {\n  background: #047857;\n}\n\n/* ── Toast ── */\n#og-app .og-toast {\n  display: none;\n  position: fixed;\n  bottom: 28px;\n  right: 28px;\n  background: #1e293b;\n  color: #fff;\n  padding: 11px 20px;\n  border-radius: 8px;\n  font-size: 13px;\n  font-weight: 500;\n  z-index: 9999;\n  box-shadow: 0 4px 18px rgba(0,0,0,0.22);\n  animation: og-fadeIn 0.25s ease;\n}\n@keyframes og-fadeIn {\n  from { opacity: 0; transform: translateY(8px); }\n  to   { opacity: 1; transform: translateY(0); }\n}\n\n/* ── Section divider ── */\n#og-app .og-section-label {\n  font-size: 11px;\n  font-weight: 700;\n  color: #4f6ef7;\n  letter-spacing: 0.08em;\n  text-transform: uppercase;\n  margin: 18px 0 10px;\n  padding-left: 2px;\n}\n\n/* ── Preview panels ── */\n#og-app .og-previews {\n  margin-top: 28px;\n}\n#og-app .og-previews h2 {\n  font-size: 17px;\n  font-weight: 700;\n  color: #2c3e6b;\n  margin-bottom: 18px;\n}\n#og-app .og-preview-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 20px;\n}\n@media (max-width: 680px) {\n  #og-app .og-preview-grid {\n    grid-template-columns: 1fr;\n  }\n}\n\n/* ── Facebook card ── */\n#og-app .og-card-fb {\n  border: 1px solid #dde1ea;\n  border-radius: 4px;\n  overflow: hidden;\n  background: #fff;\n  font-family: Helvetica, Arial, sans-serif;\n  max-width: 500px;\n}\n#og-app .og-card-fb .og-card-img {\n  width: 100%;\n  aspect-ratio: 1.91/1;\n  object-fit: cover;\n  background: #e5e7eb;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: #9ca3af;\n  font-size: 13px;\n}\n#og-app .og-card-fb .og-card-img img {\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n  display: block;\n}\n#og-app .og-card-fb .og-card-body {\n  padding: 10px 12px 12px;\n  border-top: 1px solid #dde1ea;\n  background: #f2f3f5;\n}\n#og-app .og-card-fb .og-card-domain {\n  font-size: 11px;\n  color: #606770;\n  text-transform: uppercase;\n  letter-spacing: 0.02em;\n  margin-bottom: 3px;\n}\n#og-app .og-card-fb .og-card-title {\n  font-size: 16px;\n  font-weight: 700;\n  color: #1c1e21;\n  line-height: 1.3;\n  margin-bottom: 4px;\n  display: -webkit-box;\n  -webkit-line-clamp: 2;\n  -webkit-box-orient: vertical;\n  overflow: hidden;\n}\n#og-app .og-card-fb .og-card-desc {\n  font-size: 14px;\n  color: #606770;\n  line-height: 1.4;\n  display: -webkit-box;\n  -webkit-line-clamp: 2;\n  -webkit-box-orient: vertical;\n  overflow: hidden;\n}\n\n/* ── Twitter card ── */\n#og-app .og-card-tw {\n  border: 1px solid #e1e8ed;\n  border-radius: 14px;\n  overflow: hidden;\n  background: #fff;\n  font-family: -apple-system, \"Segoe UI\", sans-serif;\n  max-width: 500px;\n}\n#og-app .og-card-tw.summary-large .og-card-img-tw {\n  aspect-ratio: 2/1;\n}\n#og-app .og-card-tw.summary .og-tw-inner {\n  display: flex;\n  flex-direction: row;\n}\n#og-app .og-card-tw.summary .og-card-img-tw {\n  width: 120px;\n  min-width: 120px;\n  aspect-ratio: 1/1;\n  flex-shrink: 0;\n}\n#og-app .og-card-img-tw {\n  width: 100%;\n  background: #e5e7eb;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: #9ca3af;\n  font-size: 13px;\n  overflow: hidden;\n}\n#og-app .og-card-img-tw img {\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n  display: block;\n}\n#og-app .og-card-tw .og-card-body-tw {\n  padding: 10px 14px 12px;\n  flex: 1;\n}\n#og-app .og-card-tw .og-card-domain-tw {\n  font-size: 13px;\n  color: #8899a6;\n  margin-bottom: 3px;\n}\n#og-app .og-card-tw .og-card-title-tw {\n  font-size: 15px;\n  font-weight: 700;\n  color: #14171a;\n  line-height: 1.3;\n  margin-bottom: 3px;\n  display: -webkit-box;\n  -webkit-line-clamp: 2;\n  -webkit-box-orient: vertical;\n  overflow: hidden;\n}\n#og-app .og-card-tw .og-card-desc-tw {\n  font-size: 13px;\n  color: #657786;\n  line-height: 1.4;\n  display: -webkit-box;\n  -webkit-line-clamp: 2;\n  -webkit-box-orient: vertical;\n  overflow: hidden;\n}\n\n/* ── LinkedIn card ── */\n#og-app .og-card-li {\n  border: 1px solid #d6d6d6;\n  border-radius: 2px;\n  overflow: hidden;\n  background: #fff;\n  font-family: -apple-system, \"Segoe UI\", sans-serif;\n  max-width: 500px;\n}\n#og-app .og-card-li .og-card-img-li {\n  width: 100%;\n  aspect-ratio: 1.91/1;\n  background: #e5e7eb;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: #9ca3af;\n  font-size: 13px;\n  overflow: hidden;\n}\n#og-app .og-card-li .og-card-img-li img {\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n  display: block;\n}\n#og-app .og-card-li .og-card-body-li {\n  padding: 8px 12px 10px;\n  background: #f3f2ef;\n  border-top: 1px solid #d6d6d6;\n}\n#og-app .og-card-li .og-card-title-li {\n  font-size: 14px;\n  font-weight: 600;\n  color: rgba(0,0,0,0.9);\n  line-height: 1.35;\n  margin-bottom: 2px;\n  display: -webkit-box;\n  -webkit-line-clamp: 2;\n  -webkit-box-orient: vertical;\n  overflow: hidden;\n}\n#og-app .og-card-li .og-card-domain-li {\n  font-size: 12px;\n  color: rgba(0,0,0,0.6);\n}\n\n/* ── Google SERP ── */\n#og-app .og-card-google {\n  background: #fff;\n  padding: 14px 16px;\n  border: 1px solid #dde1ea;\n  border-radius: 8px;\n  max-width: 600px;\n  font-family: arial, sans-serif;\n}\n#og-app .og-card-google .og-serp-url {\n  font-size: 12px;\n  color: #202124;\n  line-height: 1.4;\n  margin-bottom: 4px;\n}\n#og-app .og-card-google .og-serp-breadcrumb {\n  font-size: 12px;\n  color: #5f6368;\n}\n#og-app .og-card-google .og-serp-title {\n  font-size: 20px;\n  color: #1a0dab;\n  line-height: 1.3;\n  margin-bottom: 3px;\n  display: -webkit-box;\n  -webkit-line-clamp: 1;\n  -webkit-box-orient: vertical;\n  overflow: hidden;\n  cursor: pointer;\n  text-decoration: none;\n}\n#og-app .og-card-google .og-serp-title:hover {\n  text-decoration: underline;\n}\n#og-app .og-card-google .og-serp-desc {\n  font-size: 14px;\n  color: #4d5156;\n  line-height: 1.58;\n  display: -webkit-box;\n  -webkit-line-clamp: 2;\n  -webkit-box-orient: vertical;\n  overflow: hidden;\n}\n\n/* ── Card label ── */\n#og-app .og-card-label {\n  font-size: 11px;\n  font-weight: 700;\n  color: #6b7280;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  margin-bottom: 7px;\n}\n\n/* ── Code output ── */\n#og-app .og-code-wrap {\n  margin-top: 24px;\n  display: none;\n}\n#og-app .og-code-wrap h3 {\n  font-size: 14px;\n  font-weight: 700;\n  color: #2c3e6b;\n  margin-bottom: 8px;\n}\n#og-app .og-code-box {\n  background: #0f172a;\n  color: #e2e8f0;\n  border-radius: 8px;\n  padding: 16px;\n  font-family: \"Fira Code\", \"Cascadia Code\", Consolas, monospace;\n  font-size: 12px;\n  line-height: 1.7;\n  overflow-x: auto;\n  white-space: pre;\n}\n\n/* ── Dimension table ── */\n#og-app .og-dim-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 12.5px;\n  margin-top: 4px;\n}\n#og-app .og-dim-table th {\n  text-align: left;\n  font-weight: 600;\n  color: #4a5378;\n  padding: 5px 8px;\n  background: #eef0f8;\n  border: 1px solid #dde1ea;\n}\n#og-app .og-dim-table td {\n  padding: 5px 8px;\n  border: 1px solid #dde1ea;\n  color: #374151;\n}\n#og-app .og-dim-table tr:nth-child(even) td {\n  background: #f8f9fc;\n}\n\u003c/style\u003e\n\u003cdiv class=\"og-grid\"\u003e\n  \u003c!-- ═══ LEFT: OG fields ═══ --\u003e\n  \u003cdiv class=\"og-panel\"\u003e\n    \u003ch2\u003eOpen Graph Properties\u003c/h2\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;div class=\u0026quot;og-section-label\u0026quot;\u0026gt;Core Tags\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt;\n  \u0026lt;label for=\u0026quot;og-title\u0026quot;\u0026gt;og:title\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;og-title\u0026quot; placeholder=\u0026quot;Your page title\u0026quot; maxlength=\u0026quot;200\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;og-char-info\u0026quot; id=\u0026quot;og-title-count\u0026quot;\u0026gt;0 / 60\u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt;\n  \u0026lt;label for=\u0026quot;og-description\u0026quot;\u0026gt;og:description\u0026lt;/label\u0026gt;\n  \u0026lt;textarea id=\u0026quot;og-description\u0026quot; placeholder=\u0026quot;A concise description of your page content...\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt;\n  \u0026lt;div class=\u0026quot;og-char-info\u0026quot; id=\u0026quot;og-desc-count\u0026quot;\u0026gt;0 / 160\u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt;\n  \u0026lt;label for=\u0026quot;og-url\u0026quot;\u0026gt;og:url\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;url\u0026quot; id=\u0026quot;og-url\u0026quot; placeholder=\u0026quot;https://example.com/page\u0026quot;\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt;\n  \u0026lt;label for=\u0026quot;og-image\u0026quot;\u0026gt;og:image (URL)\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;url\u0026quot; id=\u0026quot;og-image\u0026quot; placeholder=\u0026quot;https://example.com/image.jpg\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;og-img-hint\u0026quot;\u0026gt;Recommended: \u0026lt;span\u0026gt;1200 × 630 px\u0026lt;/span\u0026gt; (1.91:1) · Min 600 × 315 px · Max 8 MB\u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt;\n  \u0026lt;label for=\u0026quot;og-type\u0026quot;\u0026gt;og:type\u0026lt;/label\u0026gt;\n  \u0026lt;select id=\u0026quot;og-type\u0026quot;\u0026gt;\n    \u0026lt;option value=\u0026quot;website\u0026quot;\u0026gt;website\u0026lt;/option\u0026gt;\n    \u0026lt;option value=\u0026quot;article\u0026quot;\u0026gt;article\u0026lt;/option\u0026gt;\n    \u0026lt;option value=\u0026quot;product\u0026quot;\u0026gt;product\u0026lt;/option\u0026gt;\n    \u0026lt;option value=\u0026quot;video.other\u0026quot;\u0026gt;video.other\u0026lt;/option\u0026gt;\n    \u0026lt;option value=\u0026quot;music.song\u0026quot;\u0026gt;music.song\u0026lt;/option\u0026gt;\n    \u0026lt;option value=\u0026quot;book\u0026quot;\u0026gt;book\u0026lt;/option\u0026gt;\n    \u0026lt;option value=\u0026quot;profile\u0026quot;\u0026gt;profile\u0026lt;/option\u0026gt;\n  \u0026lt;/select\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt;\n  \u0026lt;label for=\u0026quot;og-site-name\u0026quot;\u0026gt;og:site_name\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;og-site-name\u0026quot; placeholder=\u0026quot;Your Site Name\u0026quot;\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;og-section-label\u0026quot; style=\u0026quot;margin-top:22px;\u0026quot;\u0026gt;Twitter Card\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt;\n  \u0026lt;label for=\u0026quot;tw-card\u0026quot;\u0026gt;twitter:card\u0026lt;/label\u0026gt;\n  \u0026lt;select id=\u0026quot;tw-card\u0026quot;\u0026gt;\n    \u0026lt;option value=\u0026quot;summary_large_image\u0026quot;\u0026gt;summary_large_image (large image)\u0026lt;/option\u0026gt;\n    \u0026lt;option value=\u0026quot;summary\u0026quot;\u0026gt;summary (small square image)\u0026lt;/option\u0026gt;\n  \u0026lt;/select\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt;\n  \u0026lt;label for=\u0026quot;tw-title\u0026quot;\u0026gt;twitter:title \u0026lt;em style=\u0026quot;font-weight:400;color:#9ca3af;\u0026quot;\u0026gt;(leave blank to use og:title)\u0026lt;/em\u0026gt;\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;tw-title\u0026quot; placeholder=\u0026quot;Twitter-specific title (optional)\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;og-char-info\u0026quot; id=\u0026quot;tw-title-count\u0026quot;\u0026gt;0 / 70\u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt;\n  \u0026lt;label for=\u0026quot;tw-description\u0026quot;\u0026gt;twitter:description \u0026lt;em style=\u0026quot;font-weight:400;color:#9ca3af;\u0026quot;\u0026gt;(leave blank to use og:description)\u0026lt;/em\u0026gt;\u0026lt;/label\u0026gt;\n  \u0026lt;textarea id=\u0026quot;tw-description\u0026quot; placeholder=\u0026quot;Twitter-specific description (optional)\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt;\n  \u0026lt;div class=\u0026quot;og-char-info\u0026quot; id=\u0026quot;tw-desc-count\u0026quot;\u0026gt;0 / 200\u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;og-field\u0026quot;\u0026gt;\n  \u0026lt;label for=\u0026quot;tw-image\u0026quot;\u0026gt;twitter:image \u0026lt;em style=\u0026quot;font-weight:400;color:#9ca3af;\u0026quot;\u0026gt;(leave blank to use og:image)\u0026lt;/em\u0026gt;\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;url\u0026quot; id=\u0026quot;tw-image\u0026quot; placeholder=\u0026quot;https://example.com/twitter-image.jpg\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;og-img-hint\u0026quot;\u0026gt;summary_large_image: \u0026lt;span\u0026gt;1200 × 628 px\u0026lt;/span\u0026gt; · summary: \u0026lt;span\u0026gt;144 × 144 px\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;og-btn-row\u0026quot;\u0026gt;\n  \u0026lt;button class=\u0026quot;og-btn og-btn-primary\u0026quot; id=\u0026quot;og-generate-btn\u0026quot;\u0026gt;Generate HTML Meta Tags\u0026lt;/button\u0026gt;\n  \u0026lt;button class=\u0026quot;og-btn og-btn-secondary\u0026quot; id=\u0026quot;og-clear-btn\u0026quot;\u0026gt;Clear All\u0026lt;/button\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n  \u003c!-- ═══ RIGHT: dimension reference ═══ --\u003e\n  \u003cdiv class=\"og-panel\"\u003e\n    \u003ch2\u003eImage Dimension Guide\u003c/h2\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;table class=\u0026quot;og-dim-table\u0026quot;\u0026gt;\n  \u0026lt;thead\u0026gt;\n    \u0026lt;tr\u0026gt;\n      \u0026lt;th\u0026gt;Platform\u0026lt;/th\u0026gt;\n      \u0026lt;th\u0026gt;Recommended Size\u0026lt;/th\u0026gt;\n      \u0026lt;th\u0026gt;Ratio\u0026lt;/th\u0026gt;\n      \u0026lt;th\u0026gt;Max Size\u0026lt;/th\u0026gt;\n    \u0026lt;/tr\u0026gt;\n  \u0026lt;/thead\u0026gt;\n  \u0026lt;tbody\u0026gt;\n    \u0026lt;tr\u0026gt;\n      \u0026lt;td\u0026gt;\u0026lt;strong\u0026gt;Facebook\u0026lt;/strong\u0026gt;\u0026lt;/td\u0026gt;\n      \u0026lt;td\u0026gt;1200 × 630 px\u0026lt;/td\u0026gt;\n      \u0026lt;td\u0026gt;1.91:1\u0026lt;/td\u0026gt;\n      \u0026lt;td\u0026gt;8 MB\u0026lt;/td\u0026gt;\n    \u0026lt;/tr\u0026gt;\n    \u0026lt;tr\u0026gt;\n      \u0026lt;td\u0026gt;\u0026lt;strong\u0026gt;Twitter Large\u0026lt;/strong\u0026gt;\u0026lt;/td\u0026gt;\n      \u0026lt;td\u0026gt;1200 × 628 px\u0026lt;/td\u0026gt;\n      \u0026lt;td\u0026gt;~1.91:1\u0026lt;/td\u0026gt;\n      \u0026lt;td\u0026gt;5 MB\u0026lt;/td\u0026gt;\n    \u0026lt;/tr\u0026gt;\n    \u0026lt;tr\u0026gt;\n      \u0026lt;td\u0026gt;\u0026lt;strong\u0026gt;Twitter Summary\u0026lt;/strong\u0026gt;\u0026lt;/td\u0026gt;\n      \u0026lt;td\u0026gt;144 × 144 px\u0026lt;/td\u0026gt;\n      \u0026lt;td\u0026gt;1:1\u0026lt;/td\u0026gt;\n      \u0026lt;td\u0026gt;5 MB\u0026lt;/td\u0026gt;\n    \u0026lt;/tr\u0026gt;\n    \u0026lt;tr\u0026gt;\n      \u0026lt;td\u0026gt;\u0026lt;strong\u0026gt;LinkedIn\u0026lt;/strong\u0026gt;\u0026lt;/td\u0026gt;\n      \u0026lt;td\u0026gt;1200 × 627 px\u0026lt;/td\u0026gt;\n      \u0026lt;td\u0026gt;1.91:1\u0026lt;/td\u0026gt;\n      \u0026lt;td\u0026gt;5 MB\u0026lt;/td\u0026gt;\n    \u0026lt;/tr\u0026gt;\n    \u0026lt;tr\u0026gt;\n      \u0026lt;td\u0026gt;\u0026lt;strong\u0026gt;Google (SERP)\u0026lt;/strong\u0026gt;\u0026lt;/td\u0026gt;\n      \u0026lt;td\u0026gt;N/A (uses text)\u0026lt;/td\u0026gt;\n      \u0026lt;td\u0026gt;—\u0026lt;/td\u0026gt;\n      \u0026lt;td\u0026gt;—\u0026lt;/td\u0026gt;\n    \u0026lt;/tr\u0026gt;\n  \u0026lt;/tbody\u0026gt;\n\u0026lt;/table\u0026gt;\n\n\u0026lt;div style=\u0026quot;margin-top:20px;\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;og-section-label\u0026quot;\u0026gt;Title Character Limits\u0026lt;/div\u0026gt;\n  \u0026lt;table class=\u0026quot;og-dim-table\u0026quot;\u0026gt;\n    \u0026lt;thead\u0026gt;\n      \u0026lt;tr\u0026gt;\u0026lt;th\u0026gt;Platform\u0026lt;/th\u0026gt;\u0026lt;th\u0026gt;Title\u0026lt;/th\u0026gt;\u0026lt;th\u0026gt;Description\u0026lt;/th\u0026gt;\u0026lt;/tr\u0026gt;\n    \u0026lt;/thead\u0026gt;\n    \u0026lt;tbody\u0026gt;\n      \u0026lt;tr\u0026gt;\u0026lt;td\u0026gt;\u0026lt;strong\u0026gt;Facebook OG\u0026lt;/strong\u0026gt;\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;~60 chars\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;~160 chars\u0026lt;/td\u0026gt;\u0026lt;/tr\u0026gt;\n      \u0026lt;tr\u0026gt;\u0026lt;td\u0026gt;\u0026lt;strong\u0026gt;Twitter\u0026lt;/strong\u0026gt;\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;~70 chars\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;~200 chars\u0026lt;/td\u0026gt;\u0026lt;/tr\u0026gt;\n      \u0026lt;tr\u0026gt;\u0026lt;td\u0026gt;\u0026lt;strong\u0026gt;LinkedIn\u0026lt;/strong\u0026gt;\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;~119 chars\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;~300 chars\u0026lt;/td\u0026gt;\u0026lt;/tr\u0026gt;\n      \u0026lt;tr\u0026gt;\u0026lt;td\u0026gt;\u0026lt;strong\u0026gt;Google SERP\u0026lt;/strong\u0026gt;\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;~60 chars\u0026lt;/td\u0026gt;\u0026lt;td\u0026gt;~160 chars\u0026lt;/td\u0026gt;\u0026lt;/tr\u0026gt;\n    \u0026lt;/tbody\u0026gt;\n  \u0026lt;/table\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div style=\u0026quot;margin-top:20px;\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;og-section-label\u0026quot;\u0026gt;Tips\u0026lt;/div\u0026gt;\n  \u0026lt;ul style=\u0026quot;font-size:12.5px;color:#374151;padding-left:18px;line-height:1.8;\u0026quot;\u0026gt;\n    \u0026lt;li\u0026gt;Use HTTPS for all image URLs (HTTP may be blocked).\u0026lt;/li\u0026gt;\n    \u0026lt;li\u0026gt;Facebook caches OG data — use the \u0026lt;a href=\u0026quot;https://developers.facebook.com/tools/json-formatter/\u0026quot; target=\u0026quot;_blank\u0026quot; rel=\u0026quot;noopener\u0026quot; style=\u0026quot;color:#4f6ef7;\u0026quot;\u0026gt;Sharing Debugger\u0026lt;/a\u0026gt; to refresh.\u0026lt;/li\u0026gt;\n    \u0026lt;li\u0026gt;Twitter crawls images on first share. Images smaller than 144×144 px are ignored.\u0026lt;/li\u0026gt;\n    \u0026lt;li\u0026gt;LinkedIn has a 7-day cache — post a new URL to trigger a fresh crawl.\u0026lt;/li\u0026gt;\n    \u0026lt;li\u0026gt;Keep titles under 60 characters to avoid truncation in Google search.\u0026lt;/li\u0026gt;\n    \u0026lt;li\u0026gt;Avoid duplicate og:title and \u0026amp;lt;title\u0026amp;gt; tag values where possible.\u0026lt;/li\u0026gt;\n  \u0026lt;/ul\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ═══ LIVE PREVIEWS ═══ --\u003e\n\u003cdiv class=\"og-previews\"\u003e\n  \u003ch2\u003eLive Previews\u003c/h2\u003e\n  \u003cdiv class=\"og-preview-grid\"\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;!-- Facebook --\u0026gt;\n\u0026lt;div\u0026gt;\n  \u0026lt;div class=\u0026quot;og-card-label\u0026quot;\u0026gt;Facebook\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;og-card-fb\u0026quot; id=\u0026quot;prev-fb\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;og-card-img\u0026quot; id=\u0026quot;prev-fb-img-wrap\u0026quot;\u0026gt;\n      \u0026lt;span\u0026gt;No image URL provided\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;og-card-body\u0026quot;\u0026gt;\n      \u0026lt;div class=\u0026quot;og-card-domain\u0026quot; id=\u0026quot;prev-fb-domain\u0026quot;\u0026gt;example.com\u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;og-card-title\u0026quot; id=\u0026quot;prev-fb-title\u0026quot;\u0026gt;Your page title will appear here\u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;og-card-desc\u0026quot; id=\u0026quot;prev-fb-desc\u0026quot;\u0026gt;Your page description will appear here.\u0026lt;/div\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Twitter --\u0026gt;\n\u0026lt;div\u0026gt;\n  \u0026lt;div class=\u0026quot;og-card-label\u0026quot;\u0026gt;Twitter / X\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;og-card-tw summary-large\u0026quot; id=\u0026quot;prev-tw\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;og-tw-inner\u0026quot;\u0026gt;\n      \u0026lt;div class=\u0026quot;og-card-img-tw\u0026quot; id=\u0026quot;prev-tw-img-wrap\u0026quot; style=\u0026quot;aspect-ratio:2/1;\u0026quot;\u0026gt;\n        \u0026lt;span\u0026gt;No image URL provided\u0026lt;/span\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;og-card-body-tw\u0026quot;\u0026gt;\n        \u0026lt;div class=\u0026quot;og-card-domain-tw\u0026quot; id=\u0026quot;prev-tw-domain\u0026quot;\u0026gt;example.com\u0026lt;/div\u0026gt;\n        \u0026lt;div class=\u0026quot;og-card-title-tw\u0026quot; id=\u0026quot;prev-tw-title\u0026quot;\u0026gt;Your page title will appear here\u0026lt;/div\u0026gt;\n        \u0026lt;div class=\u0026quot;og-card-desc-tw\u0026quot; id=\u0026quot;prev-tw-desc\u0026quot;\u0026gt;Your page description will appear here.\u0026lt;/div\u0026gt;\n      \u0026lt;/div\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- LinkedIn --\u0026gt;\n\u0026lt;div\u0026gt;\n  \u0026lt;div class=\u0026quot;og-card-label\u0026quot;\u0026gt;LinkedIn\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;og-card-li\u0026quot; id=\u0026quot;prev-li\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;og-card-img-li\u0026quot; id=\u0026quot;prev-li-img-wrap\u0026quot;\u0026gt;\n      \u0026lt;span\u0026gt;No image URL provided\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;og-card-body-li\u0026quot;\u0026gt;\n      \u0026lt;div class=\u0026quot;og-card-title-li\u0026quot; id=\u0026quot;prev-li-title\u0026quot;\u0026gt;Your page title will appear here\u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;og-card-domain-li\u0026quot; id=\u0026quot;prev-li-domain\u0026quot;\u0026gt;example.com\u0026lt;/div\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Google SERP --\u0026gt;\n\u0026lt;div\u0026gt;\n  \u0026lt;div class=\u0026quot;og-card-label\u0026quot;\u0026gt;Google Search Result\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;og-card-google\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;og-serp-url\u0026quot;\u0026gt;\n      \u0026lt;span id=\u0026quot;prev-g-sitename\u0026quot;\u0026gt;Your Site\u0026lt;/span\u0026gt; \u0026amp;rsaquo;\n      \u0026lt;span class=\u0026quot;og-serp-breadcrumb\u0026quot; id=\u0026quot;prev-g-breadcrumb\u0026quot;\u0026gt;example.com\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;og-serp-title\u0026quot; id=\u0026quot;prev-g-title\u0026quot;\u0026gt;Your page title will appear here\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;og-serp-desc\u0026quot; id=\u0026quot;prev-g-desc\u0026quot;\u0026gt;Your page description will appear here. Google may override this with page content it deems more relevant to the query.\u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ═══ GENERATED CODE ═══ --\u003e\n\u003cdiv class=\"og-code-wrap\" id=\"og-code-section\"\u003e\n  \u003ch3\u003eGenerated HTML Meta Tags\u003c/h3\u003e\n  \u003cdiv class=\"og-btn-row\" style=\"margin-bottom:10px;\"\u003e\n    \u003cbutton class=\"og-btn og-btn-success\" id=\"og-copy-btn\"\u003eCopy to Clipboard\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"og-code-box\" id=\"og-code-output\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"og-toast\" class=\"og-toast\"\u003eCopied to clipboard!\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  'use strict';\n\n  /* ── Helpers ── */\n  function el(id) { return document.getElementById(id); }\n\n  function getDomain(url) {\n    try {\n      return new URL(url).hostname.replace(/^www\\./, '');\n    } catch(e) {\n      return url ? url.replace(/^https?:\\/\\/(www\\.)?/, '').split('/')[0] : 'example.com';\n    }\n  }\n\n  function escapeHtml(str) {\n    return str.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;').replace(/\"/g,'\u0026quot;');\n  }\n\n  /* ── Character counter ── */\n  function bindCounter(inputId, counterId, limit) {\n    var inp = el(inputId);\n    var ctr = el(counterId);\n    if (!inp || !ctr) return;\n    inp.addEventListener('input', function() {\n      var len = inp.value.length;\n      ctr.textContent = len + ' / ' + limit;\n      ctr.className = 'og-char-info' +\n        (len \u003e limit ? ' over' : (len \u003e limit * 0.85 ? ' warn' : ''));\n    });\n  }\n\n  bindCounter('og-title',       'og-title-count',  60);\n  bindCounter('og-description', 'og-desc-count',  160);\n  bindCounter('tw-title',       'tw-title-count',   70);\n  bindCounter('tw-description', 'tw-desc-count',  200);\n\n  /* ── Live image render ── */\n  function renderImage(wrapId, url, placeholderText) {\n    var wrap = el(wrapId);\n    if (!wrap) return;\n    if (url \u0026\u0026 url.trim()) {\n      wrap.innerHTML = '\u003cimg src=\"' + escapeHtml(url.trim()) + '\" alt=\"OG Preview Image\" onerror=\"this.parentNode.innerHTML=\\'\u003cspan\u003eImage failed to load\u003c/span\u003e\\'\"\u003e';\n    } else {\n      wrap.innerHTML = '\u003cspan\u003e' + placeholderText + '\u003c/span\u003e';\n    }\n  }\n\n  /* ── Update previews ── */\n  function updatePreviews() {\n    var title    = el('og-title').value.trim()       || 'Your page title will appear here';\n    var desc     = el('og-description').value.trim() || 'Your page description will appear here.';\n    var url      = el('og-url').value.trim();\n    var image    = el('og-image').value.trim();\n    var twCard   = el('tw-card').value;\n    var twTitle  = el('tw-title').value.trim()       || title;\n    var twDesc   = el('tw-description').value.trim() || desc;\n    var twImage  = el('tw-image').value.trim()       || image;\n    var siteName = el('og-site-name').value.trim();\n    var domain   = getDomain(url) || 'example.com';\n\n    /* Facebook */\n    el('prev-fb-title').textContent  = title;\n    el('prev-fb-desc').textContent   = desc;\n    el('prev-fb-domain').textContent = domain.toUpperCase();\n    renderImage('prev-fb-img-wrap', image, 'No image URL provided');\n\n    /* Twitter */\n    var twCardEl = el('prev-tw');\n    if (twCard === 'summary') {\n      twCardEl.className = 'og-card-tw summary';\n      var imgWrap = el('prev-tw-img-wrap');\n      if (imgWrap) {\n        imgWrap.style.aspectRatio = '1/1';\n        imgWrap.style.width = '120px';\n      }\n    } else {\n      twCardEl.className = 'og-card-tw summary-large';\n      var imgWrap2 = el('prev-tw-img-wrap');\n      if (imgWrap2) {\n        imgWrap2.style.aspectRatio = '2/1';\n        imgWrap2.style.width = '100%';\n      }\n    }\n    el('prev-tw-title').textContent  = twTitle;\n    el('prev-tw-desc').textContent   = twDesc;\n    el('prev-tw-domain').textContent = domain;\n    renderImage('prev-tw-img-wrap', twImage, 'No image URL provided');\n\n    /* LinkedIn */\n    el('prev-li-title').textContent  = title;\n    el('prev-li-domain').textContent = domain;\n    renderImage('prev-li-img-wrap', image, 'No image URL provided');\n\n    /* Google */\n    el('prev-g-title').textContent      = title;\n    el('prev-g-desc').textContent       = desc;\n    el('prev-g-breadcrumb').textContent = domain;\n    el('prev-g-sitename').textContent   = siteName || domain;\n  }\n\n  /* ── Debounce ── */\n  var debounceTimer;\n  function debounce(fn, delay) {\n    clearTimeout(debounceTimer);\n    debounceTimer = setTimeout(fn, delay);\n  }\n\n  var liveInputs = ['og-title','og-description','og-url','og-image','og-site-name','tw-title','tw-description','tw-image','tw-card','og-type'];\n  liveInputs.forEach(function(id) {\n    var elem = el(id);\n    if (elem) {\n      elem.addEventListener('input',  function() { debounce(updatePreviews, 120); });\n      elem.addEventListener('change', function() { debounce(updatePreviews, 120); });\n    }\n  });\n\n  /* ── Generate meta tags ── */\n  el('og-generate-btn').addEventListener('click', function() {\n    var title    = el('og-title').value.trim();\n    var desc     = el('og-description').value.trim();\n    var url      = el('og-url').value.trim();\n    var image    = el('og-image').value.trim();\n    var type     = el('og-type').value;\n    var siteName = el('og-site-name').value.trim();\n    var twCard   = el('tw-card').value;\n    var twTitle  = el('tw-title').value.trim();\n    var twDesc   = el('tw-description').value.trim();\n    var twImage  = el('tw-image').value.trim();\n\n    var lines = [];\n    lines.push('\u003c!-- Open Graph / Facebook --\u003e');\n    if (type)     lines.push('\u003cmeta property=\"og:type\"        content=\"' + escapeHtml(type)     + '\" /\u003e');\n    if (url)      lines.push('\u003cmeta property=\"og:url\"         content=\"' + escapeHtml(url)      + '\" /\u003e');\n    if (title)    lines.push('\u003cmeta property=\"og:title\"       content=\"' + escapeHtml(title)    + '\" /\u003e');\n    if (desc)     lines.push('\u003cmeta property=\"og:description\" content=\"' + escapeHtml(desc)     + '\" /\u003e');\n    if (image)    lines.push('\u003cmeta property=\"og:image\"       content=\"' + escapeHtml(image)    + '\" /\u003e');\n    if (siteName) lines.push('\u003cmeta property=\"og:site_name\"   content=\"' + escapeHtml(siteName) + '\" /\u003e');\n    lines.push('');\n    lines.push('\u003c!-- Twitter Card --\u003e');\n    lines.push('\u003cmeta name=\"twitter:card\"        content=\"' + escapeHtml(twCard) + '\" /\u003e');\n    if (twTitle || title)  lines.push('\u003cmeta name=\"twitter:title\"       content=\"' + escapeHtml(twTitle || title)   + '\" /\u003e');\n    if (twDesc  || desc)   lines.push('\u003cmeta name=\"twitter:description\" content=\"' + escapeHtml(twDesc  || desc)    + '\" /\u003e');\n    if (twImage || image)  lines.push('\u003cmeta name=\"twitter:image\"       content=\"' + escapeHtml(twImage || image)   + '\" /\u003e');\n\n    var code = lines.join('\\n');\n    var codeSection = el('og-code-section');\n    el('og-code-output').textContent = code;\n    codeSection.style.display = 'block';\n    codeSection.scrollIntoView({ behavior: 'smooth', block: 'start' });\n  });\n\n  /* ── Copy ── */\n  el('og-copy-btn').addEventListener('click', function() {\n    var text = el('og-code-output').textContent;\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(text).then(showToast);\n    } else {\n      var ta = document.createElement('textarea');\n      ta.value = text;\n      ta.style.position = 'fixed';\n      ta.style.opacity = '0';\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      showToast();\n    }\n  });\n\n  function showToast() {\n    var t = el('og-toast');\n    t.style.display = 'block';\n    setTimeout(function() { t.style.display = 'none'; }, 2200);\n  }\n\n  /* ── Clear ── */\n  el('og-clear-btn').addEventListener('click', function() {\n    ['og-title','og-description','og-url','og-image','og-site-name','tw-title','tw-description','tw-image'].forEach(function(id) {\n      var elem = el(id);\n      if (elem) elem.value = '';\n    });\n    el('og-type').value  = 'website';\n    el('tw-card').value  = 'summary_large_image';\n    ['og-title-count','og-desc-count','tw-title-count','tw-desc-count'].forEach(function(id) {\n      var elem = el(id);\n      if (elem) { elem.textContent = '0 / ' + elem.textContent.split('/')[1].trim(); elem.className = 'og-char-info'; }\n    });\n    el('og-code-section').style.display = 'none';\n    updatePreviews();\n  });\n\n  /* ── Initial render ── */\n  updatePreviews();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch3 id=\"related-tools\"\u003eRelated Tools\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003eGenerate meta tags → \u003ca href=\"https://productivity-works.com/tools/meta-tag-generator/\"\u003eMeta Tag Generator\u003c/a\u003e\n\u003c/p\u003e","title":"Open Graph Preview — Social Share Debugger"},{"content":"A free tool to help you choose the right open source license. Answer a few questions to get a recommendation, compare licenses side by side, and copy ready-to-use license text.\nRelated tools: Git Command Generator — Changelog Generator Wizard Compare All License Type Commercial Use Modify Distribute Patent Grant Private Use Copyleft Attribution Network Use \u0026#10005; How to Choose a License Permissive licenses (MIT, Apache 2.0, BSD) let anyone use your code with minimal restrictions. MIT is the most popular for open-source projects. Apache 2.0 adds an explicit patent grant, making it better suited for larger projects.\nCopyleft licenses (GPL v2/v3) require derivative works to be released under the same license. Use these when you want improvements to always remain open.\nWeak copyleft (LGPL, MPL 2.0) applies copyleft only at the file or library boundary, letting proprietary software link against your library.\nPublic domain (Unlicense, CC0) waives all copyright. Maximum freedom for users, but note that CC0 is recommended for data/content, not software.\nQuick Reference Goal Recommended Maximum adoption MIT Patent protection Apache 2.0 Keep derivatives open GPL v3 Library, allow proprietary linking LGPL or MPL 2.0 No restrictions at all Unlicense Related tools: Git Command Generator — Changelog Generator ","permalink":"https://productivity-works.com/tools/license-chooser/","summary":"\u003cp\u003eA free tool to help you choose the right open source license. Answer a few questions to get a recommendation, compare licenses side by side, and copy ready-to-use license text.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eRelated tools:\u003c/strong\u003e \u003ca href=\"https://productivity-works.com/tools/git-command-generator/\"\u003eGit Command Generator\u003c/a\u003e\n — \u003ca href=\"https://productivity-works.com/tools/changelog-generator/\"\u003eChangelog Generator\u003c/a\u003e\n\u003c/p\u003e\n\u003cdiv id=\"ol-app\"\u003e\n\u003cstyle\u003e\n#ol-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#ol-app * { box-sizing: border-box; }\n\n/* Tabs */\n#ol-app .ol-tabs {\n  display: flex;\n  gap: 4px;\n  margin-bottom: 24px;\n  border-bottom: 2px solid #e2e8f0;\n}\n#ol-app .ol-tab-btn {\n  padding: 10px 22px;\n  border: none;\n  background: none;\n  cursor: pointer;\n  font-size: 15px;\n  font-weight: 600;\n  color: #64748b;\n  border-bottom: 3px solid transparent;\n  margin-bottom: -2px;\n  transition: color 0.2s, border-color 0.2s;\n}\n#ol-app .ol-tab-btn.active {\n  color: #4f46e5;\n  border-bottom-color: #4f46e5;\n}\n#ol-app .ol-tab-btn:hover:not(.active) { color: #334155; }\n\n/* Panel */\n#ol-app .ol-panel { display: none; }\n#ol-app .ol-panel.active { display: block; }\n\n/* Wizard */\n#ol-app .ol-wizard-step {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 28px;\n  margin-bottom: 20px;\n}\n#ol-app .ol-wizard-step h3 {\n  margin: 0 0 8px;\n  font-size: 17px;\n  color: #1e293b;\n}\n#ol-app .ol-wizard-step p {\n  margin: 0 0 16px;\n  color: #64748b;\n  font-size: 14px;\n}\n#ol-app .ol-btn-group {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n}\n#ol-app .ol-choice-btn {\n  padding: 10px 20px;\n  border: 2px solid #e2e8f0;\n  border-radius: 8px;\n  background: #fff;\n  cursor: pointer;\n  font-size: 14px;\n  font-weight: 500;\n  color: #334155;\n  transition: all 0.15s;\n}\n#ol-app .ol-choice-btn:hover { border-color: #4f46e5; color: #4f46e5; }\n#ol-app .ol-choice-btn.selected { border-color: #4f46e5; background: #eef2ff; color: #4f46e5; }\n#ol-app .ol-step-indicator {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin-bottom: 24px;\n}\n#ol-app .ol-step-dot {\n  width: 28px; height: 28px;\n  border-radius: 50%;\n  display: flex; align-items: center; justify-content: center;\n  font-size: 12px; font-weight: 700;\n  background: #e2e8f0; color: #64748b;\n  transition: all 0.2s;\n}\n#ol-app .ol-step-dot.done { background: #4f46e5; color: #fff; }\n#ol-app .ol-step-dot.current { background: #818cf8; color: #fff; }\n#ol-app .ol-step-line {\n  flex: 1; height: 2px; background: #e2e8f0;\n}\n#ol-app .ol-step-line.done { background: #4f46e5; }\n\n#ol-app .ol-result-box {\n  border: 2px solid #4f46e5;\n  border-radius: 12px;\n  padding: 28px;\n  background: #eef2ff;\n  margin-top: 24px;\n}\n#ol-app .ol-result-box h3 { margin: 0 0 6px; color: #4f46e5; font-size: 20px; }\n#ol-app .ol-result-box .ol-tldr {\n  font-size: 14px; color: #334155; margin-bottom: 16px;\n}\n#ol-app .ol-result-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 16px; }\n#ol-app .ol-btn-primary {\n  padding: 10px 20px;\n  background: #4f46e5; color: #fff;\n  border: none; border-radius: 8px;\n  cursor: pointer; font-size: 14px; font-weight: 600;\n  transition: background 0.2s;\n}\n#ol-app .ol-btn-primary:hover { background: #4338ca; }\n#ol-app .ol-btn-secondary {\n  padding: 10px 20px;\n  background: #fff; color: #4f46e5;\n  border: 2px solid #4f46e5; border-radius: 8px;\n  cursor: pointer; font-size: 14px; font-weight: 600;\n  transition: all 0.2s;\n}\n#ol-app .ol-btn-secondary:hover { background: #eef2ff; }\n\n/* Comparison table */\n#ol-app .ol-table-wrap { overflow-x: auto; }\n#ol-app .ol-table {\n  width: 100%; border-collapse: collapse;\n  font-size: 13px;\n}\n#ol-app .ol-table th {\n  background: #1e293b; color: #fff;\n  padding: 10px 14px; text-align: left;\n  white-space: nowrap;\n  position: sticky; top: 0;\n}\n#ol-app .ol-table td {\n  padding: 9px 14px;\n  border-bottom: 1px solid #e2e8f0;\n  vertical-align: top;\n}\n#ol-app .ol-table tr:hover td { background: #f8fafc; }\n#ol-app .ol-table .ol-license-name {\n  font-weight: 700; color: #4f46e5; cursor: pointer;\n  white-space: nowrap;\n}\n#ol-app .ol-table .ol-license-name:hover { text-decoration: underline; }\n#ol-app .ol-check { color: #16a34a; font-weight: 700; }\n#ol-app .ol-cross { color: #dc2626; font-weight: 700; }\n#ol-app .ol-partial { color: #d97706; font-weight: 700; }\n\n/* Badge */\n#ol-app .ol-badge {\n  display: inline-block;\n  padding: 2px 10px;\n  border-radius: 4px;\n  font-size: 11px; font-weight: 700;\n  letter-spacing: 0.02em;\n}\n#ol-app .ol-badge-permissive { background: #dcfce7; color: #15803d; }\n#ol-app .ol-badge-copyleft  { background: #fee2e2; color: #b91c1c; }\n#ol-app .ol-badge-weak     { background: #fef3c7; color: #92400e; }\n#ol-app .ol-badge-public   { background: #e0e7ff; color: #3730a3; }\n\n/* Detail modal */\n#ol-app .ol-modal-backdrop {\n  display: none;\n  position: fixed; inset: 0;\n  background: rgba(0,0,0,0.5);\n  z-index: 1000;\n  align-items: center; justify-content: center;\n}\n#ol-app .ol-modal-backdrop.open { display: flex; }\n#ol-app .ol-modal {\n  background: #fff;\n  border-radius: 14px;\n  max-width: 680px; width: 95vw;\n  max-height: 85vh;\n  overflow-y: auto;\n  padding: 32px;\n  position: relative;\n  box-shadow: 0 20px 60px rgba(0,0,0,0.2);\n}\n#ol-app .ol-modal-close {\n  position: absolute; top: 16px; right: 16px;\n  background: #f1f5f9; border: none;\n  width: 32px; height: 32px; border-radius: 50%;\n  cursor: pointer; font-size: 18px; color: #64748b;\n  display: flex; align-items: center; justify-content: center;\n}\n#ol-app .ol-modal-close:hover { background: #e2e8f0; }\n#ol-app .ol-modal h2 { margin: 0 0 4px; font-size: 22px; color: #1e293b; }\n#ol-app .ol-modal .ol-modal-tldr {\n  color: #64748b; font-size: 14px; margin-bottom: 20px;\n}\n#ol-app .ol-modal pre {\n  background: #0f172a; color: #e2e8f0;\n  padding: 20px; border-radius: 10px;\n  font-size: 12px; line-height: 1.6;\n  white-space: pre-wrap; word-break: break-word;\n  max-height: 300px; overflow-y: auto;\n}\n#ol-app .ol-copy-row {\n  display: flex; gap: 8px; margin-bottom: 14px; flex-wrap: wrap;\n}\n#ol-app .ol-input-sm {\n  padding: 7px 10px;\n  border: 1px solid #cbd5e1;\n  border-radius: 6px; font-size: 13px;\n  width: 160px;\n}\n#ol-app .ol-copy-btn {\n  padding: 7px 16px;\n  background: #4f46e5; color: #fff;\n  border: none; border-radius: 6px;\n  cursor: pointer; font-size: 13px; font-weight: 600;\n  transition: background 0.2s;\n}\n#ol-app .ol-copy-btn:hover { background: #4338ca; }\n#ol-app .ol-copy-btn.copied { background: #16a34a; }\n#ol-app .ol-shields-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 12px; }\n#ol-app .ol-shield {\n  display: inline-flex; align-items: center;\n  border-radius: 4px; overflow: hidden;\n  font-size: 11px; font-weight: 600; height: 20px;\n}\n#ol-app .ol-shield-left { background: #555; color: #fff; padding: 0 6px; }\n#ol-app .ol-shield-right { padding: 0 8px; color: #fff; }\n\n/* Sections in modal */\n#ol-app .ol-modal-section { margin-bottom: 18px; }\n#ol-app .ol-modal-section h4 { font-size: 13px; text-transform: uppercase; letter-spacing: 0.06em; color: #94a3b8; margin: 0 0 8px; }\n#ol-app .ol-pill-list { display: flex; flex-wrap: wrap; gap: 6px; }\n#ol-app .ol-pill {\n  padding: 3px 10px; border-radius: 20px; font-size: 12px; font-weight: 500;\n}\n#ol-app .ol-pill-green { background: #dcfce7; color: #15803d; }\n#ol-app .ol-pill-red   { background: #fee2e2; color: #b91c1c; }\n#ol-app .ol-pill-blue  { background: #dbeafe; color: #1d4ed8; }\n\n/* Reset btn */\n#ol-app .ol-reset-btn {\n  padding: 8px 16px; border: 1px solid #e2e8f0;\n  border-radius: 8px; background: #fff; cursor: pointer;\n  font-size: 13px; color: #64748b; margin-top: 12px;\n}\n#ol-app .ol-reset-btn:hover { border-color: #94a3b8; color: #334155; }\n\u003c/style\u003e\n\u003cdiv class=\"ol-tabs\"\u003e\n  \u003cbutton class=\"ol-tab-btn active\" onclick=\"olSwitchTab('wizard')\"\u003eWizard\u003c/button\u003e\n  \u003cbutton class=\"ol-tab-btn\" onclick=\"olSwitchTab('compare')\"\u003eCompare All\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- WIZARD PANEL --\u003e\n\u003cdiv id=\"ol-panel-wizard\" class=\"ol-panel active\"\u003e\n  \u003cdiv class=\"ol-step-indicator\" id=\"ol-step-indicator\"\u003e\u003c/div\u003e\n  \u003cdiv id=\"ol-wizard-body\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- COMPARE PANEL --\u003e\n\u003cdiv id=\"ol-panel-compare\" class=\"ol-panel\"\u003e\n  \u003cdiv class=\"ol-table-wrap\"\u003e\n    \u003ctable class=\"ol-table\" id=\"ol-compare-table\"\u003e\n      \u003cthead\u003e\n        \u003ctr\u003e\n          \u003cth\u003eLicense\u003c/th\u003e\n          \u003cth\u003eType\u003c/th\u003e\n          \u003cth\u003eCommercial Use\u003c/th\u003e\n          \u003cth\u003eModify\u003c/th\u003e\n          \u003cth\u003eDistribute\u003c/th\u003e\n          \u003cth\u003ePatent Grant\u003c/th\u003e\n          \u003cth\u003ePrivate Use\u003c/th\u003e\n          \u003cth\u003eCopyleft\u003c/th\u003e\n          \u003cth\u003eAttribution\u003c/th\u003e\n          \u003cth\u003eNetwork Use\u003c/th\u003e\n        \u003c/tr\u003e\n      \u003c/thead\u003e\n      \u003ctbody id=\"ol-compare-body\"\u003e\u003c/tbody\u003e\n    \u003c/table\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- MODAL --\u003e\n\u003cdiv class=\"ol-modal-backdrop\" id=\"ol-modal\" onclick=\"olCloseModal(event)\"\u003e\n  \u003cdiv class=\"ol-modal\" id=\"ol-modal-inner\"\u003e\n    \u003cbutton class=\"ol-modal-close\" onclick=\"olCloseModal(null, true)\"\u003e\u0026#10005;\u003c/button\u003e\n    \u003cdiv id=\"ol-modal-content\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n\"use strict\";\n\n// ─── License data ───────────────────────────────────────────────────────────\nvar LICENSES = [\n  {\n    id: \"mit\",\n    name: \"MIT\",\n    fullName: \"MIT License\",\n    type: \"permissive\",\n    badge: \"permissive\",\n    tldr: \"Short and simple permissive license. Lets people do almost anything with your code as long as they include the original copyright and license notice.\",\n    permissions: [\"Commercial use\",\"Modification\",\"Distribution\",\"Private use\"],\n    conditions: [\"License and copyright notice\"],\n    limitations: [\"Liability\",\"Warranty\"],\n    commercial: true, modify: true, distribute: true, patent: false, private: true,\n    copyleft: false, attribution: true, networkUse: false,\n    color: \"#16a34a\",\n    template: `MIT License\n\nCopyright (c) {{YEAR}} {{NAME}}\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.`\n  },\n  {\n    id: \"apache2\",\n    name: \"Apache 2.0\",\n    fullName: \"Apache License 2.0\",\n    type: \"permissive\",\n    badge: \"permissive\",\n    tldr: \"Permissive license with an explicit grant of patent rights from contributors to users. Requires preservation of copyright and license notices.\",\n    permissions: [\"Commercial use\",\"Modification\",\"Distribution\",\"Patent use\",\"Private use\"],\n    conditions: [\"License and copyright notice\",\"State changes\",\"Notice file if present\"],\n    limitations: [\"Liability\",\"Trademark use\",\"Warranty\"],\n    commercial: true, modify: true, distribute: true, patent: true, private: true,\n    copyleft: false, attribution: true, networkUse: false,\n    color: \"#2563eb\",\n    template: `Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nCopyright (c) {{YEAR}} {{NAME}}\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.`\n  },\n  {\n    id: \"gplv3\",\n    name: \"GPL v3\",\n    fullName: \"GNU General Public License v3.0\",\n    type: \"copyleft\",\n    badge: \"copyleft\",\n    tldr: \"Strong copyleft license. Derivative works must be open-sourced under the same license. Includes patent protection and anti-tivoization provisions.\",\n    permissions: [\"Commercial use\",\"Modification\",\"Distribution\",\"Patent use\",\"Private use\"],\n    conditions: [\"Disclose source\",\"License and copyright notice\",\"Same license\",\"State changes\"],\n    limitations: [\"Liability\",\"Warranty\"],\n    commercial: true, modify: true, distribute: true, patent: true, private: true,\n    copyleft: true, attribution: true, networkUse: false,\n    color: \"#dc2626\",\n    template: `GNU GENERAL PUBLIC LICENSE\nVersion 3, 29 June 2007\n\nCopyright (C) {{YEAR}} {{NAME}}\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.`\n  },\n  {\n    id: \"gplv2\",\n    name: \"GPL v2\",\n    fullName: \"GNU General Public License v2.0\",\n    type: \"copyleft\",\n    badge: \"copyleft\",\n    tldr: \"Classic strong copyleft license. Derivative works must be distributed under the same license. No explicit patent grant.\",\n    permissions: [\"Commercial use\",\"Modification\",\"Distribution\",\"Private use\"],\n    conditions: [\"Disclose source\",\"License and copyright notice\",\"Same license\",\"State changes\"],\n    limitations: [\"Liability\",\"Warranty\"],\n    commercial: true, modify: true, distribute: true, patent: false, private: true,\n    copyleft: true, attribution: true, networkUse: false,\n    color: \"#b91c1c\",\n    template: `GNU GENERAL PUBLIC LICENSE\nVersion 2, June 1991\n\nCopyright (C) {{YEAR}} {{NAME}}\n\nEveryone is permitted to copy and distribute verbatim copies\nof this license document, but changing it is not allowed.\n\nThis program is free software; you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation; either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.`\n  },\n  {\n    id: \"lgpl\",\n    name: \"LGPL v2.1\",\n    fullName: \"GNU Lesser General Public License v2.1\",\n    type: \"weak-copyleft\",\n    badge: \"weak\",\n    tldr: \"Weak copyleft license primarily for libraries. Allows linking from proprietary software, but modifications to the library itself must be open-sourced.\",\n    permissions: [\"Commercial use\",\"Modification\",\"Distribution\",\"Private use\"],\n    conditions: [\"Disclose source\",\"License and copyright notice\",\"Same license (library changes only)\",\"State changes\"],\n    limitations: [\"Liability\",\"Warranty\"],\n    commercial: true, modify: true, distribute: true, patent: false, private: true,\n    copyleft: true, attribution: true, networkUse: false,\n    color: \"#d97706\",\n    template: `GNU LESSER GENERAL PUBLIC LICENSE\nVersion 2.1, February 1999\n\nCopyright (C) {{YEAR}} {{NAME}}\n\nThis library is free software; you can redistribute it and/or\nmodify it under the terms of the GNU Lesser General Public\nLicense as published by the Free Software Foundation; either\nversion 2.1 of the License, or (at your option) any later version.\n\nThis library is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\nLesser General Public License for more details.`\n  },\n  {\n    id: \"bsd2\",\n    name: \"BSD 2-Clause\",\n    fullName: 'BSD 2-Clause \"Simplified\" License',\n    type: \"permissive\",\n    badge: \"permissive\",\n    tldr: \"Permissive license with two simple conditions: preserve the copyright notice in source and binary distributions.\",\n    permissions: [\"Commercial use\",\"Modification\",\"Distribution\",\"Private use\"],\n    conditions: [\"License and copyright notice\"],\n    limitations: [\"Liability\",\"Warranty\"],\n    commercial: true, modify: true, distribute: true, patent: false, private: true,\n    copyleft: false, attribution: true, networkUse: false,\n    color: \"#059669\",\n    template: `BSD 2-Clause License\n\nCopyright (c) {{YEAR}}, {{NAME}}\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice,\n   this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.`\n  },\n  {\n    id: \"bsd3\",\n    name: \"BSD 3-Clause\",\n    fullName: 'BSD 3-Clause \"New\" or \"Revised\" License',\n    type: \"permissive\",\n    badge: \"permissive\",\n    tldr: \"Like BSD 2-Clause plus a non-endorsement clause: you cannot use the project name or contributors to promote derivative products without permission.\",\n    permissions: [\"Commercial use\",\"Modification\",\"Distribution\",\"Private use\"],\n    conditions: [\"License and copyright notice\",\"No endorsement\"],\n    limitations: [\"Liability\",\"Warranty\"],\n    commercial: true, modify: true, distribute: true, patent: false, private: true,\n    copyleft: false, attribution: true, networkUse: false,\n    color: \"#0891b2\",\n    template: `BSD 3-Clause License\n\nCopyright (c) {{YEAR}}, {{NAME}}\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice,\n   this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its contributors\n   may be used to endorse or promote products derived from this software\n   without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.`\n  },\n  {\n    id: \"mpl2\",\n    name: \"MPL 2.0\",\n    fullName: \"Mozilla Public License 2.0\",\n    type: \"weak-copyleft\",\n    badge: \"weak\",\n    tldr: \"File-level copyleft: modifications to existing files must be open-sourced, but you can combine MPL code with proprietary code in separate files.\",\n    permissions: [\"Commercial use\",\"Modification\",\"Distribution\",\"Patent use\",\"Private use\"],\n    conditions: [\"Disclose source (modified files only)\",\"License and copyright notice\",\"Same license (modified files only)\"],\n    limitations: [\"Liability\",\"Trademark use\",\"Warranty\"],\n    commercial: true, modify: true, distribute: true, patent: true, private: true,\n    copyleft: true, attribution: true, networkUse: false,\n    color: \"#7c3aed\",\n    template: `Mozilla Public License Version 2.0\n\nCopyright (c) {{YEAR}} {{NAME}}\n\nThis Source Code Form is subject to the terms of the Mozilla Public\nLicense, v. 2.0. If a copy of the MPL was not distributed with this\nfile, You can obtain one at https://mozilla.org/MPL/2.0/.`\n  },\n  {\n    id: \"unlicense\",\n    name: \"Unlicense\",\n    fullName: \"The Unlicense\",\n    type: \"public-domain\",\n    badge: \"public\",\n    tldr: \"Dedication to the public domain. No restrictions whatsoever — anyone can do anything with the code, no credit required.\",\n    permissions: [\"Commercial use\",\"Modification\",\"Distribution\",\"Private use\"],\n    conditions: [],\n    limitations: [\"Liability\",\"Warranty\"],\n    commercial: true, modify: true, distribute: true, patent: false, private: true,\n    copyleft: false, attribution: false, networkUse: false,\n    color: \"#6b7280\",\n    template: `This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any means.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the software\nto the public domain. We make this dedication for the benefit of the\npublic at large and to the detriment of our heirs and successors. We\nintend this dedication to be an overt act of relinquishment in perpetuity\nof all present and future rights to this software under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to \u003chttps://unlicense.org\u003e`\n  },\n  {\n    id: \"cc0\",\n    name: \"CC0 1.0\",\n    fullName: \"Creative Commons Zero v1.0 Universal\",\n    type: \"public-domain\",\n    badge: \"public\",\n    tldr: \"Creative Commons public domain dedication. Waives all copyright to the fullest extent possible under law. Often used for data and creative works, not recommended for software.\",\n    permissions: [\"Commercial use\",\"Modification\",\"Distribution\",\"Private use\"],\n    conditions: [],\n    limitations: [\"Liability\",\"Patent use\",\"Trademark use\",\"Warranty\"],\n    commercial: true, modify: true, distribute: true, patent: false, private: true,\n    copyleft: false, attribution: false, networkUse: false,\n    color: \"#4b5563\",\n    template: `CC0 1.0 Universal\n\nStatement of Purpose\n\nThe laws of most jurisdictions throughout the world automatically confer\nexclusive Copyright and Related Rights (defined below) upon the creator and\nsubsequent owner(s) of an original work of authorship and/or a database.\n\nTo the extent possible under law, {{NAME}} has waived all copyright and\nrelated or neighboring rights to this work. This work is published from the\nUnited States, {{YEAR}}.\n\nYou should have received a copy of the CC0 legalcode along with this work.\nIf not, see \u003chttps://creativecommons.org/publicdomain/zero/1.0/\u003e.`\n  }\n];\n\n// Wizard questions\nvar QUESTIONS = [\n  {\n    id: \"commercial\",\n    q: \"Do you want to allow commercial use of your code?\",\n    hint: \"Can companies use or sell products built with your code?\",\n    options: [\n      { label: \"Yes, anyone can use it commercially\", value: true },\n      { label: \"No, restrict commercial use\", value: false }\n    ]\n  },\n  {\n    id: \"patent\",\n    q: \"Do you want to explicitly grant patent rights?\",\n    hint: \"Protects users from patent claims by contributors.\",\n    options: [\n      { label: \"Yes, include patent grant (recommended)\", value: true },\n      { label: \"No explicit patent grant needed\", value: false }\n    ]\n  },\n  {\n    id: \"copyleft\",\n    q: \"Do you want copyleft (share-alike)?\",\n    hint: \"Requires derivative works to use the same license.\",\n    options: [\n      { label: \"No — keep it permissive\", value: \"none\" },\n      { label: \"Weak copyleft (file/library level)\", value: \"weak\" },\n      { label: \"Strong copyleft (all derivatives)\", value: \"strong\" }\n    ]\n  },\n  {\n    id: \"attribution\",\n    q: \"Must users credit you / include your license notice?\",\n    hint: \"Even permissive licenses often require keeping your copyright notice.\",\n    options: [\n      { label: \"Yes, require attribution\", value: true },\n      { label: \"No restrictions at all (public domain)\", value: false }\n    ]\n  }\n];\n\n// State\nvar answers = {};\nvar currentStep = 0;\n\nfunction olSwitchTab(tab) {\n  document.querySelectorAll('#ol-app .ol-tab-btn').forEach(function(b) { b.classList.remove('active'); });\n  document.querySelectorAll('#ol-app .ol-panel').forEach(function(p) { p.classList.remove('active'); });\n  var btn = document.querySelector('#ol-app .ol-tab-btn[onclick*=\"' + tab + '\"]');\n  if (btn) btn.classList.add('active');\n  var panel = document.getElementById('ol-panel-' + tab);\n  if (panel) panel.classList.add('active');\n}\nwindow.olSwitchTab = olSwitchTab;\n\n// ─── Wizard ──────────────────────────────────────────────────────────────────\nfunction renderWizard() {\n  renderStepIndicator();\n  var body = document.getElementById('ol-wizard-body');\n  if (currentStep \u003c QUESTIONS.length) {\n    var q = QUESTIONS[currentStep];\n    var html = '\u003cdiv class=\"ol-wizard-step\"\u003e\u003ch3\u003e' + q.q + '\u003c/h3\u003e\u003cp\u003e' + q.hint + '\u003c/p\u003e","title":"Open Source License Chooser"},{"content":"Enter your distance and time to instantly get your pace, speed, and projected race finish times — or work backwards from a target pace.\nUnits: Metric (km) Imperial (mi) Pace from Distance + Time Time from Distance + Pace Distance from Pace + Time Calculate Pace Distance (km) Preset Distance — select — 5K 10K Half Marathon Marathon Time (h : min : sec) Hours Minutes Seconds Calculate Pace Calculate Finish Time Distance (km) Preset Distance — select — 5K 10K Half Marathon Marathon Pace (min : sec / km) Min Sec Calculate Time Calculate Distance Pace (min : sec / km) Min Sec Duration (h : min : sec) Hours Minutes Seconds Calculate Distance Results Clear / Reset Race Time Predictions Race Distance Predicted Time Pace Predictions assume constant pace. Actual race performance varies with terrain, weather, and fatigue.\nSplit Table Related Tools Track your fitness calories → Calorie Calculator Check your body weight range → BMI Calculator ","permalink":"https://productivity-works.com/tools/pace-calculator/","summary":"\u003cp\u003eEnter your distance and time to instantly get your pace, speed, and projected race finish times — or work backwards from a target pace.\u003c/p\u003e\n\u003cdiv id=\"pc-app\"\u003e\n\u003cstyle\u003e\n#pc-app {\n  font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n  max-width: 720px;\n  margin: 0 auto;\n  color: #1e293b;\n  font-size: 15px;\n  line-height: 1.6;\n}\n#pc-app * {\n  box-sizing: border-box;\n}\n#pc-app h2 {\n  font-size: 1.05rem;\n  font-weight: 700;\n  color: #0f172a;\n  margin: 0 0 1rem;\n  padding-bottom: 0.4rem;\n  border-bottom: 2px solid #e2e8f0;\n}\n#pc-app .pc-card {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 1.25rem 1.5rem;\n  margin-bottom: 1.25rem;\n}\n#pc-app .pc-tabs {\n  display: flex;\n  gap: 0.5rem;\n  margin-bottom: 1.25rem;\n  flex-wrap: wrap;\n}\n#pc-app .pc-tab {\n  flex: 1;\n  min-width: 150px;\n  padding: 0.55rem 0.75rem;\n  background: #f1f5f9;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 8px;\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #64748b;\n  cursor: pointer;\n  text-align: center;\n  transition: all 0.15s;\n}\n#pc-app .pc-tab.active {\n  background: #1e293b;\n  border-color: #1e293b;\n  color: #fff;\n}\n#pc-app .pc-tab:hover:not(.active) {\n  background: #e2e8f0;\n  color: #334155;\n}\n#pc-app .pc-unit-toggle {\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n  margin-bottom: 1.25rem;\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #64748b;\n}\n#pc-app .pc-toggle-btn {\n  display: flex;\n  background: #f1f5f9;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 8px;\n  overflow: hidden;\n  font-size: 0.82rem;\n}\n#pc-app .pc-toggle-btn span {\n  padding: 0.35rem 0.9rem;\n  cursor: pointer;\n  font-weight: 600;\n  color: #64748b;\n  transition: all 0.15s;\n}\n#pc-app .pc-toggle-btn span.active {\n  background: #1e293b;\n  color: #fff;\n}\n#pc-app .pc-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1rem;\n}\n@media (max-width: 520px) {\n  #pc-app .pc-grid {\n    grid-template-columns: 1fr;\n  }\n  #pc-app .pc-tab {\n    min-width: 100%;\n  }\n}\n#pc-app .pc-field {\n  display: flex;\n  flex-direction: column;\n  gap: 0.3rem;\n}\n#pc-app .pc-field label {\n  font-size: 0.78rem;\n  font-weight: 600;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#pc-app .pc-field input,\n#pc-app .pc-field select {\n  padding: 0.5rem 0.75rem;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 0.95rem;\n  color: #1e293b;\n  background: #fff;\n  outline: none;\n  transition: border-color 0.15s;\n  width: 100%;\n}\n#pc-app .pc-field input:focus,\n#pc-app .pc-field select:focus {\n  border-color: #334155;\n}\n#pc-app .pc-time-row {\n  display: grid;\n  grid-template-columns: 1fr 1fr 1fr;\n  gap: 0.5rem;\n}\n#pc-app .pc-time-label {\n  font-size: 0.7rem;\n  color: #94a3b8;\n  text-align: center;\n  margin-top: 0.15rem;\n}\n#pc-app .pc-btn {\n  width: 100%;\n  padding: 0.75rem;\n  background: #1e293b;\n  color: #fff;\n  border: none;\n  border-radius: 10px;\n  font-size: 1rem;\n  font-weight: 700;\n  cursor: pointer;\n  margin-top: 1rem;\n  transition: background 0.15s;\n}\n#pc-app .pc-btn:hover {\n  background: #334155;\n}\n#pc-app .pc-results {\n  display: none;\n}\n#pc-app .pc-results.visible {\n  display: block;\n}\n#pc-app .pc-result-grid {\n  display: grid;\n  grid-template-columns: repeat(2, 1fr);\n  gap: 0.75rem;\n  margin-bottom: 1rem;\n}\n@media (max-width: 400px) {\n  #pc-app .pc-result-grid {\n    grid-template-columns: 1fr;\n  }\n}\n#pc-app .pc-result-box {\n  background: #fff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 0.9rem 1rem;\n  text-align: center;\n}\n#pc-app .pc-result-box.highlight {\n  border-color: #334155;\n  background: #1e293b;\n  color: #fff;\n}\n#pc-app .pc-result-label {\n  font-size: 0.72rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  color: #94a3b8;\n  margin-bottom: 0.3rem;\n}\n#pc-app .pc-result-box.highlight .pc-result-label {\n  color: #94a3b8;\n}\n#pc-app .pc-result-val {\n  font-size: 1.6rem;\n  font-weight: 800;\n  line-height: 1.1;\n  color: #1e293b;\n}\n#pc-app .pc-result-box.highlight .pc-result-val {\n  color: #fff;\n}\n#pc-app .pc-result-unit {\n  font-size: 0.78rem;\n  color: #64748b;\n  margin-top: 0.15rem;\n}\n#pc-app .pc-result-box.highlight .pc-result-unit {\n  color: #cbd5e1;\n}\n#pc-app .pc-race-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.88rem;\n}\n#pc-app .pc-race-table th {\n  background: #1e293b;\n  color: #fff;\n  padding: 0.5rem 0.75rem;\n  text-align: left;\n  font-weight: 600;\n  font-size: 0.78rem;\n}\n#pc-app .pc-race-table td {\n  padding: 0.5rem 0.75rem;\n  border-bottom: 1px solid #f1f5f9;\n}\n#pc-app .pc-race-table tr:last-child td {\n  border-bottom: none;\n}\n#pc-app .pc-race-table tr:nth-child(even) td {\n  background: #f8fafc;\n}\n#pc-app .pc-split-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.85rem;\n}\n#pc-app .pc-split-table th {\n  background: #334155;\n  color: #fff;\n  padding: 0.45rem 0.75rem;\n  text-align: right;\n  font-weight: 600;\n  font-size: 0.76rem;\n}\n#pc-app .pc-split-table th:first-child {\n  text-align: left;\n}\n#pc-app .pc-split-table td {\n  padding: 0.45rem 0.75rem;\n  border-bottom: 1px solid #f1f5f9;\n  text-align: right;\n}\n#pc-app .pc-split-table td:first-child {\n  text-align: left;\n  font-weight: 600;\n}\n#pc-app .pc-split-table tr:nth-child(even) td {\n  background: #f8fafc;\n}\n#pc-app .pc-error {\n  color: #dc2626;\n  font-size: 0.85rem;\n  font-weight: 600;\n  padding: 0.5rem 0.75rem;\n  background: #fef2f2;\n  border: 1px solid #fecaca;\n  border-radius: 8px;\n  margin-bottom: 0.75rem;\n  display: none;\n}\n#pc-app .pc-error.visible {\n  display: block;\n}\n#pc-app .pc-mode-panel {\n  display: none;\n}\n#pc-app .pc-mode-panel.active {\n  display: block;\n}\n#pc-app .pc-reset-btn {\n  background: none;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  color: #64748b;\n  padding: 0.45rem 1rem;\n  font-size: 0.82rem;\n  font-weight: 600;\n  cursor: pointer;\n  margin-top: 0.5rem;\n  transition: all 0.15s;\n}\n#pc-app .pc-reset-btn:hover {\n  background: #f1f5f9;\n}\n\u003c/style\u003e\n\u003c!-- Unit Toggle --\u003e\n\u003cdiv class=\"pc-unit-toggle\"\u003e\n  \u003cspan\u003eUnits:\u003c/span\u003e\n  \u003cdiv class=\"pc-toggle-btn\"\u003e\n    \u003cspan id=\"pc-unit-km\" class=\"active\" onclick=\"pcSetUnit('km')\"\u003eMetric (km)\u003c/span\u003e\n    \u003cspan id=\"pc-unit-mi\" onclick=\"pcSetUnit('mi')\"\u003eImperial (mi)\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Mode Tabs --\u003e\n\u003cdiv class=\"pc-tabs\"\u003e\n  \u003cdiv class=\"pc-tab active\" id=\"pc-tab-pace\" onclick=\"pcSetMode('pace')\"\u003ePace from Distance + Time\u003c/div\u003e\n  \u003cdiv class=\"pc-tab\" id=\"pc-tab-time\" onclick=\"pcSetMode('time')\"\u003eTime from Distance + Pace\u003c/div\u003e\n  \u003cdiv class=\"pc-tab\" id=\"pc-tab-dist\" onclick=\"pcSetMode('dist')\"\u003eDistance from Pace + Time\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- MODE 1: Calc Pace from Distance + Time --\u003e\n\u003cdiv class=\"pc-mode-panel active\" id=\"pc-panel-pace\"\u003e\n  \u003cdiv class=\"pc-card\"\u003e\n    \u003ch2\u003eCalculate Pace\u003c/h2\u003e\n    \u003cdiv class=\"pc-grid\" style=\"margin-bottom:1rem;\"\u003e\n      \u003cdiv class=\"pc-field\"\u003e\n        \u003clabel id=\"pc-dist-label-1\"\u003eDistance (km)\u003c/label\u003e\n        \u003cinput type=\"number\" id=\"pc-dist-1\" min=\"0.01\" step=\"0.01\" placeholder=\"e.g. 10\"\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"pc-field\"\u003e\n        \u003clabel\u003ePreset Distance\u003c/label\u003e\n        \u003cselect id=\"pc-dist-preset-1\" onchange=\"pcApplyPreset(1)\"\u003e\n          \u003coption value=\"\"\u003e— select —\u003c/option\u003e\n          \u003coption value=\"5\"\u003e5K\u003c/option\u003e\n          \u003coption value=\"10\"\u003e10K\u003c/option\u003e\n          \u003coption value=\"21.0975\"\u003eHalf Marathon\u003c/option\u003e\n          \u003coption value=\"42.195\"\u003eMarathon\u003c/option\u003e\n        \u003c/select\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"pc-field\" style=\"margin-bottom:1rem;\"\u003e\n      \u003clabel\u003eTime (h : min : sec)\u003c/label\u003e\n      \u003cdiv class=\"pc-time-row\"\u003e\n        \u003cdiv\u003e\n          \u003cinput type=\"number\" id=\"pc-h-1\" min=\"0\" max=\"99\" placeholder=\"0\"\u003e\n          \u003cdiv class=\"pc-time-label\"\u003eHours\u003c/div\u003e\n        \u003c/div\u003e\n        \u003cdiv\u003e\n          \u003cinput type=\"number\" id=\"pc-m-1\" min=\"0\" max=\"59\" placeholder=\"0\"\u003e\n          \u003cdiv class=\"pc-time-label\"\u003eMinutes\u003c/div\u003e\n        \u003c/div\u003e\n        \u003cdiv\u003e\n          \u003cinput type=\"number\" id=\"pc-s-1\" min=\"0\" max=\"59\" placeholder=\"0\"\u003e\n          \u003cdiv class=\"pc-time-label\"\u003eSeconds\u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"pc-error\" id=\"pc-err-1\"\u003e\u003c/div\u003e\n    \u003cbutton class=\"pc-btn\" onclick=\"pcCalcPace()\"\u003eCalculate Pace\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- MODE 2: Calc Time from Distance + Pace --\u003e\n\u003cdiv class=\"pc-mode-panel\" id=\"pc-panel-time\"\u003e\n  \u003cdiv class=\"pc-card\"\u003e\n    \u003ch2\u003eCalculate Finish Time\u003c/h2\u003e\n    \u003cdiv class=\"pc-grid\" style=\"margin-bottom:1rem;\"\u003e\n      \u003cdiv class=\"pc-field\"\u003e\n        \u003clabel id=\"pc-dist-label-2\"\u003eDistance (km)\u003c/label\u003e\n        \u003cinput type=\"number\" id=\"pc-dist-2\" min=\"0.01\" step=\"0.01\" placeholder=\"e.g. 42.195\"\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"pc-field\"\u003e\n        \u003clabel\u003ePreset Distance\u003c/label\u003e\n        \u003cselect id=\"pc-dist-preset-2\" onchange=\"pcApplyPreset(2)\"\u003e\n          \u003coption value=\"\"\u003e— select —\u003c/option\u003e\n          \u003coption value=\"5\"\u003e5K\u003c/option\u003e\n          \u003coption value=\"10\"\u003e10K\u003c/option\u003e\n          \u003coption value=\"21.0975\"\u003eHalf Marathon\u003c/option\u003e\n          \u003coption value=\"42.195\"\u003eMarathon\u003c/option\u003e\n        \u003c/select\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"pc-field\" style=\"margin-bottom:1rem;\"\u003e\n      \u003clabel id=\"pc-pace-label-2\"\u003ePace (min : sec / km)\u003c/label\u003e\n      \u003cdiv class=\"pc-time-row\" style=\"grid-template-columns:1fr 1fr;\"\u003e\n        \u003cdiv\u003e\n          \u003cinput type=\"number\" id=\"pc-pm-2\" min=\"0\" max=\"59\" placeholder=\"5\"\u003e\n          \u003cdiv class=\"pc-time-label\"\u003eMin\u003c/div\u003e\n        \u003c/div\u003e\n        \u003cdiv\u003e\n          \u003cinput type=\"number\" id=\"pc-ps-2\" min=\"0\" max=\"59\" placeholder=\"30\"\u003e\n          \u003cdiv class=\"pc-time-label\"\u003eSec\u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"pc-error\" id=\"pc-err-2\"\u003e\u003c/div\u003e\n    \u003cbutton class=\"pc-btn\" onclick=\"pcCalcTime()\"\u003eCalculate Time\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- MODE 3: Calc Distance from Pace + Time --\u003e\n\u003cdiv class=\"pc-mode-panel\" id=\"pc-panel-dist\"\u003e\n  \u003cdiv class=\"pc-card\"\u003e\n    \u003ch2\u003eCalculate Distance\u003c/h2\u003e\n    \u003cdiv class=\"pc-field\" style=\"margin-bottom:1rem;\"\u003e\n      \u003clabel id=\"pc-pace-label-3\"\u003ePace (min : sec / km)\u003c/label\u003e\n      \u003cdiv class=\"pc-time-row\" style=\"grid-template-columns:1fr 1fr;\"\u003e\n        \u003cdiv\u003e\n          \u003cinput type=\"number\" id=\"pc-pm-3\" min=\"0\" max=\"59\" placeholder=\"5\"\u003e\n          \u003cdiv class=\"pc-time-label\"\u003eMin\u003c/div\u003e\n        \u003c/div\u003e\n        \u003cdiv\u003e\n          \u003cinput type=\"number\" id=\"pc-ps-3\" min=\"0\" max=\"59\" placeholder=\"30\"\u003e\n          \u003cdiv class=\"pc-time-label\"\u003eSec\u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"pc-field\" style=\"margin-bottom:1rem;\"\u003e\n      \u003clabel\u003eDuration (h : min : sec)\u003c/label\u003e\n      \u003cdiv class=\"pc-time-row\"\u003e\n        \u003cdiv\u003e\n          \u003cinput type=\"number\" id=\"pc-h-3\" min=\"0\" max=\"99\" placeholder=\"1\"\u003e\n          \u003cdiv class=\"pc-time-label\"\u003eHours\u003c/div\u003e\n        \u003c/div\u003e\n        \u003cdiv\u003e\n          \u003cinput type=\"number\" id=\"pc-m-3\" min=\"0\" max=\"59\" placeholder=\"0\"\u003e\n          \u003cdiv class=\"pc-time-label\"\u003eMinutes\u003c/div\u003e\n        \u003c/div\u003e\n        \u003cdiv\u003e\n          \u003cinput type=\"number\" id=\"pc-s-3\" min=\"0\" max=\"59\" placeholder=\"0\"\u003e\n          \u003cdiv class=\"pc-time-label\"\u003eSeconds\u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"pc-error\" id=\"pc-err-3\"\u003e\u003c/div\u003e\n    \u003cbutton class=\"pc-btn\" onclick=\"pcCalcDist()\"\u003eCalculate Distance\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Results --\u003e\n\u003cdiv class=\"pc-results\" id=\"pc-results\"\u003e\n  \u003cdiv class=\"pc-card\"\u003e\n    \u003ch2\u003eResults\u003c/h2\u003e\n    \u003cdiv class=\"pc-result-grid\" id=\"pc-result-grid\"\u003e\u003c/div\u003e\n    \u003cbutton class=\"pc-reset-btn\" onclick=\"pcReset()\"\u003eClear / Reset\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"pc-card\" id=\"pc-race-section\"\u003e\n    \u003ch2\u003eRace Time Predictions\u003c/h2\u003e\n    \u003ctable class=\"pc-race-table\"\u003e\n      \u003cthead\u003e\n        \u003ctr\u003e\n          \u003cth\u003eRace\u003c/th\u003e\n          \u003cth\u003eDistance\u003c/th\u003e\n          \u003cth\u003ePredicted Time\u003c/th\u003e\n          \u003cth id=\"pc-race-pace-header\"\u003ePace\u003c/th\u003e\n        \u003c/tr\u003e\n      \u003c/thead\u003e\n      \u003ctbody id=\"pc-race-body\"\u003e\u003c/tbody\u003e\n    \u003c/table\u003e\n    \u003cp style=\"font-size:0.75rem;color:#94a3b8;margin-top:0.6rem;\"\u003ePredictions assume constant pace. Actual race performance varies with terrain, weather, and fatigue.\u003c/p\u003e","title":"Pace Calculator - Running \u0026 Cycling"},{"content":" Enter the size of each asset type on your web page, select your target connection speed, and instantly see your estimated load time, performance score, and tailored recommendations. Page Asset Sizes HTML (KB) CSS (KB) JavaScript (KB) Images (KB) Fonts (KB) Other (KB) Connection Speed 3G Slow (3 Mbps) 3G Fast / 4G (10 Mbps) WiFi (50 Mbps) Fiber (200 Mbps) Analyze Reset Total Page Weight — Est. Load Time — Largest Asset — — Optimization Recommendations Before / After Comparison Enter optimized sizes to see how much you can improve load time.\nOptimized HTML (KB) Optimized CSS (KB) Optimized JS (KB) Optimized Images (KB) Optimized Fonts (KB) Optimized Other (KB) Compare Related Tools More Free Productivity Tools Reading Time Calculator Word Counter Pomodoro Timer ROI Calculator Password Strength Checker Unit Converter ","permalink":"https://productivity-works.com/tools/page-size-analyzer/","summary":"\u003cdiv id=\"psa-app\"\u003e\n\u003cstyle\u003e\n#psa-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#psa-app h2 {\n  font-size: 1.5rem;\n  margin: 1.5rem 0 0.75rem;\n  color: #16213e;\n  border-left: 4px solid #0f3460;\n  padding-left: 0.6rem;\n}\n#psa-app .psa-intro {\n  background: #eef2ff;\n  border-radius: 10px;\n  padding: 1rem 1.25rem;\n  margin-bottom: 1.5rem;\n  font-size: 0.95rem;\n  color: #3a3a5c;\n}\n#psa-app .psa-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1rem;\n  margin-bottom: 1rem;\n}\n@media (max-width: 600px) {\n  #psa-app .psa-grid { grid-template-columns: 1fr; }\n}\n#psa-app .psa-field {\n  display: flex;\n  flex-direction: column;\n  gap: 0.3rem;\n}\n#psa-app label {\n  font-size: 0.85rem;\n  font-weight: 600;\n  color: #444;\n}\n#psa-app input[type=\"number\"],\n#psa-app select {\n  padding: 0.55rem 0.75rem;\n  border: 1.5px solid #c8d0e7;\n  border-radius: 7px;\n  font-size: 1rem;\n  background: #fff;\n  color: #1a1a2e;\n  outline: none;\n  transition: border-color 0.2s;\n}\n#psa-app input[type=\"number\"]:focus,\n#psa-app select:focus {\n  border-color: #0f3460;\n}\n#psa-app .psa-speed-row {\n  display: flex;\n  gap: 1rem;\n  align-items: flex-end;\n  margin-bottom: 1rem;\n  flex-wrap: wrap;\n}\n#psa-app .psa-speed-row .psa-field {\n  flex: 1;\n  min-width: 180px;\n}\n#psa-app .psa-btn {\n  padding: 0.7rem 2rem;\n  background: #0f3460;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 1rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.2s, transform 0.1s;\n  white-space: nowrap;\n}\n#psa-app .psa-btn:hover { background: #16213e; }\n#psa-app .psa-btn:active { transform: scale(0.97); }\n#psa-app .psa-reset-btn {\n  padding: 0.7rem 1.2rem;\n  background: #e8eaf6;\n  color: #0f3460;\n  border: 1.5px solid #c8d0e7;\n  border-radius: 8px;\n  font-size: 0.9rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n#psa-app .psa-reset-btn:hover { background: #dde0f5; }\n#psa-app .psa-results {\n  display: none;\n  margin-top: 1.5rem;\n  animation: psaFadeIn 0.35s ease;\n}\n@keyframes psaFadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }\n#psa-app .psa-summary-cards {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 1rem;\n  margin-bottom: 1.5rem;\n}\n@media (max-width: 600px) {\n  #psa-app .psa-summary-cards { grid-template-columns: 1fr 1fr; }\n}\n#psa-app .psa-card {\n  background: #fff;\n  border: 1.5px solid #e0e5f0;\n  border-radius: 10px;\n  padding: 1rem;\n  text-align: center;\n  box-shadow: 0 2px 8px rgba(15,52,96,0.06);\n}\n#psa-app .psa-card .psa-card-label {\n  font-size: 0.78rem;\n  color: #888;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  margin-bottom: 0.4rem;\n}\n#psa-app .psa-card .psa-card-value {\n  font-size: 1.7rem;\n  font-weight: 800;\n  color: #0f3460;\n  line-height: 1.1;\n}\n#psa-app .psa-card .psa-card-sub {\n  font-size: 0.8rem;\n  color: #aaa;\n  margin-top: 0.2rem;\n}\n#psa-app .psa-score-wrap {\n  display: flex;\n  align-items: center;\n  gap: 1.25rem;\n  background: #fff;\n  border: 1.5px solid #e0e5f0;\n  border-radius: 10px;\n  padding: 1.1rem 1.4rem;\n  margin-bottom: 1.5rem;\n  box-shadow: 0 2px 8px rgba(15,52,96,0.06);\n  flex-wrap: wrap;\n}\n#psa-app .psa-score-circle {\n  width: 90px;\n  height: 90px;\n  border-radius: 50%;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  flex-shrink: 0;\n}\n#psa-app .psa-score-num {\n  font-size: 2.1rem;\n  font-weight: 900;\n  color: #fff;\n}\n#psa-app .psa-score-info { flex: 1; min-width: 160px; }\n#psa-app .psa-score-grade {\n  font-size: 1.2rem;\n  font-weight: 800;\n  margin-bottom: 0.25rem;\n}\n#psa-app .psa-score-desc {\n  font-size: 0.88rem;\n  color: #666;\n  line-height: 1.5;\n}\n#psa-app .psa-chart-wrap {\n  background: #fff;\n  border: 1.5px solid #e0e5f0;\n  border-radius: 10px;\n  padding: 1.2rem;\n  margin-bottom: 1.5rem;\n  box-shadow: 0 2px 8px rgba(15,52,96,0.06);\n}\n#psa-app canvas { display: block; max-width: 100%; }\n#psa-app .psa-recs {\n  background: #fff;\n  border: 1.5px solid #e0e5f0;\n  border-radius: 10px;\n  padding: 1.2rem 1.4rem;\n  margin-bottom: 1.5rem;\n  box-shadow: 0 2px 8px rgba(15,52,96,0.06);\n}\n#psa-app .psa-recs h3 {\n  font-size: 1.05rem;\n  font-weight: 700;\n  color: #16213e;\n  margin: 0 0 0.75rem;\n}\n#psa-app .psa-rec-item {\n  display: flex;\n  align-items: flex-start;\n  gap: 0.6rem;\n  margin-bottom: 0.55rem;\n  font-size: 0.92rem;\n  color: #333;\n  line-height: 1.5;\n}\n#psa-app .psa-rec-icon {\n  font-size: 1rem;\n  flex-shrink: 0;\n  margin-top: 0.1rem;\n}\n#psa-app .psa-compare {\n  background: #f0f7ff;\n  border: 1.5px solid #b8d4f0;\n  border-radius: 10px;\n  padding: 1.2rem 1.4rem;\n  margin-bottom: 1.5rem;\n}\n#psa-app .psa-compare h3 {\n  font-size: 1.05rem;\n  font-weight: 700;\n  color: #0f3460;\n  margin: 0 0 0.75rem;\n}\n#psa-app .psa-compare-note {\n  font-size: 0.85rem;\n  color: #555;\n  margin-bottom: 0.75rem;\n}\n#psa-app .psa-opt-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr 1fr;\n  gap: 0.75rem;\n  margin-bottom: 0.75rem;\n}\n@media (max-width: 600px) {\n  #psa-app .psa-opt-grid { grid-template-columns: 1fr; }\n}\n#psa-app .psa-opt-label {\n  font-size: 0.8rem;\n  font-weight: 600;\n  color: #444;\n  margin-bottom: 0.3rem;\n}\n#psa-app .psa-opt-input {\n  width: 100%;\n  box-sizing: border-box;\n}\n#psa-app .psa-compare-result {\n  display: none;\n  margin-top: 1rem;\n  padding-top: 1rem;\n  border-top: 1px solid #c8dff0;\n}\n#psa-app .psa-compare-bars {\n  display: flex;\n  flex-direction: column;\n  gap: 0.6rem;\n  margin-top: 0.5rem;\n}\n#psa-app .psa-bar-row {\n  display: flex;\n  align-items: center;\n  gap: 0.75rem;\n  font-size: 0.88rem;\n}\n#psa-app .psa-bar-lbl { width: 60px; font-weight: 600; color: #555; }\n#psa-app .psa-bar-track {\n  flex: 1;\n  height: 18px;\n  background: #dde8f5;\n  border-radius: 9px;\n  overflow: hidden;\n}\n#psa-app .psa-bar-fill {\n  height: 100%;\n  border-radius: 9px;\n  transition: width 0.7s ease;\n  display: flex;\n  align-items: center;\n  padding-right: 6px;\n  justify-content: flex-end;\n}\n#psa-app .psa-bar-val { width: 55px; text-align: right; color: #333; font-weight: 700; }\n#psa-app .psa-related {\n  margin-top: 2rem;\n  padding-top: 1rem;\n  border-top: 2px solid #e0e5f0;\n}\n#psa-app .psa-related h3 {\n  font-size: 1rem;\n  font-weight: 700;\n  color: #16213e;\n  margin-bottom: 0.75rem;\n}\n#psa-app .psa-related ul {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem;\n}\n#psa-app .psa-related ul li a {\n  display: inline-block;\n  padding: 0.4rem 0.9rem;\n  background: #eef2ff;\n  border-radius: 20px;\n  color: #0f3460;\n  text-decoration: none;\n  font-size: 0.88rem;\n  font-weight: 600;\n  border: 1.5px solid #c8d0e7;\n  transition: background 0.2s;\n}\n#psa-app .psa-related ul li a:hover { background: #dde0f5; }\n\u003c/style\u003e\n\u003cdiv class=\"psa-intro\"\u003e\n  Enter the size of each asset type on your web page, select your target connection speed, and instantly see your estimated load time, performance score, and tailored recommendations.\n\u003c/div\u003e\n\u003ch2\u003ePage Asset Sizes\u003c/h2\u003e\n\u003cdiv class=\"psa-grid\"\u003e\n  \u003cdiv class=\"psa-field\"\u003e\n    \u003clabel for=\"psa-html\"\u003eHTML (KB)\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"psa-html\" min=\"0\" step=\"0.1\" value=\"30\" placeholder=\"e.g. 30\"\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"psa-field\"\u003e\n    \u003clabel for=\"psa-css\"\u003eCSS (KB)\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"psa-css\" min=\"0\" step=\"0.1\" value=\"80\" placeholder=\"e.g. 80\"\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"psa-field\"\u003e\n    \u003clabel for=\"psa-js\"\u003eJavaScript (KB)\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"psa-js\" min=\"0\" step=\"0.1\" value=\"200\" placeholder=\"e.g. 200\"\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"psa-field\"\u003e\n    \u003clabel for=\"psa-img\"\u003eImages (KB)\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"psa-img\" min=\"0\" step=\"1\" value=\"500\" placeholder=\"e.g. 500\"\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"psa-field\"\u003e\n    \u003clabel for=\"psa-font\"\u003eFonts (KB)\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"psa-font\" min=\"0\" step=\"0.1\" value=\"60\" placeholder=\"e.g. 60\"\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"psa-field\"\u003e\n    \u003clabel for=\"psa-other\"\u003eOther (KB)\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"psa-other\" min=\"0\" step=\"0.1\" value=\"20\" placeholder=\"e.g. 20\"\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"psa-speed-row\"\u003e\n  \u003cdiv class=\"psa-field\"\u003e\n    \u003clabel for=\"psa-speed\"\u003eConnection Speed\u003c/label\u003e\n    \u003cselect id=\"psa-speed\"\u003e\n      \u003coption value=\"0.375\"\u003e3G Slow (3 Mbps)\u003c/option\u003e\n      \u003coption value=\"1.25\" selected\u003e3G Fast / 4G (10 Mbps)\u003c/option\u003e\n      \u003coption value=\"6.25\"\u003eWiFi (50 Mbps)\u003c/option\u003e\n      \u003coption value=\"25\"\u003eFiber (200 Mbps)\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n  \u003cbutton class=\"psa-btn\" onclick=\"psaAnalyze()\"\u003eAnalyze\u003c/button\u003e\n  \u003cbutton class=\"psa-reset-btn\" onclick=\"psaReset()\"\u003eReset\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"psa-results\" id=\"psa-results\"\u003e\n  \u003cdiv class=\"psa-summary-cards\"\u003e\n    \u003cdiv class=\"psa-card\"\u003e\n      \u003cdiv class=\"psa-card-label\"\u003eTotal Page Weight\u003c/div\u003e\n      \u003cdiv class=\"psa-card-value\" id=\"psa-total-weight\"\u003e—\u003c/div\u003e\n      \u003cdiv class=\"psa-card-sub\" id=\"psa-weight-sub\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"psa-card\"\u003e\n      \u003cdiv class=\"psa-card-label\"\u003eEst. Load Time\u003c/div\u003e\n      \u003cdiv class=\"psa-card-value\" id=\"psa-load-time\"\u003e—\u003c/div\u003e\n      \u003cdiv class=\"psa-card-sub\" id=\"psa-speed-label\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"psa-card\"\u003e\n      \u003cdiv class=\"psa-card-label\"\u003eLargest Asset\u003c/div\u003e\n      \u003cdiv class=\"psa-card-value\" id=\"psa-largest\"\u003e—\u003c/div\u003e\n      \u003cdiv class=\"psa-card-sub\" id=\"psa-largest-type\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"psa-score-wrap\"\u003e\n    \u003cdiv class=\"psa-score-circle\" id=\"psa-score-circle\"\u003e\n      \u003cspan class=\"psa-score-num\" id=\"psa-score-num\"\u003e—\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"psa-score-info\"\u003e\n      \u003cdiv class=\"psa-score-grade\" id=\"psa-score-grade\"\u003e\u003c/div\u003e\n      \u003cdiv class=\"psa-score-desc\" id=\"psa-score-desc\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"psa-chart-wrap\"\u003e\n    \u003ccanvas id=\"psa-chart\" height=\"220\"\u003e\u003c/canvas\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"psa-recs\" id=\"psa-recs\"\u003e\n    \u003ch3\u003eOptimization Recommendations\u003c/h3\u003e\n    \u003cdiv id=\"psa-recs-list\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"psa-compare\"\u003e\n    \u003ch3\u003eBefore / After Comparison\u003c/h3\u003e\n    \u003cp class=\"psa-compare-note\"\u003eEnter optimized sizes to see how much you can improve load time.\u003c/p\u003e","title":"Page Size Analyzer - Estimate Load Time"},{"content":" Paper Size Guide — Look up dimensions for ISO A, ISO B, and US paper sizes instantly. Toggle between mm, cm, and inches. Click any row to see a visual comparison and usage details. Unit: mm cm inches Series: All ISO A ISO B US Visual comparison — click a row to highlight a size Size Series Width Height Area Aspect Ratio Common Use About Paper Sizes ISO A Series: The A series is the most widely used internationally. A0 has an area of exactly 1 m² with an aspect ratio of 1:√2 (≈1:1.414). Each subsequent size (A1, A2…) is half the area of the previous. A4 is the global standard for documents, letters, and forms.\nISO B Series: B sizes are the geometric mean between adjacent A sizes. Useful for posters, passports, and books where A sizes don't quite fit. B5 is common for books and notebooks.\nUS Sizes: Letter (8.5×11 in) is the US/Canada standard. Legal is longer for legal documents. Tabloid (11×17 in) is used for newspapers and large prints.\nRelated Tools Unit Converter — mm, cm, inches, feet Aspect Ratio Calculator PDF Page Size Checker ","permalink":"https://productivity-works.com/tools/paper-size-guide/","summary":"\u003cdiv id=\"ps-app\"\u003e\n\u003cstyle\u003e\n#ps-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 960px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#ps-app h2 {\n  font-size: 1.4rem;\n  font-weight: 700;\n  margin: 1.5rem 0 0.75rem;\n  color: #16213e;\n  border-left: 4px solid #0f3460;\n  padding-left: 0.6rem;\n}\n#ps-app .ps-intro {\n  background: #f0f4ff;\n  border-radius: 8px;\n  padding: 1rem 1.2rem;\n  margin-bottom: 1.2rem;\n  font-size: 0.95rem;\n  color: #333;\n  line-height: 1.6;\n}\n#ps-app .ps-controls {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem;\n  align-items: center;\n  margin-bottom: 1rem;\n}\n#ps-app .ps-controls label {\n  font-size: 0.88rem;\n  font-weight: 600;\n  color: #555;\n  margin-right: 0.25rem;\n}\n#ps-app .ps-unit-btn {\n  padding: 0.35rem 0.9rem;\n  border: 2px solid #0f3460;\n  background: #fff;\n  color: #0f3460;\n  border-radius: 20px;\n  cursor: pointer;\n  font-size: 0.85rem;\n  font-weight: 600;\n  transition: all 0.18s;\n}\n#ps-app .ps-unit-btn.active {\n  background: #0f3460;\n  color: #fff;\n}\n#ps-app .ps-unit-btn:hover:not(.active) {\n  background: #e8edf7;\n}\n#ps-app .ps-filter-btn {\n  padding: 0.3rem 0.8rem;\n  border: 1px solid #bbb;\n  background: #fff;\n  color: #444;\n  border-radius: 20px;\n  cursor: pointer;\n  font-size: 0.82rem;\n  transition: all 0.18s;\n  margin-left: 0.5rem;\n}\n#ps-app .ps-filter-btn.active {\n  background: #0f3460;\n  color: #fff;\n  border-color: #0f3460;\n}\n#ps-app .ps-table-wrap {\n  overflow-x: auto;\n  border-radius: 8px;\n  box-shadow: 0 1px 6px rgba(0,0,0,0.08);\n  margin-bottom: 1.5rem;\n}\n#ps-app table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.88rem;\n  background: #fff;\n}\n#ps-app thead th {\n  background: #0f3460;\n  color: #fff;\n  padding: 0.6rem 0.75rem;\n  text-align: left;\n  font-weight: 600;\n  white-space: nowrap;\n}\n#ps-app tbody tr {\n  border-bottom: 1px solid #e8ecf0;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n#ps-app tbody tr:hover {\n  background: #f0f4ff;\n}\n#ps-app tbody tr.highlighted {\n  background: #dce8ff;\n  font-weight: 600;\n}\n#ps-app tbody td {\n  padding: 0.5rem 0.75rem;\n  white-space: nowrap;\n}\n#ps-app tbody td:first-child {\n  font-weight: 700;\n  color: #0f3460;\n}\n#ps-app .ps-badge {\n  display: inline-block;\n  font-size: 0.72rem;\n  padding: 0.1rem 0.45rem;\n  border-radius: 10px;\n  font-weight: 600;\n  vertical-align: middle;\n  margin-left: 0.3rem;\n}\n#ps-app .badge-iso { background: #d4edda; color: #155724; }\n#ps-app .badge-us  { background: #fff3cd; color: #856404; }\n#ps-app .ps-canvas-wrap {\n  background: #f8f9fc;\n  border: 1px solid #dde3ee;\n  border-radius: 8px;\n  padding: 1rem;\n  margin-bottom: 1.5rem;\n  text-align: center;\n}\n#ps-app .ps-canvas-wrap canvas {\n  max-width: 100%;\n  border-radius: 4px;\n}\n#ps-app .ps-canvas-label {\n  font-size: 0.82rem;\n  color: #666;\n  margin-top: 0.5rem;\n}\n#ps-app .ps-detail-panel {\n  background: #fff;\n  border: 1px solid #c8d6f0;\n  border-radius: 8px;\n  padding: 1rem 1.2rem;\n  margin-bottom: 1.5rem;\n  display: none;\n}\n#ps-app .ps-detail-panel.visible {\n  display: block;\n}\n#ps-app .ps-detail-panel h3 {\n  margin: 0 0 0.6rem;\n  font-size: 1.15rem;\n  color: #0f3460;\n}\n#ps-app .ps-detail-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));\n  gap: 0.6rem;\n}\n#ps-app .ps-detail-item {\n  background: #f0f4ff;\n  border-radius: 6px;\n  padding: 0.5rem 0.75rem;\n}\n#ps-app .ps-detail-item .di-label {\n  font-size: 0.75rem;\n  color: #666;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#ps-app .ps-detail-item .di-value {\n  font-size: 1rem;\n  font-weight: 700;\n  color: #0f3460;\n}\n#ps-app .ps-usage {\n  font-size: 0.88rem;\n  color: #444;\n  margin-top: 0.6rem;\n  background: #fffbe6;\n  border-left: 3px solid #f0b429;\n  padding: 0.4rem 0.7rem;\n  border-radius: 0 4px 4px 0;\n}\n#ps-app .ps-related {\n  background: #f0f4ff;\n  border-radius: 8px;\n  padding: 1rem 1.2rem;\n  margin-top: 1.5rem;\n}\n#ps-app .ps-related h3 {\n  margin: 0 0 0.5rem;\n  font-size: 1rem;\n  color: #0f3460;\n}\n#ps-app .ps-related ul {\n  margin: 0;\n  padding-left: 1.2rem;\n  font-size: 0.9rem;\n  line-height: 1.8;\n}\n#ps-app .ps-section-sep {\n  border: none;\n  border-top: 2px solid #e8ecf0;\n  margin: 1.5rem 0;\n}\n\u003c/style\u003e\n\u003cdiv class=\"ps-intro\"\u003e\n  \u003cstrong\u003ePaper Size Guide\u003c/strong\u003e — Look up dimensions for ISO A, ISO B, and US paper sizes instantly. Toggle between mm, cm, and inches. Click any row to see a visual comparison and usage details.\n\u003c/div\u003e\n\u003cdiv class=\"ps-controls\"\u003e\n  \u003clabel\u003eUnit:\u003c/label\u003e\n  \u003cbutton class=\"ps-unit-btn active\" onclick=\"psSetUnit('mm')\"\u003emm\u003c/button\u003e\n  \u003cbutton class=\"ps-unit-btn\" onclick=\"psSetUnit('cm')\"\u003ecm\u003c/button\u003e\n  \u003cbutton class=\"ps-unit-btn\" onclick=\"psSetUnit('in')\"\u003einches\u003c/button\u003e\n  \u003cspan style=\"display:inline-block;width:1rem;\"\u003e\u003c/span\u003e\n  \u003clabel\u003eSeries:\u003c/label\u003e\n  \u003cbutton class=\"ps-filter-btn active\" onclick=\"psSetFilter('all')\"\u003eAll\u003c/button\u003e\n  \u003cbutton class=\"ps-filter-btn\" onclick=\"psSetFilter('A')\"\u003eISO A\u003c/button\u003e\n  \u003cbutton class=\"ps-filter-btn\" onclick=\"psSetFilter('B')\"\u003eISO B\u003c/button\u003e\n  \u003cbutton class=\"ps-filter-btn\" onclick=\"psSetFilter('US')\"\u003eUS\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv id=\"ps-detail\" class=\"ps-detail-panel\"\u003e\n  \u003ch3 id=\"ps-detail-name\"\u003e\u003c/h3\u003e\n  \u003cdiv class=\"ps-detail-grid\" id=\"ps-detail-grid\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"ps-usage\" id=\"ps-detail-usage\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ps-canvas-wrap\"\u003e\n  \u003ccanvas id=\"ps-canvas\" width=\"780\" height=\"240\"\u003e\u003c/canvas\u003e\n  \u003cdiv class=\"ps-canvas-label\" id=\"ps-canvas-label\"\u003eVisual comparison — click a row to highlight a size\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ps-table-wrap\"\u003e\n  \u003ctable id=\"ps-table\"\u003e\n    \u003cthead\u003e\n      \u003ctr\u003e\n        \u003cth\u003eSize\u003c/th\u003e\n        \u003cth\u003eSeries\u003c/th\u003e\n        \u003cth\u003eWidth\u003c/th\u003e\n        \u003cth\u003eHeight\u003c/th\u003e\n        \u003cth\u003eArea\u003c/th\u003e\n        \u003cth\u003eAspect Ratio\u003c/th\u003e\n        \u003cth\u003eCommon Use\u003c/th\u003e\n      \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody id=\"ps-tbody\"\u003e\u003c/tbody\u003e\n  \u003c/table\u003e\n\u003c/div\u003e\n\u003chr class=\"ps-section-sep\"\u003e\n\u003ch2\u003eAbout Paper Sizes\u003c/h2\u003e\n\u003cdiv class=\"ps-intro\" style=\"background:#fff;border:1px solid #dde3ee;\"\u003e\n  \u003cp\u003e\u003cstrong\u003eISO A Series\u003c/strong\u003e: The A series is the most widely used internationally. A0 has an area of exactly 1 m² with an aspect ratio of 1:√2 (≈1:1.414). Each subsequent size (A1, A2…) is half the area of the previous. A4 is the global standard for documents, letters, and forms.\u003c/p\u003e","title":"Paper Size Guide - A Series, B Series \u0026 US Sizes"},{"content":"Generate strong, random passwords instantly — all processing happens in your browser. Nothing is sent to any server.\nMode Random Pronounceable Passphrase Length Uppercase (A-Z) Lowercase (a-z) Numbers (0-9) Symbols (!@#$...) Exclude ambiguous (0/O, l/1/I) Exclude chars: Word count Capitalize words Append number Separator: Generate password(s) at once (max 20) Output Strength -- Click Generate Copy \u0026#x21bb; Generate Copy All History (session only) Clear No passwords generated yet. How It Works Passwords are generated entirely in your browser using the Web Crypto API (crypto.getRandomValues()), which produces cryptographically secure random values. No data is ever sent to a server.\nEntropy measures how unpredictable a password is. The calculation is length x log2(charset size). A 20-character password using all character types reaches approximately 120 bits of entropy — well beyond brute-force reach with current hardware.\nMode Guide Mode Best for Random Maximum security; use with a password manager Pronounceable Easier to type aloud; consonant-vowel alternation Passphrase Memorable; high entropy from word combinations Tips for Strong Passwords Use 16+ characters for everyday accounts; 24+ for critical ones. Enable symbols to maximize entropy in Random mode. Use Passphrase mode for passwords you need to remember. Never reuse passwords across sites — use a password manager. The \u0026ldquo;Exclude ambiguous\u0026rdquo; option avoids confusion between 0/O and l/1/I when reading passwords aloud. Generate hashes → Hash Generator Encode/decode text → Universal Encoder/Decoder Related Articles Best AI Tools for Small Business 2026: The Complete Roundup Best Password Managers 2026: Secure Every Account for $3/Month Best VPN Services 2026: Privacy, Streaming \u0026amp; Remote Work ","permalink":"https://productivity-works.com/tools/password-generator/","summary":"\u003cp\u003eGenerate strong, random passwords instantly — all processing happens in your browser. Nothing is sent to any server.\u003c/p\u003e\n\u003cdiv id=\"pw-app\"\u003e\n\u003cstyle\u003e\n#pw-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 680px;\n  margin: 0 auto;\n  color: #1e293b;\n}\n#pw-app * { box-sizing: border-box; }\n\n#pw-app .pw-section {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 20px 24px;\n  margin-bottom: 16px;\n}\n\n#pw-app h3.pw-section-title {\n  margin: 0 0 14px 0;\n  font-size: 13px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  color: #64748b;\n}\n\n#pw-app .pw-output-box {\n  background: #fff;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  padding: 14px 16px;\n  margin-bottom: 10px;\n  min-height: 52px;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 10px;\n}\n#pw-app .pw-output-box .pw-pass-text {\n  font-family: \"Courier New\", Courier, monospace;\n  font-size: 15px;\n  word-break: break-all;\n  flex: 1;\n  color: #0f172a;\n  line-height: 1.5;\n}\n#pw-app .pw-copy-btn {\n  flex-shrink: 0;\n  background: #3b82f6;\n  color: #fff;\n  border: none;\n  border-radius: 6px;\n  padding: 6px 14px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n#pw-app .pw-copy-btn:hover { background: #2563eb; }\n#pw-app .pw-copy-btn.pw-copied { background: #16a34a; }\n\n#pw-app .pw-actions {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n  margin-bottom: 4px;\n}\n#pw-app .pw-btn-primary {\n  background: #3b82f6;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  padding: 11px 22px;\n  font-size: 15px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.15s;\n  flex: 1;\n  min-width: 150px;\n}\n#pw-app .pw-btn-primary:hover { background: #2563eb; }\n#pw-app .pw-btn-secondary {\n  background: #fff;\n  color: #374151;\n  border: 1.5px solid #d1d5db;\n  border-radius: 8px;\n  padding: 11px 18px;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, border-color 0.15s;\n}\n#pw-app .pw-btn-secondary:hover { background: #f1f5f9; border-color: #94a3b8; }\n\n#pw-app .pw-strength-wrap { margin-bottom: 16px; }\n#pw-app .pw-strength-label {\n  font-size: 13px;\n  font-weight: 600;\n  margin-bottom: 5px;\n  display: flex;\n  justify-content: space-between;\n}\n#pw-app .pw-strength-bar-bg {\n  background: #e2e8f0;\n  border-radius: 99px;\n  height: 8px;\n  overflow: hidden;\n}\n#pw-app .pw-strength-bar {\n  height: 8px;\n  border-radius: 99px;\n  transition: width 0.3s, background 0.3s;\n  width: 0%;\n}\n\n#pw-app .pw-row {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  margin-bottom: 12px;\n}\n#pw-app .pw-row label {\n  font-size: 14px;\n  font-weight: 600;\n  min-width: 110px;\n  color: #374151;\n}\n#pw-app input[type=\"range\"] {\n  flex: 1;\n  accent-color: #3b82f6;\n  cursor: pointer;\n}\n#pw-app .pw-num-input {\n  width: 62px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 6px;\n  padding: 5px 8px;\n  font-size: 14px;\n  text-align: center;\n  color: #0f172a;\n}\n#pw-app .pw-num-input:focus { outline: 2px solid #3b82f6; border-color: transparent; }\n\n#pw-app .pw-row-count {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  margin-bottom: 12px;\n}\n#pw-app .pw-row-count label {\n  font-size: 14px;\n  font-weight: 600;\n  min-width: 110px;\n  color: #374151;\n}\n\n#pw-app .pw-checks {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px 22px;\n  margin-bottom: 14px;\n}\n#pw-app .pw-check-item {\n  display: flex;\n  align-items: center;\n  gap: 7px;\n  font-size: 14px;\n  cursor: pointer;\n  user-select: none;\n}\n#pw-app .pw-check-item input[type=\"checkbox\"] {\n  width: 16px;\n  height: 16px;\n  accent-color: #3b82f6;\n  cursor: pointer;\n}\n\n#pw-app .pw-exclude-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-top: 6px;\n}\n#pw-app .pw-exclude-row label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #374151;\n  white-space: nowrap;\n}\n#pw-app .pw-exclude-input {\n  flex: 1;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 6px;\n  padding: 6px 10px;\n  font-size: 13px;\n  font-family: \"Courier New\", Courier, monospace;\n  color: #0f172a;\n}\n#pw-app .pw-exclude-input:focus { outline: 2px solid #3b82f6; border-color: transparent; }\n\n#pw-app .pw-mode-tabs {\n  display: flex;\n  gap: 6px;\n  margin-bottom: 16px;\n}\n#pw-app .pw-mode-tab {\n  flex: 1;\n  text-align: center;\n  padding: 9px 10px;\n  border: 1.5px solid #d1d5db;\n  border-radius: 8px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  background: #fff;\n  color: #374151;\n  transition: all 0.15s;\n}\n#pw-app .pw-mode-tab.pw-tab-active {\n  background: #3b82f6;\n  color: #fff;\n  border-color: #3b82f6;\n}\n#pw-app .pw-mode-tab:hover:not(.pw-tab-active) { background: #f1f5f9; }\n\n#pw-app .pw-history-list {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n  max-height: 220px;\n  overflow-y: auto;\n}\n#pw-app .pw-history-list li {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  padding: 7px 0;\n  border-bottom: 1px solid #e2e8f0;\n  font-family: \"Courier New\", Courier, monospace;\n  font-size: 13px;\n  word-break: break-all;\n  gap: 8px;\n  color: #0f172a;\n}\n#pw-app .pw-history-list li:last-child { border-bottom: none; }\n#pw-app .pw-history-copy {\n  flex-shrink: 0;\n  background: none;\n  border: 1px solid #cbd5e1;\n  border-radius: 5px;\n  padding: 3px 9px;\n  font-size: 12px;\n  cursor: pointer;\n  color: #374151;\n}\n#pw-app .pw-history-copy:hover { background: #f1f5f9; }\n#pw-app .pw-empty-history {\n  font-size: 13px;\n  color: #94a3b8;\n  text-align: center;\n  padding: 14px 0;\n}\n\n#pw-app .pw-multi-list {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n}\n#pw-app .pw-multi-list li {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  padding: 7px 0;\n  border-bottom: 1px solid #e2e8f0;\n  gap: 8px;\n}\n#pw-app .pw-multi-list li:last-child { border-bottom: none; }\n#pw-app .pw-multi-list .pw-multi-text {\n  font-family: \"Courier New\", Courier, monospace;\n  font-size: 13px;\n  word-break: break-all;\n  color: #0f172a;\n  flex: 1;\n}\n\n@media (max-width: 480px) {\n  #pw-app .pw-row { flex-wrap: wrap; }\n  #pw-app .pw-row label { min-width: 100%; margin-bottom: 2px; }\n  #pw-app .pw-section { padding: 16px; }\n  #pw-app .pw-mode-tab { font-size: 12px; padding: 8px 6px; }\n}\n\u003c/style\u003e\n\u003c!-- Mode selector --\u003e\n\u003cdiv class=\"pw-section\"\u003e\n  \u003ch3 class=\"pw-section-title\"\u003eMode\u003c/h3\u003e\n  \u003cdiv class=\"pw-mode-tabs\"\u003e\n    \u003cdiv class=\"pw-mode-tab pw-tab-active\" onclick=\"pwSetMode('random',this)\"\u003eRandom\u003c/div\u003e\n    \u003cdiv class=\"pw-mode-tab\" onclick=\"pwSetMode('pronounceable',this)\"\u003ePronounceable\u003c/div\u003e\n    \u003cdiv class=\"pw-mode-tab\" onclick=\"pwSetMode('passphrase',this)\"\u003ePassphrase\u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Random / Pronounceable options --\u003e\n  \u003cdiv id=\"pw-random-opts\"\u003e\n    \u003cdiv class=\"pw-row\"\u003e\n      \u003clabel for=\"pw-length-slider\"\u003eLength\u003c/label\u003e\n      \u003cinput type=\"range\" id=\"pw-length-slider\" min=\"4\" max=\"128\" value=\"20\" oninput=\"pwSyncLength(this.value)\"\u003e\n      \u003cinput type=\"number\" class=\"pw-num-input\" id=\"pw-length-num\" min=\"4\" max=\"128\" value=\"20\" oninput=\"pwSyncLength(this.value)\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"pw-checks\"\u003e\n      \u003clabel class=\"pw-check-item\"\u003e\u003cinput type=\"checkbox\" id=\"pw-upper\" checked onchange=\"pwGenerate()\"\u003e Uppercase (A-Z)\u003c/label\u003e\n      \u003clabel class=\"pw-check-item\"\u003e\u003cinput type=\"checkbox\" id=\"pw-lower\" checked onchange=\"pwGenerate()\"\u003e Lowercase (a-z)\u003c/label\u003e\n      \u003clabel class=\"pw-check-item\"\u003e\u003cinput type=\"checkbox\" id=\"pw-nums\" checked onchange=\"pwGenerate()\"\u003e Numbers (0-9)\u003c/label\u003e\n      \u003clabel class=\"pw-check-item\"\u003e\u003cinput type=\"checkbox\" id=\"pw-syms\" onchange=\"pwGenerate()\"\u003e Symbols (!@#$...)\u003c/label\u003e\n      \u003clabel class=\"pw-check-item\"\u003e\u003cinput type=\"checkbox\" id=\"pw-noambig\" onchange=\"pwGenerate()\"\u003e Exclude ambiguous (0/O, l/1/I)\u003c/label\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"pw-exclude-row\"\u003e\n      \u003clabel for=\"pw-exclude-chars\"\u003eExclude chars:\u003c/label\u003e\n      \u003cinput type=\"text\" class=\"pw-exclude-input\" id=\"pw-exclude-chars\" placeholder=\"e.g. {}[]\" oninput=\"pwGenerate()\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Passphrase options --\u003e\n  \u003cdiv id=\"pw-passphrase-opts\" style=\"display:none;\"\u003e\n    \u003cdiv class=\"pw-row\"\u003e\n      \u003clabel for=\"pw-word-count\"\u003eWord count\u003c/label\u003e\n      \u003cinput type=\"range\" id=\"pw-word-count\" min=\"2\" max=\"10\" value=\"4\" oninput=\"pwSyncWords(this.value)\"\u003e\n      \u003cinput type=\"number\" class=\"pw-num-input\" id=\"pw-word-count-num\" min=\"2\" max=\"10\" value=\"4\" oninput=\"pwSyncWords(this.value)\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"pw-checks\"\u003e\n      \u003clabel class=\"pw-check-item\"\u003e\u003cinput type=\"checkbox\" id=\"pw-pp-cap\" checked onchange=\"pwGenerate()\"\u003e Capitalize words\u003c/label\u003e\n      \u003clabel class=\"pw-check-item\"\u003e\u003cinput type=\"checkbox\" id=\"pw-pp-num\" checked onchange=\"pwGenerate()\"\u003e Append number\u003c/label\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"pw-exclude-row\"\u003e\n      \u003clabel for=\"pw-pp-sep\"\u003eSeparator:\u003c/label\u003e\n      \u003cinput type=\"text\" class=\"pw-exclude-input\" id=\"pw-pp-sep\" value=\"-\" maxlength=\"5\" style=\"max-width:80px;\" oninput=\"pwGenerate()\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Count --\u003e\n  \u003cdiv class=\"pw-row-count\" style=\"margin-top:14px;\"\u003e\n    \u003clabel for=\"pw-count\"\u003eGenerate\u003c/label\u003e\n    \u003cinput type=\"number\" class=\"pw-num-input\" id=\"pw-count\" min=\"1\" max=\"20\" value=\"1\" oninput=\"pwGenerate()\"\u003e\n    \u003cspan style=\"font-size:13px;color:#64748b;\"\u003epassword(s) at once (max 20)\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Output --\u003e\n\u003cdiv class=\"pw-section\"\u003e\n  \u003ch3 class=\"pw-section-title\"\u003eOutput\u003c/h3\u003e\n  \u003cdiv class=\"pw-strength-wrap\" id=\"pw-strength-wrap\"\u003e\n    \u003cdiv class=\"pw-strength-label\"\u003e\n      \u003cspan\u003eStrength\u003c/span\u003e\n      \u003cspan id=\"pw-strength-text\" style=\"color:#94a3b8;\"\u003e--\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"pw-strength-bar-bg\"\u003e\n      \u003cdiv class=\"pw-strength-bar\" id=\"pw-strength-bar\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"pw-output-box\" id=\"pw-single-out\"\u003e\n    \u003cspan class=\"pw-pass-text\" id=\"pw-pass-display\"\u003eClick Generate\u003c/span\u003e\n    \u003cbutton class=\"pw-copy-btn\" id=\"pw-copy-main\" onclick=\"pwCopyMain()\"\u003eCopy\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cul class=\"pw-multi-list\" id=\"pw-multi-out\" style=\"display:none;\"\u003e\u003c/ul\u003e\n  \u003cdiv class=\"pw-actions\"\u003e\n    \u003cbutton class=\"pw-btn-primary\" onclick=\"pwGenerate()\"\u003e\u0026#x21bb; Generate\u003c/button\u003e\n    \u003cbutton class=\"pw-btn-secondary\" id=\"pw-copy-all-btn\" onclick=\"pwCopyAll()\" style=\"display:none;\"\u003eCopy All\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- History --\u003e\n\u003cdiv class=\"pw-section\"\u003e\n  \u003cdiv style=\"display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;\"\u003e\n    \u003ch3 class=\"pw-section-title\" style=\"margin:0;\"\u003eHistory \u003cspan style=\"font-weight:400;color:#94a3b8;font-size:11px;\"\u003e(session only)\u003c/span\u003e\u003c/h3\u003e\n    \u003cbutton class=\"pw-btn-secondary\" style=\"padding:5px 12px;font-size:12px;\" onclick=\"pwClearHistory()\"\u003eClear\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cul class=\"pw-history-list\" id=\"pw-history-list\"\u003e\n    \u003cli\u003e\u003cspan class=\"pw-empty-history\" style=\"width:100%;\"\u003eNo passwords generated yet.\u003c/span\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n  var _pwMode = 'random';\n  var _pwHistory = [];\n\n  var _WORDS = [\n    \"apple\",\"brave\",\"cloud\",\"dance\",\"eagle\",\"flame\",\"grace\",\"house\",\"ivory\",\"juice\",\n    \"kneel\",\"lemon\",\"mango\",\"night\",\"ocean\",\"piano\",\"queen\",\"river\",\"stone\",\"tiger\",\n    \"ultra\",\"viper\",\"wheat\",\"xenon\",\"yacht\",\"zebra\",\"amber\",\"blaze\",\"coral\",\"drift\",\n    \"ember\",\"frost\",\"globe\",\"haste\",\"inlet\",\"joker\",\"karma\",\"laser\",\"maple\",\"novel\",\n    \"olive\",\"pearl\",\"quill\",\"ridge\",\"solar\",\"tidal\",\"umbra\",\"vault\",\"waltz\",\"pixel\",\n    \"azure\",\"boost\",\"crisp\",\"delta\",\"epoch\",\"flint\",\"grove\",\"hinge\",\"irony\",\"jazzy\",\n    \"knife\",\"lunar\",\"moose\",\"nerve\",\"orbit\",\"prism\",\"quartz\",\"robin\",\"scout\",\"torch\",\n    \"unity\",\"visor\",\"woven\",\"oxide\",\"yield\",\"zonal\",\"alpha\",\"bench\",\"cedar\",\"depot\",\n    \"easel\",\"field\",\"glare\",\"hedge\",\"index\",\"jelly\",\"kinky\",\"lotus\",\"metal\",\"noble\",\n    \"ozone\",\"plumb\",\"quota\",\"ranch\",\"swift\",\"trout\",\"urban\",\"vivid\",\"water\",\"xylem\",\n    \"yearn\",\"zippy\",\"alert\",\"blunt\",\"curve\",\"dense\",\"erupt\",\"fluke\",\"groan\",\"hyper\",\n    \"image\",\"jewel\",\"knack\",\"lofty\",\"micro\",\"nifty\",\"opera\",\"plain\",\"quiet\",\"risky\",\n    \"spite\",\"tango\",\"usher\",\"vigor\",\"wider\",\"extra\",\"young\",\"zesty\",\"agile\",\"brisk\",\n    \"crimp\",\"dwell\",\"elbow\",\"finch\",\"guava\",\"hyena\",\"infer\",\"jaunt\",\"kudos\",\"lyric\",\n    \"merch\",\"navel\",\"onset\",\"patch\",\"quirk\",\"ruddy\",\"sleek\",\"taunt\",\"unfold\",\"venom\",\n    \"wrath\",\"adept\",\"brine\",\"clamp\",\"drive\",\"elegy\",\"forge\",\"glyph\",\"hatch\",\"intro\",\n    \"jumbo\",\"latch\",\"mirth\",\"nudge\",\"optic\",\"plaid\",\"quest\",\"radar\",\"snowy\",\"tapir\",\n    \"untie\",\"vocal\",\"whirl\",\"exert\",\"zilch\",\"amber\",\"birch\",\"candy\",\"daisy\",\"denim\",\n    \"fable\",\"gauge\",\"hazel\",\"kiosk\",\"lilac\",\"melon\",\"nicer\",\"onion\",\"pansy\",\"racer\",\n    \"rivet\",\"swamp\",\"vivid\",\"windy\",\"boxer\",\"crane\",\"floss\",\"graze\",\"kneel\",\"primo\"\n  ];\n\n  var _CONSONANTS = 'bcdfghjklmnpqrstvwxyz';\n  var _VOWELS     = 'aeiou';\n\n  function _randInt(max) {\n    var a = new Uint32Array(1);\n    var lim = Math.floor(4294967296 / max) * max;\n    do { crypto.getRandomValues(a); } while (a[0] \u003e= lim);\n    return a[0] % max;\n  }\n\n  function _charset() {\n    var upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';\n    var lower = 'abcdefghijklmnopqrstuvwxyz';\n    var nums  = '0123456789';\n    var syms  = '!@#$%^\u0026*()-_=+[]{}|;:,.\u003c\u003e?';\n    var ambig = '0Ol1I';\n    var useUp = document.getElementById('pw-upper').checked;\n    var useLo = document.getElementById('pw-lower').checked;\n    var useNu = document.getElementById('pw-nums').checked;\n    var useSy = document.getElementById('pw-syms').checked;\n    var noAmb = document.getElementById('pw-noambig').checked;\n    var excl  = document.getElementById('pw-exclude-chars').value;\n    var cs = '';\n    if (useUp) cs += upper;\n    if (useLo) cs += lower;\n    if (useNu) cs += nums;\n    if (useSy) cs += syms;\n    if (noAmb) { var t=''; for(var i=0;i\u003ccs.length;i++) if(ambig.indexOf(cs[i])===-1) t+=cs[i]; cs=t; }\n    if (excl)  { var t=''; for(var i=0;i\u003ccs.length;i++) if(excl.indexOf(cs[i])===-1) t+=cs[i]; cs=t; }\n    return cs;\n  }\n\n  function _genRandom(len) {\n    var cs = _charset();\n    if (!cs) return '(select at least one character type)';\n    var out = '';\n    for (var i=0; i\u003clen; i++) out += cs[_randInt(cs.length)];\n    return out;\n  }\n\n  function _genPronounceable(len) {\n    var useUp = document.getElementById('pw-upper').checked;\n    var noAmb = document.getElementById('pw-noambig').checked;\n    var excl  = document.getElementById('pw-exclude-chars').value;\n    var con = noAmb ? _CONSONANTS.replace(/l/g,'') : _CONSONANTS;\n    var vow = _VOWELS;\n    var out = '';\n    var useC = true;\n    for (var i=0; i\u003clen; i++) {\n      var pool = useC ? con : vow;\n      var ch = pool[_randInt(pool.length)];\n      if (useUp \u0026\u0026 _randInt(4)===0) ch = ch.toUpperCase();\n      out += ch;\n      useC = !useC;\n    }\n    if (excl) {\n      var fixed = '';\n      for (var j=0; j\u003cout.length; j++) {\n        if (excl.indexOf(out[j])===-1) { fixed += out[j]; }\n        else { var p=(j%2===0)?con:vow; fixed += p[_randInt(p.length)]; }\n      }\n      out = fixed;\n    }\n    return out;\n  }\n\n  function _genPassphrase() {\n    var cnt = Math.min(10,Math.max(2,parseInt(document.getElementById('pw-word-count').value)||4));\n    var cap = document.getElementById('pw-pp-cap').checked;\n    var num = document.getElementById('pw-pp-num').checked;\n    var sep = document.getElementById('pw-pp-sep').value;\n    var ws = [];\n    for (var i=0; i\u003ccnt; i++) {\n      var w = _WORDS[_randInt(_WORDS.length)];\n      if (cap) w = w.charAt(0).toUpperCase()+w.slice(1);\n      ws.push(w);\n    }\n    var r = ws.join(sep);\n    if (num) r += sep + (_randInt(90)+10);\n    return r;\n  }\n\n  function _genOne() {\n    var len = parseInt(document.getElementById('pw-length-slider').value)||20;\n    if (_pwMode==='random')       return _genRandom(len);\n    if (_pwMode==='pronounceable') return _genPronounceable(len);\n    if (_pwMode==='passphrase')    return _genPassphrase();\n    return '';\n  }\n\n  function _entropy(pass) {\n    var cs = _charset();\n    var pool = cs ? cs.length : 26;\n    return pass.length * Math.log2(pool \u003e 1 ? pool : 2);\n  }\n\n  function _strengthInfo(bits) {\n    if (bits \u003c 28)  return {label:'Very Weak',  pct:10,  color:'#ef4444'};\n    if (bits \u003c 36)  return {label:'Weak',        pct:25,  color:'#f97316'};\n    if (bits \u003c 60)  return {label:'Fair',         pct:50,  color:'#eab308'};\n    if (bits \u003c 80)  return {label:'Good',         pct:72,  color:'#22c55e'};\n    if (bits \u003c 100) return {label:'Strong',       pct:88,  color:'#16a34a'};\n    return               {label:'Very Strong',  pct:100, color:'#15803d'};\n  }\n\n  function _passphraseEntropy(pass) {\n    var sep = document.getElementById('pw-pp-sep').value || '-';\n    var sepE = sep.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g,'\\\\$\u0026');\n    var wc = (pass.match(new RegExp('['+sepE+']','g'))||[]).length + 1;\n    return wc * Math.log2(_WORDS.length);\n  }\n\n  function _updateStrength(pass) {\n    var bits = (_pwMode==='passphrase') ? _passphraseEntropy(pass) : _entropy(pass);\n    var info = _strengthInfo(bits);\n    document.getElementById('pw-strength-text').textContent = info.label + ' (~'+Math.round(bits)+' bits)';\n    document.getElementById('pw-strength-text').style.color = info.color;\n    document.getElementById('pw-strength-bar').style.width = info.pct+'%';\n    document.getElementById('pw-strength-bar').style.background = info.color;\n  }\n\n  function _addHistory(passwords) {\n    for (var i=0; i\u003cpasswords.length; i++) _pwHistory.unshift(passwords[i]);\n    if (_pwHistory.length\u003e10) _pwHistory = _pwHistory.slice(0,10);\n    _renderHistory();\n  }\n\n  function _renderHistory() {\n    var ul = document.getElementById('pw-history-list');\n    ul.innerHTML = '';\n    if (_pwHistory.length===0) {\n      ul.innerHTML = '\u003cli\u003e\u003cspan class=\"pw-empty-history\" style=\"width:100%;\"\u003eNo passwords generated yet.\u003c/span\u003e\u003c/li\u003e';\n      return;\n    }\n    _pwHistory.forEach(function(p) {\n      var li = document.createElement('li');\n      var sp = document.createElement('span'); sp.textContent = p;\n      var btn = document.createElement('button');\n      btn.className = 'pw-history-copy'; btn.textContent = 'Copy';\n      (function(pass,b){ b.onclick=function(){ _clip(pass); b.textContent='Copied!'; setTimeout(function(){ b.textContent='Copy'; },1500); }; })(p,btn);\n      li.appendChild(sp); li.appendChild(btn); ul.appendChild(li);\n    });\n  }\n\n  function _clip(text) {\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(text).catch(function(){});\n    } else {\n      var ta=document.createElement('textarea'); ta.value=text;\n      ta.style.position='fixed'; ta.style.opacity='0';\n      document.body.appendChild(ta); ta.select();\n      document.execCommand('copy'); document.body.removeChild(ta);\n    }\n  }\n\n  window.pwGenerate = function() {\n    var count = Math.min(20,Math.max(1,parseInt(document.getElementById('pw-count').value)||1));\n    var passwords = [];\n    for (var i=0; i\u003ccount; i++) passwords.push(_genOne());\n\n    var singleOut    = document.getElementById('pw-single-out');\n    var multiOut     = document.getElementById('pw-multi-out');\n    var copyAllBtn   = document.getElementById('pw-copy-all-btn');\n    var strengthWrap = document.getElementById('pw-strength-wrap');\n\n    if (count===1) {\n      document.getElementById('pw-pass-display').textContent = passwords[0];\n      singleOut.style.display    = 'flex';\n      multiOut.style.display     = 'none';\n      copyAllBtn.style.display   = 'none';\n      strengthWrap.style.display = 'block';\n      _updateStrength(passwords[0]);\n    } else {\n      singleOut.style.display    = 'none';\n      strengthWrap.style.display = 'none';\n      multiOut.style.display     = 'block';\n      copyAllBtn.style.display   = 'inline-block';\n      multiOut.innerHTML = '';\n      passwords.forEach(function(p) {\n        var li = document.createElement('li');\n        var sp = document.createElement('span'); sp.className='pw-multi-text'; sp.textContent=p;\n        var btn = document.createElement('button');\n        btn.className='pw-copy-btn'; btn.textContent='Copy';\n        btn.style.fontSize='12px'; btn.style.padding='5px 12px';\n        (function(pass,b){ b.onclick=function(){ _clip(pass); b.textContent='Copied!'; b.classList.add('pw-copied'); setTimeout(function(){ b.textContent='Copy'; b.classList.remove('pw-copied'); },1500); }; })(p,btn);\n        li.appendChild(sp); li.appendChild(btn); multiOut.appendChild(li);\n      });\n    }\n    _addHistory(passwords);\n  };\n\n  window.pwCopyMain = function() {\n    var text = document.getElementById('pw-pass-display').textContent;\n    if (!text || text==='Click Generate') return;\n    _clip(text);\n    var btn = document.getElementById('pw-copy-main');\n    btn.textContent='Copied!'; btn.classList.add('pw-copied');\n    setTimeout(function(){ btn.textContent='Copy'; btn.classList.remove('pw-copied'); },1500);\n  };\n\n  window.pwCopyAll = function() {\n    var items = document.querySelectorAll('#pw-multi-out .pw-multi-text');\n    var all = Array.prototype.slice.call(items).map(function(el){ return el.textContent; }).join('\\n');\n    _clip(all);\n    var btn = document.getElementById('pw-copy-all-btn');\n    btn.textContent='Copied All!';\n    setTimeout(function(){ btn.textContent='Copy All'; },1500);\n  };\n\n  window.pwClearHistory = function() { _pwHistory=[]; _renderHistory(); };\n\n  window.pwSyncLength = function(val) {\n    var v = Math.min(128,Math.max(4,parseInt(val)||4));\n    document.getElementById('pw-length-slider').value = v;\n    document.getElementById('pw-length-num').value    = v;\n    pwGenerate();\n  };\n\n  window.pwSyncWords = function(val) {\n    var v = Math.min(10,Math.max(2,parseInt(val)||4));\n    document.getElementById('pw-word-count').value     = v;\n    document.getElementById('pw-word-count-num').value = v;\n    pwGenerate();\n  };\n\n  window.pwSetMode = function(mode, el) {\n    _pwMode = mode;\n    var tabs = document.querySelectorAll('#pw-app .pw-mode-tab');\n    for (var i=0; i\u003ctabs.length; i++) tabs[i].classList.remove('pw-tab-active');\n    el.classList.add('pw-tab-active');\n    var isPhrase = (mode==='passphrase');\n    document.getElementById('pw-random-opts').style.display     = isPhrase ? 'none'  : 'block';\n    document.getElementById('pw-passphrase-opts').style.display = isPhrase ? 'block' : 'none';\n    pwGenerate();\n  };\n\n  pwGenerate();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-it-works\"\u003eHow It Works\u003c/h2\u003e\n\u003cp\u003ePasswords are generated entirely in your browser using the \u003cstrong\u003eWeb Crypto API\u003c/strong\u003e (\u003ccode\u003ecrypto.getRandomValues()\u003c/code\u003e), which produces cryptographically secure random values. No data is ever sent to a server.\u003c/p\u003e","title":"Password Generator"},{"content":"Check how strong your password really is — entropy, estimated crack time, pattern detection, and improvement suggestions. All analysis runs locally in your browser; nothing is ever transmitted.\nEnter Password \u0026#128065; \u0026#9888; This is one of the most commonly used passwords — avoid it! \u0026#128274; All analysis is done locally. Your password never leaves your device.\n— Analysis Length — Charset Size — Entropy — Crack Time (10B/s) — Checks Suggestions How It Works All analysis runs entirely in your browser using plain JavaScript — no data is ever sent to a server.\nEntropy (measured in bits) quantifies how unpredictable a password is. The formula is length × log₂(charset size). A 20-character password using all four character types (upper, lower, digit, symbol) produces roughly 131 bits of entropy — effectively uncrackable by brute force with current hardware.\nCrack Time is estimated at 10 billion guesses per second, representing a well-resourced offline attack. Online attacks are far slower due to rate limiting.\nStrength Levels Level Entropy Typical Scenario Very Weak \u0026lt; 28 bits Short or common password Weak 28–39 bits Dictionary word + small variation Fair 40–59 bits Mixed types, moderate length Strong 60–79 bits Long mixed-type password Very Strong 80+ bits Full character set, 16+ characters Tips Use 16+ characters for everyday accounts; 24+ for critical ones. Enable all character types to maximise entropy. Avoid real words, names, dates, and keyboard patterns. Use a password manager — never reuse passwords across sites. Consider a passphrase (e.g. correct-horse-battery-staple) for passwords you must memorise. Generate a new password → Password Generator Create and verify hashes → Hash Generator Related Articles Best Password Managers 2026: Secure Every Account for $3/Month ","permalink":"https://productivity-works.com/tools/password-strength-meter/","summary":"\u003cp\u003eCheck how strong your password really is — entropy, estimated crack time, pattern detection, and improvement suggestions. All analysis runs locally in your browser; nothing is ever transmitted.\u003c/p\u003e\n\u003cdiv id=\"pm-app\"\u003e\n\u003cstyle\u003e\n#pm-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 680px;\n  margin: 0 auto;\n  color: #1e293b;\n}\n#pm-app * { box-sizing: border-box; }\n\n#pm-app .pm-section {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 20px 24px;\n  margin-bottom: 16px;\n}\n\n#pm-app h3.pm-section-title {\n  margin: 0 0 14px 0;\n  font-size: 13px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  color: #64748b;\n}\n\n/* Input row */\n#pm-app .pm-input-wrap {\n  position: relative;\n  display: flex;\n  align-items: center;\n}\n#pm-app #pm-input {\n  width: 100%;\n  padding: 13px 46px 13px 14px;\n  font-size: 16px;\n  font-family: \"Courier New\", Courier, monospace;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  outline: none;\n  color: #0f172a;\n  background: #fff;\n  transition: border-color 0.15s;\n}\n#pm-app #pm-input:focus { border-color: #3b82f6; }\n#pm-app .pm-toggle-vis {\n  position: absolute;\n  right: 12px;\n  background: none;\n  border: none;\n  cursor: pointer;\n  padding: 4px;\n  color: #64748b;\n  font-size: 18px;\n  line-height: 1;\n  user-select: none;\n}\n#pm-app .pm-toggle-vis:hover { color: #1e293b; }\n\n/* Strength bar */\n#pm-app .pm-bar-wrap {\n  margin-top: 14px;\n}\n#pm-app .pm-bar-track {\n  height: 8px;\n  background: #e2e8f0;\n  border-radius: 99px;\n  overflow: hidden;\n  margin-bottom: 6px;\n}\n#pm-app .pm-bar-fill {\n  height: 100%;\n  border-radius: 99px;\n  transition: width 0.3s ease, background 0.3s ease;\n  width: 0%;\n  background: #e2e8f0;\n}\n#pm-app .pm-bar-label {\n  font-size: 14px;\n  font-weight: 700;\n  color: #475569;\n}\n\n/* Strength levels */\n#pm-app .pm-lvl-0 { width: 5%;  background: #ef4444; }\n#pm-app .pm-lvl-1 { width: 25%; background: #f97316; }\n#pm-app .pm-lvl-2 { width: 50%; background: #eab308; }\n#pm-app .pm-lvl-3 { width: 75%; background: #22c55e; }\n#pm-app .pm-lvl-4 { width: 100%; background: #16a34a; }\n\n#pm-app .pm-label-0 { color: #ef4444; }\n#pm-app .pm-label-1 { color: #f97316; }\n#pm-app .pm-label-2 { color: #ca8a04; }\n#pm-app .pm-label-3 { color: #16a34a; }\n#pm-app .pm-label-4 { color: #15803d; }\n\n/* Stats grid */\n#pm-app .pm-stats {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 10px;\n  margin-top: 14px;\n}\n#pm-app .pm-stat {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 8px;\n  padding: 10px 14px;\n}\n#pm-app .pm-stat-label {\n  font-size: 11px;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  color: #94a3b8;\n  margin-bottom: 2px;\n}\n#pm-app .pm-stat-value {\n  font-size: 15px;\n  font-weight: 700;\n  color: #0f172a;\n}\n\n/* Checks list */\n#pm-app .pm-checks {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n}\n#pm-app .pm-check-item {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  font-size: 13px;\n  color: #475569;\n}\n#pm-app .pm-check-icon {\n  width: 18px;\n  height: 18px;\n  border-radius: 50%;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 11px;\n  flex-shrink: 0;\n  font-weight: 700;\n}\n#pm-app .pm-check-pass .pm-check-icon { background: #dcfce7; color: #16a34a; }\n#pm-app .pm-check-fail .pm-check-icon { background: #fee2e2; color: #dc2626; }\n#pm-app .pm-check-warn .pm-check-icon { background: #fef9c3; color: #ca8a04; }\n\n/* Suggestions */\n#pm-app .pm-suggestions {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  display: flex;\n  flex-direction: column;\n  gap: 5px;\n}\n#pm-app .pm-suggestion {\n  font-size: 13px;\n  color: #475569;\n  padding: 6px 10px;\n  background: #fff7ed;\n  border-left: 3px solid #f97316;\n  border-radius: 0 6px 6px 0;\n}\n#pm-app .pm-no-suggestions {\n  font-size: 13px;\n  color: #16a34a;\n  font-weight: 600;\n}\n\n/* Common password warning */\n#pm-app .pm-common-warning {\n  display: none;\n  padding: 10px 14px;\n  background: #fef2f2;\n  border: 1px solid #fecaca;\n  border-radius: 8px;\n  font-size: 13px;\n  color: #b91c1c;\n  font-weight: 600;\n  margin-top: 8px;\n}\n\n/* Privacy note */\n#pm-app .pm-privacy {\n  font-size: 11px;\n  color: #94a3b8;\n  text-align: center;\n  margin-top: 4px;\n}\n\n@media (max-width: 480px) {\n  #pm-app .pm-stats { grid-template-columns: 1fr; }\n  #pm-app .pm-section { padding: 16px; }\n}\n\u003c/style\u003e\n\u003c!-- Input --\u003e\n\u003cdiv class=\"pm-section\"\u003e\n  \u003ch3 class=\"pm-section-title\"\u003eEnter Password\u003c/h3\u003e\n  \u003cdiv class=\"pm-input-wrap\"\u003e\n    \u003cinput type=\"password\" id=\"pm-input\" autocomplete=\"off\" autocorrect=\"off\" autocapitalize=\"off\" spellcheck=\"false\" placeholder=\"Type or paste a password…\" oninput=\"pmAnalyze()\"\u003e\n    \u003cbutton class=\"pm-toggle-vis\" onclick=\"pmToggleVis()\" id=\"pm-vis-btn\" title=\"Show / Hide\"\u003e\u0026#128065;\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv id=\"pm-common-warning\" class=\"pm-common-warning\"\u003e\u0026#9888; This is one of the most commonly used passwords — avoid it!\u003c/div\u003e\n  \u003cp class=\"pm-privacy\"\u003e\u0026#128274; All analysis is done locally. Your password never leaves your device.\u003c/p\u003e","title":"Password Strength Meter"},{"content":"Plan your retirement with confidence. Enter your details below to estimate your savings at retirement, monthly income, and whether you\u0026rsquo;re on track to meet your goals — no account required.\nScenario Preset Conservative Moderate Aggressive Your Details Current Age Retirement Age Current Savings ($) Total already saved for retirement Monthly Contribution ($) Expected Annual Return (%) Expected Inflation Rate (%) Desired Monthly Income in Retirement ($) Life Expectancy Calculate\nResults Savings at Retirement Nominal (before inflation) Real Savings (today's $) Inflation-adjusted Monthly Pension Income From savings (real terms) Savings Growth by Decade Monthly Shortfall / Surplus vs. desired monthly income Required Monthly Contribution To meet desired income Years Savings Will Last At desired withdrawal rate Retirement Duration Years in retirement (target) Calculate take-home pay → Salary Calculator Plan your budget → 50/30/20 Budget Calculator Track compound growth → Compound Interest Calculator Related Articles Retirement Planning in Japan for Foreign Residents: What the Pension System Won\u0026rsquo;t Cover ","permalink":"https://productivity-works.com/tools/pension-simulator/","summary":"\u003cp\u003ePlan your retirement with confidence. Enter your details below to estimate your savings at retirement, monthly income, and whether you\u0026rsquo;re on track to meet your goals — no account required.\u003c/p\u003e\n\u003cdiv id=\"ps-app\"\u003e\n\u003cstyle\u003e\n#ps-app {\n  font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n  color: #1e293b;\n  max-width: 860px;\n  margin: 0 auto;\n  padding: 0 0 2rem 0;\n}\n#ps-app * {\n  box-sizing: border-box;\n}\n#ps-app .ps-section-title {\n  font-size: 1rem;\n  font-weight: 700;\n  color: #475569;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  margin: 1.8rem 0 0.8rem 0;\n}\n#ps-app .ps-presets {\n  display: flex;\n  gap: 0.5rem;\n  margin-bottom: 1.4rem;\n  flex-wrap: wrap;\n}\n#ps-app .ps-preset-btn {\n  padding: 0.4rem 1.1rem;\n  border-radius: 999px;\n  border: 1.5px solid #cbd5e1;\n  background: #f8fafc;\n  color: #475569;\n  font-size: 0.875rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, border-color 0.15s, color 0.15s;\n}\n#ps-app .ps-preset-btn:hover,\n#ps-app .ps-preset-btn.active {\n  background: #1e293b;\n  border-color: #1e293b;\n  color: #f8fafc;\n}\n#ps-app .ps-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1rem 1.5rem;\n}\n@media (max-width: 560px) {\n  #ps-app .ps-grid {\n    grid-template-columns: 1fr;\n  }\n}\n#ps-app .ps-field {\n  display: flex;\n  flex-direction: column;\n  gap: 0.3rem;\n}\n#ps-app .ps-field label {\n  font-size: 0.85rem;\n  font-weight: 600;\n  color: #475569;\n}\n#ps-app .ps-field input {\n  padding: 0.5rem 0.75rem;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 1rem;\n  color: #1e293b;\n  background: #f8fafc;\n  outline: none;\n  transition: border-color 0.15s;\n  width: 100%;\n}\n#ps-app .ps-field input:focus {\n  border-color: #475569;\n  background: #fff;\n}\n#ps-app .ps-field .ps-hint {\n  font-size: 0.75rem;\n  color: #94a3b8;\n}\n#ps-app .ps-calc-btn {\n  margin-top: 1.4rem;\n  width: 100%;\n  padding: 0.85rem;\n  border: none;\n  border-radius: 10px;\n  background: #1e293b;\n  color: #f8fafc;\n  font-size: 1.05rem;\n  font-weight: 700;\n  cursor: pointer;\n  letter-spacing: 0.02em;\n  transition: background 0.15s;\n}\n#ps-app .ps-calc-btn:hover {\n  background: #475569;\n}\n#ps-app .ps-results {\n  margin-top: 2rem;\n  display: none;\n}\n#ps-app .ps-results.visible {\n  display: block;\n}\n#ps-app .ps-cards {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 0.85rem;\n  margin-bottom: 1.4rem;\n}\n@media (max-width: 640px) {\n  #ps-app .ps-cards {\n    grid-template-columns: 1fr 1fr;\n  }\n}\n@media (max-width: 380px) {\n  #ps-app .ps-cards {\n    grid-template-columns: 1fr;\n  }\n}\n#ps-app .ps-card {\n  background: #f8fafc;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 12px;\n  padding: 1rem 1.1rem;\n  display: flex;\n  flex-direction: column;\n  gap: 0.25rem;\n}\n#ps-app .ps-card .ps-card-label {\n  font-size: 0.75rem;\n  font-weight: 700;\n  color: #94a3b8;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n}\n#ps-app .ps-card .ps-card-value {\n  font-size: 1.35rem;\n  font-weight: 800;\n  color: #1e293b;\n  word-break: break-all;\n}\n#ps-app .ps-card .ps-card-sub {\n  font-size: 0.78rem;\n  color: #64748b;\n}\n#ps-app .ps-status-bar {\n  border-radius: 10px;\n  padding: 0.85rem 1.1rem;\n  font-size: 0.95rem;\n  font-weight: 600;\n  margin-bottom: 1.4rem;\n}\n#ps-app .ps-status-bar.green {\n  background: #dcfce7;\n  color: #166534;\n  border: 1.5px solid #86efac;\n}\n#ps-app .ps-status-bar.orange {\n  background: #fff7ed;\n  color: #9a3412;\n  border: 1.5px solid #fed7aa;\n}\n#ps-app .ps-status-bar.red {\n  background: #fef2f2;\n  color: #991b1b;\n  border: 1.5px solid #fecaca;\n}\n#ps-app .ps-chart-wrap {\n  background: #f8fafc;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 12px;\n  padding: 1.2rem 1.2rem 0.8rem 1.2rem;\n  margin-bottom: 1.2rem;\n}\n#ps-app .ps-chart-title {\n  font-size: 0.85rem;\n  font-weight: 700;\n  color: #475569;\n  margin-bottom: 0.8rem;\n}\n#ps-app canvas#ps-chart {\n  width: 100%;\n  display: block;\n}\n#ps-app .ps-extra-cards {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 0.85rem;\n  margin-bottom: 0.5rem;\n}\n@media (max-width: 480px) {\n  #ps-app .ps-extra-cards {\n    grid-template-columns: 1fr;\n  }\n}\n#ps-app .ps-card.highlight {\n  border-color: #1e293b;\n  background: #1e293b;\n}\n#ps-app .ps-card.highlight .ps-card-label,\n#ps-app .ps-card.highlight .ps-card-sub {\n  color: #94a3b8;\n}\n#ps-app .ps-card.highlight .ps-card-value {\n  color: #f8fafc;\n}\n\u003c/style\u003e\n\u003cdiv class=\"ps-section-title\"\u003eScenario Preset\u003c/div\u003e\n\u003cdiv class=\"ps-presets\"\u003e\n  \u003cbutton class=\"ps-preset-btn\" id=\"ps-preset-conservative\" onclick=\"psSetPreset('conservative')\"\u003eConservative\u003c/button\u003e\n  \u003cbutton class=\"ps-preset-btn active\" id=\"ps-preset-moderate\" onclick=\"psSetPreset('moderate')\"\u003eModerate\u003c/button\u003e\n  \u003cbutton class=\"ps-preset-btn\" id=\"ps-preset-aggressive\" onclick=\"psSetPreset('aggressive')\"\u003eAggressive\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ps-section-title\"\u003eYour Details\u003c/div\u003e\n\u003cdiv class=\"ps-grid\"\u003e\n  \u003cdiv class=\"ps-field\"\u003e\n    \u003clabel for=\"ps-current-age\"\u003eCurrent Age\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"ps-current-age\" value=\"30\" min=\"16\" max=\"99\" /\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ps-field\"\u003e\n    \u003clabel for=\"ps-retire-age\"\u003eRetirement Age\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"ps-retire-age\" value=\"65\" min=\"40\" max=\"99\" /\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ps-field\"\u003e\n    \u003clabel for=\"ps-savings\"\u003eCurrent Savings ($)\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"ps-savings\" value=\"50000\" min=\"0\" /\u003e\n    \u003cspan class=\"ps-hint\"\u003eTotal already saved for retirement\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ps-field\"\u003e\n    \u003clabel for=\"ps-monthly-contrib\"\u003eMonthly Contribution ($)\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"ps-monthly-contrib\" value=\"500\" min=\"0\" /\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ps-field\"\u003e\n    \u003clabel for=\"ps-return\"\u003eExpected Annual Return (%)\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"ps-return\" value=\"7\" min=\"0\" max=\"30\" step=\"0.1\" /\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ps-field\"\u003e\n    \u003clabel for=\"ps-inflation\"\u003eExpected Inflation Rate (%)\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"ps-inflation\" value=\"2.5\" min=\"0\" max=\"20\" step=\"0.1\" /\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ps-field\"\u003e\n    \u003clabel for=\"ps-desired-income\"\u003eDesired Monthly Income in Retirement ($)\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"ps-desired-income\" value=\"3000\" min=\"0\" /\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ps-field\"\u003e\n    \u003clabel for=\"ps-life-exp\"\u003eLife Expectancy\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"ps-life-exp\" value=\"90\" min=\"60\" max=\"120\" /\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cp\u003e\u003cbutton class=\"ps-calc-btn\" onclick=\"psCalculate()\"\u003eCalculate\u003c/button\u003e\u003c/p\u003e","title":"Pension Simulator"},{"content":" Phone Number Formatter \u0026 Validator Single Number Bulk Formatter Country Code Reference Phone Number Country United States (+1) Canada (+1) United Kingdom (+44) Japan (+81) Germany (+49) France (+33) Australia (+61) South Korea (+82) China (+86) India (+91) Brazil (+55) Mexico (+52) Italy (+39) Spain (+34) Netherlands (+31) Singapore (+65) Hong Kong (+852) Taiwan (+886) New Zealand (+64) South Africa (+27) International Copy National Copy E.164 Copy RFC 3966 Copy Digits Only Copy Country for all numbers United States (+1) Canada (+1) United Kingdom (+44) Japan (+81) Germany (+49) France (+33) Australia (+61) South Korea (+82) China (+86) India (+91) Brazil (+55) Mexico (+52) Italy (+39) Spain (+34) Netherlands (+31) Singapore (+65) Hong Kong (+852) Taiwan (+886) New Zealand (+64) South Africa (+27) Paste phone numbers (one per line) Format All Clear Copy Results Country dial codes, trunk prefixes, and typical number lengths for reference.\nCountry Dial Code Trunk Prefix Subscriber Digits Example Related tools:\nTime Zone Converter — Convert times across world time zones instantly Unit Converter — Length, weight, temperature, and more Currency Converter — Live exchange rates for 150+ currencies QR Code Generator — Create QR codes for URLs, text, and contact info ","permalink":"https://productivity-works.com/tools/phone-formatter/","summary":"\u003cdiv id=\"pf-app\"\u003e\n\u003cstyle\u003e\n#pf-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#pf-app h2 {\n  font-size: 1.4rem;\n  font-weight: 700;\n  margin: 2rem 0 1rem;\n  color: #0f3460;\n  border-left: 4px solid #e94560;\n  padding-left: 0.75rem;\n}\n#pf-app .pf-card {\n  background: #fff;\n  border: 1px solid #e0e0e0;\n  border-radius: 10px;\n  padding: 1.5rem;\n  margin-bottom: 1.5rem;\n  box-shadow: 0 2px 8px rgba(0,0,0,0.05);\n}\n#pf-app label {\n  display: block;\n  font-size: 0.85rem;\n  font-weight: 600;\n  color: #444;\n  margin-bottom: 0.4rem;\n}\n#pf-app input[type=\"text\"],\n#pf-app select,\n#pf-app textarea {\n  width: 100%;\n  padding: 0.65rem 0.9rem;\n  border: 1px solid #ccc;\n  border-radius: 6px;\n  font-size: 1rem;\n  box-sizing: border-box;\n  transition: border-color 0.2s;\n}\n#pf-app input[type=\"text\"]:focus,\n#pf-app select:focus,\n#pf-app textarea:focus {\n  outline: none;\n  border-color: #0f3460;\n}\n#pf-app textarea {\n  min-height: 120px;\n  resize: vertical;\n  font-family: monospace;\n  font-size: 0.9rem;\n}\n#pf-app .pf-row {\n  display: flex;\n  gap: 1rem;\n  flex-wrap: wrap;\n}\n#pf-app .pf-row \u003e * {\n  flex: 1;\n  min-width: 220px;\n}\n#pf-app .pf-btn {\n  display: inline-block;\n  background: #0f3460;\n  color: #fff;\n  border: none;\n  border-radius: 6px;\n  padding: 0.65rem 1.4rem;\n  font-size: 0.95rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.2s;\n  margin-top: 0.5rem;\n}\n#pf-app .pf-btn:hover {\n  background: #e94560;\n}\n#pf-app .pf-btn-secondary {\n  background: #6c757d;\n}\n#pf-app .pf-btn-secondary:hover {\n  background: #495057;\n}\n#pf-app .pf-result-grid {\n  display: grid;\n  grid-template-columns: 160px 1fr;\n  gap: 0.5rem 1rem;\n  align-items: center;\n  margin-top: 1rem;\n}\n#pf-app .pf-result-label {\n  font-size: 0.8rem;\n  font-weight: 700;\n  color: #666;\n  text-transform: uppercase;\n  letter-spacing: 0.03em;\n}\n#pf-app .pf-result-value {\n  background: #f4f6fb;\n  border: 1px solid #dde3f0;\n  border-radius: 5px;\n  padding: 0.45rem 0.75rem;\n  font-family: monospace;\n  font-size: 1rem;\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n  min-height: 2.2rem;\n  word-break: break-all;\n}\n#pf-app .pf-copy-btn {\n  background: none;\n  border: 1px solid #ccc;\n  border-radius: 4px;\n  padding: 2px 8px;\n  font-size: 0.75rem;\n  cursor: pointer;\n  color: #555;\n  white-space: nowrap;\n  margin-left: auto;\n  transition: background 0.15s;\n}\n#pf-app .pf-copy-btn:hover {\n  background: #e0e7ff;\n}\n#pf-app .pf-badge {\n  display: inline-block;\n  padding: 3px 10px;\n  border-radius: 12px;\n  font-size: 0.78rem;\n  font-weight: 700;\n}\n#pf-app .pf-badge-valid {\n  background: #d4edda;\n  color: #155724;\n}\n#pf-app .pf-badge-invalid {\n  background: #f8d7da;\n  color: #721c24;\n}\n#pf-app .pf-badge-warn {\n  background: #fff3cd;\n  color: #856404;\n}\n#pf-app .pf-validation-box {\n  margin-top: 1rem;\n  padding: 0.75rem 1rem;\n  border-radius: 6px;\n  font-size: 0.9rem;\n}\n#pf-app .pf-validation-box.valid {\n  background: #d4edda;\n  border: 1px solid #c3e6cb;\n  color: #155724;\n}\n#pf-app .pf-validation-box.invalid {\n  background: #f8d7da;\n  border: 1px solid #f5c6cb;\n  color: #721c24;\n}\n#pf-app .pf-validation-box.warn {\n  background: #fff3cd;\n  border: 1px solid #ffeeba;\n  color: #856404;\n}\n#pf-app .bulk-output {\n  margin-top: 1rem;\n}\n#pf-app .bulk-output-line {\n  font-family: monospace;\n  font-size: 0.88rem;\n  padding: 0.3rem 0.5rem;\n  border-bottom: 1px solid #f0f0f0;\n  display: flex;\n  gap: 0.75rem;\n  align-items: center;\n  flex-wrap: wrap;\n}\n#pf-app .bulk-output-line:nth-child(even) {\n  background: #f9f9f9;\n}\n#pf-app .pf-ref-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.88rem;\n}\n#pf-app .pf-ref-table th {\n  background: #0f3460;\n  color: #fff;\n  padding: 0.5rem 0.75rem;\n  text-align: left;\n}\n#pf-app .pf-ref-table td {\n  padding: 0.45rem 0.75rem;\n  border-bottom: 1px solid #eee;\n}\n#pf-app .pf-ref-table tr:nth-child(even) td {\n  background: #f7f9fc;\n}\n#pf-app .pf-tabs {\n  display: flex;\n  gap: 0;\n  border-bottom: 2px solid #e0e0e0;\n  margin-bottom: 1.5rem;\n}\n#pf-app .pf-tab {\n  padding: 0.55rem 1.2rem;\n  cursor: pointer;\n  font-weight: 600;\n  font-size: 0.9rem;\n  color: #666;\n  border-bottom: 3px solid transparent;\n  margin-bottom: -2px;\n  transition: color 0.15s, border-color 0.15s;\n  background: none;\n  border-top: none;\n  border-left: none;\n  border-right: none;\n}\n#pf-app .pf-tab.active {\n  color: #0f3460;\n  border-bottom-color: #e94560;\n}\n#pf-app .pf-tab-panel {\n  display: none;\n}\n#pf-app .pf-tab-panel.active {\n  display: block;\n}\n#pf-app .pf-related {\n  background: #f7f9fc;\n  border: 1px solid #dde3f0;\n  border-radius: 8px;\n  padding: 1rem 1.25rem;\n  margin-top: 2rem;\n  font-size: 0.9rem;\n}\n#pf-app .pf-related a {\n  color: #0f3460;\n  text-decoration: none;\n  font-weight: 600;\n}\n#pf-app .pf-related a:hover {\n  color: #e94560;\n  text-decoration: underline;\n}\n#pf-app .pf-related ul {\n  margin: 0.5rem 0 0;\n  padding-left: 1.25rem;\n}\n#pf-app .pf-related li {\n  margin-bottom: 0.35rem;\n}\n@media (max-width: 600px) {\n  #pf-app .pf-result-grid {\n    grid-template-columns: 1fr;\n  }\n  #pf-app .pf-result-label {\n    margin-top: 0.5rem;\n  }\n}\n\u003c/style\u003e\n\u003ch2\u003ePhone Number Formatter \u0026 Validator\u003c/h2\u003e\n\u003cdiv class=\"pf-tabs\"\u003e\n  \u003cbutton class=\"pf-tab active\" onclick=\"pfSwitchTab('single')\"\u003eSingle Number\u003c/button\u003e\n  \u003cbutton class=\"pf-tab\" onclick=\"pfSwitchTab('bulk')\"\u003eBulk Formatter\u003c/button\u003e\n  \u003cbutton class=\"pf-tab\" onclick=\"pfSwitchTab('ref')\"\u003eCountry Code Reference\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- SINGLE FORMATTER TAB --\u003e\n\u003cdiv id=\"pf-tab-single\" class=\"pf-tab-panel active\"\u003e\n  \u003cdiv class=\"pf-card\"\u003e\n    \u003cdiv class=\"pf-row\"\u003e\n      \u003cdiv\u003e\n        \u003clabel for=\"pf-input\"\u003ePhone Number\u003c/label\u003e\n        \u003cinput type=\"text\" id=\"pf-input\" placeholder=\"e.g. 03-1234-5678 or +1 555 123 4567\" oninput=\"pfFormat()\" /\u003e\n      \u003c/div\u003e\n      \u003cdiv\u003e\n        \u003clabel for=\"pf-country\"\u003eCountry\u003c/label\u003e\n        \u003cselect id=\"pf-country\" onchange=\"pfFormat()\"\u003e\n          \u003coption value=\"US\"\u003eUnited States (+1)\u003c/option\u003e\n          \u003coption value=\"CA\"\u003eCanada (+1)\u003c/option\u003e\n          \u003coption value=\"GB\"\u003eUnited Kingdom (+44)\u003c/option\u003e\n          \u003coption value=\"JP\" selected\u003eJapan (+81)\u003c/option\u003e\n          \u003coption value=\"DE\"\u003eGermany (+49)\u003c/option\u003e\n          \u003coption value=\"FR\"\u003eFrance (+33)\u003c/option\u003e\n          \u003coption value=\"AU\"\u003eAustralia (+61)\u003c/option\u003e\n          \u003coption value=\"KR\"\u003eSouth Korea (+82)\u003c/option\u003e\n          \u003coption value=\"CN\"\u003eChina (+86)\u003c/option\u003e\n          \u003coption value=\"IN\"\u003eIndia (+91)\u003c/option\u003e\n          \u003coption value=\"BR\"\u003eBrazil (+55)\u003c/option\u003e\n          \u003coption value=\"MX\"\u003eMexico (+52)\u003c/option\u003e\n          \u003coption value=\"IT\"\u003eItaly (+39)\u003c/option\u003e\n          \u003coption value=\"ES\"\u003eSpain (+34)\u003c/option\u003e\n          \u003coption value=\"NL\"\u003eNetherlands (+31)\u003c/option\u003e\n          \u003coption value=\"SG\"\u003eSingapore (+65)\u003c/option\u003e\n          \u003coption value=\"HK\"\u003eHong Kong (+852)\u003c/option\u003e\n          \u003coption value=\"TW\"\u003eTaiwan (+886)\u003c/option\u003e\n          \u003coption value=\"NZ\"\u003eNew Zealand (+64)\u003c/option\u003e\n          \u003coption value=\"ZA\"\u003eSouth Africa (+27)\u003c/option\u003e\n        \u003c/select\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv id=\"pf-validation-box\" class=\"pf-validation-box warn\" style=\"display:none;\"\u003e\u003c/div\u003e\n    \u003cdiv id=\"pf-result-grid\" class=\"pf-result-grid\" style=\"display:none;\"\u003e\n      \u003cspan class=\"pf-result-label\"\u003eInternational\u003c/span\u003e\n      \u003cspan class=\"pf-result-value\" id=\"res-intl\"\u003e\u003cspan id=\"val-intl\"\u003e\u003c/span\u003e\u003cbutton class=\"pf-copy-btn\" onclick=\"pfCopy('val-intl')\"\u003eCopy\u003c/button\u003e\u003c/span\u003e\n      \u003cspan class=\"pf-result-label\"\u003eNational\u003c/span\u003e\n      \u003cspan class=\"pf-result-value\" id=\"res-natl\"\u003e\u003cspan id=\"val-natl\"\u003e\u003c/span\u003e\u003cbutton class=\"pf-copy-btn\" onclick=\"pfCopy('val-natl')\"\u003eCopy\u003c/button\u003e\u003c/span\u003e\n      \u003cspan class=\"pf-result-label\"\u003eE.164\u003c/span\u003e\n      \u003cspan class=\"pf-result-value\" id=\"res-e164\"\u003e\u003cspan id=\"val-e164\"\u003e\u003c/span\u003e\u003cbutton class=\"pf-copy-btn\" onclick=\"pfCopy('val-e164')\"\u003eCopy\u003c/button\u003e\u003c/span\u003e\n      \u003cspan class=\"pf-result-label\"\u003eRFC 3966\u003c/span\u003e\n      \u003cspan class=\"pf-result-value\" id=\"res-rfc\"\u003e\u003cspan id=\"val-rfc\"\u003e\u003c/span\u003e\u003cbutton class=\"pf-copy-btn\" onclick=\"pfCopy('val-rfc')\"\u003eCopy\u003c/button\u003e\u003c/span\u003e\n      \u003cspan class=\"pf-result-label\"\u003eDigits Only\u003c/span\u003e\n      \u003cspan class=\"pf-result-value\" id=\"res-raw\"\u003e\u003cspan id=\"val-raw\"\u003e\u003c/span\u003e\u003cbutton class=\"pf-copy-btn\" onclick=\"pfCopy('val-raw')\"\u003eCopy\u003c/button\u003e\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- BULK FORMATTER TAB --\u003e\n\u003cdiv id=\"pf-tab-bulk\" class=\"pf-tab-panel\"\u003e\n  \u003cdiv class=\"pf-card\"\u003e\n    \u003clabel for=\"pf-bulk-country\"\u003eCountry for all numbers\u003c/label\u003e\n    \u003cselect id=\"pf-bulk-country\" style=\"margin-bottom:1rem;\"\u003e\n      \u003coption value=\"US\"\u003eUnited States (+1)\u003c/option\u003e\n      \u003coption value=\"CA\"\u003eCanada (+1)\u003c/option\u003e\n      \u003coption value=\"GB\"\u003eUnited Kingdom (+44)\u003c/option\u003e\n      \u003coption value=\"JP\" selected\u003eJapan (+81)\u003c/option\u003e\n      \u003coption value=\"DE\"\u003eGermany (+49)\u003c/option\u003e\n      \u003coption value=\"FR\"\u003eFrance (+33)\u003c/option\u003e\n      \u003coption value=\"AU\"\u003eAustralia (+61)\u003c/option\u003e\n      \u003coption value=\"KR\"\u003eSouth Korea (+82)\u003c/option\u003e\n      \u003coption value=\"CN\"\u003eChina (+86)\u003c/option\u003e\n      \u003coption value=\"IN\"\u003eIndia (+91)\u003c/option\u003e\n      \u003coption value=\"BR\"\u003eBrazil (+55)\u003c/option\u003e\n      \u003coption value=\"MX\"\u003eMexico (+52)\u003c/option\u003e\n      \u003coption value=\"IT\"\u003eItaly (+39)\u003c/option\u003e\n      \u003coption value=\"ES\"\u003eSpain (+34)\u003c/option\u003e\n      \u003coption value=\"NL\"\u003eNetherlands (+31)\u003c/option\u003e\n      \u003coption value=\"SG\"\u003eSingapore (+65)\u003c/option\u003e\n      \u003coption value=\"HK\"\u003eHong Kong (+852)\u003c/option\u003e\n      \u003coption value=\"TW\"\u003eTaiwan (+886)\u003c/option\u003e\n      \u003coption value=\"NZ\"\u003eNew Zealand (+64)\u003c/option\u003e\n      \u003coption value=\"ZA\"\u003eSouth Africa (+27)\u003c/option\u003e\n    \u003c/select\u003e\n    \u003clabel for=\"pf-bulk-input\"\u003ePaste phone numbers (one per line)\u003c/label\u003e\n    \u003ctextarea id=\"pf-bulk-input\" placeholder=\"03-1234-5678\u0026#10;090-9876-5432\u0026#10;0120-123-456\u0026#10;+81 3 1234 5678\"\u003e\u003c/textarea\u003e\n    \u003cdiv style=\"display:flex;gap:0.75rem;flex-wrap:wrap;\"\u003e\n      \u003cbutton class=\"pf-btn\" onclick=\"pfBulkFormat()\"\u003eFormat All\u003c/button\u003e\n      \u003cbutton class=\"pf-btn pf-btn-secondary\" onclick=\"pfBulkClear()\"\u003eClear\u003c/button\u003e\n      \u003cbutton class=\"pf-btn pf-btn-secondary\" onclick=\"pfBulkCopyAll()\"\u003eCopy Results\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv id=\"pf-bulk-output\" class=\"bulk-output\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- COUNTRY CODE REFERENCE TAB --\u003e\n\u003cdiv id=\"pf-tab-ref\" class=\"pf-tab-panel\"\u003e\n  \u003cdiv class=\"pf-card\"\u003e\n    \u003cp style=\"margin-top:0;color:#555;font-size:0.9rem;\"\u003eCountry dial codes, trunk prefixes, and typical number lengths for reference.\u003c/p\u003e","title":"Phone Number Formatter - Format \u0026 Validate Phone Numbers"},{"content":" 🖼️ Drop your photo here Supports JPEG, PNG, WebP, GIF — processed locally in your browser\nChoose Image Filtered \u0026lt;!-- Preset Filters --\u0026gt; \u0026lt;div class=\u0026quot;pf-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;pf-card-title\u0026quot;\u0026gt;Preset Filters\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pf-presets\u0026quot; id=\u0026quot;pf-presets\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Adjustments --\u0026gt; \u0026lt;div class=\u0026quot;pf-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;pf-card-title\u0026quot;\u0026gt;Adjustments\u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;pf-sliders\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Actions --\u0026gt; \u0026lt;div class=\u0026quot;pf-card\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;pf-card-title\u0026quot;\u0026gt;Actions\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pf-actions\u0026quot;\u0026gt; \u0026lt;label class=\u0026quot;pf-compare-toggle\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;pf-compare-chk\u0026quot;\u0026gt; Show Before / After \u0026lt;/label\u0026gt; \u0026lt;button class=\u0026quot;pf-btn pf-btn-primary\u0026quot; id=\u0026quot;pf-download-btn\u0026quot;\u0026gt;Download PNG\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;pf-btn pf-btn-secondary\u0026quot; id=\u0026quot;pf-new-btn\u0026quot;\u0026gt;Load New Image\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;pf-btn pf-btn-danger\u0026quot; id=\u0026quot;pf-reset-btn\u0026quot;\u0026gt;Reset Filters\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Related Tools Resize photos to exact dimensions → Image Resizer Pick and convert colors → Color Picker ","permalink":"https://productivity-works.com/tools/photo-filter/","summary":"\u003cdiv id=\"pf-app\"\u003e\n\u003cstyle\u003e\n#pf-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 0 0 48px 0;\n  color: #1a202c;\n}\n\n#pf-app * {\n  box-sizing: border-box;\n}\n\n/* Hero */\n.pf-hero {\n  background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 50%, #4c1d95 100%);\n  border-radius: 16px;\n  padding: 32px 28px;\n  margin-bottom: 24px;\n  color: #fff;\n}\n\n.pf-hero h2 {\n  margin: 0 0 6px 0;\n  font-size: 24px;\n  font-weight: 800;\n}\n\n.pf-hero p {\n  margin: 0;\n  font-size: 14px;\n  opacity: 0.88;\n  line-height: 1.6;\n}\n\n/* Drop Zone */\n.pf-dropzone {\n  border: 3px dashed #7c3aed;\n  border-radius: 14px;\n  padding: 48px 24px;\n  text-align: center;\n  background: #f5f3ff;\n  cursor: pointer;\n  transition: background 0.2s, border-color 0.2s;\n  margin-bottom: 24px;\n}\n\n.pf-dropzone:hover,\n.pf-dropzone.pf-drag-over {\n  background: #ede9fe;\n  border-color: #6d28d9;\n}\n\n.pf-dropzone-icon {\n  font-size: 48px;\n  margin-bottom: 12px;\n}\n\n.pf-dropzone h3 {\n  margin: 0 0 6px 0;\n  font-size: 17px;\n  font-weight: 700;\n  color: #4c1d95;\n}\n\n.pf-dropzone p {\n  margin: 0 0 16px 0;\n  font-size: 13px;\n  color: #6b7280;\n}\n\n.pf-btn-upload {\n  display: inline-block;\n  padding: 10px 24px;\n  background: #7c3aed;\n  color: #fff;\n  border-radius: 8px;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  border: none;\n  transition: background 0.2s;\n}\n\n.pf-btn-upload:hover {\n  background: #6d28d9;\n}\n\n/* Main Editor */\n.pf-editor {\n  display: none;\n  gap: 24px;\n}\n\n.pf-editor.pf-visible {\n  display: grid;\n  grid-template-columns: 1fr 320px;\n}\n\n@media (max-width: 700px) {\n  .pf-editor.pf-visible {\n    grid-template-columns: 1fr;\n  }\n}\n\n/* Canvas Area */\n.pf-canvas-wrap {\n  background: #0f172a;\n  border-radius: 14px;\n  padding: 16px;\n  display: flex;\n  flex-direction: column;\n  gap: 12px;\n  min-height: 300px;\n}\n\n.pf-canvas-label {\n  color: #94a3b8;\n  font-size: 11px;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  text-align: center;\n}\n\n.pf-canvas-container {\n  position: relative;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n\n#pf-canvas {\n  max-width: 100%;\n  max-height: 480px;\n  border-radius: 8px;\n  display: block;\n}\n\n.pf-comparison-wrap {\n  display: flex;\n  gap: 8px;\n}\n\n.pf-comparison-wrap .pf-canvas-container {\n  flex: 1;\n  flex-direction: column;\n  gap: 4px;\n}\n\n/* Controls Panel */\n.pf-panel {\n  display: flex;\n  flex-direction: column;\n  gap: 16px;\n}\n\n.pf-card {\n  background: #fff;\n  border: 1px solid #e5e7eb;\n  border-radius: 12px;\n  padding: 16px;\n}\n\n.pf-card-title {\n  font-size: 12px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  color: #7c3aed;\n  margin: 0 0 12px 0;\n}\n\n/* Preset Filters */\n.pf-presets {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 8px;\n}\n\n.pf-preset-btn {\n  position: relative;\n  border: 2px solid transparent;\n  border-radius: 8px;\n  padding: 0;\n  background: #f3f4f6;\n  cursor: pointer;\n  overflow: hidden;\n  transition: border-color 0.15s, transform 0.1s;\n  aspect-ratio: 1;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: flex-end;\n}\n\n.pf-preset-btn:hover {\n  transform: scale(1.04);\n}\n\n.pf-preset-btn.pf-active {\n  border-color: #7c3aed;\n}\n\n.pf-preset-swatch {\n  width: 100%;\n  height: 100%;\n  position: absolute;\n  top: 0;\n  left: 0;\n  border-radius: 6px;\n}\n\n.pf-preset-label {\n  position: relative;\n  z-index: 1;\n  font-size: 10px;\n  font-weight: 700;\n  color: #fff;\n  text-shadow: 0 1px 3px rgba(0,0,0,0.7);\n  padding: 4px 2px;\n  width: 100%;\n  text-align: center;\n  background: linear-gradient(transparent, rgba(0,0,0,0.55));\n}\n\n/* Sliders */\n.pf-slider-row {\n  margin-bottom: 10px;\n}\n\n.pf-slider-row:last-child {\n  margin-bottom: 0;\n}\n\n.pf-slider-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin-bottom: 4px;\n}\n\n.pf-slider-name {\n  font-size: 12px;\n  font-weight: 600;\n  color: #374151;\n}\n\n.pf-slider-val {\n  font-size: 12px;\n  color: #7c3aed;\n  font-weight: 700;\n  min-width: 36px;\n  text-align: right;\n}\n\n.pf-slider-row input[type=\"range\"] {\n  width: 100%;\n  height: 4px;\n  -webkit-appearance: none;\n  appearance: none;\n  background: #e5e7eb;\n  border-radius: 2px;\n  outline: none;\n  cursor: pointer;\n}\n\n.pf-slider-row input[type=\"range\"]::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  width: 16px;\n  height: 16px;\n  border-radius: 50%;\n  background: #7c3aed;\n  cursor: pointer;\n  box-shadow: 0 1px 4px rgba(124,58,237,0.4);\n}\n\n.pf-slider-row input[type=\"range\"]::-moz-range-thumb {\n  width: 16px;\n  height: 16px;\n  border-radius: 50%;\n  background: #7c3aed;\n  cursor: pointer;\n  border: none;\n}\n\n/* Action Buttons */\n.pf-actions {\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n}\n\n.pf-btn {\n  width: 100%;\n  padding: 11px 16px;\n  border: none;\n  border-radius: 9px;\n  font-size: 13px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: all 0.15s;\n  letter-spacing: 0.01em;\n}\n\n.pf-btn-primary {\n  background: #7c3aed;\n  color: #fff;\n}\n\n.pf-btn-primary:hover {\n  background: #6d28d9;\n}\n\n.pf-btn-secondary {\n  background: #f3f4f6;\n  color: #374151;\n}\n\n.pf-btn-secondary:hover {\n  background: #e5e7eb;\n}\n\n.pf-btn-danger {\n  background: #fef2f2;\n  color: #dc2626;\n}\n\n.pf-btn-danger:hover {\n  background: #fee2e2;\n}\n\n/* Toggle Compare */\n.pf-compare-toggle {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  font-size: 12px;\n  color: #6b7280;\n  cursor: pointer;\n  user-select: none;\n  padding: 6px 0;\n}\n\n.pf-compare-toggle input[type=\"checkbox\"] {\n  accent-color: #7c3aed;\n  width: 14px;\n  height: 14px;\n  cursor: pointer;\n}\n\n/* File info */\n.pf-file-info {\n  font-size: 11px;\n  color: #9ca3af;\n  text-align: center;\n  padding: 4px 0;\n}\n\n/* Related tools */\n.pf-related {\n  margin-top: 32px;\n  padding: 20px;\n  background: #f9fafb;\n  border-radius: 12px;\n  border: 1px solid #e5e7eb;\n}\n\n.pf-related h3 {\n  margin: 0 0 12px 0;\n  font-size: 14px;\n  font-weight: 700;\n  color: #374151;\n}\n\n.pf-related-links {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n}\n\n.pf-related-links a {\n  display: inline-block;\n  padding: 6px 14px;\n  background: #fff;\n  border: 1px solid #d1d5db;\n  border-radius: 20px;\n  font-size: 13px;\n  color: #7c3aed;\n  text-decoration: none;\n  transition: all 0.15s;\n  font-weight: 500;\n}\n\n.pf-related-links a:hover {\n  background: #f5f3ff;\n  border-color: #7c3aed;\n}\n\u003c/style\u003e\n\u003c!-- Drop Zone --\u003e\n\u003cdiv class=\"pf-dropzone\" id=\"pf-dropzone\"\u003e\n  \u003cdiv class=\"pf-dropzone-icon\"\u003e🖼️\u003c/div\u003e\n  \u003ch3\u003eDrop your photo here\u003c/h3\u003e\n  \u003cp\u003eSupports JPEG, PNG, WebP, GIF — processed locally in your browser\u003c/p\u003e","title":"Photo Filter \u0026 Image Effects"},{"content":" Pixel Art Editor Draw on a grid, choose colors, export PNG — fully offline, no sign-up.\n\u0026lt;!-- Tools --\u0026gt; \u0026lt;div class=\u0026quot;px-panel\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;px-panel-label\u0026quot;\u0026gt;Tool\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;px-tools\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;px-tool-btn active\u0026quot; id=\u0026quot;px-tool-pen\u0026quot; title=\u0026quot;Pen (P)\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;px-tool-icon\u0026quot;\u0026gt;\u0026amp;#9998;\u0026lt;/span\u0026gt;Pen \u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;px-tool-btn\u0026quot; id=\u0026quot;px-tool-eraser\u0026quot; title=\u0026quot;Eraser (E)\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;px-tool-icon\u0026quot;\u0026gt;\u0026amp;#9641;\u0026lt;/span\u0026gt;Eraser \u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;px-tool-btn\u0026quot; id=\u0026quot;px-tool-fill\u0026quot; title=\u0026quot;Fill (F)\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;px-tool-icon\u0026quot;\u0026gt;\u0026amp;#9724;\u0026lt;/span\u0026gt;Fill \u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;px-tool-btn\u0026quot; id=\u0026quot;px-tool-picker\u0026quot; title=\u0026quot;Eyedropper (I)\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;px-tool-icon\u0026quot;\u0026gt;\u0026amp;#128451;\u0026lt;/span\u0026gt;Pick \u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Color --\u0026gt; \u0026lt;div class=\u0026quot;px-panel\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;px-panel-label\u0026quot;\u0026gt;Color\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;px-palette-grid\u0026quot; id=\u0026quot;px-palette\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;px-color-row\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;px-current-color\u0026quot; id=\u0026quot;px-current-color-swatch\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;span class=\u0026quot;px-color-label\u0026quot; id=\u0026quot;px-current-color-hex\u0026quot;\u0026gt;#000000\u0026lt;/span\u0026gt; \u0026lt;label class=\u0026quot;px-custom-color\u0026quot; title=\u0026quot;Custom color\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;px-custom-color-input\u0026quot; value=\u0026quot;#000000\u0026quot;\u0026gt; \u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Grid size --\u0026gt; \u0026lt;div class=\u0026quot;px-panel\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;px-panel-label\u0026quot;\u0026gt;Grid Size\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;px-size-btns\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;px-size-btn\u0026quot; data-size=\u0026quot;8\u0026quot;\u0026gt;8x8\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;px-size-btn active\u0026quot; data-size=\u0026quot;16\u0026quot;\u0026gt;16x16\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;px-size-btn\u0026quot; data-size=\u0026quot;32\u0026quot;\u0026gt;32x32\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;px-size-btn\u0026quot; data-size=\u0026quot;64\u0026quot;\u0026gt;64x64\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- View --\u0026gt; \u0026lt;div class=\u0026quot;px-panel\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;px-panel-label\u0026quot;\u0026gt;View\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;px-zoom-row\u0026quot; style=\u0026quot;margin-bottom:8px;\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;px-zoom-btn\u0026quot; id=\u0026quot;px-zoom-out\u0026quot;\u0026gt;\u0026amp;#8722;\u0026lt;/button\u0026gt; \u0026lt;span class=\u0026quot;px-zoom-label\u0026quot; id=\u0026quot;px-zoom-label\u0026quot;\u0026gt;1x\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026quot;px-zoom-btn\u0026quot; id=\u0026quot;px-zoom-in\u0026quot;\u0026gt;\u0026amp;#43;\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;px-toggle-row\u0026quot;\u0026gt; \u0026lt;label class=\u0026quot;px-toggle\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;px-grid-toggle\u0026quot; checked\u0026gt; \u0026lt;span class=\u0026quot;px-toggle-slider\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/label\u0026gt; \u0026lt;span class=\u0026quot;px-toggle-label\u0026quot;\u0026gt;Show grid\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Actions --\u0026gt; \u0026lt;div class=\u0026quot;px-panel\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;px-panel-label\u0026quot;\u0026gt;Actions\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;px-actions\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;px-btn-row\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;px-btn\u0026quot; id=\u0026quot;px-undo\u0026quot; title=\u0026quot;Undo (Ctrl+Z)\u0026quot; disabled\u0026gt;\u0026amp;#8592; Undo\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;px-btn\u0026quot; id=\u0026quot;px-redo\u0026quot; title=\u0026quot;Redo (Ctrl+Y)\u0026quot; disabled\u0026gt;Redo \u0026amp;#8594;\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;px-btn danger\u0026quot; id=\u0026quot;px-clear\u0026quot;\u0026gt;Clear Canvas\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;px-btn success\u0026quot; id=\u0026quot;px-export\u0026quot;\u0026gt;\u0026amp;#8595; Export PNG\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; 16 \u0026times; 16 \u0026mdash; hover over canvas to begin drawing Keyboard Shortcuts\nKey Action P Pen tool E Eraser tool F Fill tool I Color picker (eyedropper) Ctrl + Z Undo Ctrl + Y Redo + / - Zoom in / out Tips\nRight-click (or right-drag) on the canvas to erase pixels quickly. Use Fill (F) to flood-fill a region with the current color. Exported PNG is scaled up to 512 px for crisp results — safe to use in projects. Switch to 64x64 grid for detailed sprites; 8x8 for retro icons. Generate ASCII art from text → ASCII Art Generator Pick colors from images → Image Color Picker Create placeholder images → Placeholder Image Generator ","permalink":"https://productivity-works.com/tools/pixel-art-editor/","summary":"\u003cdiv id=\"px-app\"\u003e\n\u003cstyle\u003e\n#px-app {\n  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;\n  background: #0f172a;\n  color: #e2e8f0;\n  padding: 16px;\n  border-radius: 12px;\n  user-select: none;\n  -webkit-user-select: none;\n}\n\n#px-app * {\n  box-sizing: border-box;\n}\n\n#px-app .px-header {\n  text-align: center;\n  margin-bottom: 14px;\n}\n\n#px-app .px-header h2 {\n  margin: 0 0 4px;\n  font-size: 1.3rem;\n  color: #f1f5f9;\n  font-weight: 700;\n}\n\n#px-app .px-header p {\n  margin: 0;\n  font-size: 0.8rem;\n  color: #94a3b8;\n}\n\n#px-app .px-layout {\n  display: flex;\n  gap: 14px;\n  align-items: flex-start;\n  flex-wrap: wrap;\n  justify-content: center;\n}\n\n/* ---- Sidebar ---- */\n#px-app .px-sidebar {\n  display: flex;\n  flex-direction: column;\n  gap: 12px;\n  min-width: 180px;\n  max-width: 200px;\n  flex-shrink: 0;\n}\n\n#px-app .px-panel {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 8px;\n  padding: 10px;\n}\n\n#px-app .px-panel-label {\n  font-size: 0.68rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #64748b;\n  margin-bottom: 8px;\n}\n\n/* ---- Tools ---- */\n#px-app .px-tools {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 6px;\n}\n\n#px-app .px-tool-btn {\n  background: #0f172a;\n  border: 1px solid #334155;\n  color: #cbd5e1;\n  border-radius: 6px;\n  padding: 7px 4px;\n  cursor: pointer;\n  font-size: 0.72rem;\n  font-weight: 600;\n  text-align: center;\n  transition: background 0.15s, border-color 0.15s, color 0.15s;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 3px;\n}\n\n#px-app .px-tool-btn:hover {\n  background: #1e3a5f;\n  border-color: #3b82f6;\n  color: #f1f5f9;\n}\n\n#px-app .px-tool-btn.active {\n  background: #1d4ed8;\n  border-color: #60a5fa;\n  color: #fff;\n}\n\n#px-app .px-tool-icon {\n  font-size: 1.1rem;\n  line-height: 1;\n}\n\n/* ---- Color palette ---- */\n#px-app .px-palette-grid {\n  display: grid;\n  grid-template-columns: repeat(4, 1fr);\n  gap: 4px;\n  margin-bottom: 8px;\n}\n\n#px-app .px-swatch {\n  width: 100%;\n  aspect-ratio: 1;\n  border-radius: 4px;\n  cursor: pointer;\n  border: 2px solid transparent;\n  transition: transform 0.1s, border-color 0.1s;\n}\n\n#px-app .px-swatch:hover {\n  transform: scale(1.15);\n}\n\n#px-app .px-swatch.selected {\n  border-color: #f1f5f9;\n  outline: 1px solid #1d4ed8;\n}\n\n#px-app .px-color-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin-top: 6px;\n}\n\n#px-app .px-current-color {\n  width: 32px;\n  height: 32px;\n  border-radius: 6px;\n  border: 2px solid #475569;\n  flex-shrink: 0;\n}\n\n#px-app .px-color-label {\n  font-size: 0.72rem;\n  color: #94a3b8;\n  flex: 1;\n}\n\n#px-app .px-custom-color {\n  width: 32px;\n  height: 32px;\n  padding: 0;\n  border: none;\n  border-radius: 6px;\n  cursor: pointer;\n  background: none;\n  overflow: hidden;\n  flex-shrink: 0;\n}\n\n#px-app .px-custom-color input[type=\"color\"] {\n  width: 40px;\n  height: 40px;\n  margin: -4px 0 0 -4px;\n  cursor: pointer;\n  border: none;\n  padding: 0;\n}\n\n/* ---- Grid size ---- */\n#px-app .px-size-btns {\n  display: flex;\n  gap: 4px;\n  flex-wrap: wrap;\n}\n\n#px-app .px-size-btn {\n  flex: 1;\n  min-width: 38px;\n  background: #0f172a;\n  border: 1px solid #334155;\n  color: #94a3b8;\n  border-radius: 5px;\n  padding: 5px 2px;\n  cursor: pointer;\n  font-size: 0.65rem;\n  font-weight: 600;\n  text-align: center;\n  transition: background 0.15s, border-color 0.15s, color 0.15s;\n}\n\n#px-app .px-size-btn:hover {\n  background: #1e3a5f;\n  border-color: #3b82f6;\n  color: #f1f5f9;\n}\n\n#px-app .px-size-btn.active {\n  background: #155e75;\n  border-color: #22d3ee;\n  color: #fff;\n}\n\n/* ---- Action buttons ---- */\n#px-app .px-actions {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n}\n\n#px-app .px-btn {\n  background: #0f172a;\n  border: 1px solid #334155;\n  color: #cbd5e1;\n  border-radius: 6px;\n  padding: 7px 10px;\n  cursor: pointer;\n  font-size: 0.75rem;\n  font-weight: 600;\n  text-align: center;\n  width: 100%;\n  transition: background 0.15s, border-color 0.15s, color 0.15s;\n}\n\n#px-app .px-btn:hover {\n  background: #1e3a5f;\n  border-color: #3b82f6;\n  color: #f1f5f9;\n}\n\n#px-app .px-btn:disabled {\n  opacity: 0.35;\n  cursor: default;\n}\n\n#px-app .px-btn.danger:hover {\n  background: #7f1d1d;\n  border-color: #f87171;\n  color: #fff;\n}\n\n#px-app .px-btn.success {\n  background: #14532d;\n  border-color: #4ade80;\n  color: #bbf7d0;\n}\n\n#px-app .px-btn.success:hover {\n  background: #166534;\n  border-color: #86efac;\n  color: #fff;\n}\n\n#px-app .px-btn-row {\n  display: flex;\n  gap: 6px;\n}\n\n#px-app .px-btn-row .px-btn {\n  flex: 1;\n}\n\n/* ---- Zoom ---- */\n#px-app .px-zoom-row {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n\n#px-app .px-zoom-btn {\n  background: #0f172a;\n  border: 1px solid #334155;\n  color: #cbd5e1;\n  border-radius: 5px;\n  width: 28px;\n  height: 28px;\n  cursor: pointer;\n  font-size: 1rem;\n  font-weight: 700;\n  line-height: 1;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  transition: background 0.15s, border-color 0.15s;\n  flex-shrink: 0;\n}\n\n#px-app .px-zoom-btn:hover {\n  background: #1e3a5f;\n  border-color: #3b82f6;\n}\n\n#px-app .px-zoom-label {\n  flex: 1;\n  text-align: center;\n  font-size: 0.75rem;\n  color: #94a3b8;\n  font-weight: 600;\n}\n\n/* ---- Toggle ---- */\n#px-app .px-toggle-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n\n#px-app .px-toggle {\n  position: relative;\n  width: 36px;\n  height: 20px;\n  flex-shrink: 0;\n}\n\n#px-app .px-toggle input {\n  opacity: 0;\n  width: 0;\n  height: 0;\n  position: absolute;\n}\n\n#px-app .px-toggle-slider {\n  position: absolute;\n  inset: 0;\n  background: #334155;\n  border-radius: 20px;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n\n#px-app .px-toggle-slider::before {\n  content: '';\n  position: absolute;\n  width: 14px;\n  height: 14px;\n  background: #94a3b8;\n  border-radius: 50%;\n  top: 3px;\n  left: 3px;\n  transition: transform 0.2s, background 0.2s;\n}\n\n#px-app .px-toggle input:checked + .px-toggle-slider {\n  background: #1d4ed8;\n}\n\n#px-app .px-toggle input:checked + .px-toggle-slider::before {\n  transform: translateX(16px);\n  background: #fff;\n}\n\n#px-app .px-toggle-label {\n  font-size: 0.75rem;\n  color: #94a3b8;\n}\n\n/* ---- Canvas area ---- */\n#px-app .px-canvas-wrap {\n  flex: 1;\n  min-width: 280px;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 10px;\n}\n\n#px-app .px-canvas-scroll {\n  overflow: auto;\n  max-width: 100%;\n  border-radius: 8px;\n  background: #1e293b;\n  border: 1px solid #334155;\n  padding: 8px;\n}\n\n#px-app #px-canvas {\n  display: block;\n  cursor: crosshair;\n  image-rendering: pixelated;\n  image-rendering: crisp-edges;\n}\n\n#px-app .px-status {\n  font-size: 0.7rem;\n  color: #475569;\n  text-align: center;\n}\n\n/* ---- Responsive ---- */\n@media (max-width: 600px) {\n  #px-app .px-layout {\n    flex-direction: column;\n    align-items: center;\n  }\n\n  #px-app .px-sidebar {\n    flex-direction: row;\n    flex-wrap: wrap;\n    max-width: 100%;\n    width: 100%;\n    min-width: unset;\n  }\n\n  #px-app .px-panel {\n    flex: 1;\n    min-width: 140px;\n  }\n}\n\u003c/style\u003e\n\u003cdiv class=\"px-header\"\u003e\n  \u003ch2\u003ePixel Art Editor\u003c/h2\u003e\n  \u003cp\u003eDraw on a grid, choose colors, export PNG — fully offline, no sign-up.\u003c/p\u003e","title":"Pixel Art Editor"},{"content":" Draw pixel art in your browser — no sign-up, no install. Choose your grid size, pick colors, and download your creation as a PNG.\nGrid: 16×16 32×32 64×64 Color: Pencil Eraser Fill Eyedropper Hide Grid Ready — click or drag to draw Undo Redo Clear Canvas Download PNG Related Tools Other Free Tools Pomodoro Timer — stay focused with timed work sessions Text Counter — count characters, words, and lines instantly Color Palette Generator — create harmonious color schemes Image Resizer — resize images in your browser without uploading ","permalink":"https://productivity-works.com/tools/pixel-art-maker/","summary":"\u003cdiv id=\"pam-app\"\u003e\n\u003cstyle\u003e\n#pam-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 16px;\n  color: #222;\n}\n#pam-app h1 {\n  font-size: 1.6rem;\n  margin-bottom: 4px;\n}\n#pam-app p.pam-desc {\n  color: #555;\n  margin-top: 0;\n  margin-bottom: 16px;\n}\n#pam-app .pam-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  align-items: center;\n  margin-bottom: 12px;\n  background: #f5f5f5;\n  border: 1px solid #ddd;\n  border-radius: 8px;\n  padding: 10px 12px;\n}\n#pam-app .pam-toolbar label {\n  font-size: 0.82rem;\n  color: #444;\n  margin-right: 2px;\n}\n#pam-app select, #pam-app button {\n  font-size: 0.85rem;\n  border: 1px solid #ccc;\n  border-radius: 5px;\n  padding: 4px 9px;\n  background: #fff;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n#pam-app button:hover { background: #e8e8e8; }\n#pam-app button.pam-active {\n  background: #3b82f6;\n  color: #fff;\n  border-color: #2563eb;\n}\n#pam-app .pam-color-wrap {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n#pam-app input[type=color] {\n  width: 34px;\n  height: 30px;\n  border: 1px solid #ccc;\n  border-radius: 5px;\n  padding: 1px;\n  cursor: pointer;\n  background: none;\n}\n#pam-app .pam-recent-colors {\n  display: flex;\n  gap: 4px;\n  align-items: center;\n}\n#pam-app .pam-recent-swatch {\n  width: 20px;\n  height: 20px;\n  border-radius: 3px;\n  border: 1px solid #aaa;\n  cursor: pointer;\n  flex-shrink: 0;\n  transition: transform 0.1s;\n}\n#pam-app .pam-recent-swatch:hover { transform: scale(1.2); }\n#pam-app .pam-canvas-wrap {\n  display: flex;\n  justify-content: center;\n  margin-bottom: 12px;\n  overflow: auto;\n}\n#pam-app #pam-canvas {\n  display: block;\n  cursor: crosshair;\n  border: 2px solid #555;\n  image-rendering: pixelated;\n  image-rendering: crisp-edges;\n}\n#pam-app .pam-status {\n  font-size: 0.8rem;\n  color: #666;\n  text-align: center;\n  margin-bottom: 14px;\n  min-height: 18px;\n}\n#pam-app .pam-actions {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 20px;\n}\n#pam-app .pam-actions button {\n  padding: 6px 14px;\n}\n#pam-app .pam-related {\n  border-top: 1px solid #e0e0e0;\n  padding-top: 14px;\n  margin-top: 8px;\n}\n#pam-app .pam-related h3 {\n  font-size: 1rem;\n  margin-bottom: 8px;\n  color: #333;\n}\n#pam-app .pam-related ul {\n  list-style: disc;\n  padding-left: 20px;\n  font-size: 0.9rem;\n  line-height: 1.9;\n}\n#pam-app .pam-sep {\n  width: 1px;\n  height: 24px;\n  background: #ccc;\n  margin: 0 2px;\n}\n\u003c/style\u003e\n\u003cp class=\"pam-desc\"\u003eDraw pixel art in your browser — no sign-up, no install. Choose your grid size, pick colors, and download your creation as a PNG.\u003c/p\u003e","title":"Pixel Art Maker - Draw Pixel Art Online"},{"content":"Measure distances and positions directly on your screen. Drag the ruler to set a measurement, track your cursor\u0026rsquo;s exact pixel coordinates, detect your screen\u0026rsquo;s DPI, and customize the ruler\u0026rsquo;s color and length — all without installing anything.\nOrientation: Horizontal Vertical Both Length: 500 px Color: Units: px cm in DPI: detecting… Reset x: 0 \u0026nbsp; y: 0 Measurement — pixels Centimeters — cm Inches — in Screen DPI — dots per inch How to use: Hover to track cursor position. Click and drag inside the ruler area to measure a distance. The ruler snaps to the selected orientation. Adjust length, color, and unit above. Check screen resolution → Screen Resolution ","permalink":"https://productivity-works.com/tools/pixel-ruler/","summary":"\u003cp\u003eMeasure distances and positions directly on your screen. Drag the ruler to set a measurement, track your cursor\u0026rsquo;s exact pixel coordinates, detect your screen\u0026rsquo;s DPI, and customize the ruler\u0026rsquo;s color and length — all without installing anything.\u003c/p\u003e\n\u003cdiv id=\"pr-app\"\u003e\n\u003cstyle\u003e\n/* ── Reset ──────────────────────────────────────────────────── */\n#pr-app *, #pr-app *::before, #pr-app *::after {\n  box-sizing: border-box; margin: 0; padding: 0;\n}\n#pr-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  font-size: 14px;\n  color: #1a1a2e;\n  line-height: 1.5;\n  user-select: none;\n}\n\n/* ── Controls bar ───────────────────────────────────────────── */\n#pr-app .pr-controls {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 12px;\n  align-items: center;\n  background: #f8f9fc;\n  border: 1px solid #e2e6f0;\n  border-radius: 12px;\n  padding: 14px 16px;\n  margin-bottom: 16px;\n}\n#pr-app .pr-ctrl-group {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  font-size: 13px;\n  color: #374151;\n}\n#pr-app .pr-ctrl-group label {\n  font-weight: 600;\n  white-space: nowrap;\n}\n#pr-app .pr-ctrl-group input[type=\"range\"] {\n  width: 100px;\n  accent-color: #6366f1;\n}\n#pr-app .pr-ctrl-group input[type=\"color\"] {\n  width: 34px;\n  height: 30px;\n  border: 1.5px solid #d1d5db;\n  border-radius: 6px;\n  cursor: pointer;\n  padding: 2px;\n  background: #fff;\n}\n#pr-app .pr-ctrl-group select {\n  border: 1px solid #d1d5db;\n  border-radius: 6px;\n  padding: 5px 8px;\n  font-size: 13px;\n  background: #fff;\n  cursor: pointer;\n  outline: none;\n}\n#pr-app .pr-ctrl-group select:focus { border-color: #6366f1; }\n#pr-app .pr-sep {\n  width: 1px;\n  height: 28px;\n  background: #e2e6f0;\n}\n\n/* ── DPI info ───────────────────────────────────────────────── */\n#pr-app .pr-dpi-badge {\n  font-size: 12px;\n  background: #eef2ff;\n  color: #4338ca;\n  padding: 3px 10px;\n  border-radius: 20px;\n  font-weight: 600;\n  border: 1px solid #c7d2fe;\n}\n\n/* ── Canvas area ────────────────────────────────────────────── */\n#pr-app .pr-canvas-wrap {\n  position: relative;\n  background: #fff;\n  border: 1px solid #e2e6f0;\n  border-radius: 12px;\n  overflow: hidden;\n  height: 380px;\n  cursor: crosshair;\n}\n\n/* ── Coordinate display ─────────────────────────────────────── */\n#pr-app .pr-coords {\n  position: absolute;\n  top: 10px;\n  right: 14px;\n  background: rgba(15,23,42,.82);\n  color: #f8fafc;\n  font-size: 12px;\n  font-family: monospace;\n  padding: 6px 12px;\n  border-radius: 6px;\n  pointer-events: none;\n  z-index: 10;\n  white-space: nowrap;\n}\n\n/* ── Measurement label ──────────────────────────────────────── */\n#pr-app .pr-measure-label {\n  position: absolute;\n  background: rgba(15,23,42,.82);\n  color: #f8fafc;\n  font-size: 12px;\n  font-family: monospace;\n  padding: 4px 10px;\n  border-radius: 6px;\n  pointer-events: none;\n  z-index: 10;\n  white-space: nowrap;\n}\n\n/* ── Canvas ─────────────────────────────────────────────────── */\n#pr-app canvas {\n  display: block;\n  width: 100%;\n  height: 100%;\n}\n\n/* ── Info row ───────────────────────────────────────────────── */\n#pr-app .pr-info-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 12px;\n  margin-top: 14px;\n}\n#pr-app .pr-info-card {\n  flex: 1;\n  min-width: 140px;\n  background: #f8f9fc;\n  border: 1px solid #e2e6f0;\n  border-radius: 10px;\n  padding: 14px 16px;\n}\n#pr-app .pr-info-card .pr-ic-label {\n  font-size: 11px;\n  color: #6b7280;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: .07em;\n  margin-bottom: 4px;\n}\n#pr-app .pr-info-card .pr-ic-val {\n  font-size: 20px;\n  font-weight: 800;\n  color: #1a1a2e;\n  font-family: monospace;\n}\n#pr-app .pr-info-card .pr-ic-sub {\n  font-size: 11px;\n  color: #6b7280;\n  margin-top: 2px;\n}\n\n/* ── Instructions ───────────────────────────────────────────── */\n#pr-app .pr-hint {\n  font-size: 12px;\n  color: #6b7280;\n  margin-top: 10px;\n  padding: 10px 14px;\n  background: #f8f9fc;\n  border-radius: 8px;\n  border-left: 3px solid #6366f1;\n}\n\n/* ── Buttons ────────────────────────────────────────────────── */\n#pr-app .pr-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  padding: 7px 14px;\n  border-radius: 7px;\n  border: 1.5px solid #d1d5db;\n  background: #fff;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  color: #374151;\n  transition: background .15s;\n}\n#pr-app .pr-btn:hover { background: #f3f4f6; }\n#pr-app .pr-btn-primary {\n  background: #6366f1;\n  color: #fff;\n  border-color: #6366f1;\n}\n#pr-app .pr-btn-primary:hover { background: #4f46e5; }\n\u003c/style\u003e\n\u003c!-- Controls --\u003e\n\u003cdiv class=\"pr-controls\"\u003e\n  \u003cdiv class=\"pr-ctrl-group\"\u003e\n    \u003clabel\u003eOrientation:\u003c/label\u003e\n    \u003cselect id=\"pr-orient\"\u003e\n      \u003coption value=\"h\"\u003eHorizontal\u003c/option\u003e\n      \u003coption value=\"v\"\u003eVertical\u003c/option\u003e\n      \u003coption value=\"both\"\u003eBoth\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"pr-sep\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"pr-ctrl-group\"\u003e\n    \u003clabel\u003eLength:\u003c/label\u003e\n    \u003cinput type=\"range\" id=\"pr-length\" min=\"100\" max=\"900\" value=\"500\"\u003e\n    \u003cspan id=\"pr-length-lbl\"\u003e500 px\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"pr-sep\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"pr-ctrl-group\"\u003e\n    \u003clabel\u003eColor:\u003c/label\u003e\n    \u003cinput type=\"color\" id=\"pr-color\" value=\"#6366f1\"\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"pr-sep\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"pr-ctrl-group\"\u003e\n    \u003clabel\u003eUnits:\u003c/label\u003e\n    \u003cselect id=\"pr-unit\"\u003e\n      \u003coption value=\"px\"\u003epx\u003c/option\u003e\n      \u003coption value=\"cm\"\u003ecm\u003c/option\u003e\n      \u003coption value=\"in\"\u003ein\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"pr-sep\"\u003e\u003c/div\u003e\n  \u003cspan class=\"pr-dpi-badge\" id=\"pr-dpi-badge\"\u003eDPI: detecting…\u003c/span\u003e\n  \u003cbutton class=\"pr-btn pr-btn-primary\" id=\"pr-reset-btn\"\u003eReset\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Canvas area --\u003e\n\u003cdiv class=\"pr-canvas-wrap\" id=\"pr-canvas-wrap\"\u003e\n  \u003ccanvas id=\"pr-canvas\"\u003e\u003c/canvas\u003e\n  \u003cdiv class=\"pr-coords\" id=\"pr-coords\"\u003ex: 0 \u0026nbsp; y: 0\u003c/div\u003e\n  \u003cdiv class=\"pr-measure-label\" id=\"pr-meas-label\" style=\"display:none;\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Info cards --\u003e\n\u003cdiv class=\"pr-info-row\"\u003e\n  \u003cdiv class=\"pr-info-card\"\u003e\n    \u003cdiv class=\"pr-ic-label\"\u003eMeasurement\u003c/div\u003e\n    \u003cdiv class=\"pr-ic-val\" id=\"pr-val-px\"\u003e—\u003c/div\u003e\n    \u003cdiv class=\"pr-ic-sub\"\u003epixels\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"pr-info-card\"\u003e\n    \u003cdiv class=\"pr-ic-label\"\u003eCentimeters\u003c/div\u003e\n    \u003cdiv class=\"pr-ic-val\" id=\"pr-val-cm\"\u003e—\u003c/div\u003e\n    \u003cdiv class=\"pr-ic-sub\"\u003ecm\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"pr-info-card\"\u003e\n    \u003cdiv class=\"pr-ic-label\"\u003eInches\u003c/div\u003e\n    \u003cdiv class=\"pr-ic-val\" id=\"pr-val-in\"\u003e—\u003c/div\u003e\n    \u003cdiv class=\"pr-ic-sub\"\u003ein\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"pr-info-card\"\u003e\n    \u003cdiv class=\"pr-ic-label\"\u003eScreen DPI\u003c/div\u003e\n    \u003cdiv class=\"pr-ic-val\" id=\"pr-val-dpi\"\u003e—\u003c/div\u003e\n    \u003cdiv class=\"pr-ic-sub\"\u003edots per inch\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cp class=\"pr-hint\"\u003e\n  \u003cstrong\u003eHow to use:\u003c/strong\u003e Hover to track cursor position. Click and drag inside the ruler area to measure a distance. The ruler snaps to the selected orientation. Adjust length, color, and unit above.\n\u003c/p\u003e","title":"Pixel Ruler / Screen Measurement Tool — Free Online"},{"content":"Generate placeholder images instantly in your browser. Set width, height, background color, text color, and custom label — then download as PNG or JPEG. No uploads, no sign-up, fully client-side.\nPreset Sizes\nWeb Banner (728×90) OG / Social (1200×630) Instagram Square (1080×1080) Mobile (375×667) Settings\nWidth (px) Height (px) Background Color Text Color Label Text (leave blank to show dimensions) Font Size: Auto px PNG JPEG Preview Download Click \"Preview\" to generate.\nRelated Tools Resize real images → Image Resizer Generate favicons → Favicon Generator Build color palettes → Color Palette Generator ","permalink":"https://productivity-works.com/tools/placeholder-image/","summary":"\u003cp\u003eGenerate placeholder images instantly in your browser. Set width, height, background color, text color, and custom label — then download as PNG or JPEG. No uploads, no sign-up, fully client-side.\u003c/p\u003e\n\u003cdiv id=\"ph-app\"\u003e\n\u003cstyle\u003e\n#ph-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  color: #1e293b;\n  max-width: 780px;\n  margin: 0 auto;\n}\n#ph-app .ph-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 12px;\n  margin-bottom: 16px;\n}\n#ph-app .ph-field {\n  display: flex;\n  flex-direction: column;\n  gap: 4px;\n}\n#ph-app .ph-field label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #475569;\n}\n#ph-app .ph-field input[type=\"number\"],\n#ph-app .ph-field input[type=\"text\"],\n#ph-app .ph-field select {\n  padding: 8px 10px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  font-size: 14px;\n  color: #1e293b;\n  background: #f8fafc;\n  outline: none;\n  transition: border-color 0.15s;\n  width: 100%;\n  box-sizing: border-box;\n}\n#ph-app .ph-field input:focus,\n#ph-app .ph-field select:focus {\n  border-color: #64748b;\n  background: #fff;\n}\n#ph-app .ph-color-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n#ph-app .ph-color-row input[type=\"color\"] {\n  width: 42px;\n  height: 36px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  padding: 2px 3px;\n  background: #f8fafc;\n  cursor: pointer;\n}\n#ph-app .ph-color-row input[type=\"text\"] {\n  flex: 1;\n}\n#ph-app .ph-presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 16px;\n}\n#ph-app .ph-presets button {\n  padding: 6px 13px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 20px;\n  background: #f1f5f9;\n  font-size: 12px;\n  font-weight: 600;\n  color: #475569;\n  cursor: pointer;\n  transition: background 0.15s, border-color 0.15s, color 0.15s;\n}\n#ph-app .ph-presets button:hover {\n  background: #64748b;\n  color: #fff;\n  border-color: #64748b;\n}\n#ph-app .ph-font-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-bottom: 16px;\n}\n#ph-app .ph-font-row label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #475569;\n  white-space: nowrap;\n}\n#ph-app .ph-font-row input[type=\"checkbox\"] {\n  width: 16px;\n  height: 16px;\n  cursor: pointer;\n}\n#ph-app .ph-font-row input[type=\"number\"] {\n  width: 80px;\n  padding: 7px 9px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  font-size: 14px;\n  color: #1e293b;\n  background: #f8fafc;\n  outline: none;\n}\n#ph-app .ph-actions {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  margin-bottom: 20px;\n  flex-wrap: wrap;\n}\n#ph-app .ph-actions select {\n  padding: 9px 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  font-size: 14px;\n  font-weight: 600;\n  color: #1e293b;\n  background: #f8fafc;\n  outline: none;\n  cursor: pointer;\n}\n#ph-app .ph-btn-generate {\n  padding: 9px 22px;\n  background: #64748b;\n  color: #fff;\n  border: none;\n  border-radius: 7px;\n  font-size: 14px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n#ph-app .ph-btn-generate:hover {\n  background: #475569;\n}\n#ph-app .ph-btn-download {\n  padding: 9px 22px;\n  background: #0f172a;\n  color: #fff;\n  border: none;\n  border-radius: 7px;\n  font-size: 14px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n#ph-app .ph-btn-download:hover {\n  background: #1e293b;\n}\n#ph-app .ph-preview-wrap {\n  border: 2px dashed #cbd5e1;\n  border-radius: 10px;\n  padding: 16px;\n  background: #f8fafc;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 10px;\n  min-height: 160px;\n  justify-content: center;\n}\n#ph-app canvas#ph-canvas {\n  max-width: 100%;\n  height: auto;\n  border-radius: 6px;\n  display: block;\n  box-shadow: 0 2px 8px rgba(0,0,0,0.10);\n}\n#ph-app .ph-info {\n  font-size: 12px;\n  color: #94a3b8;\n}\n#ph-app .ph-section-title {\n  font-size: 13px;\n  font-weight: 700;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin: 0 0 8px;\n}\n#ph-app .ph-divider {\n  border: none;\n  border-top: 1.5px solid #e2e8f0;\n  margin: 16px 0;\n}\n@media (max-width: 520px) {\n  #ph-app .ph-grid {\n    grid-template-columns: 1fr;\n  }\n}\n\u003c/style\u003e\n\u003cp class=\"ph-section-title\"\u003ePreset Sizes\u003c/p\u003e","title":"Placeholder Image Generator"},{"content":" Settings \u0026lt;div class=\u0026quot;pg-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Dimensions (px)\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;pg-size-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;pg-width\u0026quot; value=\u0026quot;300\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;4000\u0026quot; /\u0026gt; \u0026lt;span class=\u0026quot;pg-size-sep\u0026quot;\u0026gt;×\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;pg-height\u0026quot; value=\u0026quot;250\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;4000\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pg-field\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;pg-presets-label\u0026quot;\u0026gt;Presets\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pg-presets-group\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;pg-presets-group-title\u0026quot;\u0026gt;Common\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pg-preset-btns\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;150\u0026quot; data-h=\u0026quot;150\u0026quot;\u0026gt;150×150\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;300\u0026quot; data-h=\u0026quot;250\u0026quot;\u0026gt;300×250\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;400\u0026quot; data-h=\u0026quot;300\u0026quot;\u0026gt;400×300\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;800\u0026quot; data-h=\u0026quot;600\u0026quot;\u0026gt;800×600\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;1200\u0026quot; data-h=\u0026quot;630\u0026quot;\u0026gt;1200×630\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;1920\u0026quot; data-h=\u0026quot;1080\u0026quot;\u0026gt;1920×1080\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pg-presets-group\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;pg-presets-group-title\u0026quot;\u0026gt;Social Media\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pg-preset-btns\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;820\u0026quot; data-h=\u0026quot;312\u0026quot;\u0026gt;FB Cover\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;1500\u0026quot; data-h=\u0026quot;500\u0026quot;\u0026gt;Twitter Header\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;1080\u0026quot; data-h=\u0026quot;1080\u0026quot;\u0026gt;Instagram Post\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;1080\u0026quot; data-h=\u0026quot;1920\u0026quot;\u0026gt;Instagram Story\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;1200\u0026quot; data-h=\u0026quot;627\u0026quot;\u0026gt;LinkedIn Post\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;1280\u0026quot; data-h=\u0026quot;720\u0026quot;\u0026gt;YouTube Thumb\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pg-presets-group\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;pg-presets-group-title\u0026quot;\u0026gt;Ad Banners\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pg-preset-btns\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;728\u0026quot; data-h=\u0026quot;90\u0026quot;\u0026gt;Leaderboard\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;300\u0026quot; data-h=\u0026quot;600\u0026quot;\u0026gt;Half Page\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;160\u0026quot; data-h=\u0026quot;600\u0026quot;\u0026gt;Skyscraper\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;320\u0026quot; data-h=\u0026quot;50\u0026quot;\u0026gt;Mobile Banner\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;468\u0026quot; data-h=\u0026quot;60\u0026quot;\u0026gt;Full Banner\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pg-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Background Color\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;pg-color-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;pg-bg-picker\u0026quot; value=\u0026quot;#cccccc\u0026quot; /\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;pg-bg-hex\u0026quot; value=\u0026quot;#cccccc\u0026quot; maxlength=\u0026quot;7\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pg-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Text Color\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;pg-color-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;pg-text-picker\u0026quot; value=\u0026quot;#666666\u0026quot; /\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;pg-text-hex\u0026quot; value=\u0026quot;#666666\u0026quot; maxlength=\u0026quot;7\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pg-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Custom Text (leave blank for auto)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;pg-custom-text\u0026quot; placeholder=\u0026quot;e.g. Logo Here\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;pg-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Font Size (px)\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;pg-font-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;pg-font-size\u0026quot; value=\u0026quot;0\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;500\u0026quot; /\u0026gt; \u0026lt;span\u0026gt;0 = auto\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Preview 300 × 250 px Download PNG Copy Data URL Copied! Related Free Tools Color Contrast Checker Favicon Generator Base64 Image Encoder Aspect Ratio Calculator CSS Gradient Generator ","permalink":"https://productivity-works.com/tools/placeholder-generator/","summary":"\u003cdiv id=\"pg-app\"\u003e\n\u003cstyle\u003e\n#pg-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#pg-app h2 {\n  font-size: 1.3rem;\n  font-weight: 700;\n  margin: 0 0 1rem 0;\n  color: #1a1a2e;\n}\n#pg-app .pg-layout {\n  display: grid;\n  grid-template-columns: 340px 1fr;\n  gap: 1.5rem;\n  align-items: start;\n}\n@media (max-width: 680px) {\n  #pg-app .pg-layout {\n    grid-template-columns: 1fr;\n  }\n}\n#pg-app .pg-panel {\n  background: #f8f9fc;\n  border: 1px solid #e2e5f0;\n  border-radius: 10px;\n  padding: 1.25rem;\n}\n#pg-app .pg-field {\n  margin-bottom: 1rem;\n}\n#pg-app .pg-field label {\n  display: block;\n  font-size: 0.78rem;\n  font-weight: 600;\n  color: #555;\n  margin-bottom: 0.35rem;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#pg-app .pg-field input[type=\"number\"],\n#pg-app .pg-field input[type=\"text\"] {\n  width: 100%;\n  padding: 0.5rem 0.7rem;\n  border: 1px solid #d0d5e8;\n  border-radius: 6px;\n  font-size: 0.95rem;\n  background: #fff;\n  box-sizing: border-box;\n  transition: border-color 0.2s;\n}\n#pg-app .pg-field input[type=\"number\"]:focus,\n#pg-app .pg-field input[type=\"text\"]:focus {\n  outline: none;\n  border-color: #4f6ef7;\n}\n#pg-app .pg-size-row {\n  display: flex;\n  gap: 0.5rem;\n  align-items: center;\n}\n#pg-app .pg-size-row input {\n  flex: 1;\n}\n#pg-app .pg-size-sep {\n  font-weight: 700;\n  color: #888;\n  font-size: 1.1rem;\n}\n#pg-app .pg-color-row {\n  display: flex;\n  gap: 0.6rem;\n  align-items: center;\n}\n#pg-app .pg-color-row input[type=\"color\"] {\n  width: 42px;\n  height: 38px;\n  border: 1px solid #d0d5e8;\n  border-radius: 6px;\n  padding: 2px;\n  cursor: pointer;\n  background: #fff;\n  flex-shrink: 0;\n}\n#pg-app .pg-color-row input[type=\"text\"] {\n  flex: 1;\n  font-family: monospace;\n}\n#pg-app .pg-presets-label {\n  font-size: 0.78rem;\n  font-weight: 600;\n  color: #555;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  margin-bottom: 0.4rem;\n}\n#pg-app .pg-presets-group {\n  margin-bottom: 0.9rem;\n}\n#pg-app .pg-presets-group-title {\n  font-size: 0.72rem;\n  font-weight: 700;\n  color: #888;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 0.3rem;\n}\n#pg-app .pg-preset-btns {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.35rem;\n}\n#pg-app .pg-preset-btn {\n  background: #fff;\n  border: 1px solid #d0d5e8;\n  border-radius: 5px;\n  padding: 0.28rem 0.6rem;\n  font-size: 0.78rem;\n  cursor: pointer;\n  color: #333;\n  transition: background 0.15s, border-color 0.15s;\n  white-space: nowrap;\n}\n#pg-app .pg-preset-btn:hover {\n  background: #4f6ef7;\n  border-color: #4f6ef7;\n  color: #fff;\n}\n#pg-app .pg-font-row {\n  display: flex;\n  gap: 0.5rem;\n  align-items: center;\n}\n#pg-app .pg-font-row input[type=\"number\"] {\n  width: 80px;\n}\n#pg-app .pg-font-row span {\n  font-size: 0.85rem;\n  color: #666;\n}\n#pg-app .pg-preview-area {\n  background: #fff;\n  border: 1px solid #e2e5f0;\n  border-radius: 10px;\n  padding: 1.25rem;\n}\n#pg-app .pg-canvas-wrap {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  min-height: 200px;\n  background: repeating-conic-gradient(#e8eaf0 0% 25%, #f5f6fa 0% 50%) 0 0 / 20px 20px;\n  border-radius: 6px;\n  margin-bottom: 1rem;\n  overflow: hidden;\n}\n#pg-app #pg-canvas {\n  display: block;\n  max-width: 100%;\n  max-height: 400px;\n  border-radius: 3px;\n  box-shadow: 0 2px 12px rgba(0,0,0,0.12);\n  object-fit: contain;\n}\n#pg-app .pg-info {\n  font-size: 0.82rem;\n  color: #666;\n  margin-bottom: 1rem;\n  text-align: center;\n}\n#pg-app .pg-btn-row {\n  display: flex;\n  gap: 0.7rem;\n  flex-wrap: wrap;\n}\n#pg-app .pg-btn {\n  flex: 1;\n  min-width: 120px;\n  padding: 0.65rem 1rem;\n  border: none;\n  border-radius: 7px;\n  font-size: 0.92rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: opacity 0.15s, transform 0.1s;\n}\n#pg-app .pg-btn:hover {\n  opacity: 0.88;\n  transform: translateY(-1px);\n}\n#pg-app .pg-btn:active {\n  transform: translateY(0);\n}\n#pg-app .pg-btn-primary {\n  background: #4f6ef7;\n  color: #fff;\n}\n#pg-app .pg-btn-secondary {\n  background: #1a1a2e;\n  color: #fff;\n}\n#pg-app .pg-url-box {\n  margin-top: 1rem;\n  background: #f0f2fa;\n  border: 1px solid #d0d5e8;\n  border-radius: 6px;\n  padding: 0.6rem 0.8rem;\n  font-size: 0.72rem;\n  font-family: monospace;\n  word-break: break-all;\n  color: #333;\n  max-height: 80px;\n  overflow-y: auto;\n  display: none;\n}\n#pg-app .pg-toast {\n  position: fixed;\n  bottom: 2rem;\n  left: 50%;\n  transform: translateX(-50%) translateY(20px);\n  background: #1a1a2e;\n  color: #fff;\n  padding: 0.6rem 1.4rem;\n  border-radius: 30px;\n  font-size: 0.88rem;\n  font-weight: 600;\n  opacity: 0;\n  pointer-events: none;\n  transition: opacity 0.25s, transform 0.25s;\n  z-index: 9999;\n}\n#pg-app .pg-toast.show {\n  opacity: 1;\n  transform: translateX(-50%) translateY(0);\n}\n#pg-app .pg-related {\n  margin-top: 2rem;\n  padding-top: 1.25rem;\n  border-top: 1px solid #e2e5f0;\n}\n#pg-app .pg-related h3 {\n  font-size: 1rem;\n  font-weight: 700;\n  margin: 0 0 0.7rem 0;\n}\n#pg-app .pg-related ul {\n  margin: 0;\n  padding: 0;\n  list-style: none;\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem;\n}\n#pg-app .pg-related ul li a {\n  display: inline-block;\n  padding: 0.35rem 0.85rem;\n  background: #f0f2fa;\n  border: 1px solid #d0d5e8;\n  border-radius: 20px;\n  font-size: 0.84rem;\n  color: #4f6ef7;\n  text-decoration: none;\n  transition: background 0.15s;\n}\n#pg-app .pg-related ul li a:hover {\n  background: #4f6ef7;\n  color: #fff;\n  border-color: #4f6ef7;\n}\n\u003c/style\u003e\n\u003cdiv class=\"pg-layout\"\u003e\n  \u003cdiv class=\"pg-panel\"\u003e\n    \u003ch2\u003eSettings\u003c/h2\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;div class=\u0026quot;pg-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Dimensions (px)\u0026lt;/label\u0026gt;\n  \u0026lt;div class=\u0026quot;pg-size-row\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;pg-width\u0026quot; value=\u0026quot;300\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;4000\u0026quot; /\u0026gt;\n    \u0026lt;span class=\u0026quot;pg-size-sep\u0026quot;\u0026gt;×\u0026lt;/span\u0026gt;\n    \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;pg-height\u0026quot; value=\u0026quot;250\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;4000\u0026quot; /\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;pg-field\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;pg-presets-label\u0026quot;\u0026gt;Presets\u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;pg-presets-group\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;pg-presets-group-title\u0026quot;\u0026gt;Common\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;pg-preset-btns\u0026quot;\u0026gt;\n      \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;150\u0026quot; data-h=\u0026quot;150\u0026quot;\u0026gt;150×150\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;300\u0026quot; data-h=\u0026quot;250\u0026quot;\u0026gt;300×250\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;400\u0026quot; data-h=\u0026quot;300\u0026quot;\u0026gt;400×300\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;800\u0026quot; data-h=\u0026quot;600\u0026quot;\u0026gt;800×600\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;1200\u0026quot; data-h=\u0026quot;630\u0026quot;\u0026gt;1200×630\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;1920\u0026quot; data-h=\u0026quot;1080\u0026quot;\u0026gt;1920×1080\u0026lt;/button\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;pg-presets-group\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;pg-presets-group-title\u0026quot;\u0026gt;Social Media\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;pg-preset-btns\u0026quot;\u0026gt;\n      \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;820\u0026quot; data-h=\u0026quot;312\u0026quot;\u0026gt;FB Cover\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;1500\u0026quot; data-h=\u0026quot;500\u0026quot;\u0026gt;Twitter Header\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;1080\u0026quot; data-h=\u0026quot;1080\u0026quot;\u0026gt;Instagram Post\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;1080\u0026quot; data-h=\u0026quot;1920\u0026quot;\u0026gt;Instagram Story\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;1200\u0026quot; data-h=\u0026quot;627\u0026quot;\u0026gt;LinkedIn Post\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;1280\u0026quot; data-h=\u0026quot;720\u0026quot;\u0026gt;YouTube Thumb\u0026lt;/button\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;div class=\u0026quot;pg-presets-group\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;pg-presets-group-title\u0026quot;\u0026gt;Ad Banners\u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;pg-preset-btns\u0026quot;\u0026gt;\n      \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;728\u0026quot; data-h=\u0026quot;90\u0026quot;\u0026gt;Leaderboard\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;300\u0026quot; data-h=\u0026quot;600\u0026quot;\u0026gt;Half Page\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;160\u0026quot; data-h=\u0026quot;600\u0026quot;\u0026gt;Skyscraper\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;320\u0026quot; data-h=\u0026quot;50\u0026quot;\u0026gt;Mobile Banner\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;pg-preset-btn\u0026quot; data-w=\u0026quot;468\u0026quot; data-h=\u0026quot;60\u0026quot;\u0026gt;Full Banner\u0026lt;/button\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;pg-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Background Color\u0026lt;/label\u0026gt;\n  \u0026lt;div class=\u0026quot;pg-color-row\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;pg-bg-picker\u0026quot; value=\u0026quot;#cccccc\u0026quot; /\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;pg-bg-hex\u0026quot; value=\u0026quot;#cccccc\u0026quot; maxlength=\u0026quot;7\u0026quot; /\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;pg-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Text Color\u0026lt;/label\u0026gt;\n  \u0026lt;div class=\u0026quot;pg-color-row\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;pg-text-picker\u0026quot; value=\u0026quot;#666666\u0026quot; /\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;pg-text-hex\u0026quot; value=\u0026quot;#666666\u0026quot; maxlength=\u0026quot;7\u0026quot; /\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;pg-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Custom Text (leave blank for auto)\u0026lt;/label\u0026gt;\n  \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;pg-custom-text\u0026quot; placeholder=\u0026quot;e.g. Logo Here\u0026quot; /\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;pg-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;Font Size (px)\u0026lt;/label\u0026gt;\n  \u0026lt;div class=\u0026quot;pg-font-row\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;pg-font-size\u0026quot; value=\u0026quot;0\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;500\u0026quot; /\u0026gt;\n    \u0026lt;span\u0026gt;0 = auto\u0026lt;/span\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"pg-preview-area\"\u003e\n    \u003ch2\u003ePreview\u003c/h2\u003e\n    \u003cdiv class=\"pg-canvas-wrap\"\u003e\n      \u003ccanvas id=\"pg-canvas\"\u003e\u003c/canvas\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"pg-info\" id=\"pg-info\"\u003e300 × 250 px\u003c/div\u003e\n    \u003cdiv class=\"pg-btn-row\"\u003e\n      \u003cbutton class=\"pg-btn pg-btn-primary\" id=\"pg-download\"\u003eDownload PNG\u003c/button\u003e\n      \u003cbutton class=\"pg-btn pg-btn-secondary\" id=\"pg-copy-url\"\u003eCopy Data URL\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"pg-url-box\" id=\"pg-url-box\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"pg-toast\" id=\"pg-toast\"\u003eCopied!\u003c/div\u003e\n\u003cdiv class=\"pg-related\"\u003e\n  \u003ch3\u003eRelated Free Tools\u003c/h3\u003e\n  \u003cul\u003e\n    \u003cli\u003e\u003ca href=\"/tools/color-contrast-checker/\"\u003eColor Contrast Checker\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/favicon-generator/\"\u003eFavicon Generator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/base64-encoder/\"\u003eBase64 Image Encoder\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/aspect-ratio-calculator/\"\u003eAspect Ratio Calculator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/css-gradient-generator/\"\u003eCSS Gradient Generator\u003c/a\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var canvas = document.getElementById('pg-canvas');\n  var ctx = canvas.getContext('2d');\n  var wInput = document.getElementById('pg-width');\n  var hInput = document.getElementById('pg-height');\n  var bgPicker = document.getElementById('pg-bg-picker');\n  var bgHex = document.getElementById('pg-bg-hex');\n  var textPicker = document.getElementById('pg-text-picker');\n  var textHex = document.getElementById('pg-text-hex');\n  var customText = document.getElementById('pg-custom-text');\n  var fontSizeInput = document.getElementById('pg-font-size');\n  var info = document.getElementById('pg-info');\n  var urlBox = document.getElementById('pg-url-box');\n  var toast = document.getElementById('pg-toast');\n\n  function isValidHex(v) {\n    return /^#[0-9a-fA-F]{6}$/.test(v);\n  }\n\n  function render() {\n    var w = Math.max(1, Math.min(4000, parseInt(wInput.value) || 300));\n    var h = Math.max(1, Math.min(4000, parseInt(hInput.value) || 250));\n    var bg = isValidHex(bgHex.value) ? bgHex.value : '#cccccc';\n    var tc = isValidHex(textHex.value) ? textHex.value : '#666666';\n    var label = customText.value.trim() || (w + '\\u00d7' + h);\n    var fsRaw = parseInt(fontSizeInput.value) || 0;\n    var fs = fsRaw \u003e 0 ? fsRaw : Math.max(12, Math.min(Math.floor(Math.min(w, h) / 6), 72));\n\n    canvas.width = w;\n    canvas.height = h;\n\n    ctx.fillStyle = bg;\n    ctx.fillRect(0, 0, w, h);\n\n    // Subtle grid lines\n    ctx.strokeStyle = tc;\n    ctx.globalAlpha = 0.08;\n    ctx.lineWidth = 1;\n    ctx.beginPath();\n    ctx.moveTo(0, 0); ctx.lineTo(w, h);\n    ctx.moveTo(w, 0); ctx.lineTo(0, h);\n    ctx.stroke();\n    ctx.globalAlpha = 1;\n\n    // Border\n    ctx.strokeStyle = tc;\n    ctx.globalAlpha = 0.3;\n    ctx.lineWidth = Math.max(1, Math.round(Math.min(w, h) / 100));\n    ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, w - ctx.lineWidth, h - ctx.lineWidth);\n    ctx.globalAlpha = 1;\n\n    // Text\n    ctx.fillStyle = tc;\n    ctx.font = 'bold ' + fs + 'px -apple-system, BlinkMacSystemFont, Arial, sans-serif';\n    ctx.textAlign = 'center';\n    ctx.textBaseline = 'middle';\n    ctx.fillText(label, w / 2, h / 2);\n\n    info.textContent = w + ' \\u00d7 ' + h + ' px';\n  }\n\n  function showToast(msg) {\n    toast.textContent = msg;\n    toast.classList.add('show');\n    setTimeout(function() { toast.classList.remove('show'); }, 2000);\n  }\n\n  // Sync color pickers with hex inputs\n  bgPicker.addEventListener('input', function() {\n    bgHex.value = bgPicker.value;\n    render();\n  });\n  bgHex.addEventListener('input', function() {\n    if (isValidHex(bgHex.value)) bgPicker.value = bgHex.value;\n    render();\n  });\n  textPicker.addEventListener('input', function() {\n    textHex.value = textPicker.value;\n    render();\n  });\n  textHex.addEventListener('input', function() {\n    if (isValidHex(textHex.value)) textPicker.value = textHex.value;\n    render();\n  });\n\n  [wInput, hInput, customText, fontSizeInput].forEach(function(el) {\n    el.addEventListener('input', render);\n  });\n\n  // Presets\n  document.querySelectorAll('#pg-app .pg-preset-btn').forEach(function(btn) {\n    btn.addEventListener('click', function() {\n      wInput.value = btn.dataset.w;\n      hInput.value = btn.dataset.h;\n      render();\n    });\n  });\n\n  // Download\n  document.getElementById('pg-download').addEventListener('click', function() {\n    render();\n    var link = document.createElement('a');\n    link.download = 'placeholder-' + canvas.width + 'x' + canvas.height + '.png';\n    link.href = canvas.toDataURL('image/png');\n    link.click();\n  });\n\n  // Copy data URL\n  document.getElementById('pg-copy-url').addEventListener('click', function() {\n    render();\n    var dataUrl = canvas.toDataURL('image/png');\n    urlBox.textContent = dataUrl;\n    urlBox.style.display = 'block';\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(dataUrl).then(function() {\n        showToast('Data URL copied!');\n      });\n    } else {\n      var ta = document.createElement('textarea');\n      ta.value = dataUrl;\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      showToast('Data URL copied!');\n    }\n  });\n\n  render();\n})();\n\u003c/script\u003e\n\u003c/div\u003e","title":"Placeholder Image Generator - Custom Size \u0026 Color"},{"content":"Stay focused and accomplish more with this free browser-based Pomodoro timer — no downloads or sign-ups needed. Customize your work and break intervals, track your daily focus sessions, and receive audio notifications when each phase ends.\nClassic (25/5) Deep Work (50/10) Sprint (15/3) 25:00 Work Start Reset Skip \u0026#x276F; Sessions Focus today: 0 min Session 1 of 4 before long break Settings \u0026#9660; Work duration (min) Short break (min) Long break (min) Sessions before long break Auto-start breaks Auto-start work Changes apply when the current session ends or after a reset.\n\u0026rsaquo; Test your typing speed \u0026rarr; Typing Speed Test\n\u0026rsaquo; Plan your budget \u0026rarr; 50/30/20 Budget Calculator\n\u0026rsaquo; Track time zones \u0026rarr; Time Zone Converter Related Tools Age Calculator Countdown Timer Date Calculator Related Articles 15 Side Hustles You Can Start with AI Tools Today Best AI Tools for Small Business 2026: The Complete Roundup AI Tools for Freelancers to Earn More 2026 ","permalink":"https://productivity-works.com/tools/pomodoro-timer/","summary":"\u003cp\u003eStay focused and accomplish more with this free browser-based Pomodoro timer — no downloads or sign-ups needed. Customize your work and break intervals, track your daily focus sessions, and receive audio notifications when each phase ends.\u003c/p\u003e\n\u003cdiv id=\"pm-app\"\u003e\n\u003cstyle\u003e\n  #pm-app {\n    font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n    max-width: 640px;\n    margin: 0 auto;\n    padding: 1rem;\n    color: #1e293b;\n  }\n\u003cp\u003e/* Preset buttons */\n#pm-app .pm-presets {\ndisplay: flex;\ngap: 0.5rem;\njustify-content: center;\nflex-wrap: wrap;\nmargin-bottom: 1.5rem;\n}\n#pm-app .pm-preset-btn {\nbackground: #f1f5f9;\nborder: 1.5px solid #cbd5e1;\nborder-radius: 999px;\npadding: 0.35rem 1rem;\nfont-size: 0.85rem;\nfont-weight: 500;\ncolor: #475569;\ncursor: pointer;\ntransition: background 0.15s, border-color 0.15s, color 0.15s;\n}\n#pm-app .pm-preset-btn:hover,\n#pm-app .pm-preset-btn.pm-preset-active {\nbackground: #0f172a;\nborder-color: #0f172a;\ncolor: #f8fafc;\n}\u003c/p\u003e","title":"Pomodoro Timer"},{"content":" Enter your last menstrual period (LMP) date or your conception date to instantly calculate your estimated due date, current pregnancy week, trimester, and key upcoming milestones. Pregnancy Due Date Calculator Last Menstrual Period (LMP) Date OR Conception / Ovulation Date Please enter either an LMP date or a conception date to continue. Calculate My Due Date\nEstimated Due Date (EDD) Current Week — weeks Day in Week — days Trimester — trimester Pregnancy Progress Week 1 1st Tri (W1–13) 2nd Tri (W14–27) 3rd Tri (W28+) Week 40 Key Milestones Week-by-Week Development Highlights Weeks 8–10 All Major Organs Forming The embryo transitions to a fetus. Tiny fingers and toes appear. The heart beats at ~170 bpm. Facial features are becoming distinct. Weeks 18–22 First Movements \u0026 Anatomy Scan You may feel the first kicks (\"quickening\"). The 20-week anatomy scan checks organ development and can often reveal the baby's sex. Weeks 28–32 Rapid Brain Growth The brain develops rapidly with characteristic grooves and folds. The baby can open and close its eyes. Lungs are maturing in preparation for birth. Weeks 37–40 Full Term — Ready for Birth At 37 weeks, the baby is considered full term. The baby continues gaining weight and the immune system receives antibodies. Birth can happen any day. Related Tools Ovulation \u0026 Fertile Window Calculator BMI Calculator Daily Calorie Calculator Age Calculator Related Tools Age Calculator Dog Age Calculator ","permalink":"https://productivity-works.com/tools/pregnancy-due-date-calculator/","summary":"\u003cdiv id=\"pdd-app\"\u003e\n\u003cstyle\u003e\n#pdd-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 760px;\n  margin: 0 auto;\n  color: #2d2d2d;\n  line-height: 1.6;\n}\n\n#pdd-app h2 {\n  font-size: 1.5rem;\n  font-weight: 700;\n  color: #c2185b;\n  margin: 0 0 1rem 0;\n}\n\n#pdd-app h3 {\n  font-size: 1.1rem;\n  font-weight: 600;\n  margin: 0 0 0.75rem 0;\n  color: #2d2d2d;\n}\n\n#pdd-app .pdd-intro {\n  background: #fce4ec;\n  border-left: 4px solid #e91e63;\n  border-radius: 0 8px 8px 0;\n  padding: 1rem 1.25rem;\n  margin-bottom: 1.5rem;\n  font-size: 0.95rem;\n  color: #555;\n}\n\n#pdd-app .pdd-card {\n  background: #fff;\n  border: 1px solid #f0c0d0;\n  border-radius: 12px;\n  padding: 1.5rem;\n  margin-bottom: 1.5rem;\n  box-shadow: 0 2px 8px rgba(194,24,91,0.06);\n}\n\n#pdd-app .pdd-input-group {\n  margin-bottom: 1.25rem;\n}\n\n#pdd-app .pdd-input-group label {\n  display: block;\n  font-weight: 600;\n  margin-bottom: 0.4rem;\n  font-size: 0.9rem;\n  color: #444;\n}\n\n#pdd-app .pdd-input-group input[type=\"date\"] {\n  width: 100%;\n  padding: 0.65rem 0.9rem;\n  border: 2px solid #f0c0d0;\n  border-radius: 8px;\n  font-size: 1rem;\n  color: #2d2d2d;\n  background: #fff;\n  box-sizing: border-box;\n  transition: border-color 0.2s;\n  outline: none;\n}\n\n#pdd-app .pdd-input-group input[type=\"date\"]:focus {\n  border-color: #e91e63;\n}\n\n#pdd-app .pdd-divider {\n  display: flex;\n  align-items: center;\n  gap: 0.75rem;\n  margin: 1rem 0;\n  color: #aaa;\n  font-size: 0.85rem;\n  font-weight: 600;\n}\n\n#pdd-app .pdd-divider::before,\n#pdd-app .pdd-divider::after {\n  content: '';\n  flex: 1;\n  height: 1px;\n  background: #f0c0d0;\n}\n\n#pdd-app .pdd-btn {\n  display: inline-block;\n  background: #e91e63;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  padding: 0.75rem 2rem;\n  font-size: 1rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.2s, transform 0.1s;\n  width: 100%;\n  margin-top: 0.5rem;\n}\n\n#pdd-app .pdd-btn:hover {\n  background: #c2185b;\n  transform: translateY(-1px);\n}\n\n#pdd-app .pdd-btn:active {\n  transform: translateY(0);\n}\n\n#pdd-app .pdd-result {\n  display: none;\n}\n\n#pdd-app .pdd-result.visible {\n  display: block;\n}\n\n#pdd-app .pdd-due-date-box {\n  background: linear-gradient(135deg, #e91e63 0%, #c2185b 100%);\n  color: #fff;\n  border-radius: 12px;\n  padding: 1.5rem;\n  text-align: center;\n  margin-bottom: 1.5rem;\n}\n\n#pdd-app .pdd-due-date-box .pdd-due-label {\n  font-size: 0.9rem;\n  opacity: 0.85;\n  margin-bottom: 0.4rem;\n  font-weight: 500;\n}\n\n#pdd-app .pdd-due-date-box .pdd-due-value {\n  font-size: 2rem;\n  font-weight: 800;\n  letter-spacing: 0.02em;\n  margin-bottom: 0.5rem;\n}\n\n#pdd-app .pdd-due-date-box .pdd-due-sub {\n  font-size: 0.9rem;\n  opacity: 0.85;\n}\n\n#pdd-app .pdd-stats-grid {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 1rem;\n  margin-bottom: 1.5rem;\n}\n\n@media (max-width: 520px) {\n  #pdd-app .pdd-stats-grid {\n    grid-template-columns: 1fr 1fr;\n  }\n}\n\n#pdd-app .pdd-stat-box {\n  background: #fce4ec;\n  border-radius: 10px;\n  padding: 0.9rem 0.75rem;\n  text-align: center;\n}\n\n#pdd-app .pdd-stat-box .pdd-stat-label {\n  font-size: 0.75rem;\n  color: #888;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  margin-bottom: 0.3rem;\n}\n\n#pdd-app .pdd-stat-box .pdd-stat-value {\n  font-size: 1.4rem;\n  font-weight: 800;\n  color: #c2185b;\n  line-height: 1.1;\n}\n\n#pdd-app .pdd-stat-box .pdd-stat-unit {\n  font-size: 0.75rem;\n  color: #c2185b;\n  font-weight: 500;\n}\n\n#pdd-app .pdd-trimester-bar {\n  background: #fce4ec;\n  border-radius: 10px;\n  padding: 1rem 1.25rem;\n  margin-bottom: 1.5rem;\n}\n\n#pdd-app .pdd-trimester-bar h3 {\n  margin-bottom: 0.75rem;\n  font-size: 0.95rem;\n  color: #888;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n\n#pdd-app .pdd-progress-wrap {\n  background: #f8bbd0;\n  border-radius: 999px;\n  height: 14px;\n  position: relative;\n  overflow: hidden;\n  margin-bottom: 0.4rem;\n}\n\n#pdd-app .pdd-progress-fill {\n  height: 100%;\n  background: linear-gradient(90deg, #e91e63, #c2185b);\n  border-radius: 999px;\n  transition: width 0.6s ease;\n}\n\n#pdd-app .pdd-trimester-labels {\n  display: flex;\n  justify-content: space-between;\n  font-size: 0.72rem;\n  color: #aaa;\n  font-weight: 500;\n}\n\n#pdd-app .pdd-trimester-badge {\n  display: inline-block;\n  background: #e91e63;\n  color: #fff;\n  border-radius: 999px;\n  padding: 0.2rem 0.9rem;\n  font-size: 0.82rem;\n  font-weight: 700;\n  margin-top: 0.6rem;\n}\n\n#pdd-app .pdd-milestones {\n  margin-bottom: 1.5rem;\n}\n\n#pdd-app .pdd-milestone-item {\n  display: flex;\n  align-items: flex-start;\n  gap: 0.9rem;\n  padding: 0.8rem 0;\n  border-bottom: 1px solid #fce4ec;\n}\n\n#pdd-app .pdd-milestone-item:last-child {\n  border-bottom: none;\n}\n\n#pdd-app .pdd-milestone-icon {\n  width: 36px;\n  height: 36px;\n  border-radius: 50%;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 1rem;\n  flex-shrink: 0;\n  background: #fce4ec;\n}\n\n#pdd-app .pdd-milestone-icon.past {\n  background: #e0e0e0;\n}\n\n#pdd-app .pdd-milestone-icon.current {\n  background: #e91e63;\n}\n\n#pdd-app .pdd-milestone-body {\n  flex: 1;\n}\n\n#pdd-app .pdd-milestone-body .pdd-ms-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin-bottom: 0.2rem;\n}\n\n#pdd-app .pdd-milestone-body .pdd-ms-name {\n  font-weight: 700;\n  font-size: 0.92rem;\n  color: #2d2d2d;\n}\n\n#pdd-app .pdd-milestone-body .pdd-ms-date {\n  font-size: 0.8rem;\n  color: #e91e63;\n  font-weight: 600;\n}\n\n#pdd-app .pdd-milestone-body .pdd-ms-desc {\n  font-size: 0.82rem;\n  color: #777;\n}\n\n#pdd-app .pdd-dev-section {\n  margin-bottom: 1.5rem;\n}\n\n#pdd-app .pdd-dev-card {\n  background: #fff9fb;\n  border: 1px solid #f8bbd0;\n  border-radius: 10px;\n  padding: 1rem 1.1rem;\n  margin-bottom: 0.75rem;\n}\n\n#pdd-app .pdd-dev-card .pdd-dev-week {\n  font-weight: 800;\n  color: #e91e63;\n  font-size: 0.85rem;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  margin-bottom: 0.2rem;\n}\n\n#pdd-app .pdd-dev-card .pdd-dev-title {\n  font-weight: 700;\n  font-size: 0.95rem;\n  margin-bottom: 0.25rem;\n}\n\n#pdd-app .pdd-dev-card .pdd-dev-desc {\n  font-size: 0.85rem;\n  color: #666;\n}\n\n#pdd-app .pdd-related {\n  background: #f9f9f9;\n  border-radius: 10px;\n  padding: 1.1rem 1.25rem;\n  margin-top: 1.5rem;\n}\n\n#pdd-app .pdd-related h3 {\n  font-size: 0.95rem;\n  color: #888;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  margin-bottom: 0.75rem;\n}\n\n#pdd-app .pdd-related ul {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\n\n#pdd-app .pdd-related ul li {\n  margin-bottom: 0.5rem;\n}\n\n#pdd-app .pdd-related ul li a {\n  color: #e91e63;\n  text-decoration: none;\n  font-size: 0.9rem;\n  font-weight: 500;\n}\n\n#pdd-app .pdd-related ul li a:hover {\n  text-decoration: underline;\n}\n\n#pdd-app .pdd-error {\n  display: none;\n  color: #c2185b;\n  background: #fce4ec;\n  border-radius: 8px;\n  padding: 0.65rem 1rem;\n  font-size: 0.88rem;\n  font-weight: 600;\n  margin-top: 0.5rem;\n}\n\n#pdd-app .pdd-error.visible {\n  display: block;\n}\n\u003c/style\u003e\n\u003cdiv class=\"pdd-intro\"\u003e\n  Enter your \u003cstrong\u003elast menstrual period (LMP)\u003c/strong\u003e date or your \u003cstrong\u003econception date\u003c/strong\u003e to instantly calculate your estimated due date, current pregnancy week, trimester, and key upcoming milestones.\n\u003c/div\u003e\n\u003cdiv class=\"pdd-card\"\u003e\n  \u003ch2\u003ePregnancy Due Date Calculator\u003c/h2\u003e\n  \u003cdiv class=\"pdd-input-group\"\u003e\n    \u003clabel for=\"pdd-lmp\"\u003eLast Menstrual Period (LMP) Date\u003c/label\u003e\n    \u003cinput type=\"date\" id=\"pdd-lmp\" /\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"pdd-divider\"\u003eOR\u003c/div\u003e\n  \u003cdiv class=\"pdd-input-group\"\u003e\n    \u003clabel for=\"pdd-conception\"\u003eConception / Ovulation Date\u003c/label\u003e\n    \u003cinput type=\"date\" id=\"pdd-conception\" /\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"pdd-error\" id=\"pdd-error\"\u003ePlease enter either an LMP date or a conception date to continue.\u003c/div\u003e\n\u003cp\u003e\u003cbutton class=\"pdd-btn\" onclick=\"pddCalculate()\"\u003eCalculate My Due Date\u003c/button\u003e\u003c/p\u003e","title":"Pregnancy Due Date Calculator - Estimate Your Baby's Arrival"},{"content":" Website Information Website Name * Website URL * Contact Email * Company / Owner Name (optional) Last Updated Date Auto-filled with today's date. Adjust if needed. Data Types Collected Personal Information Cookies \u0026amp; Tracking Usage Analytics Third-Party Services Payment Information Location Data Compliance Options GDPR (EU) CCPA (California) COPPA (Children's Privacy) Third-Party Services Used Google Analytics Google AdSense Stripe PayPal Mailchimp Social Media Plugins Generate Privacy Policy\nLegal Disclaimer This generated policy is a template for informational purposes only. It does not constitute legal advice. You should have this document reviewed by a qualified legal professional before publishing it on your website to ensure it meets your specific legal obligations. Copy as HTML Copy as Plain Text Download .txt Related Tools Generate meta tags → Meta Tag Generator Build robots.txt → Robots.txt Generator Create markdown tables → Markdown Table Generator ","permalink":"https://productivity-works.com/tools/privacy-policy-generator/","summary":"\u003cstyle\u003e\n#pp-app *,\n#pp-app *::before,\n#pp-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#pp-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  color: #1e293b;\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 0 0 2rem;\n}\n#pp-app h2 {\n  font-size: 1.1rem;\n  font-weight: 700;\n  color: #0f172a;\n  margin: 1.75rem 0 1rem;\n  padding-bottom: 0.4rem;\n  border-bottom: 2px solid #e2e8f0;\n}\n#pp-app .field {\n  margin-bottom: 1.1rem;\n}\n#pp-app label {\n  display: block;\n  font-size: 0.875rem;\n  font-weight: 600;\n  color: #374151;\n  margin-bottom: 0.35rem;\n}\n#pp-app input[type=\"text\"],\n#pp-app input[type=\"url\"],\n#pp-app input[type=\"email\"],\n#pp-app input[type=\"date\"] {\n  width: 100%;\n  padding: 0.55rem 0.75rem;\n  border: 1px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 0.9rem;\n  color: #1e293b;\n  background: #fff;\n  transition: border-color 0.15s;\n}\n#pp-app input[type=\"text\"]:focus,\n#pp-app input[type=\"url\"]:focus,\n#pp-app input[type=\"email\"]:focus,\n#pp-app input[type=\"date\"]:focus {\n  outline: none;\n  border-color: #3b82f6;\n  box-shadow: 0 0 0 3px rgba(59,130,246,0.12);\n}\n#pp-app .hint {\n  font-size: 0.78rem;\n  color: #64748b;\n  margin-top: 0.3rem;\n}\n#pp-app .checkbox-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\n  gap: 0.55rem;\n}\n#pp-app .checkbox-item {\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n  font-size: 0.875rem;\n  color: #374151;\n  cursor: pointer;\n  padding: 0.45rem 0.6rem;\n  border: 1px solid #e2e8f0;\n  border-radius: 6px;\n  transition: background 0.12s, border-color 0.12s;\n}\n#pp-app .checkbox-item:hover {\n  background: #f8fafc;\n  border-color: #93c5fd;\n}\n#pp-app .checkbox-item input[type=\"checkbox\"] {\n  accent-color: #3b82f6;\n  width: 15px;\n  height: 15px;\n  flex-shrink: 0;\n}\n#pp-app .toggle-grid {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.75rem;\n}\n#pp-app .toggle-item {\n  display: flex;\n  align-items: center;\n  gap: 0.6rem;\n  font-size: 0.875rem;\n  font-weight: 500;\n  color: #374151;\n  cursor: pointer;\n  padding: 0.5rem 0.9rem;\n  border: 1px solid #e2e8f0;\n  border-radius: 20px;\n  transition: all 0.15s;\n  user-select: none;\n}\n#pp-app .toggle-item input[type=\"checkbox\"] {\n  accent-color: #3b82f6;\n  width: 15px;\n  height: 15px;\n}\n#pp-app .toggle-item.active {\n  background: #eff6ff;\n  border-color: #3b82f6;\n  color: #1d4ed8;\n}\n#pp-app .btn-generate {\n  display: inline-flex;\n  align-items: center;\n  gap: 0.5rem;\n  margin-top: 1.5rem;\n  padding: 0.7rem 1.8rem;\n  background: #2563eb;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 1rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s;\n}\n#pp-app .btn-generate:hover {\n  background: #1d4ed8;\n  transform: translateY(-1px);\n}\n#pp-app .btn-generate:active {\n  transform: translateY(0);\n}\n#pp-app .output-section {\n  display: none;\n  margin-top: 2rem;\n}\n#pp-app .output-section.visible {\n  display: block;\n}\n#pp-app .disclaimer-box {\n  background: #fef9c3;\n  border: 1px solid #fde047;\n  border-radius: 8px;\n  padding: 0.85rem 1rem;\n  font-size: 0.83rem;\n  color: #713f12;\n  margin-bottom: 1.25rem;\n  line-height: 1.55;\n}\n#pp-app .disclaimer-box strong {\n  display: block;\n  margin-bottom: 0.2rem;\n  font-size: 0.875rem;\n}\n#pp-app .action-bar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.6rem;\n  margin-bottom: 1rem;\n}\n#pp-app .btn-action {\n  display: inline-flex;\n  align-items: center;\n  gap: 0.4rem;\n  padding: 0.5rem 1rem;\n  border: 1px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 0.85rem;\n  font-weight: 500;\n  cursor: pointer;\n  background: #fff;\n  color: #374151;\n  transition: all 0.12s;\n}\n#pp-app .btn-action:hover {\n  background: #f1f5f9;\n  border-color: #94a3b8;\n}\n#pp-app .btn-action.copied {\n  background: #dcfce7;\n  border-color: #86efac;\n  color: #166534;\n}\n#pp-app .policy-preview {\n  border: 1px solid #e2e8f0;\n  border-radius: 8px;\n  padding: 2rem 2.5rem;\n  background: #fff;\n  font-size: 0.9rem;\n  line-height: 1.75;\n  color: #1e293b;\n  max-height: 600px;\n  overflow-y: auto;\n}\n#pp-app .policy-preview h1 {\n  font-size: 1.5rem;\n  font-weight: 700;\n  margin-bottom: 0.5rem;\n  color: #0f172a;\n}\n#pp-app .policy-preview h2 {\n  font-size: 1.05rem;\n  font-weight: 700;\n  margin: 1.5rem 0 0.6rem;\n  padding-bottom: 0.3rem;\n  border-bottom: 1px solid #e2e8f0;\n  color: #0f172a;\n}\n#pp-app .policy-preview p {\n  margin-bottom: 0.8rem;\n}\n#pp-app .policy-preview ul {\n  margin: 0.5rem 0 0.8rem 1.4rem;\n}\n#pp-app .policy-preview ul li {\n  margin-bottom: 0.3rem;\n}\n#pp-app .policy-preview .last-updated {\n  font-size: 0.82rem;\n  color: #64748b;\n  margin-bottom: 1.2rem;\n}\n#pp-app .section-cols {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1rem;\n}\n@media (max-width: 600px) {\n  #pp-app .checkbox-grid {\n    grid-template-columns: 1fr 1fr;\n  }\n  #pp-app .section-cols {\n    grid-template-columns: 1fr;\n  }\n  #pp-app .policy-preview {\n    padding: 1rem 1.2rem;\n  }\n}\n@media (max-width: 420px) {\n  #pp-app .checkbox-grid {\n    grid-template-columns: 1fr;\n  }\n}\n\u003c/style\u003e\n\u003cdiv id=\"pp-app\"\u003e\n\u003ch2\u003eWebsite Information\u003c/h2\u003e\n\u003cdiv class=\"section-cols\"\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"pp-site-name\"\u003eWebsite Name \u003cspan style=\"color:#ef4444\"\u003e*\u003c/span\u003e\u003c/label\u003e\n    \u003cinput type=\"text\" id=\"pp-site-name\" placeholder=\"e.g. My Awesome Blog\"\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"pp-site-url\"\u003eWebsite URL \u003cspan style=\"color:#ef4444\"\u003e*\u003c/span\u003e\u003c/label\u003e\n    \u003cinput type=\"url\" id=\"pp-site-url\" placeholder=\"https://example.com\"\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"section-cols\"\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"pp-contact-email\"\u003eContact Email \u003cspan style=\"color:#ef4444\"\u003e*\u003c/span\u003e\u003c/label\u003e\n    \u003cinput type=\"email\" id=\"pp-contact-email\" placeholder=\"privacy@example.com\"\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"pp-company-name\"\u003eCompany / Owner Name \u003cspan style=\"color:#94a3b8\"\u003e(optional)\u003c/span\u003e\u003c/label\u003e\n    \u003cinput type=\"text\" id=\"pp-company-name\" placeholder=\"e.g. Acme Inc.\"\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"field\"\u003e\n  \u003clabel for=\"pp-last-updated\"\u003eLast Updated Date\u003c/label\u003e\n  \u003cinput type=\"date\" id=\"pp-last-updated\"\u003e\n  \u003cdiv class=\"hint\"\u003eAuto-filled with today's date. Adjust if needed.\u003c/div\u003e\n\u003c/div\u003e\n\u003ch2\u003eData Types Collected\u003c/h2\u003e\n\u003cdiv class=\"checkbox-grid\"\u003e\n  \u003clabel class=\"checkbox-item\"\u003e\u003cinput type=\"checkbox\" id=\"pp-personal\" checked\u003e Personal Information\u003c/label\u003e\n  \u003clabel class=\"checkbox-item\"\u003e\u003cinput type=\"checkbox\" id=\"pp-cookies\" checked\u003e Cookies \u0026amp; Tracking\u003c/label\u003e\n  \u003clabel class=\"checkbox-item\"\u003e\u003cinput type=\"checkbox\" id=\"pp-analytics\" checked\u003e Usage Analytics\u003c/label\u003e\n  \u003clabel class=\"checkbox-item\"\u003e\u003cinput type=\"checkbox\" id=\"pp-third-party\"\u003e Third-Party Services\u003c/label\u003e\n  \u003clabel class=\"checkbox-item\"\u003e\u003cinput type=\"checkbox\" id=\"pp-payment\"\u003e Payment Information\u003c/label\u003e\n  \u003clabel class=\"checkbox-item\"\u003e\u003cinput type=\"checkbox\" id=\"pp-location\"\u003e Location Data\u003c/label\u003e\n\u003c/div\u003e\n\u003ch2\u003eCompliance Options\u003c/h2\u003e\n\u003cdiv class=\"toggle-grid\" id=\"pp-compliance-toggles\"\u003e\n  \u003clabel class=\"toggle-item\" id=\"lbl-gdpr\"\u003e\u003cinput type=\"checkbox\" id=\"pp-gdpr\"\u003e GDPR (EU)\u003c/label\u003e\n  \u003clabel class=\"toggle-item\" id=\"lbl-ccpa\"\u003e\u003cinput type=\"checkbox\" id=\"pp-ccpa\"\u003e CCPA (California)\u003c/label\u003e\n  \u003clabel class=\"toggle-item\" id=\"lbl-coppa\"\u003e\u003cinput type=\"checkbox\" id=\"pp-coppa\"\u003e COPPA (Children's Privacy)\u003c/label\u003e\n\u003c/div\u003e\n\u003ch2\u003eThird-Party Services Used\u003c/h2\u003e\n\u003cdiv class=\"checkbox-grid\"\u003e\n  \u003clabel class=\"checkbox-item\"\u003e\u003cinput type=\"checkbox\" id=\"pp-ga\"\u003e Google Analytics\u003c/label\u003e\n  \u003clabel class=\"checkbox-item\"\u003e\u003cinput type=\"checkbox\" id=\"pp-adsense\"\u003e Google AdSense\u003c/label\u003e\n  \u003clabel class=\"checkbox-item\"\u003e\u003cinput type=\"checkbox\" id=\"pp-stripe\"\u003e Stripe\u003c/label\u003e\n  \u003clabel class=\"checkbox-item\"\u003e\u003cinput type=\"checkbox\" id=\"pp-paypal\"\u003e PayPal\u003c/label\u003e\n  \u003clabel class=\"checkbox-item\"\u003e\u003cinput type=\"checkbox\" id=\"pp-mailchimp\"\u003e Mailchimp\u003c/label\u003e\n  \u003clabel class=\"checkbox-item\"\u003e\u003cinput type=\"checkbox\" id=\"pp-social\"\u003e Social Media Plugins\u003c/label\u003e\n\u003c/div\u003e\n\u003cp\u003e\u003cbutton class=\"btn-generate\" onclick=\"ppGenerate()\"\u003eGenerate Privacy Policy\u003c/button\u003e\u003c/p\u003e","title":"Privacy Policy Generator — Free Template Tool"},{"content":"Generate QR codes instantly in your browser — no uploads, no accounts, no tracking. Supports URLs, plain text, WiFi credentials, and email links.\nURL Text WiFi Email Website URL Plain Text Network Name (SSID) Password Encryption WPA/WPA2 WEP None (open) To Address Subject Body (optional) Appearance Size 300px Foreground #000000 Background #ffffff Preview \u0026amp; Download Enter content above to generate a QR code.\n\u0026#8595; Download PNG \u0026#8595; Download SVG Encode/decode URLs → URL Encoder/Decoder Convert to Base64 → Base64 Encoder/Decoder Generate placeholder images → Placeholder Image Generator ","permalink":"https://productivity-works.com/tools/qr-code-generator/","summary":"\u003cp\u003eGenerate QR codes instantly in your browser — no uploads, no accounts, no tracking. Supports URLs, plain text, WiFi credentials, and email links.\u003c/p\u003e\n\u003cdiv id=\"qr-app\"\u003e\n\u003cstyle\u003e\n#qr-app *,#qr-app *::before,#qr-app *::after{box-sizing:border-box;margin:0;padding:0}\n#qr-app{font-family:system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;color:#1e293b;max-width:680px;margin:2rem auto;padding:0 1rem}\n#qr-app .qa-card{background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:1.5rem;margin-bottom:1.25rem;box-shadow:0 1px 4px rgba(0,0,0,.06)}\n#qr-app .qa-tabs{display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1.25rem}\n#qr-app .qa-tab{padding:.45rem 1rem;border:1px solid #cbd5e1;border-radius:6px;background:#f8fafc;color:#475569;font-size:.875rem;cursor:pointer;transition:all .15s}\n#qr-app .qa-tab:hover{background:#f1f5f9;border-color:#94a3b8}\n#qr-app .qa-tab.active{background:#0f172a;color:#fff;border-color:#0f172a}\n#qr-app .qa-panel{display:none}\n#qr-app .qa-panel.active{display:block}\n#qr-app .qa-field{margin-bottom:.9rem}\n#qr-app .qa-label{display:block;font-size:.8rem;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:.04em;margin-bottom:.35rem}\n#qr-app .qa-input{width:100%;padding:.55rem .75rem;border:1px solid #cbd5e1;border-radius:7px;font-size:.95rem;color:#1e293b;background:#f8fafc;transition:border-color .15s,box-shadow .15s;outline:none}\n#qr-app .qa-input:focus{border-color:#6366f1;box-shadow:0 0 0 3px rgba(99,102,241,.15);background:#fff}\n#qr-app select.qa-input{appearance:none;-webkit-appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%2364748b' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E\");background-repeat:no-repeat;background-position:right .75rem center;background-color:#f8fafc;padding-right:2.25rem;cursor:pointer}\n#qr-app .qa-row{display:grid;grid-template-columns:1fr 1fr;gap:.75rem}\n#qr-app .qa-section-title{font-size:.8rem;font-weight:700;color:#94a3b8;text-transform:uppercase;letter-spacing:.07em;margin-bottom:.85rem}\n#qr-app .qa-customize{display:grid;grid-template-columns:1fr 1fr 1fr;gap:.75rem;align-items:end}\n#qr-app .qa-slider-wrap{display:flex;align-items:center;gap:.6rem}\n#qr-app .qa-slider{-webkit-appearance:none;appearance:none;width:100%;height:5px;border-radius:3px;background:#e2e8f0;outline:none;cursor:pointer}\n#qr-app .qa-slider::-webkit-slider-thumb{-webkit-appearance:none;width:16px;height:16px;border-radius:50%;background:#6366f1;cursor:pointer;border:2px solid #fff;box-shadow:0 0 0 1px #6366f1}\n#qr-app .qa-slider::-moz-range-thumb{width:16px;height:16px;border-radius:50%;background:#6366f1;cursor:pointer;border:2px solid #fff}\n#qr-app .qa-slider-val{font-size:.8rem;color:#64748b;white-space:nowrap;min-width:3.2rem;text-align:right}\n#qr-app .qa-color-wrap{display:flex;align-items:center;gap:.5rem}\n#qr-app .qa-color-swatch{width:36px;height:36px;border-radius:7px;border:1px solid #cbd5e1;cursor:pointer;overflow:hidden;padding:2px;background:#fff;flex-shrink:0}\n#qr-app .qa-color-swatch input[type=color]{width:100%;height:100%;border:none;cursor:pointer;background:transparent;padding:0;appearance:none;-webkit-appearance:none;display:block}\n#qr-app .qa-color-label{font-size:.82rem;color:#64748b;font-family:monospace}\n#qr-app .qa-preview{display:flex;flex-direction:column;align-items:center;gap:1.25rem}\n#qr-app .qa-canvas-wrap{background:#f8fafc;border:1px solid #e2e8f0;border-radius:10px;padding:1.25rem;display:flex;align-items:center;justify-content:center;min-height:220px;width:100%}\n#qr-app canvas{display:block;border-radius:4px;max-width:100%;image-rendering:pixelated}\n#qr-app .qa-btns{display:flex;gap:.75rem;flex-wrap:wrap;justify-content:center}\n#qr-app .qa-btn{display:inline-flex;align-items:center;gap:.45rem;padding:.55rem 1.25rem;border-radius:8px;font-size:.875rem;font-weight:600;cursor:pointer;border:none;transition:all .15s;text-decoration:none}\n#qr-app .qa-btn-primary{background:#0f172a;color:#fff}\n#qr-app .qa-btn-primary:hover{background:#1e293b}\n#qr-app .qa-btn-primary:disabled{background:#94a3b8;cursor:not-allowed}\n#qr-app .qa-btn-outline{background:#fff;color:#0f172a;border:1.5px solid #cbd5e1}\n#qr-app .qa-btn-outline:hover{background:#f1f5f9;border-color:#94a3b8}\n#qr-app .qa-btn-outline:disabled{color:#94a3b8;border-color:#e2e8f0;cursor:not-allowed;background:#fff}\n#qr-app .qa-empty{color:#94a3b8;font-size:.9rem;text-align:center;padding:2rem 0}\n@media(max-width:520px){\n  #qr-app .qa-customize{grid-template-columns:1fr 1fr}\n  #qr-app .qa-row{grid-template-columns:1fr}\n}\n\u003c/style\u003e\n\u003c!-- Input Card --\u003e\n\u003cdiv class=\"qa-card\"\u003e\n  \u003cdiv class=\"qa-tabs\"\u003e\n    \u003cbutton class=\"qa-tab active\" data-tab=\"url\"\u003eURL\u003c/button\u003e\n    \u003cbutton class=\"qa-tab\" data-tab=\"text\"\u003eText\u003c/button\u003e\n    \u003cbutton class=\"qa-tab\" data-tab=\"wifi\"\u003eWiFi\u003c/button\u003e\n    \u003cbutton class=\"qa-tab\" data-tab=\"email\"\u003eEmail\u003c/button\u003e\n  \u003c/div\u003e\n  \u003c!-- URL --\u003e\n  \u003cdiv class=\"qa-panel active\" id=\"qa-panel-url\"\u003e\n    \u003cdiv class=\"qa-field\"\u003e\n      \u003clabel class=\"qa-label\" for=\"qa-url-input\"\u003eWebsite URL\u003c/label\u003e\n      \u003cinput class=\"qa-input\" id=\"qa-url-input\" type=\"url\" placeholder=\"https://example.com\" value=\"https://\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Text --\u003e\n  \u003cdiv class=\"qa-panel\" id=\"qa-panel-text\"\u003e\n    \u003cdiv class=\"qa-field\"\u003e\n      \u003clabel class=\"qa-label\" for=\"qa-text-input\"\u003ePlain Text\u003c/label\u003e\n      \u003ctextarea class=\"qa-input\" id=\"qa-text-input\" rows=\"3\" placeholder=\"Enter any text\u0026#8230;\" style=\"resize:vertical\"\u003e\u003c/textarea\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- WiFi --\u003e\n  \u003cdiv class=\"qa-panel\" id=\"qa-panel-wifi\"\u003e\n    \u003cdiv class=\"qa-field\"\u003e\n      \u003clabel class=\"qa-label\" for=\"qa-wifi-ssid\"\u003eNetwork Name (SSID)\u003c/label\u003e\n      \u003cinput class=\"qa-input\" id=\"qa-wifi-ssid\" type=\"text\" placeholder=\"MyNetwork\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"qa-row\"\u003e\n      \u003cdiv class=\"qa-field\"\u003e\n        \u003clabel class=\"qa-label\" for=\"qa-wifi-pass\"\u003ePassword\u003c/label\u003e\n        \u003cinput class=\"qa-input\" id=\"qa-wifi-pass\" type=\"password\" placeholder=\"password\"\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"qa-field\"\u003e\n        \u003clabel class=\"qa-label\" for=\"qa-wifi-enc\"\u003eEncryption\u003c/label\u003e\n        \u003cselect class=\"qa-input\" id=\"qa-wifi-enc\"\u003e\n          \u003coption value=\"WPA\"\u003eWPA/WPA2\u003c/option\u003e\n          \u003coption value=\"WEP\"\u003eWEP\u003c/option\u003e\n          \u003coption value=\"nopass\"\u003eNone (open)\u003c/option\u003e\n        \u003c/select\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Email --\u003e\n  \u003cdiv class=\"qa-panel\" id=\"qa-panel-email\"\u003e\n    \u003cdiv class=\"qa-field\"\u003e\n      \u003clabel class=\"qa-label\" for=\"qa-email-to\"\u003eTo Address\u003c/label\u003e\n      \u003cinput class=\"qa-input\" id=\"qa-email-to\" type=\"email\" placeholder=\"name@example.com\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"qa-field\"\u003e\n      \u003clabel class=\"qa-label\" for=\"qa-email-subject\"\u003eSubject\u003c/label\u003e\n      \u003cinput class=\"qa-input\" id=\"qa-email-subject\" type=\"text\" placeholder=\"Subject line\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"qa-field\"\u003e\n      \u003clabel class=\"qa-label\" for=\"qa-email-body\"\u003eBody (optional)\u003c/label\u003e\n      \u003ctextarea class=\"qa-input\" id=\"qa-email-body\" rows=\"2\" placeholder=\"Message body\u0026#8230;\" style=\"resize:vertical\"\u003e\u003c/textarea\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Customization Card --\u003e\n\u003cdiv class=\"qa-card\"\u003e\n  \u003cdiv class=\"qa-section-title\"\u003eAppearance\u003c/div\u003e\n  \u003cdiv class=\"qa-customize\"\u003e\n    \u003cdiv class=\"qa-field\"\u003e\n      \u003clabel class=\"qa-label\"\u003eSize\u003c/label\u003e\n      \u003cdiv class=\"qa-slider-wrap\"\u003e\n        \u003cinput class=\"qa-slider\" type=\"range\" id=\"qa-size\" min=\"200\" max=\"600\" value=\"300\" step=\"10\"\u003e\n        \u003cspan class=\"qa-slider-val\" id=\"qa-size-val\"\u003e300px\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"qa-field\"\u003e\n      \u003clabel class=\"qa-label\"\u003eForeground\u003c/label\u003e\n      \u003cdiv class=\"qa-color-wrap\"\u003e\n        \u003cdiv class=\"qa-color-swatch\"\u003e\u003cinput type=\"color\" id=\"qa-fg\" value=\"#000000\"\u003e\u003c/div\u003e\n        \u003cspan class=\"qa-color-label\" id=\"qa-fg-label\"\u003e#000000\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"qa-field\"\u003e\n      \u003clabel class=\"qa-label\"\u003eBackground\u003c/label\u003e\n      \u003cdiv class=\"qa-color-wrap\"\u003e\n        \u003cdiv class=\"qa-color-swatch\"\u003e\u003cinput type=\"color\" id=\"qa-bg\" value=\"#ffffff\"\u003e\u003c/div\u003e\n        \u003cspan class=\"qa-color-label\" id=\"qa-bg-label\"\u003e#ffffff\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Preview Card --\u003e\n\u003cdiv class=\"qa-card\"\u003e\n  \u003cdiv class=\"qa-section-title\"\u003ePreview \u0026amp; Download\u003c/div\u003e\n  \u003cdiv class=\"qa-preview\"\u003e\n    \u003cdiv class=\"qa-canvas-wrap\" id=\"qa-canvas-wrap\"\u003e\n      \u003cp class=\"qa-empty\" id=\"qa-empty-msg\"\u003eEnter content above to generate a QR code.\u003c/p\u003e","title":"QR Code Generator"},{"content":" QR Code Generator Text or URL 0 / 2953 characters Output Size 200 × 200 px 300 × 300 px 400 × 400 px 500 × 500 px Error Correction L — 7% M — 15% Q — 25% H — 30% Foreground Color Background Color Generate QR Code Clear Generated QR Code Download PNG Copy to Clipboard History No QR codes generated yet. Clear History Generate barcodes → Barcode Generator Create business cards → Business Card Generator ","permalink":"https://productivity-works.com/tools/qr-code-generator/","summary":"\u003cdiv id=\"qr-app\"\u003e\n\u003cstyle\u003e\n#qr-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 740px;\n  margin: 0 auto;\n  color: #1e293b;\n}\n#qr-app h2.qr-section-title {\n  font-size: 1.05rem;\n  font-weight: 700;\n  color: #0f172a;\n  margin: 22px 0 10px;\n  padding-bottom: 5px;\n  border-bottom: 2px solid #e2e8f0;\n}\n#qr-app .qr-card {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 20px;\n  margin-bottom: 16px;\n}\n#qr-app label.qr-label {\n  display: block;\n  font-size: 13px;\n  font-weight: 600;\n  color: #475569;\n  margin-bottom: 5px;\n}\n#qr-app textarea.qr-textarea,\n#qr-app input.qr-input,\n#qr-app select.qr-select {\n  width: 100%;\n  padding: 9px 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  font-size: 14px;\n  background: #fff;\n  color: #1e293b;\n  box-sizing: border-box;\n  margin-bottom: 12px;\n  transition: border-color 0.2s;\n  font-family: inherit;\n}\n#qr-app textarea.qr-textarea { resize: vertical; min-height: 80px; }\n#qr-app textarea.qr-textarea:focus,\n#qr-app input.qr-input:focus,\n#qr-app select.qr-select:focus {\n  outline: none;\n  border-color: #3b82f6;\n}\n#qr-app .qr-row {\n  display: flex;\n  gap: 14px;\n  flex-wrap: wrap;\n}\n#qr-app .qr-row \u003e div { flex: 1; min-width: 150px; }\n#qr-app .qr-color-row {\n  display: flex;\n  gap: 14px;\n  flex-wrap: wrap;\n  align-items: flex-end;\n  margin-bottom: 12px;\n}\n#qr-app .qr-color-group { flex: 1; min-width: 130px; }\n#qr-app .qr-color-group label.qr-label { margin-bottom: 4px; }\n#qr-app .qr-color-input-wrap {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n#qr-app .qr-color-swatch {\n  width: 36px; height: 36px;\n  border-radius: 6px;\n  border: 1.5px solid #cbd5e1;\n  cursor: pointer;\n  padding: 0;\n  background: none;\n}\n#qr-app .qr-color-hex {\n  width: 90px;\n  padding: 6px 8px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 13px;\n  font-family: monospace;\n  background: #fff;\n  color: #1e293b;\n}\n#qr-app .qr-btn {\n  display: inline-block;\n  padding: 10px 22px;\n  border-radius: 7px;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  border: none;\n  transition: background 0.2s, opacity 0.2s;\n}\n#qr-app .qr-btn-primary {\n  background: #3b82f6;\n  color: #fff;\n}\n#qr-app .qr-btn-primary:hover { background: #2563eb; }\n#qr-app .qr-btn-secondary {\n  background: #e2e8f0;\n  color: #1e293b;\n}\n#qr-app .qr-btn-secondary:hover { background: #cbd5e1; }\n#qr-app .qr-btn-green {\n  background: #22c55e;\n  color: #fff;\n}\n#qr-app .qr-btn-green:hover { background: #16a34a; }\n#qr-app .qr-output-area {\n  text-align: center;\n  padding: 24px 16px;\n}\n#qr-app canvas#qr-canvas {\n  display: block;\n  margin: 0 auto 16px;\n  border: 1px solid #e2e8f0;\n  border-radius: 8px;\n  image-rendering: pixelated;\n}\n#qr-app .qr-char-count {\n  font-size: 12px;\n  color: #64748b;\n  text-align: right;\n  margin-top: -10px;\n  margin-bottom: 10px;\n}\n#qr-app .qr-error-msg {\n  background: #fef2f2;\n  border: 1px solid #fca5a5;\n  border-radius: 8px;\n  color: #dc2626;\n  padding: 10px 14px;\n  font-size: 13px;\n  margin-bottom: 12px;\n  display: none;\n}\n#qr-app .qr-info-bar {\n  font-size: 12px;\n  color: #64748b;\n  margin-bottom: 10px;\n}\n#qr-app .qr-history-list {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n}\n#qr-app .qr-history-list li {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  padding: 8px 10px;\n  border-bottom: 1px solid #f1f5f9;\n  font-size: 13px;\n  gap: 8px;\n}\n#qr-app .qr-history-list li:last-child { border-bottom: none; }\n#qr-app .qr-history-text {\n  flex: 1;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  color: #334155;\n  cursor: pointer;\n}\n#qr-app .qr-history-text:hover { color: #3b82f6; text-decoration: underline; }\n#qr-app .qr-history-del {\n  background: none;\n  border: none;\n  color: #94a3b8;\n  cursor: pointer;\n  font-size: 16px;\n  padding: 0 4px;\n  line-height: 1;\n}\n#qr-app .qr-history-del:hover { color: #ef4444; }\n#qr-app .qr-empty-history {\n  text-align: center;\n  color: #94a3b8;\n  font-size: 13px;\n  padding: 16px 0;\n}\n#qr-app .qr-ec-info {\n  display: flex;\n  gap: 8px;\n  flex-wrap: wrap;\n  margin-bottom: 12px;\n}\n#qr-app .qr-ec-badge {\n  padding: 4px 10px;\n  border-radius: 20px;\n  font-size: 12px;\n  font-weight: 600;\n  background: #f1f5f9;\n  color: #475569;\n  border: 1.5px solid #e2e8f0;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#qr-app .qr-ec-badge.active {\n  background: #3b82f6;\n  color: #fff;\n  border-color: #3b82f6;\n}\n@media (max-width: 500px) {\n  #qr-app .qr-row \u003e div { min-width: 100%; }\n  #qr-app .qr-color-group { min-width: 100%; }\n}\n\u003c/style\u003e\n\u003ch2 class=\"qr-section-title\"\u003eQR Code Generator\u003c/h2\u003e\n\u003cdiv class=\"qr-card\"\u003e\n  \u003clabel class=\"qr-label\" for=\"qr-text\"\u003eText or URL\u003c/label\u003e\n  \u003ctextarea class=\"qr-textarea\" id=\"qr-text\" placeholder=\"Enter text, URL, phone number, email...\" maxlength=\"2953\"\u003e\u003c/textarea\u003e\n  \u003cdiv class=\"qr-char-count\"\u003e\u003cspan id=\"qr-char-cur\"\u003e0\u003c/span\u003e / 2953 characters\u003c/div\u003e\n  \u003cdiv class=\"qr-row\"\u003e\n    \u003cdiv\u003e\n      \u003clabel class=\"qr-label\" for=\"qr-size\"\u003eOutput Size\u003c/label\u003e\n      \u003cselect class=\"qr-select\" id=\"qr-size\"\u003e\n        \u003coption value=\"200\"\u003e200 × 200 px\u003c/option\u003e\n        \u003coption value=\"300\" selected\u003e300 × 300 px\u003c/option\u003e\n        \u003coption value=\"400\"\u003e400 × 400 px\u003c/option\u003e\n        \u003coption value=\"500\"\u003e500 × 500 px\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003clabel class=\"qr-label\"\u003eError Correction\u003c/label\u003e\n      \u003cdiv class=\"qr-ec-info\"\u003e\n        \u003cspan class=\"qr-ec-badge active\" data-ec=\"L\"\u003eL — 7%\u003c/span\u003e\n        \u003cspan class=\"qr-ec-badge\" data-ec=\"M\"\u003eM — 15%\u003c/span\u003e\n        \u003cspan class=\"qr-ec-badge\" data-ec=\"Q\"\u003eQ — 25%\u003c/span\u003e\n        \u003cspan class=\"qr-ec-badge\" data-ec=\"H\"\u003eH — 30%\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"qr-color-row\"\u003e\n    \u003cdiv class=\"qr-color-group\"\u003e\n      \u003clabel class=\"qr-label\"\u003eForeground Color\u003c/label\u003e\n      \u003cdiv class=\"qr-color-input-wrap\"\u003e\n        \u003cinput type=\"color\" class=\"qr-color-swatch\" id=\"qr-fg-picker\" value=\"#000000\"\u003e\n        \u003cinput type=\"text\" class=\"qr-color-hex\" id=\"qr-fg-hex\" value=\"#000000\" maxlength=\"7\"\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"qr-color-group\"\u003e\n      \u003clabel class=\"qr-label\"\u003eBackground Color\u003c/label\u003e\n      \u003cdiv class=\"qr-color-input-wrap\"\u003e\n        \u003cinput type=\"color\" class=\"qr-color-swatch\" id=\"qr-bg-picker\" value=\"#ffffff\"\u003e\n        \u003cinput type=\"text\" class=\"qr-color-hex\" id=\"qr-bg-hex\" value=\"#ffffff\" maxlength=\"7\"\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"qr-error-msg\" id=\"qr-error\"\u003e\u003c/div\u003e\n  \u003cdiv style=\"display:flex;gap:10px;flex-wrap:wrap;\"\u003e\n    \u003cbutton class=\"qr-btn qr-btn-primary\" id=\"qr-generate-btn\"\u003eGenerate QR Code\u003c/button\u003e\n    \u003cbutton class=\"qr-btn qr-btn-secondary\" id=\"qr-clear-btn\"\u003eClear\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"qr-card\" id=\"qr-output-card\" style=\"display:none;\"\u003e\n  \u003ch2 class=\"qr-section-title\" style=\"margin-top:0;\"\u003eGenerated QR Code\u003c/h2\u003e\n  \u003cdiv class=\"qr-output-area\"\u003e\n    \u003ccanvas id=\"qr-canvas\"\u003e\u003c/canvas\u003e\n    \u003cdiv class=\"qr-info-bar\" id=\"qr-info-bar\"\u003e\u003c/div\u003e\n    \u003cdiv style=\"display:flex;gap:10px;justify-content:center;flex-wrap:wrap;\"\u003e\n      \u003cbutton class=\"qr-btn qr-btn-green\" id=\"qr-download-btn\"\u003eDownload PNG\u003c/button\u003e\n      \u003cbutton class=\"qr-btn qr-btn-secondary\" id=\"qr-copy-btn\"\u003eCopy to Clipboard\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"qr-card\" id=\"qr-history-card\"\u003e\n  \u003ch2 class=\"qr-section-title\" style=\"margin-top:0;\"\u003eHistory\u003c/h2\u003e\n  \u003cul class=\"qr-history-list\" id=\"qr-history-list\"\u003e\n    \u003cli\u003e\u003cdiv class=\"qr-empty-history\"\u003eNo QR codes generated yet.\u003c/div\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n  \u003cdiv style=\"text-align:right;margin-top:6px;\"\u003e\n    \u003cbutton class=\"qr-btn qr-btn-secondary\" id=\"qr-clear-history-btn\" style=\"font-size:12px;padding:6px 14px;\"\u003eClear History\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n'use strict';\n\n// ─── GF(256) Arithmetic ───────────────────────────────────────────────────────\nvar GF_EXP = new Uint8Array(512);\nvar GF_LOG  = new Uint8Array(256);\n(function(){\n  var x = 1;\n  for (var i = 0; i \u003c 255; i++) {\n    GF_EXP[i] = x;\n    GF_LOG[x] = i;\n    x \u003c\u003c= 1;\n    if (x \u0026 0x100) x ^= 0x11d;\n  }\n  for (var i = 255; i \u003c 512; i++) GF_EXP[i] = GF_EXP[i - 255];\n})();\n\nfunction gfMul(a, b) {\n  if (a === 0 || b === 0) return 0;\n  return GF_EXP[(GF_LOG[a] + GF_LOG[b]) % 255];\n}\nfunction gfPow(x, power) {\n  return GF_EXP[(GF_LOG[x] * power) % 255];\n}\n\nfunction rsGeneratorPoly(degree) {\n  var poly = [1];\n  for (var i = 0; i \u003c degree; i++) {\n    var term = [1, GF_EXP[i]];\n    var res = new Array(poly.length + term.length - 1).fill(0);\n    for (var j = 0; j \u003c poly.length; j++)\n      for (var k = 0; k \u003c term.length; k++)\n        res[j + k] ^= gfMul(poly[j], term[k]);\n    poly = res;\n  }\n  return poly;\n}\n\nfunction rsEncode(data, ecLen) {\n  var gen = rsGeneratorPoly(ecLen);\n  var msg = data.slice();\n  for (var i = 0; i \u003c ecLen; i++) msg.push(0);\n  for (var i = 0; i \u003c data.length; i++) {\n    var coef = msg[i];\n    if (coef !== 0) {\n      for (var j = 0; j \u003c gen.length; j++)\n        msg[i + j] ^= gfMul(gen[j], coef);\n    }\n  }\n  return msg.slice(data.length);\n}\n\n// ─── QR Code Spec Tables ──────────────────────────────────────────────────────\n// EC codewords and blocks per version (1-40), per EC level L/M/Q/H\n// Format: [ecPerBlock, blocks1, dataPerBlock1, blocks2, dataPerBlock2]\nvar EC_TABLE = {\n  L: [\n    null, // index 0 unused\n    [7,1,19,0,0],[10,1,34,0,0],[15,1,55,0,0],[20,1,80,0,0],[26,1,108,0,0],\n    [18,2,68,0,0],[20,2,78,0,0],[24,2,97,0,0],[30,2,116,0,0],[18,2,68,2,69],\n    [20,4,81,0,0],[24,2,92,2,93],[26,4,107,0,0],[30,3,115,1,116],[22,5,87,1,88],\n    [24,5,98,1,99],[28,1,107,5,108],[30,5,120,1,121],[28,3,113,4,114],[28,3,107,5,108],\n    [28,4,116,4,117],[28,2,111,7,112],[30,4,121,5,122],[30,6,117,4,118],[26,8,106,4,107],\n    [28,10,114,2,115],[30,8,122,4,123],[30,3,117,10,118],[30,7,116,7,117],[30,5,115,10,116],\n    [30,13,115,3,116],[30,17,115,0,0],[30,17,115,1,116],[30,13,115,6,116],[30,12,121,7,122],\n    [30,6,121,14,122],[30,17,122,4,123],[30,4,122,18,123],[30,20,117,4,118],[30,19,118,6,119]\n  ],\n  M: [\n    null,\n    [10,1,16,0,0],[16,1,28,0,0],[26,1,44,0,0],[18,2,32,0,0],[24,2,43,0,0],\n    [16,4,27,0,0],[18,4,31,0,0],[22,2,38,2,39],[22,3,36,2,37],[26,4,43,1,44],\n    [30,1,50,4,51],[22,6,36,2,37],[22,8,37,1,38],[24,4,40,5,41],[24,5,41,5,42],\n    [28,7,45,3,46],[28,10,46,1,47],[26,9,43,4,44],[26,3,44,11,45],[26,3,41,13,42],\n    [26,17,42,0,0],[28,17,46,0,0],[28,4,47,14,48],[28,6,45,14,46],[28,8,46,13,47],\n    [28,19,46,4,47],[28,22,45,3,46],[28,3,45,23,46],[28,21,45,7,46],[28,19,45,10,46],\n    [28,2,45,29,46],[28,10,45,23,46],[28,14,45,21,46],[28,14,46,21,47],[28,12,45,26,46],\n    [28,6,45,34,46],[28,29,45,14,46],[28,13,45,32,46],[28,40,45,7,46],[28,18,45,31,46]\n  ],\n  Q: [\n    null,\n    [13,1,13,0,0],[22,1,22,0,0],[18,2,17,0,0],[26,2,24,0,0],[18,2,15,2,16],\n    [24,4,19,0,0],[18,2,14,4,15],[22,4,18,2,19],[20,4,16,4,17],[24,6,19,2,20],\n    [28,4,22,6,23],[26,4,20,6,21],[24,8,20,4,21],[20,11,16,5,17],[30,5,24,7,25],\n    [24,15,19,2,20],[28,1,22,15,23],[28,17,22,1,23],[26,17,21,4,22],[30,15,24,5,25],\n    [28,17,22,6,23],[30,7,24,16,25],[30,11,24,14,25],[30,11,24,16,25],[30,7,24,22,25],\n    [28,28,22,6,23],[30,8,23,26,24],[30,4,24,31,25],[30,1,23,37,24],[30,15,24,25,25],\n    [30,42,24,1,25],[30,10,24,35,25],[30,29,24,19,25],[30,44,24,7,25],[30,39,24,14,25],\n    [30,46,24,10,25],[30,49,24,10,25],[30,48,24,14,25],[30,43,24,22,25],[30,34,24,34,25]\n  ],\n  H: [\n    null,\n    [17,1,9,0,0],[28,1,16,0,0],[22,2,13,0,0],[16,4,9,0,0],[22,2,11,2,12],\n    [28,4,15,0,0],[26,4,13,1,14],[26,4,14,2,15],[24,4,12,4,13],[28,6,15,2,16],\n    [24,3,12,8,13],[28,7,14,4,15],[22,11,11,5,12],[30,11,12,5,13],[24,11,12,7,13],\n    [28,3,15,13,16],[28,2,14,17,15],[28,2,14,19,15],[26,9,13,16,14],[28,15,15,10,16],\n    [30,19,16,6,17],[24,34,13,0,0],[30,16,15,14,16],[28,30,16,2,17],[30,22,15,13,16],\n    [30,33,15,4,16],[30,12,15,28,16],[30,11,15,31,16],[30,19,15,26,16],[30,23,15,25,16],\n    [30,23,15,28,16],[30,19,15,35,16],[30,11,15,46,16],[30,59,16,1,17],[30,22,15,41,16],\n    [30,2,15,64,16],[30,24,15,46,16],[30,42,15,32,16],[30,10,15,67,16],[30,20,15,61,16]\n  ]\n};\n\n// Number of data codewords per version/EC level\nfunction getDataCapacity(ver, ecLevel) {\n  var row = EC_TABLE[ecLevel][ver];\n  return row[1] * row[2] + row[3] * row[4];\n}\n\n// Alignment pattern centers per version\nvar ALIGN_POS = [\n  null,[],[6,18],[6,22],[6,26],[6,30],[6,34],\n  [6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],\n  [6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],\n  [6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],\n  [6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],\n  [6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],\n  [6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],\n  [6,30,56,82,108,132],[6,34,60,86,112,136],[6,30,58,86,114,142],\n  [6,34,62,90,118,146],[6,30,54,78,102,126,150],\n  [6,24,50,76,102,128,154],[6,28,54,80,106,132,158],\n  [6,32,58,84,110,136,162],[6,26,54,82,110,138,166],\n  [6,30,58,86,114,142,170]\n];\n\n// Format info strings (15-bit, EC+mask) pre-computed for all EC levels + 8 masks\n// These include BCH error correction\nvar FORMAT_INFO = {\n  L: [0x77c4,0x72f3,0x7daa,0x789d,0x662f,0x6318,0x6c41,0x6976],\n  M: [0x5412,0x5125,0x5e7c,0x5b4b,0x45f9,0x40ce,0x4f97,0x4aa0],\n  Q: [0x355f,0x3068,0x3f31,0x3a06,0x24b4,0x2183,0x2eda,0x2bed],\n  H: [0x1689,0x13be,0x1ce7,0x19d0,0x0762,0x0255,0x0d0c,0x083b]\n};\n\n// Version info (18-bit) for versions 7+\nvar VERSION_INFO = [\n  null,null,null,null,null,null,null,\n  0x07c94,0x085bc,0x09a99,0x0a4d3,0x0bbf6,0x0c762,0x0d847,0x0e60d,0x0f928,\n  0x10b78,0x1145d,0x12a17,0x13532,0x149a6,0x15683,0x168c9,0x177ec,\n  0x18ec4,0x191e1,0x1afab,0x1b08e,0x1cc1a,0x1d33f,0x1ed75,0x1f250,\n  0x209d5,0x216f0,0x228ba,0x2379f,0x24b0b,0x2542e,0x26a64,0x27541,\n  0x28c69\n];\n\n// ─── QR Encoder ──────────────────────────────────────────────────────────────\nfunction QRCode(text, ecLevel) {\n  this.text = text;\n  this.ecLevel = ecLevel || 'M';\n  this.version = 1;\n  this.modules = null;\n  this.size = 0;\n  this._encode();\n}\n\nQRCode.prototype._encode = function() {\n  var data = this._encodeData();\n  this.modules = this._buildMatrix(data);\n};\n\nQRCode.prototype._encodeData = function() {\n  var text = this.text;\n  var ecLevel = this.ecLevel;\n\n  // Determine mode: numeric, alphanumeric, or byte\n  var mode = this._chooseMode(text);\n  var modeIndicator = { numeric: 0x1, alphanumeric: 0x2, byte: 0x4 };\n\n  // Find minimum version\n  var ver = 1;\n  for (ver = 1; ver \u003c= 40; ver++) {\n    var cap = getDataCapacity(ver, ecLevel);\n    var len = this._encodedLength(text, mode, ver);\n    if (Math.ceil(len / 8) \u003c= cap) break;\n  }\n  if (ver \u003e 40) throw new Error('Data too long for QR code');\n  this.version = ver;\n  this.size = ver * 4 + 17;\n\n  // Build data bit stream\n  var bits = [];\n  var push = function(val, count) {\n    for (var i = count - 1; i \u003e= 0; i--)\n      bits.push((val \u003e\u003e i) \u0026 1);\n  };\n\n  // Mode indicator\n  push(modeIndicator[mode], 4);\n\n  // Character count indicator\n  var ccBits = this._ccBits(mode, ver);\n  push(text.length, ccBits);\n\n  // Data\n  if (mode === 'numeric') {\n    for (var i = 0; i \u003c text.length; ) {\n      var chunk = text.slice(i, i + 3);\n      var digits = chunk.length;\n      push(parseInt(chunk, 10), digits === 3 ? 10 : digits === 2 ? 7 : 4);\n      i += 3;\n    }\n  } else if (mode === 'alphanumeric') {\n    var ANC = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';\n    for (var i = 0; i \u003c text.length; ) {\n      if (i + 1 \u003c text.length) {\n        push(ANC.indexOf(text[i]) * 45 + ANC.indexOf(text[i+1]), 11);\n        i += 2;\n      } else {\n        push(ANC.indexOf(text[i]), 6);\n        i++;\n      }\n    }\n  } else { // byte\n    var bytes = this._toUTF8(text);\n    for (var i = 0; i \u003c bytes.length; i++)\n      push(bytes[i], 8);\n  }\n\n  // Terminator\n  var totalBits = getDataCapacity(ver, ecLevel) * 8;\n  for (var i = 0; i \u003c 4 \u0026\u0026 bits.length \u003c totalBits; i++) bits.push(0);\n\n  // Pad to byte boundary\n  while (bits.length % 8 !== 0) bits.push(0);\n\n  // Pad codewords\n  var padBytes = [0xec, 0x11];\n  var pi = 0;\n  while (bits.length \u003c totalBits) {\n    push(padBytes[pi % 2], 8);\n    pi++;\n  }\n\n  // Convert bits to bytes\n  var dataBytes = [];\n  for (var i = 0; i \u003c bits.length; i += 8) {\n    var b = 0;\n    for (var j = 0; j \u003c 8; j++) b = (b \u003c\u003c 1) | bits[i + j];\n    dataBytes.push(b);\n  }\n\n  // Interleave data and EC codewords per QR spec\n  var row = EC_TABLE[ecLevel][ver];\n  var ecPerBlock = row[0];\n  var blocks1 = row[1], dataPerBlock1 = row[2];\n  var blocks2 = row[3], dataPerBlock2 = row[4];\n  var totalBlocks = blocks1 + blocks2;\n\n  // Split into blocks\n  var blocks = [];\n  var offset = 0;\n  for (var b = 0; b \u003c blocks1; b++) {\n    blocks.push(dataBytes.slice(offset, offset + dataPerBlock1));\n    offset += dataPerBlock1;\n  }\n  for (var b = 0; b \u003c blocks2; b++) {\n    blocks.push(dataBytes.slice(offset, offset + dataPerBlock2));\n    offset += dataPerBlock2;\n  }\n\n  // Generate EC for each block\n  var ecBlocks = blocks.map(function(blk) { return rsEncode(blk, ecPerBlock); });\n\n  // Interleave data codewords\n  var result = [];\n  var maxData = Math.max(dataPerBlock1, dataPerBlock2);\n  for (var i = 0; i \u003c maxData; i++)\n    for (var b = 0; b \u003c blocks.length; b++)\n      if (i \u003c blocks[b].length) result.push(blocks[b][i]);\n\n  // Interleave EC codewords\n  for (var i = 0; i \u003c ecPerBlock; i++)\n    for (var b = 0; b \u003c ecBlocks.length; b++)\n      result.push(ecBlocks[b][i]);\n\n  return result;\n};\n\nQRCode.prototype._chooseMode = function(text) {\n  if (/^\\d+$/.test(text)) return 'numeric';\n  if (/^[0-9A-Z $%*+\\-./:]+$/.test(text)) return 'alphanumeric';\n  return 'byte';\n};\n\nQRCode.prototype._ccBits = function(mode, ver) {\n  var table = {\n    numeric:      [10, 12, 14],\n    alphanumeric: [9, 11, 13],\n    byte:         [8, 16, 16]\n  };\n  var idx = ver \u003c= 9 ? 0 : ver \u003c= 26 ? 1 : 2;\n  return table[mode][idx];\n};\n\nQRCode.prototype._encodedLength = function(text, mode, ver) {\n  var ccBits = this._ccBits(mode, ver);\n  if (mode === 'numeric') {\n    var full = Math.floor(text.length / 3);\n    var rem  = text.length % 3;\n    return 4 + ccBits + full * 10 + (rem === 2 ? 7 : rem === 1 ? 4 : 0);\n  } else if (mode === 'alphanumeric') {\n    return 4 + ccBits + Math.floor(text.length / 2) * 11 + (text.length % 2) * 6;\n  } else {\n    var bytes = this._toUTF8(text);\n    return 4 + ccBits + bytes.length * 8;\n  }\n};\n\nQRCode.prototype._toUTF8 = function(str) {\n  var bytes = [];\n  for (var i = 0; i \u003c str.length; i++) {\n    var code = str.charCodeAt(i);\n    if (code \u003c 0x80) {\n      bytes.push(code);\n    } else if (code \u003c 0x800) {\n      bytes.push(0xc0 | (code \u003e\u003e 6), 0x80 | (code \u0026 0x3f));\n    } else if (code \u003e= 0xd800 \u0026\u0026 code \u003c= 0xdbff \u0026\u0026 i + 1 \u003c str.length) {\n      var hi = code, lo = str.charCodeAt(++i);\n      var cp = 0x10000 + ((hi - 0xd800) \u003c\u003c 10) + (lo - 0xdc00);\n      bytes.push(0xf0|(cp\u003e\u003e18), 0x80|((cp\u003e\u003e12)\u00260x3f), 0x80|((cp\u003e\u003e6)\u00260x3f), 0x80|(cp\u00260x3f));\n    } else {\n      bytes.push(0xe0|(code\u003e\u003e12), 0x80|((code\u003e\u003e6)\u00260x3f), 0x80|(code\u00260x3f));\n    }\n  }\n  return bytes;\n};\n\nQRCode.prototype._buildMatrix = function(data) {\n  var ver = this.version;\n  var size = this.size;\n  var N = size;\n\n  // Matrix: true = dark, false = light, null = unset\n  var mat = [];\n  var func = []; // functional module (cannot be overwritten by data)\n  for (var r = 0; r \u003c N; r++) {\n    mat.push(new Array(N).fill(false));\n    func.push(new Array(N).fill(false));\n  }\n\n  var setFunc = function(r, c, dark) {\n    mat[r][c] = dark;\n    func[r][c] = true;\n  };\n\n  // Finder patterns\n  var placeFinder = function(row, col) {\n    for (var r = -1; r \u003c= 7; r++) {\n      for (var c = -1; c \u003c= 7; c++) {\n        if (row + r \u003c 0 || row + r \u003e= N || col + c \u003c 0 || col + c \u003e= N) continue;\n        var dark = (r \u003e= 0 \u0026\u0026 r \u003c= 6 \u0026\u0026 (c === 0 || c === 6)) ||\n                   (c \u003e= 0 \u0026\u0026 c \u003c= 6 \u0026\u0026 (r === 0 || r === 6)) ||\n                   (r \u003e= 2 \u0026\u0026 r \u003c= 4 \u0026\u0026 c \u003e= 2 \u0026\u0026 c \u003c= 4);\n        setFunc(row + r, col + c, dark);\n      }\n    }\n  };\n  placeFinder(0, 0);\n  placeFinder(0, N - 7);\n  placeFinder(N - 7, 0);\n\n  // Separators are already set to light by finder (border of -1)\n\n  // Timing patterns\n  for (var i = 8; i \u003c N - 8; i++) {\n    setFunc(6, i, i % 2 === 0);\n    setFunc(i, 6, i % 2 === 0);\n  }\n\n  // Dark module (always dark, version 1+)\n  setFunc(N - 8, 8, true);\n\n  // Alignment patterns\n  var ap = ALIGN_POS[ver];\n  for (var ai = 0; ai \u003c ap.length; ai++) {\n    for (var aj = 0; aj \u003c ap.length; aj++) {\n      var cr = ap[ai], cc = ap[aj];\n      if (func[cr][cc]) continue; // skip if finder already placed\n      for (var r = -2; r \u003c= 2; r++) {\n        for (var c = -2; c \u003c= 2; c++) {\n          setFunc(cr + r, cc + c,\n            r === -2 || r === 2 || c === -2 || c === 2 || (r === 0 \u0026\u0026 c === 0));\n        }\n      }\n    }\n  }\n\n  // Format info placeholders (will be overwritten)\n  for (var i = 0; i \u003c= 8; i++) {\n    if (!func[8][i]) func[8][i] = true;\n    if (!func[i][8]) func[i][8] = true;\n  }\n  for (var i = N - 8; i \u003c N; i++) {\n    func[8][i] = true;\n    func[i][8] = true;\n  }\n\n  // Version info placeholders (version 7+)\n  if (ver \u003e= 7) {\n    for (var r = 0; r \u003c 6; r++) {\n      for (var c = N - 11; c \u003c N - 8; c++) {\n        func[r][c] = true;\n        func[c][r] = true;\n      }\n    }\n  }\n\n  // Place data bits\n  var dataBits = [];\n  for (var i = 0; i \u003c data.length; i++)\n    for (var b = 7; b \u003e= 0; b--)\n      dataBits.push((data[i] \u003e\u003e b) \u0026 1);\n\n  var bitIdx = 0;\n  var right = N - 1;\n  var goUp = true;\n  while (right \u003e= 1) {\n    if (right === 6) right--; // skip timing column\n    for (var vert = 0; vert \u003c N; vert++) {\n      var row = goUp ? N - 1 - vert : vert;\n      for (var col = right; col \u003e= right - 1; col--) {\n        if (!func[row][col]) {\n          mat[row][col] = bitIdx \u003c dataBits.length ? dataBits[bitIdx] === 1 : false;\n          bitIdx++;\n        }\n      }\n    }\n    right -= 2;\n    goUp = !goUp;\n  }\n\n  // Apply best mask\n  var bestMask = 0, bestPenalty = Infinity;\n  for (var mask = 0; mask \u003c 8; mask++) {\n    var masked = applyMask(mat, func, mask, N);\n    var penalty = calcPenalty(masked, N);\n    if (penalty \u003c bestPenalty) {\n      bestPenalty = penalty;\n      bestMask = mask;\n    }\n  }\n\n  var final = applyMask(mat, func, bestMask, N);\n\n  // Place format information\n  var fmtBits = FORMAT_INFO[this.ecLevel][bestMask];\n  placeFormat(final, fmtBits, N);\n\n  // Place version information\n  if (ver \u003e= 7) {\n    var verBits = VERSION_INFO[ver];\n    placeVersion(final, verBits, N);\n  }\n\n  return final;\n};\n\nfunction applyMask(mat, func, mask, N) {\n  var out = [];\n  for (var r = 0; r \u003c N; r++) out.push(mat[r].slice());\n  var conditions = [\n    function(r,c){return (r+c)%2===0;},\n    function(r,c){return r%2===0;},\n    function(r,c){return c%3===0;},\n    function(r,c){return (r+c)%3===0;},\n    function(r,c){return (Math.floor(r/2)+Math.floor(c/3))%2===0;},\n    function(r,c){return (r*c)%2+(r*c)%3===0;},\n    function(r,c){return ((r*c)%2+(r*c)%3)%2===0;},\n    function(r,c){return ((r+c)%2+(r*c)%3)%2===0;}\n  ];\n  var cond = conditions[mask];\n  for (var r = 0; r \u003c N; r++)\n    for (var c = 0; c \u003c N; c++)\n      if (!func[r][c] \u0026\u0026 cond(r, c)) out[r][c] = !out[r][c];\n  return out;\n}\n\nfunction calcPenalty(mat, N) {\n  var pen = 0;\n  // Rule 1: 5+ in a row\n  for (var r = 0; r \u003c N; r++) {\n    for (var c = 0; c \u003c N; ) {\n      var color = mat[r][c], len = 0;\n      while (c \u003c N \u0026\u0026 mat[r][c] === color) { len++; c++; }\n      if (len \u003e= 5) pen += len - 2;\n    }\n  }\n  for (var c = 0; c \u003c N; c++) {\n    for (var r = 0; r \u003c N; ) {\n      var color = mat[r][c], len = 0;\n      while (r \u003c N \u0026\u0026 mat[r][c] === color) { len++; r++; }\n      if (len \u003e= 5) pen += len - 2;\n    }\n  }\n  // Rule 2: 2x2 blocks\n  for (var r = 0; r \u003c N - 1; r++)\n    for (var c = 0; c \u003c N - 1; c++)\n      if (mat[r][c] === mat[r+1][c] \u0026\u0026 mat[r][c] === mat[r][c+1] \u0026\u0026 mat[r][c] === mat[r+1][c+1])\n        pen += 3;\n  // Rule 3: finder-like patterns\n  var p1 = [1,0,1,1,1,0,1,0,0,0,0], p2 = [0,0,0,0,1,0,1,1,1,0,1];\n  for (var r = 0; r \u003c N; r++) {\n    for (var c = 0; c \u003c= N - 11; c++) {\n      var m1=true,m2=true;\n      for (var k=0;k\u003c11;k++){\n        if(mat[r][c+k]!==(p1[k]===1)) m1=false;\n        if(mat[r][c+k]!==(p2[k]===1)) m2=false;\n      }\n      if(m1||m2) pen+=40;\n      m1=true; m2=true;\n      for(var k=0;k\u003c11;k++){\n        if(mat[c+k][r]!==(p1[k]===1)) m1=false;\n        if(mat[c+k][r]!==(p2[k]===1)) m2=false;\n      }\n      if(m1||m2) pen+=40;\n    }\n  }\n  // Rule 4: dark module ratio\n  var dark = 0;\n  for (var r = 0; r \u003c N; r++) for (var c = 0; c \u003c N; c++) if (mat[r][c]) dark++;\n  var ratio = dark / (N * N);\n  pen += Math.abs(Math.round(ratio * 20) - 10) * 10;\n  return pen;\n}\n\nfunction placeFormat(mat, bits, N) {\n  // Format around top-left finder\n  var seq = [8,8,8,8,8,8,8,8,7,5,4,3,2,1,0];\n  var pos = [\n    [8,0],[8,1],[8,2],[8,3],[8,4],[8,5],[8,7],[8,8],\n    [7,8],[5,8],[4,8],[3,8],[2,8],[1,8],[0,8]\n  ];\n  for (var i = 0; i \u003c 15; i++) {\n    var dark = ((bits \u003e\u003e (14 - i)) \u0026 1) === 1;\n    mat[pos[i][0]][pos[i][1]] = dark;\n    if (i \u003c 8)\n      mat[N - 1 - i][8] = dark;\n    else\n      mat[8][N - 15 + i] = dark;\n  }\n  mat[N - 8][8] = true; // dark module\n}\n\nfunction placeVersion(mat, bits, N) {\n  for (var i = 0; i \u003c 18; i++) {\n    var dark = ((bits \u003e\u003e i) \u0026 1) === 1;\n    var r = Math.floor(i / 3), c = N - 11 + (i % 3);\n    mat[r][c] = dark;\n    mat[c][r] = dark;\n  }\n}\n\n// ─── Canvas Rendering ─────────────────────────────────────────────────────────\nfunction renderQR(qr, canvas, size, fg, bg) {\n  var N = qr.size;\n  var quiet = 4; // quiet zone modules\n  var total = N + quiet * 2;\n  var mod = size / total;\n  canvas.width = size;\n  canvas.height = size;\n  var ctx = canvas.getContext('2d');\n  ctx.fillStyle = bg;\n  ctx.fillRect(0, 0, size, size);\n  ctx.fillStyle = fg;\n  for (var r = 0; r \u003c N; r++) {\n    for (var c = 0; c \u003c N; c++) {\n      if (qr.modules[r][c]) {\n        var x = (c + quiet) * mod;\n        var y = (r + quiet) * mod;\n        ctx.fillRect(Math.floor(x), Math.floor(y), Math.ceil(mod), Math.ceil(mod));\n      }\n    }\n  }\n}\n\n// ─── UI Logic ─────────────────────────────────────────────────────────────────\nvar currentEC = 'L';\nvar history = [];\n\n// EC badge selection\ndocument.querySelectorAll('#qr-app .qr-ec-badge').forEach(function(badge) {\n  badge.addEventListener('click', function() {\n    document.querySelectorAll('#qr-app .qr-ec-badge').forEach(function(b) { b.classList.remove('active'); });\n    badge.classList.add('active');\n    currentEC = badge.getAttribute('data-ec');\n  });\n});\n\n// Character count\nvar textArea = document.getElementById('qr-text');\nvar charCur = document.getElementById('qr-char-cur');\ntextArea.addEventListener('input', function() {\n  charCur.textContent = textArea.value.length;\n});\n\n// Color pickers\nfunction syncColor(picker, hex) {\n  picker.addEventListener('input', function() {\n    hex.value = picker.value.toUpperCase();\n  });\n  hex.addEventListener('input', function() {\n    if (/^#[0-9A-Fa-f]{6}$/.test(hex.value)) picker.value = hex.value;\n  });\n}\nsyncColor(document.getElementById('qr-fg-picker'), document.getElementById('qr-fg-hex'));\nsyncColor(document.getElementById('qr-bg-picker'), document.getElementById('qr-bg-hex'));\n\n// Generate\ndocument.getElementById('qr-generate-btn').addEventListener('click', function() {\n  var text = textArea.value.trim();\n  var errEl = document.getElementById('qr-error');\n  errEl.style.display = 'none';\n  if (!text) {\n    errEl.textContent = 'Please enter some text or URL.';\n    errEl.style.display = 'block';\n    return;\n  }\n  var size = parseInt(document.getElementById('qr-size').value, 10);\n  var fg = document.getElementById('qr-fg-hex').value || '#000000';\n  var bg = document.getElementById('qr-bg-hex').value || '#ffffff';\n  try {\n    var qr = new QRCode(text, currentEC);\n    var canvas = document.getElementById('qr-canvas');\n    renderQR(qr, canvas, size, fg, bg);\n    document.getElementById('qr-output-card').style.display = 'block';\n    document.getElementById('qr-info-bar').textContent =\n      'Version ' + qr.version + ' · ' + qr.size + '×' + qr.size + ' modules · EC Level ' + currentEC;\n    addHistory(text);\n  } catch (e) {\n    errEl.textContent = 'Error: ' + e.message;\n    errEl.style.display = 'block';\n  }\n});\n\n// Clear\ndocument.getElementById('qr-clear-btn').addEventListener('click', function() {\n  textArea.value = '';\n  charCur.textContent = '0';\n  document.getElementById('qr-error').style.display = 'none';\n  document.getElementById('qr-output-card').style.display = 'none';\n});\n\n// Download\ndocument.getElementById('qr-download-btn').addEventListener('click', function() {\n  var canvas = document.getElementById('qr-canvas');\n  var a = document.createElement('a');\n  a.download = 'qrcode.png';\n  a.href = canvas.toDataURL('image/png');\n  a.click();\n});\n\n// Copy to clipboard\ndocument.getElementById('qr-copy-btn').addEventListener('click', function() {\n  var canvas = document.getElementById('qr-canvas');\n  canvas.toBlob(function(blob) {\n    try {\n      navigator.clipboard.write([new ClipboardItem({'image/png': blob})]).then(function() {\n        var btn = document.getElementById('qr-copy-btn');\n        btn.textContent = 'Copied!';\n        setTimeout(function() { btn.textContent = 'Copy to Clipboard'; }, 2000);\n      });\n    } catch(e) {\n      alert('Copy to clipboard not supported in this browser. Use Download instead.');\n    }\n  });\n});\n\n// History\nfunction addHistory(text) {\n  history = history.filter(function(h) { return h !== text; });\n  history.unshift(text);\n  if (history.length \u003e 20) history.pop();\n  renderHistory();\n}\n\nfunction renderHistory() {\n  var list = document.getElementById('qr-history-list');\n  if (history.length === 0) {\n    list.innerHTML = '\u003cli\u003e\u003cdiv class=\"qr-empty-history\"\u003eNo QR codes generated yet.\u003c/div\u003e\u003c/li\u003e';\n    return;\n  }\n  list.innerHTML = history.map(function(text, idx) {\n    return '\u003cli\u003e' +\n      '\u003cspan class=\"qr-history-text\" data-idx=\"' + idx + '\" title=\"' + escHtml(text) + '\"\u003e' + escHtml(text.length \u003e 60 ? text.slice(0, 60) + '…' : text) + '\u003c/span\u003e' +\n      '\u003cbutton class=\"qr-history-del\" data-idx=\"' + idx + '\" title=\"Remove\"\u003e\u0026#215;\u003c/button\u003e' +\n      '\u003c/li\u003e';\n  }).join('');\n  list.querySelectorAll('.qr-history-text').forEach(function(el) {\n    el.addEventListener('click', function() {\n      textArea.value = history[parseInt(el.getAttribute('data-idx'), 10)];\n      charCur.textContent = textArea.value.length;\n      document.getElementById('qr-generate-btn').click();\n    });\n  });\n  list.querySelectorAll('.qr-history-del').forEach(function(el) {\n    el.addEventListener('click', function() {\n      history.splice(parseInt(el.getAttribute('data-idx'), 10), 1);\n      renderHistory();\n    });\n  });\n}\n\ndocument.getElementById('qr-clear-history-btn').addEventListener('click', function() {\n  history = [];\n  renderHistory();\n});\n\nfunction escHtml(s) {\n  return s.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;').replace(/\"/g,'\u0026quot;');\n}\n\nrenderHistory();\n\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003eGenerate barcodes → \u003ca href=\"https://productivity-works.com/tools/barcode-generator/\"\u003eBarcode Generator\u003c/a\u003e\n\u003c/p\u003e","title":"QR Code Generator"},{"content":" + New \u0026#8595; Download \u0026#128438; Print \u0026#128190; Save \u0026#9790; Dark \u0026lt;!-- Sidebar --\u0026gt; \u0026lt;div class=\u0026quot;qn-sidebar\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;qn-search-wrap\u0026quot;\u0026gt; \u0026lt;input class=\u0026quot;qn-search\u0026quot; id=\u0026quot;qn-search\u0026quot; type=\u0026quot;search\u0026quot; placeholder=\u0026quot;Search notes...\u0026quot; oninput=\u0026quot;QN.onSearch(this.value)\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;qn-note-list\u0026quot; id=\u0026quot;qn-note-list\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;qn-sidebar-footer\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;qn-new-btn\u0026quot; onclick=\u0026quot;QN.newNote()\u0026quot;\u0026gt;+ New Note\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Editor --\u0026gt; \u0026lt;div class=\u0026quot;qn-editor-pane\u0026quot; id=\u0026quot;qn-editor-pane\u0026quot;\u0026gt; \u0026lt;!-- empty state shown when no note selected --\u0026gt; \u0026lt;div class=\u0026quot;qn-empty\u0026quot; id=\u0026quot;qn-empty\u0026quot;\u0026gt; \u0026lt;svg width=\u0026quot;48\u0026quot; height=\u0026quot;48\u0026quot; viewBox=\u0026quot;0 0 24 24\u0026quot; fill=\u0026quot;none\u0026quot; stroke=\u0026quot;currentColor\u0026quot; stroke-width=\u0026quot;1.5\u0026quot;\u0026gt;\u0026lt;path d=\u0026quot;M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\u0026quot;/\u0026gt;\u0026lt;polyline points=\u0026quot;14 2 14 8 20 8\u0026quot;/\u0026gt;\u0026lt;line x1=\u0026quot;16\u0026quot; y1=\u0026quot;13\u0026quot; x2=\u0026quot;8\u0026quot; y2=\u0026quot;13\u0026quot;/\u0026gt;\u0026lt;line x1=\u0026quot;16\u0026quot; y1=\u0026quot;17\u0026quot; x2=\u0026quot;8\u0026quot; y2=\u0026quot;17\u0026quot;/\u0026gt;\u0026lt;polyline points=\u0026quot;10 9 9 9 8 9\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt; \u0026lt;p\u0026gt;No note selected.\u0026lt;br\u0026gt;Create one with \u0026lt;strong\u0026gt;+ New\u0026lt;/strong\u0026gt;.\u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- title bar --\u0026gt; \u0026lt;div id=\u0026quot;qn-title-bar\u0026quot; class=\u0026quot;qn-title-bar\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt; \u0026lt;input class=\u0026quot;qn-note-name-input\u0026quot; id=\u0026quot;qn-note-name\u0026quot; type=\u0026quot;text\u0026quot; placeholder=\u0026quot;Note title...\u0026quot; oninput=\u0026quot;QN.onTitleChange(this.value)\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- textarea --\u0026gt; \u0026lt;textarea class=\u0026quot;qn-textarea\u0026quot; id=\u0026quot;qn-textarea\u0026quot; placeholder=\u0026quot;Start writing your note here...\u0026quot; style=\u0026quot;display:none\u0026quot; oninput=\u0026quot;QN.onTextChange(this.value)\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;!-- status bar --\u0026gt; \u0026lt;div class=\u0026quot;qn-status\u0026quot; id=\u0026quot;qn-status\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt; \u0026lt;span id=\u0026quot;qn-words\u0026quot;\u0026gt;0 words\u0026lt;/span\u0026gt; \u0026lt;span id=\u0026quot;qn-chars\u0026quot;\u0026gt;0 chars\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;qn-saved hidden\u0026quot; id=\u0026quot;qn-saved-indicator\u0026quot;\u0026gt;Saved \u0026amp;#10003;\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; How to Use Quick Notes Type in the large text area on the right. Your note is saved automatically to your browser every 2 seconds — no account, no server, no internet connection needed after the page loads. The last-saved indicator in the status bar confirms each save. Press Ctrl+S (or Cmd+S on Mac) at any time to force an immediate save.\nMultiple notes: Click + New in the toolbar or sidebar to create a new note. Each note gets its own entry in the left sidebar, sorted by most recently edited. Click any note in the list to switch to it instantly.\nRenaming: Click the title bar at the top of the editor to rename the current note. The title updates in the sidebar as you type.\nSearch: Type in the search box above the note list to filter notes by title or content. Matching text is highlighted in yellow.\nDownload: The Download button exports the current note as a plain .txt file to your device. The filename is based on the note title.\nDelete: Hover over any note in the sidebar to reveal the × button, then click to delete. A confirmation prompt prevents accidental deletion.\nTheme: Toggle between light and dark mode with the button in the top-right corner of the toolbar. Your preference is remembered across visits.\nPrint: Click the Print button to open a clean print-preview window formatted for paper.\nWhy Keep Notes in Your Browser? Browser-based notepads have one major advantage over native apps: they are available on any device with a browser, with zero installation. When you are on a work computer, a public library terminal, or a friend\u0026rsquo;s laptop, Quick Notes is just a bookmark away. All data lives in your browser\u0026rsquo;s localStorage, which means it stays private — nothing is ever uploaded.\nFor sensitive information, however, keep in mind that localStorage is accessible to JavaScript running on the same origin, so treat it like any local file on your computer: fine for working notes, but use proper encryption tools for truly sensitive material.\nUnlike cloud note-taking services, Quick Notes works entirely offline after the first page load. If your internet drops mid-session, you will not lose anything — the auto-save timer keeps running and your data stays local.\nRelated Tools Format and preview Markdown → Markdown Editor Count words and characters in your writing → Word Counter Change text case instantly → Text Case Converter ","permalink":"https://productivity-works.com/tools/quick-notes/","summary":"\u003cdiv id=\"qn-app\"\u003e\n\u003cstyle\u003e\n/* ── Reset \u0026 Root ─────────────────────────────────────── */\n#qn-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", sans-serif;\n  max-width: 1100px;\n  margin: 0 auto;\n  color: #1e1b4b;\n  --accent: #7c3aed;\n  --accent-dk: #6d28d9;\n  --accent-lt: #ede9fe;\n  --bg: #f8f7ff;\n  --surface: #ffffff;\n  --border: #d8d3f0;\n  --text: #1e1b4b;\n  --text-muted: #6b7280;\n  --sidebar-bg: #f5f3ff;\n  --textarea-bg: #fafafe;\n  --status-bg: #f3f0ff;\n  --btn-ghost: #ede9fe;\n  --btn-ghost-text: #5b21b6;\n  --shadow: 0 2px 12px rgba(124,58,237,0.10);\n  --radius: 10px;\n  --danger: #dc2626;\n  --danger-lt: #fee2e2;\n  --success: #059669;\n}\n#qn-app * { box-sizing: border-box; }\n\n/* ── Dark theme ───────────────────────────────────────── */\n#qn-app.qn-dark {\n  --bg: #0f0e1a;\n  --surface: #1a1828;\n  --border: #3b3660;\n  --text: #e5e1ff;\n  --text-muted: #9d97c4;\n  --sidebar-bg: #13111f;\n  --textarea-bg: #15132a;\n  --status-bg: #1a1828;\n  --btn-ghost: #2a2540;\n  --btn-ghost-text: #c4b5fd;\n  --shadow: 0 2px 12px rgba(0,0,0,0.40);\n  color: var(--text);\n}\n\n/* ── Layout ───────────────────────────────────────────── */\n#qn-app .qn-wrap {\n  background: var(--bg);\n  border-radius: var(--radius);\n  border: 1px solid var(--border);\n  overflow: hidden;\n  box-shadow: var(--shadow);\n  display: flex;\n  flex-direction: column;\n  min-height: 600px;\n}\n#qn-app .qn-toolbar {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  padding: 8px 12px;\n  background: var(--surface);\n  border-bottom: 1px solid var(--border);\n  flex-wrap: wrap;\n}\n#qn-app .qn-body {\n  display: flex;\n  flex: 1;\n  min-height: 0;\n}\n#qn-app .qn-sidebar {\n  width: 220px;\n  min-width: 180px;\n  background: var(--sidebar-bg);\n  border-right: 1px solid var(--border);\n  display: flex;\n  flex-direction: column;\n  overflow: hidden;\n}\n#qn-app .qn-search-wrap {\n  padding: 8px;\n  border-bottom: 1px solid var(--border);\n}\n#qn-app .qn-search {\n  width: 100%;\n  padding: 6px 10px;\n  border: 1px solid var(--border);\n  border-radius: 6px;\n  background: var(--surface);\n  color: var(--text);\n  font-size: 13px;\n  outline: none;\n}\n#qn-app .qn-search:focus { border-color: var(--accent); }\n#qn-app .qn-note-list {\n  flex: 1;\n  overflow-y: auto;\n  padding: 4px;\n}\n#qn-app .qn-note-item {\n  padding: 8px 10px;\n  border-radius: 7px;\n  cursor: pointer;\n  margin-bottom: 2px;\n  transition: background 0.15s;\n  border: 1px solid transparent;\n  position: relative;\n}\n#qn-app .qn-note-item:hover { background: var(--btn-ghost); }\n#qn-app .qn-note-item.active {\n  background: var(--accent-lt);\n  border-color: var(--accent);\n}\n#qn-app.qn-dark .qn-note-item.active { background: var(--btn-ghost); }\n#qn-app .qn-note-title {\n  font-size: 13px;\n  font-weight: 600;\n  color: var(--text);\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  max-width: 150px;\n}\n#qn-app .qn-note-preview {\n  font-size: 11px;\n  color: var(--text-muted);\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  max-width: 160px;\n  margin-top: 2px;\n}\n#qn-app .qn-note-del {\n  position: absolute;\n  top: 6px;\n  right: 6px;\n  background: none;\n  border: none;\n  color: var(--text-muted);\n  cursor: pointer;\n  font-size: 14px;\n  line-height: 1;\n  padding: 2px 4px;\n  border-radius: 4px;\n  opacity: 0;\n  transition: opacity 0.15s;\n}\n#qn-app .qn-note-item:hover .qn-note-del { opacity: 1; }\n#qn-app .qn-note-del:hover { color: var(--danger); background: var(--danger-lt); }\n#qn-app .qn-sidebar-footer {\n  padding: 8px;\n  border-top: 1px solid var(--border);\n}\n#qn-app .qn-new-btn {\n  width: 100%;\n  padding: 8px;\n  background: var(--accent);\n  color: #fff;\n  border: none;\n  border-radius: 7px;\n  cursor: pointer;\n  font-size: 13px;\n  font-weight: 600;\n  transition: background 0.15s;\n}\n#qn-app .qn-new-btn:hover { background: var(--accent-dk); }\n\n/* ── Editor pane ──────────────────────────────────────── */\n#qn-app .qn-editor-pane {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  background: var(--surface);\n  min-width: 0;\n}\n#qn-app .qn-title-bar {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 8px 12px;\n  border-bottom: 1px solid var(--border);\n  background: var(--surface);\n}\n#qn-app .qn-note-name-input {\n  flex: 1;\n  border: 1px solid transparent;\n  background: transparent;\n  color: var(--text);\n  font-size: 15px;\n  font-weight: 700;\n  padding: 4px 8px;\n  border-radius: 6px;\n  outline: none;\n  transition: border-color 0.15s;\n}\n#qn-app .qn-note-name-input:focus {\n  border-color: var(--accent);\n  background: var(--textarea-bg);\n}\n#qn-app .qn-textarea {\n  flex: 1;\n  width: 100%;\n  border: none;\n  outline: none;\n  resize: none;\n  padding: 16px 20px;\n  font-size: 15px;\n  line-height: 1.75;\n  background: var(--textarea-bg);\n  color: var(--text);\n  font-family: inherit;\n  min-height: 420px;\n}\n#qn-app .qn-textarea::placeholder { color: var(--text-muted); }\n\n/* ── Status bar ───────────────────────────────────────── */\n#qn-app .qn-status {\n  display: flex;\n  align-items: center;\n  gap: 14px;\n  padding: 6px 14px;\n  background: var(--status-bg);\n  border-top: 1px solid var(--border);\n  font-size: 12px;\n  color: var(--text-muted);\n  flex-wrap: wrap;\n}\n#qn-app .qn-status .qn-saved {\n  margin-left: auto;\n  color: var(--success);\n  font-weight: 600;\n  font-size: 11px;\n  transition: opacity 0.4s;\n}\n#qn-app .qn-status .qn-saved.hidden { opacity: 0; }\n\n/* ── Toolbar buttons ──────────────────────────────────── */\n#qn-app .qn-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 5px;\n  padding: 5px 11px;\n  border-radius: 6px;\n  border: 1px solid var(--border);\n  background: var(--btn-ghost);\n  color: var(--btn-ghost-text);\n  cursor: pointer;\n  font-size: 13px;\n  font-weight: 500;\n  transition: background 0.15s, border-color 0.15s;\n  white-space: nowrap;\n}\n#qn-app .qn-btn:hover { background: var(--accent-lt); border-color: var(--accent); color: var(--accent-dk); }\n#qn-app .qn-btn-primary {\n  background: var(--accent);\n  color: #fff;\n  border-color: var(--accent);\n}\n#qn-app .qn-btn-primary:hover { background: var(--accent-dk); border-color: var(--accent-dk); color: #fff; }\n#qn-app .qn-spacer { flex: 1; }\n\n/* ── Search highlight ─────────────────────────────────── */\n#qn-app .qn-highlight { background: #fde68a; border-radius: 2px; }\n\n/* ── Empty state ──────────────────────────────────────── */\n#qn-app .qn-empty {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  gap: 12px;\n  color: var(--text-muted);\n  padding: 40px;\n  text-align: center;\n}\n#qn-app .qn-empty svg { opacity: 0.3; }\n#qn-app .qn-empty p { font-size: 15px; margin: 0; }\n\n/* ── freee CTA ────────────────────────────────────────── */\n#qn-freee-cta {\n  margin-top: 28px;\n  padding: 18px 20px;\n  background: var(--accent-lt, #ede9fe);\n  border: 1px solid var(--border, #d8d3f0);\n  border-radius: var(--radius, 10px);\n  font-size: 14px;\n  line-height: 1.7;\n  color: var(--text, #1e1b4b);\n}\n#qn-freee-cta strong { color: var(--accent, #7c3aed); }\n#qn-freee-cta a.qn-freee-link {\n  display: inline-block;\n  margin-top: 10px;\n  padding: 8px 20px;\n  background: var(--accent, #7c3aed);\n  color: #fff;\n  border-radius: 6px;\n  text-decoration: none;\n  font-weight: 600;\n  font-size: 13px;\n  transition: background 0.15s;\n}\n#qn-freee-cta a.qn-freee-link:hover { background: var(--accent-dk, #6d28d9); }\n\n/* ── Related ──────────────────────────────────────────── */\n#qn-related {\n  margin-top: 16px;\n  font-size: 13px;\n  color: var(--text-muted, #6b7280);\n}\n#qn-related a { color: var(--accent, #7c3aed); text-decoration: none; }\n#qn-related a:hover { text-decoration: underline; }\n\n/* ── Responsive ───────────────────────────────────────── */\n@media (max-width: 640px) {\n  #qn-app .qn-sidebar { width: 160px; min-width: 140px; }\n  #qn-app .qn-note-title { max-width: 110px; }\n  #qn-app .qn-note-preview { max-width: 120px; }\n  #qn-app .qn-toolbar { gap: 4px; }\n  #qn-app .qn-btn { padding: 5px 8px; font-size: 12px; }\n}\n\u003c/style\u003e\n\u003cdiv class=\"qn-wrap\"\u003e\n  \u003c!-- Toolbar --\u003e\n  \u003cdiv class=\"qn-toolbar\"\u003e\n    \u003cbutton class=\"qn-btn qn-btn-primary\" onclick=\"QN.newNote()\" title=\"New note (Ctrl+N)\"\u003e+ New\u003c/button\u003e\n    \u003cbutton class=\"qn-btn\" onclick=\"QN.downloadNote()\" title=\"Download current note as .txt\"\u003e\u0026#8595; Download\u003c/button\u003e\n    \u003cbutton class=\"qn-btn\" onclick=\"QN.printNote()\" title=\"Print current note\"\u003e\u0026#128438; Print\u003c/button\u003e\n    \u003cbutton class=\"qn-btn\" onclick=\"QN.forceSave()\" title=\"Force save (Ctrl+S)\"\u003e\u0026#128190; Save\u003c/button\u003e\n    \u003cdiv class=\"qn-spacer\"\u003e\u003c/div\u003e\n    \u003cbutton class=\"qn-btn\" onclick=\"QN.toggleTheme()\" id=\"qn-theme-btn\" title=\"Toggle dark/light theme\"\u003e\u0026#9790; Dark\u003c/button\u003e\n  \u003c/div\u003e\n  \u003c!-- Body: sidebar + editor --\u003e\n  \u003cdiv class=\"qn-body\"\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;!-- Sidebar --\u0026gt;\n\u0026lt;div class=\u0026quot;qn-sidebar\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;qn-search-wrap\u0026quot;\u0026gt;\n    \u0026lt;input class=\u0026quot;qn-search\u0026quot; id=\u0026quot;qn-search\u0026quot; type=\u0026quot;search\u0026quot; placeholder=\u0026quot;Search notes...\u0026quot; oninput=\u0026quot;QN.onSearch(this.value)\u0026quot; /\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;qn-note-list\u0026quot; id=\u0026quot;qn-note-list\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;qn-sidebar-footer\u0026quot;\u0026gt;\n    \u0026lt;button class=\u0026quot;qn-new-btn\u0026quot; onclick=\u0026quot;QN.newNote()\u0026quot;\u0026gt;+ New Note\u0026lt;/button\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Editor --\u0026gt;\n\u0026lt;div class=\u0026quot;qn-editor-pane\u0026quot; id=\u0026quot;qn-editor-pane\u0026quot;\u0026gt;\n  \u0026lt;!-- empty state shown when no note selected --\u0026gt;\n  \u0026lt;div class=\u0026quot;qn-empty\u0026quot; id=\u0026quot;qn-empty\u0026quot;\u0026gt;\n    \u0026lt;svg width=\u0026quot;48\u0026quot; height=\u0026quot;48\u0026quot; viewBox=\u0026quot;0 0 24 24\u0026quot; fill=\u0026quot;none\u0026quot; stroke=\u0026quot;currentColor\u0026quot; stroke-width=\u0026quot;1.5\u0026quot;\u0026gt;\u0026lt;path d=\u0026quot;M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\u0026quot;/\u0026gt;\u0026lt;polyline points=\u0026quot;14 2 14 8 20 8\u0026quot;/\u0026gt;\u0026lt;line x1=\u0026quot;16\u0026quot; y1=\u0026quot;13\u0026quot; x2=\u0026quot;8\u0026quot; y2=\u0026quot;13\u0026quot;/\u0026gt;\u0026lt;line x1=\u0026quot;16\u0026quot; y1=\u0026quot;17\u0026quot; x2=\u0026quot;8\u0026quot; y2=\u0026quot;17\u0026quot;/\u0026gt;\u0026lt;polyline points=\u0026quot;10 9 9 9 8 9\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt;\n    \u0026lt;p\u0026gt;No note selected.\u0026lt;br\u0026gt;Create one with \u0026lt;strong\u0026gt;+ New\u0026lt;/strong\u0026gt;.\u0026lt;/p\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;!-- title bar --\u0026gt;\n  \u0026lt;div id=\u0026quot;qn-title-bar\u0026quot; class=\u0026quot;qn-title-bar\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt;\n    \u0026lt;input class=\u0026quot;qn-note-name-input\u0026quot; id=\u0026quot;qn-note-name\u0026quot; type=\u0026quot;text\u0026quot; placeholder=\u0026quot;Note title...\u0026quot; oninput=\u0026quot;QN.onTitleChange(this.value)\u0026quot; /\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;!-- textarea --\u0026gt;\n  \u0026lt;textarea class=\u0026quot;qn-textarea\u0026quot; id=\u0026quot;qn-textarea\u0026quot; placeholder=\u0026quot;Start writing your note here...\u0026quot; style=\u0026quot;display:none\u0026quot; oninput=\u0026quot;QN.onTextChange(this.value)\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt;\n  \u0026lt;!-- status bar --\u0026gt;\n  \u0026lt;div class=\u0026quot;qn-status\u0026quot; id=\u0026quot;qn-status\u0026quot; style=\u0026quot;display:none\u0026quot;\u0026gt;\n    \u0026lt;span id=\u0026quot;qn-words\u0026quot;\u0026gt;0 words\u0026lt;/span\u0026gt;\n    \u0026lt;span id=\u0026quot;qn-chars\u0026quot;\u0026gt;0 chars\u0026lt;/span\u0026gt;\n    \u0026lt;span class=\u0026quot;qn-saved hidden\u0026quot; id=\u0026quot;qn-saved-indicator\u0026quot;\u0026gt;Saved \u0026amp;#10003;\u0026lt;/span\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\u003c!-- /.qn-body --\u003e\n\u003c/div\u003e\u003c!-- /.qn-wrap --\u003e\n\u003cscript\u003e\n(function() {\n  'use strict';\n\n  /* ── Storage key ──────────────────────────────────────── */\n  var STORE_KEY = 'qn_notes_v1';\n  var THEME_KEY = 'qn_theme_v1';\n\n  /* ── State ────────────────────────────────────────────── */\n  var state = {\n    notes: [],       // [{id, title, body, updatedAt}]\n    activeId: null,\n    searchQuery: '',\n    saveTimer: null,\n    savedFlashTimer: null,\n    dark: false\n  };\n\n  /* ── Helpers ──────────────────────────────────────────── */\n  function uid() { return Date.now().toString(36) + Math.random().toString(36).slice(2, 7); }\n  function esc(s) { return s.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;'); }\n\n  function load() {\n    try {\n      var raw = localStorage.getItem(STORE_KEY);\n      if (raw) state.notes = JSON.parse(raw);\n    } catch(e) {}\n    if (!Array.isArray(state.notes) || state.notes.length === 0) {\n      state.notes = [{\n        id: uid(),\n        title: 'Welcome Note',\n        body: 'Welcome to Quick Notes!\\n\\nStart typing here. Your notes are auto-saved every 2 seconds to your browser.\\n\\nTips:\\n- Press Ctrl+S to force save\\n- Create multiple notes with \"+ New\"\\n- Search across all notes using the search bar\\n- Download any note as a .txt file',\n        updatedAt: Date.now()\n      }];\n      save();\n    }\n    try {\n      state.dark = localStorage.getItem(THEME_KEY) === 'dark';\n    } catch(e) {}\n  }\n\n  function save() {\n    try { localStorage.setItem(STORE_KEY, JSON.stringify(state.notes)); } catch(e) {}\n  }\n\n  function getNote(id) { return state.notes.find(function(n){ return n.id === id; }); }\n\n  function sortedNotes() {\n    return state.notes.slice().sort(function(a,b){ return b.updatedAt - a.updatedAt; });\n  }\n\n  function filteredNotes() {\n    var q = state.searchQuery.trim().toLowerCase();\n    if (!q) return sortedNotes();\n    return sortedNotes().filter(function(n) {\n      return n.title.toLowerCase().indexOf(q) !== -1 || n.body.toLowerCase().indexOf(q) !== -1;\n    });\n  }\n\n  /* ── Stats ────────────────────────────────────────────── */\n  function updateStats(body) {\n    var words = body.trim() === '' ? 0 : body.trim().split(/\\s+/).length;\n    var chars = body.length;\n    var wEl = document.getElementById('qn-words');\n    var cEl = document.getElementById('qn-chars');\n    if (wEl) wEl.textContent = words + (words === 1 ? ' word' : ' words');\n    if (cEl) cEl.textContent = chars + (chars === 1 ? ' char' : ' chars');\n  }\n\n  /* ── Render sidebar ───────────────────────────────────── */\n  function renderList() {\n    var list = document.getElementById('qn-note-list');\n    if (!list) return;\n    var notes = filteredNotes();\n    var q = state.searchQuery.trim().toLowerCase();\n\n    if (notes.length === 0) {\n      list.innerHTML = '\u003cdiv style=\"padding:12px;font-size:12px;color:var(--text-muted);text-align:center;\"\u003eNo notes found.\u003c/div\u003e';\n      return;\n    }\n\n    list.innerHTML = notes.map(function(n) {\n      var active = n.id === state.activeId ? ' active' : '';\n      var titleDisplay = q ? highlight(esc(n.title), q) : esc(n.title);\n      var preview = n.body.replace(/\\n/g, ' ').slice(0, 60);\n      var previewDisplay = q ? highlight(esc(preview), q) : esc(preview);\n      return '\u003cdiv class=\"qn-note-item' + active + '\" onclick=\"QN.selectNote(\\'' + n.id + '\\')\" title=\"' + esc(n.title) + '\"\u003e'\n        + '\u003cdiv class=\"qn-note-title\"\u003e' + titleDisplay + '\u003c/div\u003e'\n        + '\u003cdiv class=\"qn-note-preview\"\u003e' + (previewDisplay || '\u003cem\u003eEmpty\u003c/em\u003e') + '\u003c/div\u003e'\n        + '\u003cbutton class=\"qn-note-del\" onclick=\"event.stopPropagation();QN.deleteNote(\\'' + n.id + '\\')\" title=\"Delete note\"\u003e\u0026times;\u003c/button\u003e'\n        + '\u003c/div\u003e';\n    }).join('');\n  }\n\n  function highlight(str, q) {\n    if (!q) return str;\n    var re = new RegExp('(' + q.replace(/[.*+?^${}()|[\\]\\\\]/g,'\\\\$\u0026') + ')', 'gi');\n    return str.replace(re, '\u003cmark class=\"qn-highlight\"\u003e$1\u003c/mark\u003e');\n  }\n\n  /* ── Show/hide editor ─────────────────────────────────── */\n  function showEditor(show) {\n    var empty = document.getElementById('qn-empty');\n    var titleBar = document.getElementById('qn-title-bar');\n    var ta = document.getElementById('qn-textarea');\n    var status = document.getElementById('qn-status');\n    if (empty)   empty.style.display   = show ? 'none' : 'flex';\n    if (titleBar) titleBar.style.display = show ? 'flex' : 'none';\n    if (ta)      ta.style.display      = show ? 'block' : 'none';\n    if (status)  status.style.display  = show ? 'flex'  : 'none';\n  }\n\n  /* ── Select note ──────────────────────────────────────── */\n  function selectNote(id) {\n    var note = getNote(id);\n    if (!note) return;\n    state.activeId = id;\n    showEditor(true);\n    var nameEl = document.getElementById('qn-note-name');\n    var ta = document.getElementById('qn-textarea');\n    if (nameEl) nameEl.value = note.title;\n    if (ta) ta.value = note.body;\n    updateStats(note.body);\n    renderList();\n  }\n\n  /* ── Flash saved indicator ────────────────────────────── */\n  function flashSaved() {\n    var el = document.getElementById('qn-saved-indicator');\n    if (!el) return;\n    el.classList.remove('hidden');\n    clearTimeout(state.savedFlashTimer);\n    state.savedFlashTimer = setTimeout(function() { el.classList.add('hidden'); }, 2000);\n  }\n\n  /* ── Auto-save ────────────────────────────────────────── */\n  function scheduleSave() {\n    clearTimeout(state.saveTimer);\n    state.saveTimer = setTimeout(function() { commitSave(); }, 2000);\n  }\n\n  function commitSave() {\n    if (!state.activeId) return;\n    save();\n    flashSaved();\n  }\n\n  /* ── Theme ────────────────────────────────────────────── */\n  function applyTheme() {\n    var app = document.getElementById('qn-app');\n    var btn = document.getElementById('qn-theme-btn');\n    if (state.dark) {\n      app.classList.add('qn-dark');\n      if (btn) btn.textContent = '\\u2600 Light';\n    } else {\n      app.classList.remove('qn-dark');\n      if (btn) btn.textContent = '\\u263E Dark';\n    }\n  }\n\n  /* ── Public API ───────────────────────────────────────── */\n  window.QN = {\n\n    newNote: function() {\n      var note = { id: uid(), title: 'New Note', body: '', updatedAt: Date.now() };\n      state.notes.unshift(note);\n      save();\n      selectNote(note.id);\n      // Focus title so user can rename immediately\n      setTimeout(function() {\n        var el = document.getElementById('qn-note-name');\n        if (el) { el.focus(); el.select(); }\n      }, 50);\n    },\n\n    deleteNote: function(id) {\n      if (!window.confirm('Delete this note? This cannot be undone.')) return;\n      state.notes = state.notes.filter(function(n){ return n.id !== id; });\n      save();\n      if (state.activeId === id) {\n        state.activeId = null;\n        showEditor(false);\n        var remaining = sortedNotes();\n        if (remaining.length \u003e 0) selectNote(remaining[0].id);\n      }\n      renderList();\n    },\n\n    selectNote: function(id) { selectNote(id); },\n\n    onTitleChange: function(val) {\n      if (!state.activeId) return;\n      var note = getNote(state.activeId);\n      if (!note) return;\n      note.title = val || 'Untitled';\n      note.updatedAt = Date.now();\n      renderList();\n      scheduleSave();\n    },\n\n    onTextChange: function(val) {\n      if (!state.activeId) return;\n      var note = getNote(state.activeId);\n      if (!note) return;\n      note.body = val;\n      note.updatedAt = Date.now();\n      updateStats(val);\n      renderList();\n      scheduleSave();\n    },\n\n    onSearch: function(val) {\n      state.searchQuery = val;\n      renderList();\n    },\n\n    forceSave: function() {\n      clearTimeout(state.saveTimer);\n      commitSave();\n    },\n\n    downloadNote: function() {\n      if (!state.activeId) { alert('Select a note first.'); return; }\n      var note = getNote(state.activeId);\n      if (!note) return;\n      var content = note.title + '\\n' + new Array(note.title.length + 1).join('=') + '\\n\\n' + note.body;\n      var blob = new Blob([content], { type: 'text/plain;charset=utf-8' });\n      var url = URL.createObjectURL(blob);\n      var a = document.createElement('a');\n      a.href = url;\n      a.download = (note.title.replace(/[^a-z0-9_\\-]/gi,'_') || 'note') + '.txt';\n      document.body.appendChild(a); a.click();\n      setTimeout(function() { document.body.removeChild(a); URL.revokeObjectURL(url); }, 100);\n    },\n\n    printNote: function() {\n      if (!state.activeId) { alert('Select a note first.'); return; }\n      var note = getNote(state.activeId);\n      if (!note) return;\n      var w = window.open('', '_blank');\n      w.document.write('\u003c!DOCTYPE html\u003e\u003chtml\u003e\u003chead\u003e\u003ctitle\u003e' + esc(note.title) + '\u003c/title\u003e'\n        + '\u003cstyle\u003ebody{font-family:serif;max-width:700px;margin:40px auto;font-size:15px;line-height:1.8;}'\n        + 'h1{font-size:20px;margin-bottom:8px;}pre{white-space:pre-wrap;word-break:break-word;}\u003c/style\u003e\u003c/head\u003e'\n\u003cpre\u003e\u003ccode\u003e    + '\u0026lt;body\u0026gt;\u0026lt;h1\u0026gt;' + esc(note.title) + '\u0026lt;/h1\u0026gt;\u0026lt;pre\u0026gt;' + esc(note.body) + '\u0026lt;/pre\u0026gt;\u0026lt;/body\u0026gt;\u0026lt;/html\u0026gt;');\n  w.document.close();\n  w.focus();\n  w.print();\n},\n\ntoggleTheme: function() {\n  state.dark = !state.dark;\n  try { localStorage.setItem(THEME_KEY, state.dark ? 'dark' : 'light'); } catch(e) {}\n  applyTheme();\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e};\u003c/p\u003e","title":"Quick Notes - Online Notepad"},{"content":" Numbers Dice Coin Flip List Picker Min Max Quantity \u0026lt;div class=\u0026quot;rng-toggle-row\u0026quot;\u0026gt; \u0026lt;label class=\u0026quot;rng-toggle\u0026quot; for=\u0026quot;rng-no-dup\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;rng-no-dup\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;rng-toggle-slider\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/label\u0026gt; \u0026lt;span class=\u0026quot;rng-toggle-label\u0026quot;\u0026gt;No duplicates\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rng-result-box\u0026quot; id=\u0026quot;rng-number-result-box\u0026quot;\u0026gt; \u0026lt;div id=\u0026quot;rng-number-output\u0026quot; class=\u0026quot;rng-result-number\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rng-result-label\u0026quot; id=\u0026quot;rng-number-label\u0026quot;\u0026gt;Press Generate\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rng-btn-row\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;rng-btn rng-btn-primary\u0026quot; onclick=\u0026quot;rngGenerateNumber()\u0026quot;\u0026gt;\u0026amp;#9656; Generate\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;rng-btn rng-btn-secondary\u0026quot; onclick=\u0026quot;rngCopyResult()\u0026quot;\u0026gt;\u0026amp;#128203; Copy\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; Number of Dice: \u0026#8722; 2 dice + Press Roll Dice \u0026#9656; Roll Dice ? Press Flip Coin \u0026#9656; Flip Coin Your List (one item per line) — \u0026#9656; Pick Random Item History Clear No history yet. How to Use the Random Number Generator Single number: Set your Min and Max range, leave Quantity at 1, and press Generate. Great for picking a lottery number, choosing who goes first, or any one-time decision.\nMultiple numbers: Increase the Quantity field. Enable \u0026ldquo;No duplicates\u0026rdquo; if you need each number to appear at most once — ideal for raffle draws, prize selections, or creating shuffled sequences.\nDice Roller: Switch to the Dice tab, choose how many dice (1–6), and roll. The tool shows realistic dot-face visuals and totals all dice for tabletop gaming.\nCoin Flip: The Coin Flip tab gives you a fair 50/50 result with a satisfying flip animation. Use it for quick decisions, settling disputes, or game tie-breakers.\nList Picker: Paste any list of items — one per line — and the tool randomly picks one. Perfect for choosing a restaurant, picking a team member for a task, or selecting a random book from your reading list.\nHistory: Every result is saved in the history panel so you can review past draws without re-rolling.\nCommon Uses for Random Number Generators Games \u0026amp; tabletop RPGs — roll dice, draw initiative order, generate random loot values Lotteries \u0026amp; giveaways — fairly select winners from numbered ticket pools Statistics \u0026amp; sampling — pick random samples from a numbered list for surveys Teaching \u0026amp; quizzes — randomly call on students or select practice problems Decision making — settle any binary choice with the coin flip, or rank options by number draw Security — quickly verify that your other tools are generating truly random values Related Tools Generate secure random passwords → Password Generator Time your events or intervals → Stopwatch Generate a QR code for giveaway links → QR Code Generator ","permalink":"https://productivity-works.com/tools/random-number-generator/","summary":"\u003cdiv id=\"rng-app\"\u003e\n\u003cstyle\u003e\n#rng-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 720px;\n  margin: 0 auto;\n  color: #e2e8f0;\n}\n\n#rng-app * {\n  box-sizing: border-box;\n}\n\n#rng-app .rng-card {\n  background: #1a0533;\n  border: 1px solid #4c1d95;\n  border-radius: 12px;\n  padding: 24px;\n  margin-bottom: 16px;\n}\n\n#rng-app .rng-tabs {\n  display: flex;\n  gap: 8px;\n  margin-bottom: 20px;\n  flex-wrap: wrap;\n}\n\n#rng-app .rng-tab {\n  flex: 1;\n  min-width: 100px;\n  padding: 10px 14px;\n  border: 1px solid #4c1d95;\n  border-radius: 8px;\n  background: #12002a;\n  color: #a78bfa;\n  cursor: pointer;\n  font-size: 14px;\n  font-weight: 500;\n  transition: all 0.2s;\n  text-align: center;\n  user-select: none;\n}\n\n#rng-app .rng-tab:hover {\n  border-color: #7c3aed;\n  color: #c4b5fd;\n}\n\n#rng-app .rng-tab.active {\n  background: #5b21b6;\n  border-color: #7c3aed;\n  color: #fff;\n}\n\n#rng-app .rng-result-box {\n  background: #0d001f;\n  border: 2px solid #4c1d95;\n  border-radius: 12px;\n  padding: 28px 20px;\n  text-align: center;\n  margin-bottom: 16px;\n  min-height: 100px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  flex-direction: column;\n  position: relative;\n  overflow: hidden;\n}\n\n#rng-app .rng-result-number {\n  font-size: 56px;\n  font-weight: 800;\n  color: #a78bfa;\n  letter-spacing: -1px;\n  line-height: 1;\n  transition: opacity 0.15s;\n}\n\n#rng-app .rng-result-number.animating {\n  animation: rngSpin 0.5s ease-out;\n}\n\n@keyframes rngSpin {\n  0%   { opacity: 0; transform: translateY(-20px) scale(0.85); }\n  60%  { opacity: 1; transform: translateY(4px) scale(1.05); }\n  100% { opacity: 1; transform: translateY(0) scale(1); }\n}\n\n#rng-app .rng-result-label {\n  font-size: 12px;\n  color: #6d28d9;\n  margin-top: 8px;\n  text-transform: uppercase;\n  letter-spacing: 0.1em;\n}\n\n#rng-app .rng-multi-results {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  justify-content: center;\n  padding: 10px 0;\n}\n\n#rng-app .rng-multi-chip {\n  background: #2e1065;\n  border: 1px solid #5b21b6;\n  border-radius: 8px;\n  padding: 8px 16px;\n  font-size: 20px;\n  font-weight: 700;\n  color: #c4b5fd;\n  animation: rngSpin 0.5s ease-out both;\n}\n\n#rng-app .rng-btn-row {\n  display: flex;\n  gap: 10px;\n  margin-bottom: 0;\n}\n\n#rng-app .rng-btn {\n  padding: 12px 24px;\n  border-radius: 8px;\n  border: none;\n  font-size: 15px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: all 0.2s;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  gap: 6px;\n}\n\n#rng-app .rng-btn-primary {\n  background: #7c3aed;\n  color: #fff;\n  flex: 2;\n}\n\n#rng-app .rng-btn-primary:hover {\n  background: #6d28d9;\n  transform: translateY(-1px);\n  box-shadow: 0 4px 16px #7c3aed44;\n}\n\n#rng-app .rng-btn-primary:active {\n  transform: translateY(0);\n}\n\n#rng-app .rng-btn-secondary {\n  background: #2e1065;\n  color: #c4b5fd;\n  border: 1px solid #4c1d95;\n  flex: 1;\n}\n\n#rng-app .rng-btn-secondary:hover {\n  background: #3b0f8a;\n  color: #fff;\n}\n\n#rng-app .rng-field-row {\n  display: flex;\n  gap: 12px;\n  margin-bottom: 16px;\n  flex-wrap: wrap;\n}\n\n#rng-app .rng-field {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n  flex: 1;\n  min-width: 80px;\n}\n\n#rng-app .rng-label {\n  font-size: 12px;\n  font-weight: 600;\n  color: #7c3aed;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n}\n\n#rng-app .rng-input {\n  background: #12002a;\n  border: 1px solid #4c1d95;\n  border-radius: 8px;\n  color: #e2e8f0;\n  font-size: 16px;\n  font-weight: 600;\n  padding: 10px 12px;\n  outline: none;\n  width: 100%;\n  transition: border-color 0.2s;\n}\n\n#rng-app .rng-input:focus {\n  border-color: #7c3aed;\n}\n\n#rng-app .rng-toggle-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-bottom: 16px;\n}\n\n#rng-app .rng-toggle-label {\n  font-size: 14px;\n  color: #a78bfa;\n  cursor: pointer;\n  user-select: none;\n}\n\n#rng-app .rng-toggle {\n  position: relative;\n  width: 40px;\n  height: 22px;\n  flex-shrink: 0;\n}\n\n#rng-app .rng-toggle input {\n  opacity: 0;\n  width: 0;\n  height: 0;\n  position: absolute;\n}\n\n#rng-app .rng-toggle-slider {\n  position: absolute;\n  inset: 0;\n  background: #2e1065;\n  border: 1px solid #4c1d95;\n  border-radius: 22px;\n  cursor: pointer;\n  transition: 0.2s;\n}\n\n#rng-app .rng-toggle-slider:before {\n  content: '';\n  position: absolute;\n  width: 16px;\n  height: 16px;\n  background: #6d28d9;\n  border-radius: 50%;\n  left: 2px;\n  top: 2px;\n  transition: 0.2s;\n}\n\n#rng-app .rng-toggle input:checked + .rng-toggle-slider {\n  background: #5b21b6;\n  border-color: #7c3aed;\n}\n\n#rng-app .rng-toggle input:checked + .rng-toggle-slider:before {\n  background: #c4b5fd;\n  transform: translateX(18px);\n}\n\n/* Dice */\n#rng-app .rng-dice-row {\n  display: flex;\n  gap: 14px;\n  justify-content: center;\n  flex-wrap: wrap;\n  padding: 10px 0;\n}\n\n#rng-app .rng-die {\n  width: 64px;\n  height: 64px;\n  background: #2e1065;\n  border: 2px solid #5b21b6;\n  border-radius: 12px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  flex-direction: column;\n  position: relative;\n  animation: rngDiceRoll 0.4s ease-out both;\n}\n\n@keyframes rngDiceRoll {\n  0%   { transform: rotate(-20deg) scale(0.7); opacity: 0; }\n  60%  { transform: rotate(5deg) scale(1.08); opacity: 1; }\n  100% { transform: rotate(0deg) scale(1); opacity: 1; }\n}\n\n#rng-app .rng-die-dots {\n  display: grid;\n  width: 44px;\n  height: 44px;\n  padding: 4px;\n}\n\n#rng-app .rng-dot {\n  width: 8px;\n  height: 8px;\n  border-radius: 50%;\n  background: #a78bfa;\n  margin: auto;\n}\n\n#rng-app .rng-dot.hidden {\n  background: transparent;\n}\n\n/* Coin */\n#rng-app .rng-coin-wrap {\n  perspective: 600px;\n  display: flex;\n  justify-content: center;\n  padding: 10px 0;\n}\n\n#rng-app .rng-coin {\n  width: 100px;\n  height: 100px;\n  border-radius: 50%;\n  background: linear-gradient(135deg, #7c3aed, #5b21b6);\n  border: 4px solid #a78bfa;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 28px;\n  font-weight: 800;\n  color: #fff;\n  box-shadow: 0 0 24px #7c3aed66;\n  transition: transform 0.6s cubic-bezier(0.36, 0.07, 0.19, 0.97);\n  transform-style: preserve-3d;\n}\n\n#rng-app .rng-coin.flipping {\n  animation: rngCoinFlip 0.7s ease-out;\n}\n\n@keyframes rngCoinFlip {\n  0%   { transform: rotateY(0deg); }\n  50%  { transform: rotateY(900deg) scale(0.85); }\n  100% { transform: rotateY(1800deg) scale(1); }\n}\n\n#rng-app .rng-coin-label {\n  font-size: 14px;\n  font-weight: 700;\n  color: #c4b5fd;\n  text-align: center;\n  margin-top: 10px;\n}\n\n/* List picker */\n#rng-app .rng-textarea {\n  background: #12002a;\n  border: 1px solid #4c1d95;\n  border-radius: 8px;\n  color: #e2e8f0;\n  font-size: 14px;\n  padding: 10px 12px;\n  width: 100%;\n  outline: none;\n  resize: vertical;\n  min-height: 100px;\n  font-family: inherit;\n  transition: border-color 0.2s;\n}\n\n#rng-app .rng-textarea:focus {\n  border-color: #7c3aed;\n}\n\n#rng-app .rng-list-result {\n  font-size: 28px;\n  font-weight: 800;\n  color: #a78bfa;\n  text-align: center;\n  min-height: 44px;\n  word-break: break-word;\n}\n\n/* History */\n#rng-app .rng-history-title {\n  font-size: 12px;\n  font-weight: 700;\n  color: #6d28d9;\n  text-transform: uppercase;\n  letter-spacing: 0.1em;\n  margin-bottom: 10px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n\n#rng-app .rng-history-clear {\n  font-size: 11px;\n  color: #4c1d95;\n  cursor: pointer;\n  background: none;\n  border: none;\n  padding: 2px 6px;\n  border-radius: 4px;\n  transition: color 0.2s;\n}\n\n#rng-app .rng-history-clear:hover {\n  color: #7c3aed;\n}\n\n#rng-app .rng-history-list {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 6px;\n  min-height: 32px;\n}\n\n#rng-app .rng-history-chip {\n  background: #1a0533;\n  border: 1px solid #3b0f8a;\n  border-radius: 20px;\n  padding: 4px 12px;\n  font-size: 13px;\n  color: #c4b5fd;\n}\n\n#rng-app .rng-section {\n  display: none;\n}\n\n#rng-app .rng-section.active {\n  display: block;\n}\n\n#rng-app .rng-dice-count-row {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  margin-bottom: 16px;\n  flex-wrap: wrap;\n}\n\n#rng-app .rng-dice-count-btn {\n  width: 36px;\n  height: 36px;\n  border-radius: 8px;\n  border: 1px solid #4c1d95;\n  background: #12002a;\n  color: #a78bfa;\n  font-size: 20px;\n  font-weight: 700;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  transition: all 0.2s;\n  line-height: 1;\n}\n\n#rng-app .rng-dice-count-btn:hover {\n  background: #2e1065;\n  border-color: #7c3aed;\n  color: #fff;\n}\n\n#rng-app .rng-dice-count-val {\n  font-size: 16px;\n  font-weight: 700;\n  color: #c4b5fd;\n  min-width: 60px;\n  text-align: center;\n}\n\n#rng-app .rng-dice-sum {\n  font-size: 14px;\n  color: #6d28d9;\n  margin-top: 8px;\n  text-align: center;\n}\n\n@media (max-width: 480px) {\n  #rng-app .rng-card { padding: 16px; }\n  #rng-app .rng-result-number { font-size: 40px; }\n  #rng-app .rng-btn-row { flex-direction: column; }\n  #rng-app .rng-tab { font-size: 12px; padding: 8px 6px; }\n  #rng-app .rng-die { width: 52px; height: 52px; }\n}\n\u003c/style\u003e\n\u003cdiv class=\"rng-card\"\u003e\n  \u003cdiv class=\"rng-tabs\"\u003e\n    \u003cdiv class=\"rng-tab active\" id=\"rng-tab-number\" onclick=\"rngSetMode('number')\"\u003eNumbers\u003c/div\u003e\n    \u003cdiv class=\"rng-tab\" id=\"rng-tab-dice\" onclick=\"rngSetMode('dice')\"\u003eDice\u003c/div\u003e\n    \u003cdiv class=\"rng-tab\" id=\"rng-tab-coin\" onclick=\"rngSetMode('coin')\"\u003eCoin Flip\u003c/div\u003e\n    \u003cdiv class=\"rng-tab\" id=\"rng-tab-list\" onclick=\"rngSetMode('list')\"\u003eList Picker\u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- NUMBER MODE --\u003e\n  \u003cdiv class=\"rng-section active\" id=\"rng-sec-number\"\u003e\n    \u003cdiv class=\"rng-field-row\"\u003e\n      \u003cdiv class=\"rng-field\"\u003e\n        \u003clabel class=\"rng-label\" for=\"rng-min\"\u003eMin\u003c/label\u003e\n        \u003cinput class=\"rng-input\" type=\"number\" id=\"rng-min\" value=\"1\"\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"rng-field\"\u003e\n        \u003clabel class=\"rng-label\" for=\"rng-max\"\u003eMax\u003c/label\u003e\n        \u003cinput class=\"rng-input\" type=\"number\" id=\"rng-max\" value=\"100\"\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"rng-field\"\u003e\n        \u003clabel class=\"rng-label\" for=\"rng-qty\"\u003eQuantity\u003c/label\u003e\n        \u003cinput class=\"rng-input\" type=\"number\" id=\"rng-qty\" value=\"1\" min=\"1\" max=\"100\"\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;div class=\u0026quot;rng-toggle-row\u0026quot;\u0026gt;\n  \u0026lt;label class=\u0026quot;rng-toggle\u0026quot; for=\u0026quot;rng-no-dup\u0026quot;\u0026gt;\n    \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;rng-no-dup\u0026quot;\u0026gt;\n    \u0026lt;span class=\u0026quot;rng-toggle-slider\u0026quot;\u0026gt;\u0026lt;/span\u0026gt;\n  \u0026lt;/label\u0026gt;\n  \u0026lt;span class=\u0026quot;rng-toggle-label\u0026quot;\u0026gt;No duplicates\u0026lt;/span\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;rng-result-box\u0026quot; id=\u0026quot;rng-number-result-box\u0026quot;\u0026gt;\n  \u0026lt;div id=\u0026quot;rng-number-output\u0026quot; class=\u0026quot;rng-result-number\u0026quot;\u0026gt;—\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;rng-result-label\u0026quot; id=\u0026quot;rng-number-label\u0026quot;\u0026gt;Press Generate\u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;rng-btn-row\u0026quot;\u0026gt;\n  \u0026lt;button class=\u0026quot;rng-btn rng-btn-primary\u0026quot; onclick=\u0026quot;rngGenerateNumber()\u0026quot;\u0026gt;\u0026amp;#9656; Generate\u0026lt;/button\u0026gt;\n  \u0026lt;button class=\u0026quot;rng-btn rng-btn-secondary\u0026quot; onclick=\u0026quot;rngCopyResult()\u0026quot;\u0026gt;\u0026amp;#128203; Copy\u0026lt;/button\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n  \u003c!-- DICE MODE --\u003e\n  \u003cdiv class=\"rng-section\" id=\"rng-sec-dice\"\u003e\n    \u003cdiv class=\"rng-dice-count-row\"\u003e\n      \u003cspan class=\"rng-label\"\u003eNumber of Dice:\u003c/span\u003e\n      \u003cbutton class=\"rng-dice-count-btn\" onclick=\"rngChangeDiceCount(-1)\"\u003e\u0026#8722;\u003c/button\u003e\n      \u003cspan class=\"rng-dice-count-val\" id=\"rng-dice-count-val\"\u003e2 dice\u003c/span\u003e\n      \u003cbutton class=\"rng-dice-count-btn\" onclick=\"rngChangeDiceCount(1)\"\u003e+\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"rng-result-box\" id=\"rng-dice-result-box\" style=\"flex-direction:column;gap:12px;\"\u003e\n      \u003cdiv class=\"rng-dice-row\" id=\"rng-dice-display\"\u003e\n        \u003cdiv style=\"color:#4c1d95;font-size:14px;\"\u003ePress Roll Dice\u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"rng-dice-sum\" id=\"rng-dice-sum\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"rng-btn-row\"\u003e\n      \u003cbutton class=\"rng-btn rng-btn-primary\" onclick=\"rngRollDice()\"\u003e\u0026#9656; Roll Dice\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- COIN MODE --\u003e\n  \u003cdiv class=\"rng-section\" id=\"rng-sec-coin\"\u003e\n    \u003cdiv class=\"rng-result-box\" id=\"rng-coin-result-box\" style=\"flex-direction:column;gap:12px;min-height:160px;\"\u003e\n      \u003cdiv class=\"rng-coin-wrap\"\u003e\n        \u003cdiv class=\"rng-coin\" id=\"rng-coin\"\u003e?\u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"rng-coin-label\" id=\"rng-coin-label\"\u003ePress Flip Coin\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"rng-btn-row\"\u003e\n      \u003cbutton class=\"rng-btn rng-btn-primary\" onclick=\"rngFlipCoin()\"\u003e\u0026#9656; Flip Coin\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- LIST MODE --\u003e\n  \u003cdiv class=\"rng-section\" id=\"rng-sec-list\"\u003e\n    \u003cdiv class=\"rng-field\" style=\"margin-bottom:12px;\"\u003e\n      \u003clabel class=\"rng-label\"\u003eYour List (one item per line)\u003c/label\u003e\n      \u003ctextarea class=\"rng-textarea\" id=\"rng-list-input\" placeholder=\"Apple\u0026#10;Banana\u0026#10;Cherry\u0026#10;Dragon fruit\u0026#10;Elderberry\"\u003e\u003c/textarea\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"rng-result-box\" id=\"rng-list-result-box\" style=\"min-height:80px;\"\u003e\n      \u003cdiv class=\"rng-list-result\" id=\"rng-list-output\"\u003e—\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"rng-btn-row\"\u003e\n      \u003cbutton class=\"rng-btn rng-btn-primary\" onclick=\"rngPickFromList()\"\u003e\u0026#9656; Pick Random Item\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- HISTORY --\u003e\n\u003cdiv class=\"rng-card\"\u003e\n  \u003cdiv class=\"rng-history-title\"\u003e\n    \u003cspan\u003eHistory\u003c/span\u003e\n    \u003cbutton class=\"rng-history-clear\" onclick=\"rngClearHistory()\"\u003eClear\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"rng-history-list\" id=\"rng-history-list\"\u003e\n    \u003cspan style=\"color:#3b0f8a;font-size:13px;\"\u003eNo history yet.\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var rngMode = 'number';\n  var rngHistory = [];\n  var rngDiceCount = 2;\n  var rngLastNumbers = null;\n\n  // ---- Mode switcher ----\n  window.rngSetMode = function(m) {\n    rngMode = m;\n    ['number','dice','coin','list'].forEach(function(x) {\n      document.getElementById('rng-tab-' + x).classList.toggle('active', x === m);\n      document.getElementById('rng-sec-' + x).classList.toggle('active', x === m);\n    });\n  };\n\n  // ---- Dice count ----\n  window.rngChangeDiceCount = function(delta) {\n    rngDiceCount = Math.max(1, Math.min(6, rngDiceCount + delta));\n    document.getElementById('rng-dice-count-val').textContent = rngDiceCount + (rngDiceCount === 1 ? ' die' : ' dice');\n  };\n\n  // ---- Random int helper ----\n  function randInt(min, max) {\n    min = Math.ceil(min);\n    max = Math.floor(max);\n    if (min \u003e max) { var t = min; min = max; max = t; }\n    var range = max - min + 1;\n    var arr = new Uint32Array(1);\n    var limit = Math.floor(4294967296 / range) * range;\n    do { crypto.getRandomValues(arr); } while (arr[0] \u003e= limit);\n    return min + (arr[0] % range);\n  }\n\n  // ---- Animate result ----\n  function animateEl(el) {\n    el.classList.remove('animating');\n    void el.offsetWidth;\n    el.classList.add('animating');\n  }\n\n  // ---- History ----\n  function pushHistory(entry) {\n    rngHistory.unshift(entry);\n    if (rngHistory.length \u003e 30) rngHistory.pop();\n    renderHistory();\n  }\n\n  function renderHistory() {\n    var el = document.getElementById('rng-history-list');\n    if (rngHistory.length === 0) {\n      el.innerHTML = '\u003cspan style=\"color:#3b0f8a;font-size:13px;\"\u003eNo history yet.\u003c/span\u003e';\n      return;\n    }\n    el.innerHTML = '';\n    rngHistory.forEach(function(h) {\n      var chip = document.createElement('span');\n      chip.className = 'rng-history-chip';\n      chip.textContent = h;\n      el.appendChild(chip);\n    });\n  }\n\n  window.rngClearHistory = function() {\n    rngHistory = [];\n    renderHistory();\n  };\n\n  // ---- NUMBER GENERATE ----\n  window.rngGenerateNumber = function() {\n    var min = parseInt(document.getElementById('rng-min').value);\n    var max = parseInt(document.getElementById('rng-max').value);\n    var qty = Math.max(1, Math.min(100, parseInt(document.getElementById('rng-qty').value) || 1));\n    var noDup = document.getElementById('rng-no-dup').checked;\n\n    if (isNaN(min) || isNaN(max)) return;\n    if (min \u003e max) { var t = min; min = max; max = t; }\n\n    var range = max - min + 1;\n    var outputEl = document.getElementById('rng-number-output');\n    var labelEl  = document.getElementById('rng-number-label');\n\n    if (qty === 1) {\n      var n = randInt(min, max);\n      rngLastNumbers = [n];\n      outputEl.innerHTML = '\u003cspan\u003e' + n + '\u003c/span\u003e';\n      outputEl.style.fontSize = '56px';\n      labelEl.textContent = 'Result (range ' + min + '–' + max + ')';\n      animateEl(outputEl);\n      pushHistory(String(n));\n    } else {\n      var nums = [];\n      if (noDup \u0026\u0026 qty \u003e range) {\n        qty = range;\n      }\n      if (noDup) {\n        var pool = [];\n        for (var i = min; i \u003c= max; i++) pool.push(i);\n        for (var j = pool.length - 1; j \u003e 0; j--) {\n          var k = randInt(0, j);\n          var tmp = pool[j]; pool[j] = pool[k]; pool[k] = tmp;\n        }\n        nums = pool.slice(0, qty).sort(function(a,b){return a-b;});\n      } else {\n        for (var q = 0; q \u003c qty; q++) nums.push(randInt(min, max));\n        nums.sort(function(a,b){return a-b;});\n      }\n      rngLastNumbers = nums;\n      if (qty \u003c= 12) {\n        var chips = nums.map(function(n) {\n          return '\u003cspan class=\"rng-multi-chip\"\u003e' + n + '\u003c/span\u003e';\n        }).join('');\n        outputEl.innerHTML = '\u003cdiv class=\"rng-multi-results\"\u003e' + chips + '\u003c/div\u003e';\n        outputEl.style.fontSize = '16px';\n      } else {\n        outputEl.innerHTML = '\u003cspan\u003e' + nums.join(', ') + '\u003c/span\u003e';\n        outputEl.style.fontSize = '16px';\n      }\n      labelEl.textContent = qty + ' numbers (range ' + min + '–' + max + (noDup ? ', no duplicates' : '') + ')';\n      pushHistory(nums.join(', '));\n    }\n  };\n\n  // ---- COPY ----\n  window.rngCopyResult = function() {\n    if (!rngLastNumbers) return;\n    navigator.clipboard.writeText(rngLastNumbers.join(', ')).then(function() {\n      var btn = document.querySelector('#rng-sec-number .rng-btn-secondary');\n      if (btn) { btn.textContent = 'Copied!'; setTimeout(function(){ btn.innerHTML = '\u0026#128203; Copy'; }, 1800); }\n    });\n  };\n\n  // ---- DICE ----\n  var DOT_LAYOUTS = {\n    1: [[0,0,0],[0,1,0],[0,0,0]],\n    2: [[1,0,0],[0,0,0],[0,0,1]],\n    3: [[1,0,0],[0,1,0],[0,0,1]],\n    4: [[1,0,1],[0,0,0],[1,0,1]],\n    5: [[1,0,1],[0,1,0],[1,0,1]],\n    6: [[1,0,1],[1,0,1],[1,0,1]]\n  };\n\n  function makeDie(val) {\n    var die = document.createElement('div');\n    die.className = 'rng-die';\n    var grid = document.createElement('div');\n    grid.className = 'rng-die-dots';\n    grid.style.gridTemplateColumns = 'repeat(3, 1fr)';\n    grid.style.gridTemplateRows = 'repeat(3, 1fr)';\n    var layout = DOT_LAYOUTS[val];\n    for (var r = 0; r \u003c 3; r++) {\n      for (var c = 0; c \u003c 3; c++) {\n        var dot = document.createElement('div');\n        dot.className = 'rng-dot' + (layout[r][c] ? '' : ' hidden');\n        grid.appendChild(dot);\n      }\n    }\n    die.appendChild(grid);\n    return die;\n  }\n\n  window.rngRollDice = function() {\n    var display = document.getElementById('rng-dice-display');\n    var sumEl   = document.getElementById('rng-dice-sum');\n    display.innerHTML = '';\n    var total = 0;\n    var vals = [];\n    for (var i = 0; i \u003c rngDiceCount; i++) {\n      var v = randInt(1, 6);\n      vals.push(v);\n      total += v;\n      (function(val, idx) {\n        var die = makeDie(val);\n        die.style.animationDelay = (idx * 80) + 'ms';\n        display.appendChild(die);\n      })(v, i);\n    }\n    sumEl.textContent = rngDiceCount \u003e 1 ? 'Total: ' + total + '  (' + vals.join(', ') + ')' : 'Rolled: ' + vals[0];\n    pushHistory((rngDiceCount \u003e 1 ? 'Dice ' : 'Die ') + vals.join('+') + (rngDiceCount \u003e 1 ? '=' + total : ''));\n  };\n\n  // ---- COIN FLIP ----\n  window.rngFlipCoin = function() {\n    var coin  = document.getElementById('rng-coin');\n    var label = document.getElementById('rng-coin-label');\n    coin.classList.remove('flipping');\n    void coin.offsetWidth;\n    coin.classList.add('flipping');\n    setTimeout(function() {\n      var result = randInt(0, 1);\n      coin.textContent = result ? 'H' : 'T';\n      label.textContent = result ? 'Heads!' : 'Tails!';\n      pushHistory(result ? 'Heads' : 'Tails');\n    }, 500);\n  };\n\n  // ---- LIST PICKER ----\n  window.rngPickFromList = function() {\n    var raw   = document.getElementById('rng-list-input').value;\n    var items = raw.split('\\n').map(function(s){ return s.trim(); }).filter(function(s){ return s.length \u003e 0; });\n    var outEl = document.getElementById('rng-list-output');\n    if (items.length === 0) {\n      outEl.textContent = 'Please enter items in the list.';\n      return;\n    }\n    var pick = items[randInt(0, items.length - 1)];\n    outEl.textContent = pick;\n    animateEl(outEl);\n    pushHistory('List: ' + pick);\n  };\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-to-use-the-random-number-generator\"\u003eHow to Use the Random Number Generator\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eSingle number:\u003c/strong\u003e Set your Min and Max range, leave Quantity at 1, and press Generate. Great for picking a lottery number, choosing who goes first, or any one-time decision.\u003c/p\u003e","title":"Random Number Generator - Free Online Tool"},{"content":" Reading Speed Test Reading Time Estimator Reading Speed Test Choose a passage, press Start, and read at your normal pace. Press Done when finished.\nEasy Medium Hard Press Start Reading to reveal the passage 0:00 sec \u0026#9654; Start Reading \u0026#10003; Done Reading \u0026#8635; Reset Your Results — Words per Minute — Reading Time (sec) — Comprehension \u0026lt;!-- Speed chart embedded in result --\u0026gt; \u0026lt;div class=\u0026quot;rs-chart-wrap\u0026quot;\u0026gt; \u0026lt;p class=\u0026quot;rs-chart-title\u0026quot;\u0026gt;How you compare\u0026lt;/p\u0026gt; \u0026lt;canvas id=\u0026quot;rs-chart\u0026quot; height=\u0026quot;180\u0026quot;\u0026gt;\u0026lt;/canvas\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;hr class=\u0026quot;rs-divider\u0026quot;\u0026gt; \u0026lt;!-- Comprehension quiz --\u0026gt; \u0026lt;h2 id=\u0026quot;rs-quiz-heading\u0026quot;\u0026gt;Comprehension Quiz\u0026lt;/h2\u0026gt; \u0026lt;p class=\u0026quot;rs-subtitle\u0026quot;\u0026gt;Answer to calculate your comprehension score.\u0026lt;/p\u0026gt; \u0026lt;div id=\u0026quot;rs-quiz\u0026quot; class=\u0026quot;rs-quiz\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rs-btn-row\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;rs-btn rs-btn-primary\u0026quot; id=\u0026quot;rs-btn-submit-quiz\u0026quot;\u0026gt;Submit Quiz\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;rs-quiz-result\u0026quot; class=\u0026quot;rs-hidden\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; Speed comparison\nReading Time Estimator Enter a word count or paste your text to estimate how long it will take to read.\nWord Count Your Reading Speed (WPM) Or paste text here (word count auto-calculated) Quick presets\nNovel (80k words) Blog Post (1.5k) Research Paper (8k) Textbook Chapter (5k) Calculate Reading Time — estimated reading time \u0026lt;div class=\u0026quot;rs-chart-wrap\u0026quot;\u0026gt; \u0026lt;p class=\u0026quot;rs-chart-title\u0026quot;\u0026gt;Speed comparison\u0026lt;/p\u0026gt; \u0026lt;canvas id=\u0026quot;rs-chart-est\u0026quot; height=\u0026quot;180\u0026quot;\u0026gt;\u0026lt;/canvas\u0026gt; \u0026lt;/div\u0026gt; \u0026#8250; Test your typing speed \u0026#8594; Typing Speed Test\n\u0026#8250; Count text lines \u0026#8594; Line Counter\n\u0026#8250; Generate lorem ipsum \u0026#8594; Lorem Ipsum Generator\nRelated Articles How to Use ChatGPT for Studying: The Complete Student Guide 2026 How to Build a Notion Book Tracker: Complete Template Guide (2026) Best Language Learning Apps 2026: Actually Become Conversational ","permalink":"https://productivity-works.com/tools/reading-speed-calculator/","summary":"\u003cdiv id=\"rs-app\"\u003e\n\u003cstyle\u003e\n#rs-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 800px;\n  margin: 0 auto;\n  color: #e2e8f0;\n  background: #0f172a;\n  border-radius: 12px;\n  overflow: hidden;\n  box-shadow: 0 4px 24px rgba(0,0,0,0.4);\n}\n#rs-app * { box-sizing: border-box; }\n\u003cp\u003e/* Tabs */\n#rs-app .rs-tabs {\ndisplay: flex;\nbackground: #1e293b;\nborder-bottom: 2px solid #334155;\n}\n#rs-app .rs-tab-btn {\nflex: 1;\npadding: 14px 8px;\nborder: none;\nbackground: transparent;\ncolor: #94a3b8;\nfont-size: 14px;\nfont-weight: 600;\ncursor: pointer;\ntransition: color 0.2s, border-bottom 0.2s;\nborder-bottom: 2px solid transparent;\nmargin-bottom: -2px;\nletter-spacing: 0.02em;\n}\n#rs-app .rs-tab-btn:hover { color: #e2e8f0; }\n#rs-app .rs-tab-btn.active {\ncolor: #38bdf8;\nborder-bottom: 2px solid #38bdf8;\n}\u003c/p\u003e","title":"Reading Speed Calculator"},{"content":" Paste Your Text Clear or enter word count directly Word count: (leave blank if using text above) Reading Speed WPM: 200 WPM Slow (150) Average (200) Fast (300) Speed reader (400) Results Paste text or enter a word count above to see results. Book Reading Estimate Number of pages: Words per page: Related: Count words in your content with the Word Counter. ","permalink":"https://productivity-works.com/tools/reading-time-calculator/","summary":"\u003cdiv id=\"rt-app\"\u003e\n\u003cstyle\u003e\n#rt-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  max-width: 820px;\n  margin: 0 auto;\n  color: #1f2937;\n}\n#rt-app * { box-sizing: border-box; }\n#rt-app h2 {\n  font-size: 1.25rem;\n  font-weight: 700;\n  margin: 1.5rem 0 0.75rem;\n  color: #92400e;\n}\n#rt-app .card {\n  background: #fff;\n  border: 1px solid #e5e7eb;\n  border-radius: 12px;\n  padding: 1.25rem 1.5rem;\n  margin-bottom: 1.25rem;\n  box-shadow: 0 1px 3px rgba(0,0,0,0.06);\n}\n#rt-app textarea {\n  width: 100%;\n  min-height: 160px;\n  border: 2px solid #e5e7eb;\n  border-radius: 8px;\n  padding: 0.75rem 1rem;\n  font-size: 0.95rem;\n  font-family: inherit;\n  resize: vertical;\n  transition: border-color 0.2s;\n  outline: none;\n}\n#rt-app textarea:focus { border-color: #d97706; }\n#rt-app .or-divider {\n  text-align: center;\n  color: #9ca3af;\n  font-size: 0.85rem;\n  margin: 0.75rem 0;\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n}\n#rt-app .or-divider::before,\n#rt-app .or-divider::after {\n  content: \"\";\n  flex: 1;\n  height: 1px;\n  background: #e5e7eb;\n}\n#rt-app .word-count-row {\n  display: flex;\n  align-items: center;\n  gap: 0.75rem;\n  flex-wrap: wrap;\n}\n#rt-app .word-count-row label {\n  font-size: 0.9rem;\n  color: #374151;\n  font-weight: 500;\n  white-space: nowrap;\n}\n#rt-app input[type=\"number\"] {\n  width: 120px;\n  border: 2px solid #e5e7eb;\n  border-radius: 8px;\n  padding: 0.45rem 0.75rem;\n  font-size: 0.95rem;\n  font-family: inherit;\n  outline: none;\n  transition: border-color 0.2s;\n}\n#rt-app input[type=\"number\"]:focus { border-color: #d97706; }\n#rt-app .speed-section {\n  display: flex;\n  align-items: center;\n  gap: 1rem;\n  flex-wrap: wrap;\n}\n#rt-app .speed-label {\n  font-size: 0.9rem;\n  color: #374151;\n  font-weight: 500;\n  white-space: nowrap;\n}\n#rt-app input[type=\"range\"] {\n  flex: 1;\n  min-width: 140px;\n  accent-color: #d97706;\n  height: 6px;\n  cursor: pointer;\n}\n#rt-app .speed-val {\n  background: #fef3c7;\n  color: #92400e;\n  border-radius: 20px;\n  padding: 0.2rem 0.75rem;\n  font-size: 0.88rem;\n  font-weight: 700;\n  white-space: nowrap;\n}\n#rt-app .speed-presets {\n  display: flex;\n  gap: 0.5rem;\n  flex-wrap: wrap;\n  margin-top: 0.5rem;\n}\n#rt-app .preset-btn {\n  border: 1.5px solid #d97706;\n  background: transparent;\n  color: #92400e;\n  border-radius: 20px;\n  padding: 0.2rem 0.75rem;\n  font-size: 0.82rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s;\n}\n#rt-app .preset-btn:hover,\n#rt-app .preset-btn.active {\n  background: #d97706;\n  color: #fff;\n}\n#rt-app .results-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));\n  gap: 0.75rem;\n  margin-bottom: 0.5rem;\n}\n#rt-app .result-box {\n  background: #fffbeb;\n  border: 1.5px solid #fde68a;\n  border-radius: 10px;\n  padding: 0.9rem 1rem;\n  text-align: center;\n}\n#rt-app .result-box .r-val {\n  font-size: 1.5rem;\n  font-weight: 800;\n  color: #d97706;\n  line-height: 1.1;\n}\n#rt-app .result-box .r-lbl {\n  font-size: 0.75rem;\n  color: #6b7280;\n  margin-top: 0.2rem;\n}\n#rt-app .progress-wrap {\n  margin: 1rem 0 0.25rem;\n}\n#rt-app .progress-label {\n  display: flex;\n  justify-content: space-between;\n  font-size: 0.8rem;\n  color: #9ca3af;\n  margin-bottom: 0.3rem;\n}\n#rt-app .progress-bar-bg {\n  width: 100%;\n  height: 14px;\n  background: #f3f4f6;\n  border-radius: 999px;\n  overflow: hidden;\n}\n#rt-app .progress-bar-fill {\n  height: 100%;\n  background: linear-gradient(90deg, #f59e0b, #d97706);\n  border-radius: 999px;\n  transition: width 0.4s ease;\n  min-width: 4px;\n}\n#rt-app .speed-comparison {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 0.6rem;\n  margin-top: 0.75rem;\n}\n#rt-app .cmp-box {\n  border-radius: 10px;\n  padding: 0.65rem 0.75rem;\n  text-align: center;\n  border: 1.5px solid #e5e7eb;\n}\n#rt-app .cmp-box.slow  { border-color: #fca5a5; background: #fff1f1; }\n#rt-app .cmp-box.avg   { border-color: #fde68a; background: #fffbeb; }\n#rt-app .cmp-box.fast  { border-color: #6ee7b7; background: #ecfdf5; }\n#rt-app .cmp-box .cmp-t { font-size: 0.72rem; font-weight: 700; color: #6b7280; text-transform: uppercase; letter-spacing: 0.05em; }\n#rt-app .cmp-box .cmp-v { font-size: 1.1rem; font-weight: 800; margin: 0.1rem 0; }\n#rt-app .cmp-box.slow  .cmp-v { color: #dc2626; }\n#rt-app .cmp-box.avg   .cmp-v { color: #d97706; }\n#rt-app .cmp-box.fast  .cmp-v { color: #059669; }\n#rt-app .cmp-box .cmp-wpm { font-size: 0.7rem; color: #9ca3af; }\n#rt-app .book-row {\n  display: flex;\n  align-items: center;\n  gap: 0.75rem;\n  flex-wrap: wrap;\n}\n#rt-app .book-row label { font-size: 0.9rem; color: #374151; font-weight: 500; }\n#rt-app .book-result {\n  margin-top: 0.75rem;\n  padding: 0.65rem 1rem;\n  background: #fffbeb;\n  border: 1.5px solid #fde68a;\n  border-radius: 8px;\n  font-size: 0.92rem;\n  color: #92400e;\n  font-weight: 600;\n}\n#rt-app .readability-section { margin-top: 0.5rem; }\n#rt-app .rdbl-row {\n  display: flex;\n  gap: 1rem;\n  flex-wrap: wrap;\n  align-items: flex-start;\n}\n#rt-app .rdbl-item {\n  flex: 1;\n  min-width: 160px;\n}\n#rt-app .rdbl-item .rdbl-val {\n  font-size: 1.3rem;\n  font-weight: 800;\n  color: #d97706;\n}\n#rt-app .rdbl-item .rdbl-lbl {\n  font-size: 0.75rem;\n  color: #6b7280;\n  margin-top: 0.15rem;\n}\n#rt-app .rdbl-item .rdbl-note {\n  font-size: 0.78rem;\n  color: #374151;\n  margin-top: 0.1rem;\n}\n#rt-app .clear-btn {\n  background: transparent;\n  border: 1.5px solid #e5e7eb;\n  color: #6b7280;\n  border-radius: 8px;\n  padding: 0.35rem 1rem;\n  font-size: 0.82rem;\n  cursor: pointer;\n  transition: border-color 0.15s, color 0.15s;\n  float: right;\n  margin-top: 0.25rem;\n}\n#rt-app .clear-btn:hover { border-color: #d97706; color: #d97706; }\n#rt-app .empty-state {\n  text-align: center;\n  color: #9ca3af;\n  font-size: 0.9rem;\n  padding: 1.5rem 0;\n}\n#rt-app .related-note {\n  margin-top: 2rem;\n  padding: 0.85rem 1.1rem;\n  background: #f9fafb;\n  border-left: 4px solid #d97706;\n  border-radius: 0 8px 8px 0;\n  font-size: 0.9rem;\n  color: #374151;\n}\n#rt-app .related-note a { color: #d97706; font-weight: 600; text-decoration: none; }\n#rt-app .related-note a:hover { text-decoration: underline; }\n@media (max-width: 500px) {\n  #rt-app .results-grid { grid-template-columns: 1fr 1fr; }\n  #rt-app .speed-comparison { grid-template-columns: 1fr; }\n}\n\u003c/style\u003e\n\u003c!-- INPUT --\u003e\n\u003cdiv class=\"card\"\u003e\n  \u003ch2 style=\"margin-top:0\"\u003ePaste Your Text\u003c/h2\u003e\n  \u003ctextarea id=\"rt-textarea\" placeholder=\"Paste or type your text here…\" oninput=\"rtUpdate()\"\u003e\u003c/textarea\u003e\n  \u003cbutton class=\"clear-btn\" onclick=\"rtClear()\"\u003eClear\u003c/button\u003e\n  \u003cdiv class=\"or-divider\"\u003eor enter word count directly\u003c/div\u003e\n  \u003cdiv class=\"word-count-row\"\u003e\n    \u003clabel for=\"rt-wc-input\"\u003eWord count:\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"rt-wc-input\" min=\"1\" max=\"999999\" placeholder=\"e.g. 1500\" oninput=\"rtUpdate()\" /\u003e\n    \u003cspan style=\"font-size:0.8rem;color:#9ca3af;\"\u003e(leave blank if using text above)\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- SPEED --\u003e\n\u003cdiv class=\"card\"\u003e\n  \u003ch2 style=\"margin-top:0\"\u003eReading Speed\u003c/h2\u003e\n  \u003cdiv class=\"speed-section\"\u003e\n    \u003cspan class=\"speed-label\"\u003eWPM:\u003c/span\u003e\n    \u003cinput type=\"range\" id=\"rt-speed\" min=\"100\" max=\"400\" value=\"200\" oninput=\"rtSpeedChange(this.value)\" /\u003e\n    \u003cspan class=\"speed-val\" id=\"rt-speed-val\"\u003e200 WPM\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"speed-presets\"\u003e\n    \u003cbutton class=\"preset-btn\" onclick=\"rtSetSpeed(150)\"\u003eSlow (150)\u003c/button\u003e\n    \u003cbutton class=\"preset-btn active\" onclick=\"rtSetSpeed(200)\"\u003eAverage (200)\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" onclick=\"rtSetSpeed(300)\"\u003eFast (300)\u003c/button\u003e\n    \u003cbutton class=\"preset-btn\" onclick=\"rtSetSpeed(400)\"\u003eSpeed reader (400)\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- RESULTS --\u003e\n\u003cdiv class=\"card\" id=\"rt-results-card\"\u003e\n  \u003ch2 style=\"margin-top:0\"\u003eResults\u003c/h2\u003e\n  \u003cdiv id=\"rt-results-body\"\u003e\n    \u003cdiv class=\"empty-state\"\u003ePaste text or enter a word count above to see results.\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- BOOK ESTIMATE --\u003e\n\u003cdiv class=\"card\"\u003e\n  \u003ch2 style=\"margin-top:0\"\u003eBook Reading Estimate\u003c/h2\u003e\n  \u003cdiv class=\"book-row\"\u003e\n    \u003clabel for=\"rt-pages\"\u003eNumber of pages:\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"rt-pages\" min=\"1\" max=\"10000\" placeholder=\"e.g. 300\" oninput=\"rtBookUpdate()\" /\u003e\n    \u003clabel for=\"rt-wppage\"\u003eWords per page:\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"rt-wppage\" min=\"50\" max=\"1000\" value=\"250\" placeholder=\"250\" oninput=\"rtBookUpdate()\" /\u003e\n  \u003c/div\u003e\n  \u003cdiv id=\"rt-book-result\" class=\"book-result\" style=\"display:none\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var wpm = 200;\n  var spkWpm = 130;\n\n  function fmtTime(minutes) {\n    if (minutes \u003c 1) return \"\u003c 1 min\";\n    if (minutes \u003c 60) {\n      var m = Math.round(minutes);\n      return m + \" min\" + (m !== 1 ? \"s\" : \"\");\n    }\n    var h = Math.floor(minutes / 60);\n    var m = Math.round(minutes % 60);\n    return h + \"h \" + (m \u003e 0 ? m + \"m\" : \"\");\n  }\n\n  function fmtNum(n) {\n    return n.toLocaleString();\n  }\n\n  function countSyllables(word) {\n    word = word.toLowerCase().replace(/[^a-z]/g, \"\");\n    if (!word) return 0;\n    if (word.length \u003c= 3) return 1;\n    word = word.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, \"\");\n    word = word.replace(/^y/, \"\");\n    var m = word.match(/[aeiouy]{1,2}/g);\n    return m ? m.length : 1;\n  }\n\n  function analyzeText(text) {\n    var words = text.trim().match(/\\b\\w+\\b/g) || [];\n    var wordCount = words.length;\n    var charCount = text.length;\n    var sentences = text.split(/[.!?]+/).filter(function(s) { return s.trim().length \u003e 2; });\n    var sentenceCount = sentences.length;\n    var totalSyllables = 0;\n    for (var i = 0; i \u003c words.length; i++) {\n      totalSyllables += countSyllables(words[i]);\n    }\n    var avgWordsPerSentence = sentenceCount \u003e 0 ? (wordCount / sentenceCount) : 0;\n    var avgSyllablesPerWord = wordCount \u003e 0 ? (totalSyllables / wordCount) : 0;\n    return { wordCount: wordCount, charCount: charCount, sentenceCount: sentenceCount,\n      avgWordsPerSentence: avgWordsPerSentence, avgSyllablesPerWord: avgSyllablesPerWord };\n  }\n\n  function readabilityLabel(awps, aspw) {\n    // Simple heuristic\n    var score = awps * 0.5 + aspw * 8;\n    if (score \u003c 12) return { label: \"Very Easy\", color: \"#059669\" };\n    if (score \u003c 16) return { label: \"Easy\", color: \"#10b981\" };\n    if (score \u003c 20) return { label: \"Moderate\", color: \"#d97706\" };\n    if (score \u003c 26) return { label: \"Difficult\", color: \"#f59e0b\" };\n    return { label: \"Complex\", color: \"#dc2626\" };\n  }\n\n  function progressPercent(minutes) {\n    // 0 min = 0%, 60 min = 100% (cap)\n    return Math.min(100, Math.max(2, (minutes / 60) * 100));\n  }\n\n  window.rtUpdate = function() {\n    var text = document.getElementById(\"rt-textarea\").value;\n    var wcInput = document.getElementById(\"rt-wc-input\").value;\n    var hasText = text.trim().length \u003e 0;\n    var hasWC = wcInput \u0026\u0026 parseInt(wcInput) \u003e 0;\n\n    if (!hasText \u0026\u0026 !hasWC) {\n      document.getElementById(\"rt-results-body\").innerHTML =\n        '\u003cdiv class=\"empty-state\"\u003ePaste text or enter a word count above to see results.\u003c/div\u003e';\n      return;\n    }\n\n    var data = hasText ? analyzeText(text) : null;\n    var wordCount = hasText ? data.wordCount : parseInt(wcInput);\n    var readMin = wordCount / wpm;\n    var spkMin = wordCount / spkWpm;\n\n    var html = '\u003cdiv class=\"results-grid\"\u003e';\n    html += '\u003cdiv class=\"result-box\"\u003e\u003cdiv class=\"r-val\"\u003e' + fmtTime(readMin) + '\u003c/div\u003e\u003cdiv class=\"r-lbl\"\u003eReading Time\u003c/div\u003e\u003c/div\u003e';\n    html += '\u003cdiv class=\"result-box\"\u003e\u003cdiv class=\"r-val\"\u003e' + fmtTime(spkMin) + '\u003c/div\u003e\u003cdiv class=\"r-lbl\"\u003eSpeaking Time (130 WPM)\u003c/div\u003e\u003c/div\u003e';\n    html += '\u003cdiv class=\"result-box\"\u003e\u003cdiv class=\"r-val\"\u003e' + fmtNum(wordCount) + '\u003c/div\u003e\u003cdiv class=\"r-lbl\"\u003eWords\u003c/div\u003e\u003c/div\u003e';\n    if (hasText) {\n      html += '\u003cdiv class=\"result-box\"\u003e\u003cdiv class=\"r-val\"\u003e' + fmtNum(data.charCount) + '\u003c/div\u003e\u003cdiv class=\"r-lbl\"\u003eCharacters\u003c/div\u003e\u003c/div\u003e';\n      html += '\u003cdiv class=\"result-box\"\u003e\u003cdiv class=\"r-val\"\u003e' + fmtNum(data.sentenceCount) + '\u003c/div\u003e\u003cdiv class=\"r-lbl\"\u003eSentences\u003c/div\u003e\u003c/div\u003e';\n    }\n    html += '\u003c/div\u003e';\n\n    // Progress bar\n    var pct = progressPercent(readMin);\n    html += '\u003cdiv class=\"progress-wrap\"\u003e';\n    html += '\u003cdiv class=\"progress-label\"\u003e\u003cspan\u003eReading time\u003c/span\u003e\u003cspan\u003e' + fmtTime(readMin) + ' at ' + wpm + ' WPM\u003c/span\u003e\u003c/div\u003e';\n    html += '\u003cdiv class=\"progress-bar-bg\"\u003e\u003cdiv class=\"progress-bar-fill\" style=\"width:' + pct + '%\"\u003e\u003c/div\u003e\u003c/div\u003e';\n    html += '\u003c/div\u003e';\n\n    // Speed comparison\n    html += '\u003cdiv class=\"speed-comparison\"\u003e';\n    html += '\u003cdiv class=\"cmp-box slow\"\u003e\u003cdiv class=\"cmp-t\"\u003eSlow reader\u003c/div\u003e\u003cdiv class=\"cmp-v\"\u003e' + fmtTime(wordCount/150) + '\u003c/div\u003e\u003cdiv class=\"cmp-wpm\"\u003e150 WPM\u003c/div\u003e\u003c/div\u003e';\n    html += '\u003cdiv class=\"cmp-box avg\"\u003e\u003cdiv class=\"cmp-t\"\u003eAverage\u003c/div\u003e\u003cdiv class=\"cmp-v\"\u003e' + fmtTime(wordCount/200) + '\u003c/div\u003e\u003cdiv class=\"cmp-wpm\"\u003e200 WPM\u003c/div\u003e\u003c/div\u003e';\n    html += '\u003cdiv class=\"cmp-box fast\"\u003e\u003cdiv class=\"cmp-t\"\u003eFast reader\u003c/div\u003e\u003cdiv class=\"cmp-v\"\u003e' + fmtTime(wordCount/300) + '\u003c/div\u003e\u003cdiv class=\"cmp-wpm\"\u003e300 WPM\u003c/div\u003e\u003c/div\u003e';\n    html += '\u003c/div\u003e';\n\n    // Readability\n    if (hasText \u0026\u0026 data.wordCount \u003e 5) {\n      var awps = data.avgWordsPerSentence;\n      var aspw = data.avgSyllablesPerWord;\n      var rdbl = readabilityLabel(awps, aspw);\n      html += '\u003cdiv class=\"readability-section\"\u003e\u003ch2\u003eReadability\u003c/h2\u003e';\n      html += '\u003cdiv class=\"rdbl-row\"\u003e';\n      html += '\u003cdiv class=\"rdbl-item\"\u003e\u003cdiv class=\"rdbl-val\" style=\"color:' + rdbl.color + '\"\u003e' + rdbl.label + '\u003c/div\u003e\u003cdiv class=\"rdbl-lbl\"\u003eOverall difficulty\u003c/div\u003e\u003c/div\u003e';\n      html += '\u003cdiv class=\"rdbl-item\"\u003e\u003cdiv class=\"rdbl-val\"\u003e' + awps.toFixed(1) + '\u003c/div\u003e\u003cdiv class=\"rdbl-lbl\"\u003eAvg words / sentence\u003c/div\u003e\u003cdiv class=\"rdbl-note\"\u003e' + (awps \u003c 15 ? \"Clear \u0026 concise\" : awps \u003c 22 ? \"Readable\" : \"Long sentences\") + '\u003c/div\u003e\u003c/div\u003e';\n      html += '\u003cdiv class=\"rdbl-item\"\u003e\u003cdiv class=\"rdbl-val\"\u003e' + aspw.toFixed(2) + '\u003c/div\u003e\u003cdiv class=\"rdbl-lbl\"\u003eAvg syllables / word\u003c/div\u003e\u003cdiv class=\"rdbl-note\"\u003e' + (aspw \u003c 1.5 ? \"Simple vocabulary\" : aspw \u003c 2.0 ? \"Moderate vocabulary\" : \"Complex vocabulary\") + '\u003c/div\u003e\u003c/div\u003e';\n      html += '\u003c/div\u003e\u003c/div\u003e';\n    }\n\n    document.getElementById(\"rt-results-body\").innerHTML = html;\n  };\n\n  window.rtSpeedChange = function(val) {\n    wpm = parseInt(val);\n    document.getElementById(\"rt-speed-val\").textContent = wpm + \" WPM\";\n    // Update active preset\n    document.querySelectorAll(\"#rt-app .preset-btn\").forEach(function(b) { b.classList.remove(\"active\"); });\n    rtUpdate();\n  };\n\n  window.rtSetSpeed = function(val) {\n    wpm = val;\n    document.getElementById(\"rt-speed\").value = val;\n    document.getElementById(\"rt-speed-val\").textContent = val + \" WPM\";\n    document.querySelectorAll(\"#rt-app .preset-btn\").forEach(function(b) {\n      b.classList.toggle(\"active\", parseInt(b.textContent.match(/\\d+/)[0]) === val);\n    });\n    rtUpdate();\n  };\n\n  window.rtClear = function() {\n    document.getElementById(\"rt-textarea\").value = \"\";\n    document.getElementById(\"rt-wc-input\").value = \"\";\n    rtUpdate();\n  };\n\n  window.rtBookUpdate = function() {\n    var pages = parseInt(document.getElementById(\"rt-pages\").value);\n    var wpp = parseInt(document.getElementById(\"rt-wppage\").value) || 250;\n    var el = document.getElementById(\"rt-book-result\");\n    if (!pages || pages \u003c 1) { el.style.display = \"none\"; return; }\n    var totalWords = pages * wpp;\n    var mins = totalWords / wpm;\n    el.style.display = \"block\";\n    el.innerHTML = \"A \u003cstrong\u003e\" + pages + \"-page\u003c/strong\u003e book (~\" + fmtNum(totalWords) + \" words) would take approximately \u003cstrong\u003e\" + fmtTime(mins) + \"\u003c/strong\u003e to read at your current speed (\" + wpm + \" WPM).\";\n  };\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003cdiv class=\"related-note\"\u003e\nRelated: Count words in your content with the \u003ca href=\"/tools/word-counter/\"\u003eWord Counter\u003c/a\u003e.\n\u003c/div\u003e","title":"Reading Time Calculator"},{"content":" Reading speed: 225 WPM 📖 — Reading Time 🎤 — Speaking Time\n(130 WPM) 📝 0 Words 🔤 0 Characters 📄 0 Sentences Estimated Difficulty — Based on average words per sentence and word length\nClear Text\n","permalink":"https://productivity-works.com/tools/reading-time-estimator/","summary":"\u003cdiv id=\"rte-app\"\u003e\n  \u003cstyle\u003e\n    #rte-app {\n      font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n      max-width: 760px;\n      margin: 0 auto;\n      color: #1a1a2e;\n    }\n    #rte-app .rte-textarea {\n      width: 100%;\n      height: 220px;\n      padding: 14px 16px;\n      font-size: 15px;\n      line-height: 1.6;\n      border: 2px solid #e0e4ef;\n      border-radius: 10px;\n      resize: vertical;\n      box-sizing: border-box;\n      background: #fafbff;\n      color: #1a1a2e;\n      transition: border-color 0.2s;\n      outline: none;\n    }\n    #rte-app .rte-textarea:focus {\n      border-color: #4f6ef7;\n      background: #fff;\n    }\n    #rte-app .rte-textarea::placeholder {\n      color: #aab0c8;\n    }\n    #rte-app .rte-slider-row {\n      display: flex;\n      align-items: center;\n      gap: 14px;\n      margin: 18px 0 10px;\n      flex-wrap: wrap;\n    }\n    #rte-app .rte-slider-label {\n      font-size: 14px;\n      color: #555;\n      white-space: nowrap;\n    }\n    #rte-app .rte-slider {\n      flex: 1;\n      min-width: 140px;\n      accent-color: #4f6ef7;\n      cursor: pointer;\n    }\n    #rte-app .rte-wpm-badge {\n      background: #4f6ef7;\n      color: #fff;\n      font-size: 13px;\n      font-weight: 600;\n      padding: 3px 11px;\n      border-radius: 20px;\n      white-space: nowrap;\n    }\n    #rte-app .rte-cards {\n      display: grid;\n      grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));\n      gap: 14px;\n      margin: 20px 0;\n    }\n    #rte-app .rte-card {\n      background: #fff;\n      border: 1.5px solid #e0e4ef;\n      border-radius: 12px;\n      padding: 18px 16px 14px;\n      text-align: center;\n      box-shadow: 0 2px 8px rgba(79,110,247,0.06);\n    }\n    #rte-app .rte-card-icon {\n      font-size: 26px;\n      margin-bottom: 6px;\n      display: block;\n    }\n    #rte-app .rte-card-value {\n      font-size: 26px;\n      font-weight: 700;\n      color: #4f6ef7;\n      line-height: 1.1;\n    }\n    #rte-app .rte-card-label {\n      font-size: 12px;\n      color: #888;\n      margin-top: 4px;\n      line-height: 1.4;\n    }\n    #rte-app .rte-difficulty-section {\n      margin: 10px 0 6px;\n    }\n    #rte-app .rte-difficulty-header {\n      display: flex;\n      justify-content: space-between;\n      align-items: center;\n      margin-bottom: 6px;\n    }\n    #rte-app .rte-difficulty-title {\n      font-size: 13px;\n      color: #555;\n      font-weight: 500;\n    }\n    #rte-app .rte-difficulty-label {\n      font-size: 13px;\n      font-weight: 600;\n      color: #4f6ef7;\n    }\n    #rte-app .rte-bar-track {\n      width: 100%;\n      height: 10px;\n      background: #e8eaf5;\n      border-radius: 6px;\n      overflow: hidden;\n    }\n    #rte-app .rte-bar-fill {\n      height: 100%;\n      border-radius: 6px;\n      background: linear-gradient(90deg, #6ee7b7, #4f6ef7, #f97316);\n      transition: width 0.4s cubic-bezier(.4,0,.2,1);\n    }\n    #rte-app .rte-hint {\n      font-size: 12px;\n      color: #aab0c8;\n      text-align: center;\n      margin-top: 4px;\n    }\n    #rte-app .rte-clear-btn {\n      display: block;\n      margin: 14px auto 0;\n      padding: 8px 28px;\n      font-size: 14px;\n      border: 1.5px solid #e0e4ef;\n      border-radius: 8px;\n      background: #fff;\n      color: #555;\n      cursor: pointer;\n      transition: background 0.15s, border-color 0.15s;\n    }\n    #rte-app .rte-clear-btn:hover {\n      background: #f0f2ff;\n      border-color: #4f6ef7;\n      color: #4f6ef7;\n    }\n  \u003c/style\u003e\n  \u003ctextarea\n    id=\"rte-input\"\n    class=\"rte-textarea\"\n    placeholder=\"Paste or type your article text here...\"\n  \u003e\u003c/textarea\u003e\n  \u003cdiv class=\"rte-slider-row\"\u003e\n    \u003cspan class=\"rte-slider-label\"\u003eReading speed:\u003c/span\u003e\n    \u003cinput\n      type=\"range\"\n      id=\"rte-wpm-slider\"\n      class=\"rte-slider\"\n      min=\"100\"\n      max=\"400\"\n      value=\"225\"\n      step=\"5\"\n    /\u003e\n    \u003cspan class=\"rte-wpm-badge\"\u003e\u003cspan id=\"rte-wpm-display\"\u003e225\u003c/span\u003e WPM\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"rte-cards\"\u003e\n    \u003cdiv class=\"rte-card\"\u003e\n      \u003cspan class=\"rte-card-icon\"\u003e📖\u003c/span\u003e\n      \u003cdiv class=\"rte-card-value\" id=\"rte-read-time\"\u003e—\u003c/div\u003e\n      \u003cdiv class=\"rte-card-label\"\u003eReading Time\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"rte-card\"\u003e\n      \u003cspan class=\"rte-card-icon\"\u003e🎤\u003c/span\u003e\n      \u003cdiv class=\"rte-card-value\" id=\"rte-speak-time\"\u003e—\u003c/div\u003e\n      \u003cdiv class=\"rte-card-label\"\u003eSpeaking Time\u003cbr\u003e\u003csmall style=\"font-weight:400;color:#bbb\"\u003e(130 WPM)\u003c/small\u003e\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"rte-card\"\u003e\n      \u003cspan class=\"rte-card-icon\"\u003e📝\u003c/span\u003e\n      \u003cdiv class=\"rte-card-value\" id=\"rte-words\"\u003e0\u003c/div\u003e\n      \u003cdiv class=\"rte-card-label\"\u003eWords\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"rte-card\"\u003e\n      \u003cspan class=\"rte-card-icon\"\u003e🔤\u003c/span\u003e\n      \u003cdiv class=\"rte-card-value\" id=\"rte-chars\"\u003e0\u003c/div\u003e\n      \u003cdiv class=\"rte-card-label\"\u003eCharacters\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"rte-card\"\u003e\n      \u003cspan class=\"rte-card-icon\"\u003e📄\u003c/span\u003e\n      \u003cdiv class=\"rte-card-value\" id=\"rte-sentences\"\u003e0\u003c/div\u003e\n      \u003cdiv class=\"rte-card-label\"\u003eSentences\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"rte-difficulty-section\"\u003e\n    \u003cdiv class=\"rte-difficulty-header\"\u003e\n      \u003cspan class=\"rte-difficulty-title\"\u003eEstimated Difficulty\u003c/span\u003e\n      \u003cspan class=\"rte-difficulty-label\" id=\"rte-difficulty-label\"\u003e—\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"rte-bar-track\"\u003e\n      \u003cdiv class=\"rte-bar-fill\" id=\"rte-bar-fill\" style=\"width:0%\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cp class=\"rte-hint\"\u003eBased on average words per sentence and word length\u003c/p\u003e","title":"Reading Time Estimator - Article Length Calculator"},{"content":" Regex Interactive Cheatsheet Click \"Try it\" on any row to load the pattern into the live tester below.\nLive Tester / / g global i case-insensitive m multiline s dotAll No matches Highlighted matches appear here Clear No entries match your search. Pattern Description Example Actions Character Classes . Any character except newline c.t → \"cat\", \"cut\", \"c3t\" Try itCopy \\d Any digit [0-9] \\d+ → \"42\", \"100\" Try itCopy \\D Any non-digit character \\D+ → \"abc \", \" xyz\" Try itCopy \\w Word character [a-zA-Z0-9_] \\w+ → \"hello\", \"world_2\" Try itCopy \\W Non-word character \\W → \" \", \"-\", \"@\" Try itCopy \\s Whitespace (space, tab, newline…) \\s+ → gap between words Try itCopy \\S Non-whitespace character \\S+ → \"hello\", \"world\" Try itCopy \\t Horizontal tab character \\t → tab in TSV data Try itCopy \\n Newline character \\n → line break Try itCopy [abc] Character class — matches a, b, or c [aeiou] → vowels Try itCopy [^abc] Negated class — any char except a, b, c [^aeiou\\s] → consonants Try itCopy [a-z] Character range (a through z) [a-zA-Z]+ → words Try itCopy Quantifiers * 0 or more (greedy) ab* → \"a\", \"ab\", \"abbb\" Try itCopy + 1 or more (greedy) ab+ → \"ab\", \"abbb\" (not \"a\") Try itCopy ? 0 or 1 — makes preceding token optional colou?r → \"color\", \"colour\" Try itCopy {n} Exactly n occurrences \\d{4} → \"2025\", \"1234\" Try itCopy {n,m} Between n and m occurrences \\d{2,4} → \"12\", \"123\", \"1234\" Try itCopy {n,} n or more occurrences \\d{3,} → \"123\", \"12345\" Try itCopy *? 0 or more (lazy — shortest match) \u0026lt;.*?\u0026gt; → each HTML tag Try itCopy +? 1 or more (lazy — shortest match) \".+?\" → quoted strings ","permalink":"https://productivity-works.com/tools/regex-cheatsheet/","summary":"\u003cdiv id=\"rxcs-app\"\u003e\n\u003cstyle\u003e\n#rxcs-app {\n  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;\n  background: #1a1a2e;\n  color: #e0e0e0;\n  padding: 24px;\n  border-radius: 12px;\n  max-width: 960px;\n  margin: 0 auto;\n  box-sizing: border-box;\n}\n\n#rxcs-app * {\n  box-sizing: border-box;\n}\n\n#rxcs-app h2 {\n  color: #00ff88;\n  margin: 0 0 6px 0;\n  font-size: 1.35rem;\n  letter-spacing: 0.4px;\n}\n\n#rxcs-app h3 {\n  color: #00cc70;\n  font-size: 0.95rem;\n  margin: 20px 0 10px 0;\n  text-transform: uppercase;\n  letter-spacing: 1px;\n  border-bottom: 1px solid #2a2a4a;\n  padding-bottom: 6px;\n}\n\n#rxcs-app .rxcs-subtitle {\n  color: #606080;\n  font-size: 0.82rem;\n  margin-bottom: 20px;\n}\n\n/* ── Test Area ── */\n#rxcs-app .rxcs-tester {\n  background: #0d0d1a;\n  border: 1px solid #2a2a4a;\n  border-radius: 10px;\n  padding: 18px;\n  margin-bottom: 24px;\n}\n\n#rxcs-app .rxcs-tester-title {\n  color: #a0a0c0;\n  font-size: 0.78rem;\n  text-transform: uppercase;\n  letter-spacing: 0.9px;\n  font-weight: 700;\n  margin-bottom: 12px;\n}\n\n#rxcs-app .rxcs-row {\n  display: flex;\n  gap: 8px;\n  align-items: center;\n  flex-wrap: wrap;\n  margin-bottom: 10px;\n}\n\n#rxcs-app .rxcs-pattern-wrap {\n  display: flex;\n  align-items: center;\n  background: #16162a;\n  border: 1px solid #2a2a4a;\n  border-radius: 7px;\n  flex: 1;\n  min-width: 180px;\n  transition: border-color 0.2s;\n}\n\n#rxcs-app .rxcs-pattern-wrap:focus-within {\n  border-color: #00ff88;\n}\n\n#rxcs-app .rxcs-slash {\n  color: #00ff88;\n  font-size: 1.2rem;\n  padding: 0 7px;\n  font-family: monospace;\n  user-select: none;\n}\n\n#rxcs-app .rxcs-pattern-input {\n  background: transparent;\n  border: none;\n  outline: none;\n  color: #00ff88;\n  font-family: 'Courier New', monospace;\n  font-size: 0.95rem;\n  flex: 1;\n  padding: 9px 2px;\n  width: 0;\n}\n\n#rxcs-app .rxcs-flags-wrap {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n  align-items: center;\n}\n\n#rxcs-app .rxcs-flag-label {\n  display: flex;\n  align-items: center;\n  gap: 3px;\n  cursor: pointer;\n  font-size: 0.82rem;\n  color: #9090b0;\n  user-select: none;\n}\n\n#rxcs-app .rxcs-flag-label input[type=\"checkbox\"] {\n  accent-color: #00ff88;\n  width: 13px;\n  height: 13px;\n  cursor: pointer;\n}\n\n#rxcs-app .rxcs-flag-key {\n  font-family: monospace;\n  color: #00ff88;\n  font-weight: 700;\n}\n\n#rxcs-app .rxcs-teststr {\n  width: 100%;\n  background: #16162a;\n  border: 1px solid #2a2a4a;\n  color: #e0e0e0;\n  padding: 10px 12px;\n  border-radius: 7px;\n  font-family: 'Courier New', monospace;\n  font-size: 0.88rem;\n  resize: vertical;\n  outline: none;\n  transition: border-color 0.2s;\n  line-height: 1.5;\n}\n\n#rxcs-app .rxcs-teststr:focus {\n  border-color: #00ff88;\n}\n\n#rxcs-app .rxcs-stats {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin: 8px 0;\n  flex-wrap: wrap;\n}\n\n#rxcs-app .rxcs-badge {\n  background: #00ff8820;\n  border: 1px solid #00ff8840;\n  color: #00ff88;\n  padding: 3px 11px;\n  border-radius: 20px;\n  font-size: 0.78rem;\n  font-weight: 700;\n}\n\n#rxcs-app .rxcs-badge.no-match {\n  background: #ff444420;\n  border-color: #ff444440;\n  color: #ff8888;\n}\n\n#rxcs-app .rxcs-badge.error {\n  background: #ff220020;\n  border-color: #ff220040;\n  color: #ff6666;\n}\n\n#rxcs-app .rxcs-display {\n  background: #16162a;\n  border: 1px solid #2a2a4a;\n  border-radius: 7px;\n  padding: 10px 12px;\n  font-family: 'Courier New', monospace;\n  font-size: 0.88rem;\n  line-height: 1.6;\n  min-height: 60px;\n  white-space: pre-wrap;\n  word-break: break-all;\n  color: #c0c0e0;\n  margin-top: 6px;\n}\n\n#rxcs-app .rxcs-hl {\n  background: #00ff8840;\n  border-bottom: 2px solid #00ff88;\n  border-radius: 2px;\n  color: #00ff88;\n}\n\n#rxcs-app .rxcs-hl-alt {\n  background: #ff88ff30;\n  border-bottom: 2px solid #ff88ff;\n  border-radius: 2px;\n  color: #ff88ff;\n}\n\n/* ── Search bar ── */\n#rxcs-app .rxcs-search-wrap {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin-bottom: 18px;\n}\n\n#rxcs-app .rxcs-search {\n  flex: 1;\n  background: #0d0d1a;\n  border: 1px solid #2a2a4a;\n  color: #e0e0e0;\n  padding: 9px 13px;\n  border-radius: 7px;\n  font-size: 0.88rem;\n  outline: none;\n  transition: border-color 0.2s;\n}\n\n#rxcs-app .rxcs-search:focus {\n  border-color: #00ff88;\n}\n\n#rxcs-app .rxcs-search::placeholder {\n  color: #404060;\n}\n\n#rxcs-app .rxcs-search-clear {\n  background: #0d0d1a;\n  border: 1px solid #2a2a4a;\n  color: #808090;\n  padding: 8px 13px;\n  border-radius: 7px;\n  cursor: pointer;\n  font-size: 0.82rem;\n  transition: all 0.2s;\n}\n\n#rxcs-app .rxcs-search-clear:hover {\n  border-color: #00ff88;\n  color: #00ff88;\n}\n\n/* ── Reference Table ── */\n#rxcs-app .rxcs-table-wrap {\n  overflow-x: auto;\n  margin-bottom: 6px;\n  border-radius: 8px;\n  border: 1px solid #2a2a4a;\n}\n\n#rxcs-app .rxcs-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.85rem;\n  min-width: 600px;\n}\n\n#rxcs-app .rxcs-table thead tr {\n  background: #0d0d1a;\n}\n\n#rxcs-app .rxcs-table thead th {\n  color: #606080;\n  font-size: 0.74rem;\n  text-transform: uppercase;\n  letter-spacing: 0.8px;\n  padding: 9px 12px;\n  text-align: left;\n  font-weight: 700;\n  border-bottom: 1px solid #2a2a4a;\n}\n\n#rxcs-app .rxcs-table tbody tr {\n  border-bottom: 1px solid #1a1a30;\n  transition: background 0.15s;\n}\n\n#rxcs-app .rxcs-table tbody tr:last-child {\n  border-bottom: none;\n}\n\n#rxcs-app .rxcs-table tbody tr:hover {\n  background: #1e1e38;\n}\n\n#rxcs-app .rxcs-table tbody tr.rxcs-hidden {\n  display: none;\n}\n\n#rxcs-app .rxcs-table td {\n  padding: 9px 12px;\n  vertical-align: middle;\n}\n\n#rxcs-app .rxcs-token {\n  font-family: 'Courier New', monospace;\n  color: #00ff88;\n  font-weight: 700;\n  font-size: 0.9rem;\n  white-space: nowrap;\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n\n#rxcs-app .rxcs-desc {\n  color: #c0c0d8;\n  line-height: 1.4;\n}\n\n#rxcs-app .rxcs-example {\n  font-family: 'Courier New', monospace;\n  color: #ffe066;\n  font-size: 0.82rem;\n  white-space: nowrap;\n}\n\n#rxcs-app .rxcs-actions {\n  display: flex;\n  gap: 5px;\n  align-items: center;\n  white-space: nowrap;\n}\n\n#rxcs-app .rxcs-btn-try {\n  background: #00ff8818;\n  border: 1px solid #00ff8840;\n  color: #00ff88;\n  padding: 4px 10px;\n  border-radius: 5px;\n  cursor: pointer;\n  font-size: 0.75rem;\n  font-weight: 600;\n  transition: all 0.18s;\n}\n\n#rxcs-app .rxcs-btn-try:hover {\n  background: #00ff8830;\n  border-color: #00ff88;\n}\n\n#rxcs-app .rxcs-btn-copy {\n  background: #1a1a3a;\n  border: 1px solid #2a2a4a;\n  color: #8080a0;\n  padding: 4px 9px;\n  border-radius: 5px;\n  cursor: pointer;\n  font-size: 0.75rem;\n  transition: all 0.18s;\n}\n\n#rxcs-app .rxcs-btn-copy:hover {\n  border-color: #5050a0;\n  color: #c0c0e0;\n}\n\n#rxcs-app .rxcs-btn-copy.copied {\n  border-color: #00ff88;\n  color: #00ff88;\n}\n\n/* ── Section headers ── */\n#rxcs-app .rxcs-section-header {\n  background: #0d0d1a;\n}\n\n#rxcs-app .rxcs-section-header td {\n  color: #00cc70;\n  font-size: 0.75rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 1px;\n  padding: 7px 12px;\n  border-bottom: 1px solid #2a2a4a;\n}\n\n/* ── No results ── */\n#rxcs-app .rxcs-no-results {\n  text-align: center;\n  color: #404060;\n  padding: 28px;\n  font-size: 0.88rem;\n  display: none;\n}\n\n#rxcs-app .rxcs-no-results.visible {\n  display: block;\n}\n\n/* ── Recipes ── */\n#rxcs-app .rxcs-recipes {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));\n  gap: 10px;\n  margin-top: 8px;\n}\n\n#rxcs-app .rxcs-recipe-card {\n  background: #0d0d1a;\n  border: 1px solid #2a2a4a;\n  border-radius: 8px;\n  padding: 13px 14px;\n  transition: border-color 0.2s;\n}\n\n#rxcs-app .rxcs-recipe-card:hover {\n  border-color: #3a3a6a;\n}\n\n#rxcs-app .rxcs-recipe-name {\n  color: #e0e0ff;\n  font-size: 0.88rem;\n  font-weight: 700;\n  margin-bottom: 5px;\n}\n\n#rxcs-app .rxcs-recipe-pat {\n  font-family: 'Courier New', monospace;\n  color: #00ff88;\n  font-size: 0.78rem;\n  word-break: break-all;\n  margin-bottom: 7px;\n  background: #16162a;\n  padding: 5px 8px;\n  border-radius: 5px;\n  display: block;\n}\n\n#rxcs-app .rxcs-recipe-desc {\n  color: #7070a0;\n  font-size: 0.78rem;\n  margin-bottom: 9px;\n  line-height: 1.4;\n}\n\n#rxcs-app .rxcs-recipe-btns {\n  display: flex;\n  gap: 6px;\n}\n\n/* ── Responsive ── */\n@media (max-width: 640px) {\n  #rxcs-app {\n    padding: 14px;\n  }\n  #rxcs-app .rxcs-table {\n    font-size: 0.8rem;\n    min-width: 480px;\n  }\n  #rxcs-app .rxcs-recipes {\n    grid-template-columns: 1fr;\n  }\n  #rxcs-app .rxcs-flags-wrap {\n    gap: 7px;\n  }\n}\n\u003c/style\u003e\n\u003ch2\u003eRegex Interactive Cheatsheet\u003c/h2\u003e\n\u003cp class=\"rxcs-subtitle\"\u003eClick \"Try it\" on any row to load the pattern into the live tester below.\u003c/p\u003e","title":"Regex Cheatsheet — Interactive Reference"},{"content":"Test regular expressions instantly in your browser. Paste your pattern, choose flags, and see matches highlighted in real time — no sign-up, no server, no external libraries.\nRegular Expression / / g i m s u Quick Insert — Common Patterns Email URL Phone IPv4 Date YYYY-MM-DD Hex Color Number Username Strong Password HTML Tag Test String Contact us at hello@example.com or support@company.org Visit https://www.example.com or http://docs.site.io/page Call +1 (800) 555-0123 or 020 7946 0958 Server IP: 192.168.1.1, fallback: 10.0.0.254 Today is 2025-05-16, event on 2025/12/31 Match Highlights Matches: — Execution: — Match Details Replace Explain Enter a pattern above to see match details. Replace with: Copy result Enter a pattern above to see a token-by-token explanation. Quick reference → Regex Cheatsheet Escape strings → String Escape Tool Related Articles Best Online Coding Bootcamps 2026: Honest Comparison by Career Goal ","permalink":"https://productivity-works.com/tools/regex-tester/","summary":"\u003cp\u003eTest regular expressions instantly in your browser. Paste your pattern, choose flags, and see matches highlighted in real time — no sign-up, no server, no external libraries.\u003c/p\u003e\n\u003cdiv id=\"rx-app\"\u003e\n\u003cstyle\u003e\n  #rx-app *,\n  #rx-app *::before,\n  #rx-app *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\u003cp\u003e#rx-app {\nfont-family: -apple-system, BlinkMacSystemFont, \u0026ldquo;Segoe UI\u0026rdquo;, Roboto, sans-serif;\nfont-size: 14px;\ncolor: #1e293b;\nmax-width: 860px;\nmargin: 0 auto;\n}\u003c/p\u003e\n\u003cp\u003e#rx-app .rx-card {\nbackground: #fff;\nborder: 1px solid #e2e8f0;\nborder-radius: 10px;\npadding: 18px 20px;\nmargin-bottom: 14px;\n}\n#rx-app .rx-card-title {\nfont-size: 11px;\nfont-weight: 700;\nletter-spacing: .07em;\ntext-transform: uppercase;\ncolor: #64748b;\nmargin-bottom: 10px;\n}\u003c/p\u003e","title":"Regex Tester"},{"content":"Enter a regular expression to get an instant visual breakdown of every token, highlight all matches in your test string, and extract capture groups — no sign-up, no server, no external libraries.\nRegular Expression / / g i m s Copy Common Patterns Email URL Phone (US) Date YYYY-MM-DD IP Address Hex Color Capitalized Word Number Visual Breakdown Enter a regex pattern above to see the breakdown. Anchor Quantifier Escape / Special Character Class Group Alternation Wildcard Literal Test String Highlighted Matches Matches will appear here. Matches: — Test length: 0 Match Details \u0026amp; Captured Groups No matches yet. Related tools: Regex Tester · Regex Cheatsheet ","permalink":"https://productivity-works.com/tools/regex-visualizer/","summary":"\u003cp\u003eEnter a regular expression to get an instant visual breakdown of every token, highlight all matches in your test string, and extract capture groups — no sign-up, no server, no external libraries.\u003c/p\u003e\n\u003cdiv id=\"rv-app\"\u003e\n\u003cstyle\u003e\n  #rv-app *, #rv-app *::before, #rv-app *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\u003cp\u003e#rv-app {\nfont-family: -apple-system, BlinkMacSystemFont, \u0026ldquo;Segoe UI\u0026rdquo;, Roboto, sans-serif;\nfont-size: 14px;\ncolor: #1e293b;\nmax-width: 900px;\nmargin: 0 auto;\n}\u003c/p\u003e\n\u003cp\u003e#rv-app .rv-card {\nbackground: #fff;\nborder: 1px solid #e2e8f0;\nborder-radius: 10px;\npadding: 18px 20px;\nmargin-bottom: 14px;\n}\u003c/p\u003e","title":"Regex Visualizer \u0026 Explainer"},{"content":" Rent vs Buy Calculator Compare the true long-term cost of renting vs. buying a home\nRenting Monthly Rent ($) Annual Rent Increase (%) Renter's Insurance ($/mo) \u0026lt;!-- Buy Card --\u0026gt; \u0026lt;div class=\u0026quot;rvb-card buy-card\u0026quot;\u0026gt; \u0026lt;h2\u0026gt;Buying\u0026lt;/h2\u0026gt; \u0026lt;div class=\u0026quot;rvb-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Home Price ($)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;rvb-home-price\u0026quot; value=\u0026quot;400000\u0026quot; min=\u0026quot;0\u0026quot; step=\u0026quot;1000\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rvb-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Down Payment (%)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;rvb-down-pct\u0026quot; value=\u0026quot;20\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;100\u0026quot; step=\u0026quot;0.5\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rvb-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Mortgage Rate (%)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;rvb-interest-rate\u0026quot; value=\u0026quot;6.5\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;30\u0026quot; step=\u0026quot;0.05\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rvb-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Loan Term (years)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;rvb-loan-term\u0026quot; value=\u0026quot;30\u0026quot; min=\u0026quot;5\u0026quot; max=\u0026quot;40\u0026quot; step=\u0026quot;5\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rvb-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Property Tax (% of value/yr)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;rvb-property-tax\u0026quot; value=\u0026quot;1.1\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;5\u0026quot; step=\u0026quot;0.05\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rvb-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;HOA ($/mo)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;rvb-hoa\u0026quot; value=\u0026quot;0\u0026quot; min=\u0026quot;0\u0026quot; step=\u0026quot;25\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rvb-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Maintenance (% of value/yr)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;rvb-maintenance\u0026quot; value=\u0026quot;1\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;5\u0026quot; step=\u0026quot;0.1\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rvb-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Home Insurance ($/mo)\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;rvb-home-insurance\u0026quot; value=\u0026quot;150\u0026quot; min=\u0026quot;0\u0026quot; step=\u0026quot;10\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Shared Assumptions Investment Return (%/yr) Home Appreciation (%/yr) Selling Cost (% of value) Analysis Timeframe 15 years Cumulative Net Cost Over Time Renting (net cost) Buying (net cost) \u0026#8987; Calculating breakeven... Total Rent Cost Total Buy Net Cost Difference Related Tools Mortgage Payment Calculator Compound Interest Calculator Net Worth Tracker Emergency Fund Calculator Related Tools Debt Payoff Calculator Loan Comparison Mortgage Affordability Calculator ","permalink":"https://productivity-works.com/tools/rent-vs-buy-calculator/","summary":"\u003cdiv id=\"rvb-app\"\u003e\n\u003cstyle\u003e\n#rvb-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  color: #1a1a2e;\n  background: #f8f9ff;\n  border-radius: 16px;\n  overflow: hidden;\n  box-shadow: 0 4px 24px rgba(0,0,0,0.08);\n}\n#rvb-app h2 {\n  margin: 0;\n  font-size: 1.1rem;\n  font-weight: 700;\n  letter-spacing: 0.02em;\n}\n#rvb-app .rvb-header {\n  background: linear-gradient(135deg, #16213e 0%, #0f3460 100%);\n  color: #fff;\n  padding: 28px 32px;\n  text-align: center;\n}\n#rvb-app .rvb-header h1 {\n  margin: 0 0 8px;\n  font-size: 1.6rem;\n  font-weight: 800;\n}\n#rvb-app .rvb-header p {\n  margin: 0;\n  opacity: 0.8;\n  font-size: 0.95rem;\n}\n#rvb-app .rvb-body {\n  padding: 28px 32px;\n}\n#rvb-app .rvb-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 20px;\n  margin-bottom: 20px;\n}\n@media (max-width: 620px) {\n  #rvb-app .rvb-grid { grid-template-columns: 1fr; }\n  #rvb-app .rvb-body { padding: 20px 16px; }\n}\n#rvb-app .rvb-card {\n  background: #fff;\n  border-radius: 12px;\n  padding: 20px;\n  box-shadow: 0 2px 8px rgba(0,0,0,0.06);\n}\n#rvb-app .rvb-card.rent-card { border-top: 4px solid #3498db; }\n#rvb-app .rvb-card.buy-card  { border-top: 4px solid #e67e22; }\n#rvb-app .rvb-card h2 { margin-bottom: 16px; color: #1a1a2e; }\n#rvb-app .rvb-card.rent-card h2 { color: #3498db; }\n#rvb-app .rvb-card.buy-card h2  { color: #e67e22; }\n#rvb-app .rvb-field {\n  margin-bottom: 14px;\n}\n#rvb-app .rvb-field label {\n  display: block;\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #555;\n  margin-bottom: 5px;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#rvb-app .rvb-field input[type=\"number\"],\n#rvb-app .rvb-field input[type=\"range\"] {\n  width: 100%;\n  box-sizing: border-box;\n}\n#rvb-app .rvb-field input[type=\"number\"] {\n  padding: 8px 12px;\n  border: 1.5px solid #dde1f0;\n  border-radius: 8px;\n  font-size: 0.97rem;\n  color: #1a1a2e;\n  background: #f8f9ff;\n  transition: border-color 0.2s;\n  -moz-appearance: textfield;\n}\n#rvb-app .rvb-field input[type=\"number\"]::-webkit-outer-spin-button,\n#rvb-app .rvb-field input[type=\"number\"]::-webkit-inner-spin-button { -webkit-appearance: none; }\n#rvb-app .rvb-field input[type=\"number\"]:focus {\n  outline: none;\n  border-color: #0f3460;\n  background: #fff;\n}\n#rvb-app .rvb-shared-card {\n  background: #fff;\n  border-radius: 12px;\n  padding: 20px;\n  box-shadow: 0 2px 8px rgba(0,0,0,0.06);\n  margin-bottom: 20px;\n  border-top: 4px solid #6c5ce7;\n}\n#rvb-app .rvb-shared-card h2 { color: #6c5ce7; margin-bottom: 16px; }\n#rvb-app .rvb-shared-inner {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 14px;\n}\n@media (max-width: 620px) {\n  #rvb-app .rvb-shared-inner { grid-template-columns: 1fr 1fr; }\n}\n#rvb-app .rvb-timeline-card {\n  background: #fff;\n  border-radius: 12px;\n  padding: 20px;\n  box-shadow: 0 2px 8px rgba(0,0,0,0.06);\n  margin-bottom: 20px;\n}\n#rvb-app .rvb-timeline-card label {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  font-weight: 700;\n  font-size: 1rem;\n  margin-bottom: 10px;\n}\n#rvb-app .rvb-timeline-card label span {\n  background: #0f3460;\n  color: #fff;\n  padding: 3px 12px;\n  border-radius: 20px;\n  font-size: 0.95rem;\n}\n#rvb-app input[type=\"range\"] {\n  -webkit-appearance: none;\n  height: 6px;\n  border-radius: 3px;\n  background: #dde1f0;\n  cursor: pointer;\n}\n#rvb-app input[type=\"range\"]::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  width: 20px;\n  height: 20px;\n  border-radius: 50%;\n  background: #0f3460;\n  cursor: pointer;\n  box-shadow: 0 2px 6px rgba(15,52,96,0.3);\n}\n#rvb-app .rvb-chart-card {\n  background: #fff;\n  border-radius: 12px;\n  padding: 20px;\n  box-shadow: 0 2px 8px rgba(0,0,0,0.06);\n  margin-bottom: 20px;\n}\n#rvb-app .rvb-chart-card h2 { margin-bottom: 16px; color: #1a1a2e; }\n#rvb-app canvas {\n  width: 100% !important;\n  max-width: 100%;\n  border-radius: 8px;\n}\n#rvb-app .rvb-legend {\n  display: flex;\n  gap: 20px;\n  margin-top: 10px;\n  flex-wrap: wrap;\n}\n#rvb-app .rvb-legend-item {\n  display: flex;\n  align-items: center;\n  gap: 7px;\n  font-size: 0.85rem;\n  font-weight: 600;\n}\n#rvb-app .rvb-legend-dot {\n  width: 14px;\n  height: 14px;\n  border-radius: 3px;\n  flex-shrink: 0;\n}\n#rvb-app .rvb-result-card {\n  border-radius: 12px;\n  padding: 24px;\n  margin-bottom: 20px;\n  text-align: center;\n}\n#rvb-app .rvb-result-card.rent-wins {\n  background: linear-gradient(135deg, #e8f4fd, #d0eaf9);\n  border: 2px solid #3498db;\n}\n#rvb-app .rvb-result-card.buy-wins {\n  background: linear-gradient(135deg, #fef3e8, #fde8cc);\n  border: 2px solid #e67e22;\n}\n#rvb-app .rvb-result-card.tie {\n  background: linear-gradient(135deg, #f3f0ff, #e8e3ff);\n  border: 2px solid #6c5ce7;\n}\n#rvb-app .rvb-result-title {\n  font-size: 1.3rem;\n  font-weight: 800;\n  margin-bottom: 8px;\n}\n#rvb-app .rvb-result-subtitle {\n  font-size: 0.92rem;\n  opacity: 0.8;\n  margin-bottom: 16px;\n}\n#rvb-app .rvb-stats-row {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 12px;\n  margin-top: 8px;\n}\n@media (max-width: 540px) {\n  #rvb-app .rvb-stats-row { grid-template-columns: 1fr; }\n}\n#rvb-app .rvb-stat-box {\n  background: #fff;\n  border-radius: 10px;\n  padding: 14px 10px;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.07);\n}\n#rvb-app .rvb-stat-label {\n  font-size: 0.75rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  color: #777;\n  margin-bottom: 4px;\n}\n#rvb-app .rvb-stat-value {\n  font-size: 1.1rem;\n  font-weight: 800;\n  color: #1a1a2e;\n}\n#rvb-app .rvb-breakeven {\n  background: #fff;\n  border-radius: 12px;\n  padding: 18px 20px;\n  margin-bottom: 20px;\n  box-shadow: 0 2px 8px rgba(0,0,0,0.06);\n  display: flex;\n  align-items: center;\n  gap: 14px;\n}\n#rvb-app .rvb-breakeven-icon {\n  font-size: 2rem;\n  flex-shrink: 0;\n}\n#rvb-app .rvb-breakeven-text strong {\n  display: block;\n  font-size: 1rem;\n  font-weight: 700;\n  margin-bottom: 2px;\n}\n#rvb-app .rvb-breakeven-text span {\n  font-size: 0.88rem;\n  color: #555;\n}\n#rvb-app .rvb-related {\n  background: #fff;\n  border-radius: 12px;\n  padding: 20px;\n  box-shadow: 0 2px 8px rgba(0,0,0,0.06);\n  margin-bottom: 0;\n}\n#rvb-app .rvb-related h2 { margin-bottom: 14px; color: #1a1a2e; }\n#rvb-app .rvb-related ul {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 8px;\n}\n@media (max-width: 540px) {\n  #rvb-app .rvb-related ul { grid-template-columns: 1fr; }\n}\n#rvb-app .rvb-related ul li a {\n  display: block;\n  padding: 10px 14px;\n  background: #f8f9ff;\n  border-radius: 8px;\n  text-decoration: none;\n  color: #0f3460;\n  font-size: 0.9rem;\n  font-weight: 600;\n  border: 1.5px solid #dde1f0;\n  transition: background 0.15s, border-color 0.15s;\n}\n#rvb-app .rvb-related ul li a:hover {\n  background: #e8ecfa;\n  border-color: #0f3460;\n}\n\u003c/style\u003e\n\u003cdiv class=\"rvb-header\"\u003e\n  \u003ch1\u003eRent vs Buy Calculator\u003c/h1\u003e\n  \u003cp\u003eCompare the true long-term cost of renting vs. buying a home\u003c/p\u003e","title":"Rent vs Buy Calculator - Should You Rent or Buy?"},{"content":"Build a professional resume in minutes — no account needed. Fill in your details, pick a template, and print or save as PDF.\nTemplate: Classic Modern Minimal Color: Save Load Clear Print / Save PDF \u0026lt;!-- Personal Info --\u0026gt; \u0026lt;div class=\u0026quot;rb-section-card\u0026quot; data-section=\u0026quot;personal\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;rb-section-header\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;rb-move-btn rb-move-up\u0026quot; title=\u0026quot;Move up\u0026quot;\u0026gt;↑\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;rb-move-btn rb-move-dn\u0026quot; title=\u0026quot;Move down\u0026quot;\u0026gt;↓\u0026lt;/button\u0026gt; \u0026lt;span class=\u0026quot;rb-sec-title\u0026quot;\u0026gt;Personal Info\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;rb-sec-toggle\u0026quot;\u0026gt;▾\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rb-section-body\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;rb-field\u0026quot;\u0026gt;\u0026lt;label\u0026gt;Full Name\u0026lt;/label\u0026gt;\u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;rb-name\u0026quot; placeholder=\u0026quot;Jane Smith\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rb-field-row\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;rb-field\u0026quot;\u0026gt;\u0026lt;label\u0026gt;Email\u0026lt;/label\u0026gt;\u0026lt;input type=\u0026quot;email\u0026quot; id=\u0026quot;rb-email\u0026quot; placeholder=\u0026quot;jane@example.com\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rb-field\u0026quot;\u0026gt;\u0026lt;label\u0026gt;Phone\u0026lt;/label\u0026gt;\u0026lt;input type=\u0026quot;tel\u0026quot; id=\u0026quot;rb-phone\u0026quot; placeholder=\u0026quot;+1 555-0100\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rb-field-row\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;rb-field\u0026quot;\u0026gt;\u0026lt;label\u0026gt;Location\u0026lt;/label\u0026gt;\u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;rb-location\u0026quot; placeholder=\u0026quot;New York, NY\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rb-field\u0026quot;\u0026gt;\u0026lt;label\u0026gt;LinkedIn\u0026lt;/label\u0026gt;\u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;rb-linkedin\u0026quot; placeholder=\u0026quot;linkedin.com/in/jane\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rb-field\u0026quot;\u0026gt;\u0026lt;label\u0026gt;Website / Portfolio\u0026lt;/label\u0026gt;\u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;rb-website\u0026quot; placeholder=\u0026quot;janesmith.com\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Summary --\u0026gt; \u0026lt;div class=\u0026quot;rb-section-card\u0026quot; data-section=\u0026quot;summary\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;rb-section-header\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;rb-move-btn rb-move-up\u0026quot; title=\u0026quot;Move up\u0026quot;\u0026gt;↑\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;rb-move-btn rb-move-dn\u0026quot; title=\u0026quot;Move down\u0026quot;\u0026gt;↓\u0026lt;/button\u0026gt; \u0026lt;span class=\u0026quot;rb-sec-title\u0026quot;\u0026gt;Professional Summary\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;rb-sec-toggle\u0026quot;\u0026gt;▾\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rb-section-body\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;rb-field\u0026quot;\u0026gt; \u0026lt;textarea id=\u0026quot;rb-summary\u0026quot; placeholder=\u0026quot;Brief overview of your professional background and goals…\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Work Experience --\u0026gt; \u0026lt;div class=\u0026quot;rb-section-card\u0026quot; data-section=\u0026quot;experience\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;rb-section-header\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;rb-move-btn rb-move-up\u0026quot; title=\u0026quot;Move up\u0026quot;\u0026gt;↑\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;rb-move-btn rb-move-dn\u0026quot; title=\u0026quot;Move down\u0026quot;\u0026gt;↓\u0026lt;/button\u0026gt; \u0026lt;span class=\u0026quot;rb-sec-title\u0026quot;\u0026gt;Work Experience\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;rb-sec-toggle\u0026quot;\u0026gt;▾\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rb-section-body\u0026quot;\u0026gt; \u0026lt;div id=\u0026quot;rb-exp-list\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;rb-add-btn\u0026quot; id=\u0026quot;rb-add-exp\u0026quot;\u0026gt;+ Add Experience\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Education --\u0026gt; \u0026lt;div class=\u0026quot;rb-section-card\u0026quot; data-section=\u0026quot;education\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;rb-section-header\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;rb-move-btn rb-move-up\u0026quot; title=\u0026quot;Move up\u0026quot;\u0026gt;↑\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;rb-move-btn rb-move-dn\u0026quot; title=\u0026quot;Move down\u0026quot;\u0026gt;↓\u0026lt;/button\u0026gt; \u0026lt;span class=\u0026quot;rb-sec-title\u0026quot;\u0026gt;Education\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;rb-sec-toggle\u0026quot;\u0026gt;▾\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rb-section-body\u0026quot;\u0026gt; \u0026lt;div id=\u0026quot;rb-edu-list\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;rb-add-btn\u0026quot; id=\u0026quot;rb-add-edu\u0026quot;\u0026gt;+ Add Education\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Skills --\u0026gt; \u0026lt;div class=\u0026quot;rb-section-card\u0026quot; data-section=\u0026quot;skills\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;rb-section-header\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;rb-move-btn rb-move-up\u0026quot; title=\u0026quot;Move up\u0026quot;\u0026gt;↑\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;rb-move-btn rb-move-dn\u0026quot; title=\u0026quot;Move down\u0026quot;\u0026gt;↓\u0026lt;/button\u0026gt; \u0026lt;span class=\u0026quot;rb-sec-title\u0026quot;\u0026gt;Skills\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;rb-sec-toggle\u0026quot;\u0026gt;▾\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rb-section-body\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;rb-tags-input\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;rb-skill-input\u0026quot; placeholder=\u0026quot;e.g. JavaScript\u0026quot;\u0026gt; \u0026lt;button id=\u0026quot;rb-skill-add\u0026quot;\u0026gt;Add\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;rb-tags-wrap\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Languages --\u0026gt; \u0026lt;div class=\u0026quot;rb-section-card\u0026quot; data-section=\u0026quot;languages\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;rb-section-header\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;rb-move-btn rb-move-up\u0026quot; title=\u0026quot;Move up\u0026quot;\u0026gt;↑\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;rb-move-btn rb-move-dn\u0026quot; title=\u0026quot;Move down\u0026quot;\u0026gt;↓\u0026lt;/button\u0026gt; \u0026lt;span class=\u0026quot;rb-sec-title\u0026quot;\u0026gt;Languages\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;rb-sec-toggle\u0026quot;\u0026gt;▾\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rb-section-body\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;rb-tags-input\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;rb-lang-input\u0026quot; placeholder=\u0026quot;e.g. English (Native)\u0026quot;\u0026gt; \u0026lt;button id=\u0026quot;rb-lang-add\u0026quot;\u0026gt;Add\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;rb-lang-tags-wrap\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Live Preview Create cover letters → Lorem Ipsum Generator Generate email signatures → Email Signature Generator Related Articles How to Use AI to Land Your Dream Job (Resume + Interview) How to Create a Resume with AI (Step-by-Step Guide for 2026) ChatGPT Resume Tips for Career Changers Over 50 (2026) ","permalink":"https://productivity-works.com/tools/resume-builder/","summary":"\u003cp\u003eBuild a professional resume in minutes — no account needed. Fill in your details, pick a template, and print or save as PDF.\u003c/p\u003e\n\u003cdiv id=\"rb-app\"\u003e\n\u003cstyle\u003e\n#rb-app * { box-sizing: border-box; }\n#rb-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  color: #1e293b;\n  max-width: 1200px;\n  margin: 0 auto;\n}\n#rb-app h2 { font-size: 1.3rem; margin: 0 0 16px; color: #0f172a; }\n#rb-app h3 { font-size: 1rem; margin: 0 0 10px; color: #334155; }\n\n/* Layout */\n#rb-layout {\n  display: flex;\n  gap: 24px;\n  align-items: flex-start;\n}\n#rb-editor {\n  flex: 0 0 420px;\n  min-width: 0;\n}\n#rb-preview-wrap {\n  flex: 1;\n  min-width: 0;\n  position: sticky;\n  top: 20px;\n}\n@media (max-width: 900px) {\n  #rb-layout { flex-direction: column; }\n  #rb-editor { flex: none; width: 100%; }\n  #rb-preview-wrap { position: static; width: 100%; }\n}\n\n/* Controls bar */\n#rb-controls {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n  align-items: center;\n  margin-bottom: 18px;\n  padding: 12px 14px;\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n}\n#rb-controls label { font-size: 13px; color: #64748b; font-weight: 500; }\n#rb-controls select, #rb-controls input[type=color] {\n  border: 1px solid #cbd5e1;\n  border-radius: 6px;\n  padding: 4px 8px;\n  font-size: 13px;\n  background: #fff;\n  cursor: pointer;\n}\n#rb-controls input[type=color] {\n  width: 38px; height: 30px; padding: 2px 4px;\n}\n#rb-btn-print, #rb-btn-save, #rb-btn-load, #rb-btn-clear {\n  padding: 6px 14px;\n  border-radius: 7px;\n  border: none;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: opacity .15s;\n}\n#rb-btn-print { background: #2563eb; color: #fff; }\n#rb-btn-save  { background: #059669; color: #fff; }\n#rb-btn-load  { background: #7c3aed; color: #fff; }\n#rb-btn-clear { background: #ef4444; color: #fff; }\n#rb-btn-print:hover, #rb-btn-save:hover, #rb-btn-load:hover, #rb-btn-clear:hover { opacity: .85; }\n\n/* Sections list */\n#rb-sections-list { display: flex; flex-direction: column; gap: 12px; }\n.rb-section-card {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n  overflow: hidden;\n}\n.rb-section-header {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 10px 14px;\n  background: #f1f5f9;\n  cursor: pointer;\n  user-select: none;\n}\n.rb-section-header .rb-sec-title { font-weight: 600; font-size: 14px; flex: 1; }\n.rb-section-header .rb-sec-toggle { font-size: 11px; color: #94a3b8; }\n.rb-move-btn {\n  background: none; border: 1px solid #cbd5e1; border-radius: 5px;\n  width: 24px; height: 24px; font-size: 13px; cursor: pointer; color: #64748b;\n  display: flex; align-items: center; justify-content: center; padding: 0;\n}\n.rb-move-btn:hover { background: #e2e8f0; }\n.rb-section-body { padding: 14px; display: block; }\n.rb-section-body.hidden { display: none; }\n\n/* Form fields */\n.rb-field { margin-bottom: 10px; }\n.rb-field label { display: block; font-size: 12px; font-weight: 600; color: #64748b; margin-bottom: 4px; }\n.rb-field input, .rb-field textarea {\n  width: 100%; padding: 7px 10px; border: 1px solid #cbd5e1; border-radius: 7px;\n  font-size: 13px; font-family: inherit; color: #1e293b; background: #fff;\n  transition: border-color .15s;\n}\n.rb-field input:focus, .rb-field textarea:focus {\n  outline: none; border-color: #2563eb; box-shadow: 0 0 0 3px rgba(37,99,235,.1);\n}\n.rb-field textarea { resize: vertical; min-height: 70px; }\n.rb-field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }\n\n/* Entry cards */\n.rb-entry {\n  border: 1px solid #e2e8f0; border-radius: 8px; padding: 12px;\n  margin-bottom: 10px; background: #fafafa; position: relative;\n}\n.rb-entry-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }\n.rb-entry-label { font-size: 12px; font-weight: 700; color: #475569; }\n.rb-remove-entry {\n  background: none; border: 1px solid #fca5a5; color: #ef4444;\n  border-radius: 5px; font-size: 11px; padding: 2px 8px; cursor: pointer;\n}\n.rb-remove-entry:hover { background: #fef2f2; }\n.rb-add-btn {\n  width: 100%; padding: 8px; border: 2px dashed #cbd5e1; background: #f8fafc;\n  border-radius: 7px; font-size: 13px; color: #64748b; cursor: pointer; margin-top: 4px;\n}\n.rb-add-btn:hover { border-color: #2563eb; color: #2563eb; background: #eff6ff; }\n\n/* Skills / Languages tags */\n.rb-tags-input { display: flex; gap: 6px; }\n.rb-tags-input input { flex: 1; }\n.rb-tags-input button {\n  padding: 7px 12px; background: #2563eb; color: #fff; border: none;\n  border-radius: 7px; font-size: 13px; cursor: pointer; white-space: nowrap;\n}\n.rb-tag {\n  display: inline-flex; align-items: center; gap: 4px;\n  padding: 3px 10px; background: #dbeafe; color: #1d4ed8;\n  border-radius: 20px; font-size: 12px; margin: 4px 4px 0 0;\n}\n.rb-tag button {\n  background: none; border: none; cursor: pointer; color: #1d4ed8;\n  font-size: 13px; padding: 0; line-height: 1;\n}\n#rb-tags-wrap, #rb-lang-tags-wrap { margin-top: 6px; min-height: 28px; }\n\n/* Preview */\n#rb-preview-wrap h2 { font-size: 1.1rem; }\n#rb-preview-box {\n  border: 1px solid #e2e8f0; border-radius: 10px; overflow: auto;\n  background: #fff; min-height: 500px;\n}\n#rb-preview-inner {\n  padding: 32px 36px;\n  font-family: inherit;\n}\n\n/* --- Template: Classic --- */\n.rb-tpl-classic #rb-preview-inner { font-family: Georgia, 'Times New Roman', serif; }\n.rb-tpl-classic .rbp-name { font-size: 26px; font-weight: 700; border-bottom: 2px solid var(--rb-accent, #2563eb); padding-bottom: 6px; margin-bottom: 6px; }\n.rb-tpl-classic .rbp-contact { font-size: 12px; color: #64748b; margin-bottom: 20px; }\n.rb-tpl-classic .rbp-section-title { font-size: 14px; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; color: var(--rb-accent, #2563eb); border-bottom: 1px solid #e2e8f0; padding-bottom: 4px; margin: 18px 0 10px; }\n.rb-tpl-classic .rbp-entry-title { font-weight: 700; font-size: 14px; }\n.rb-tpl-classic .rbp-entry-sub { font-size: 12px; color: #64748b; }\n.rb-tpl-classic .rbp-entry-date { font-size: 11px; color: #94a3b8; }\n.rb-tpl-classic .rbp-entry-desc { font-size: 13px; margin-top: 4px; white-space: pre-wrap; }\n.rb-tpl-classic .rbp-summary { font-size: 13px; white-space: pre-wrap; }\n.rb-tpl-classic .rbp-tag { display: inline-block; padding: 2px 10px; border: 1px solid var(--rb-accent, #2563eb); color: var(--rb-accent, #2563eb); border-radius: 3px; font-size: 12px; margin: 2px 3px 0 0; }\n\n/* --- Template: Modern --- */\n.rb-tpl-modern #rb-preview-inner { font-family: 'Segoe UI', Roboto, sans-serif; display: grid; grid-template-columns: 200px 1fr; gap: 0; padding: 0; }\n.rb-tpl-modern .rbp-sidebar { background: var(--rb-accent, #2563eb); color: #fff; padding: 28px 20px; }\n.rb-tpl-modern .rbp-main { padding: 28px 28px; }\n.rb-tpl-modern .rbp-name { font-size: 22px; font-weight: 700; color: #fff; margin-bottom: 4px; }\n.rb-tpl-modern .rbp-contact { font-size: 11px; color: rgba(255,255,255,.85); line-height: 1.8; margin-bottom: 20px; }\n.rb-tpl-modern .rbp-sidebar .rbp-section-title { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .1em; color: rgba(255,255,255,.7); border-bottom: 1px solid rgba(255,255,255,.25); padding-bottom: 4px; margin: 16px 0 8px; }\n.rb-tpl-modern .rbp-main .rbp-section-title { font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; color: var(--rb-accent, #2563eb); border-bottom: 2px solid var(--rb-accent, #2563eb); padding-bottom: 3px; margin: 18px 0 10px; }\n.rb-tpl-modern .rbp-entry-title { font-weight: 700; font-size: 14px; }\n.rb-tpl-modern .rbp-entry-sub { font-size: 12px; color: #64748b; }\n.rb-tpl-modern .rbp-entry-date { font-size: 11px; color: #94a3b8; }\n.rb-tpl-modern .rbp-entry-desc { font-size: 13px; margin-top: 4px; white-space: pre-wrap; }\n.rb-tpl-modern .rbp-summary { font-size: 13px; white-space: pre-wrap; }\n.rb-tpl-modern .rbp-tag { display: inline-block; padding: 2px 8px; background: rgba(255,255,255,.2); color: #fff; border-radius: 3px; font-size: 11px; margin: 2px 2px 0 0; }\n.rb-tpl-modern .rbp-main .rbp-tag { background: #eff6ff; color: var(--rb-accent, #2563eb); }\n@media (max-width: 600px) {\n  .rb-tpl-modern #rb-preview-inner { grid-template-columns: 1fr; }\n  .rb-tpl-modern .rbp-sidebar { padding: 20px; }\n}\n\n/* --- Template: Minimal --- */\n.rb-tpl-minimal #rb-preview-inner { font-family: 'Helvetica Neue', Arial, sans-serif; color: #1e293b; }\n.rb-tpl-minimal .rbp-name { font-size: 28px; font-weight: 300; letter-spacing: -.02em; color: #0f172a; }\n.rb-tpl-minimal .rbp-contact { font-size: 12px; color: #94a3b8; margin-bottom: 24px; }\n.rb-tpl-minimal .rbp-section-title { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .15em; color: var(--rb-accent, #2563eb); margin: 22px 0 10px; }\n.rb-tpl-minimal .rbp-entry-title { font-weight: 600; font-size: 14px; }\n.rb-tpl-minimal .rbp-entry-sub { font-size: 12px; color: #64748b; }\n.rb-tpl-minimal .rbp-entry-date { font-size: 11px; color: #94a3b8; }\n.rb-tpl-minimal .rbp-entry-desc { font-size: 13px; margin-top: 4px; color: #475569; white-space: pre-wrap; }\n.rb-tpl-minimal .rbp-summary { font-size: 13px; color: #475569; white-space: pre-wrap; }\n.rb-tpl-minimal .rbp-tag { display: inline-block; padding: 2px 8px; background: #f1f5f9; color: #475569; border-radius: 4px; font-size: 12px; margin: 2px 3px 0 0; }\n.rb-tpl-minimal .rbp-entry { border-left: 2px solid var(--rb-accent, #2563eb); padding-left: 12px; margin-bottom: 12px; }\n\n/* Print */\n@media print {\n  #rb-app #rb-controls, #rb-app #rb-editor, #rb-app #rb-preview-wrap \u003e h2 { display: none !important; }\n  #rb-app #rb-layout { display: block; }\n  #rb-app #rb-preview-wrap { position: static; }\n  #rb-app #rb-preview-box { border: none; }\n  body \u003e * { display: none; }\n  #rb-app { display: block !important; max-width: 100%; }\n}\n\u003c/style\u003e\n\u003c!-- Controls --\u003e\n\u003cdiv id=\"rb-controls\"\u003e\n  \u003clabel\u003eTemplate:\n    \u003cselect id=\"rb-tpl-select\"\u003e\n      \u003coption value=\"classic\"\u003eClassic\u003c/option\u003e\n      \u003coption value=\"modern\"\u003eModern\u003c/option\u003e\n      \u003coption value=\"minimal\"\u003eMinimal\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/label\u003e\n  \u003clabel\u003eColor:\n    \u003cinput type=\"color\" id=\"rb-color-pick\" value=\"#2563eb\"\u003e\n  \u003c/label\u003e\n  \u003cbutton id=\"rb-btn-save\"\u003eSave\u003c/button\u003e\n  \u003cbutton id=\"rb-btn-load\"\u003eLoad\u003c/button\u003e\n  \u003cbutton id=\"rb-btn-clear\"\u003eClear\u003c/button\u003e\n  \u003cbutton id=\"rb-btn-print\"\u003ePrint / Save PDF\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv id=\"rb-layout\"\u003e\n  \u003c!-- Editor --\u003e\n  \u003cdiv id=\"rb-editor\"\u003e\n    \u003cdiv id=\"rb-sections-list\"\u003e\n\u003cpre\u003e\u003ccode\u003e  \u0026lt;!-- Personal Info --\u0026gt;\n  \u0026lt;div class=\u0026quot;rb-section-card\u0026quot; data-section=\u0026quot;personal\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;rb-section-header\u0026quot;\u0026gt;\n      \u0026lt;button class=\u0026quot;rb-move-btn rb-move-up\u0026quot; title=\u0026quot;Move up\u0026quot;\u0026gt;↑\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;rb-move-btn rb-move-dn\u0026quot; title=\u0026quot;Move down\u0026quot;\u0026gt;↓\u0026lt;/button\u0026gt;\n      \u0026lt;span class=\u0026quot;rb-sec-title\u0026quot;\u0026gt;Personal Info\u0026lt;/span\u0026gt;\n      \u0026lt;span class=\u0026quot;rb-sec-toggle\u0026quot;\u0026gt;▾\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;rb-section-body\u0026quot;\u0026gt;\n      \u0026lt;div class=\u0026quot;rb-field\u0026quot;\u0026gt;\u0026lt;label\u0026gt;Full Name\u0026lt;/label\u0026gt;\u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;rb-name\u0026quot; placeholder=\u0026quot;Jane Smith\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;rb-field-row\u0026quot;\u0026gt;\n        \u0026lt;div class=\u0026quot;rb-field\u0026quot;\u0026gt;\u0026lt;label\u0026gt;Email\u0026lt;/label\u0026gt;\u0026lt;input type=\u0026quot;email\u0026quot; id=\u0026quot;rb-email\u0026quot; placeholder=\u0026quot;jane@example.com\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n        \u0026lt;div class=\u0026quot;rb-field\u0026quot;\u0026gt;\u0026lt;label\u0026gt;Phone\u0026lt;/label\u0026gt;\u0026lt;input type=\u0026quot;tel\u0026quot; id=\u0026quot;rb-phone\u0026quot; placeholder=\u0026quot;+1 555-0100\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;rb-field-row\u0026quot;\u0026gt;\n        \u0026lt;div class=\u0026quot;rb-field\u0026quot;\u0026gt;\u0026lt;label\u0026gt;Location\u0026lt;/label\u0026gt;\u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;rb-location\u0026quot; placeholder=\u0026quot;New York, NY\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n        \u0026lt;div class=\u0026quot;rb-field\u0026quot;\u0026gt;\u0026lt;label\u0026gt;LinkedIn\u0026lt;/label\u0026gt;\u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;rb-linkedin\u0026quot; placeholder=\u0026quot;linkedin.com/in/jane\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;div class=\u0026quot;rb-field\u0026quot;\u0026gt;\u0026lt;label\u0026gt;Website / Portfolio\u0026lt;/label\u0026gt;\u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;rb-website\u0026quot; placeholder=\u0026quot;janesmith.com\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;!-- Summary --\u0026gt;\n  \u0026lt;div class=\u0026quot;rb-section-card\u0026quot; data-section=\u0026quot;summary\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;rb-section-header\u0026quot;\u0026gt;\n      \u0026lt;button class=\u0026quot;rb-move-btn rb-move-up\u0026quot; title=\u0026quot;Move up\u0026quot;\u0026gt;↑\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;rb-move-btn rb-move-dn\u0026quot; title=\u0026quot;Move down\u0026quot;\u0026gt;↓\u0026lt;/button\u0026gt;\n      \u0026lt;span class=\u0026quot;rb-sec-title\u0026quot;\u0026gt;Professional Summary\u0026lt;/span\u0026gt;\n      \u0026lt;span class=\u0026quot;rb-sec-toggle\u0026quot;\u0026gt;▾\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;rb-section-body\u0026quot;\u0026gt;\n      \u0026lt;div class=\u0026quot;rb-field\u0026quot;\u0026gt;\n        \u0026lt;textarea id=\u0026quot;rb-summary\u0026quot; placeholder=\u0026quot;Brief overview of your professional background and goals…\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt;\n      \u0026lt;/div\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;!-- Work Experience --\u0026gt;\n  \u0026lt;div class=\u0026quot;rb-section-card\u0026quot; data-section=\u0026quot;experience\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;rb-section-header\u0026quot;\u0026gt;\n      \u0026lt;button class=\u0026quot;rb-move-btn rb-move-up\u0026quot; title=\u0026quot;Move up\u0026quot;\u0026gt;↑\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;rb-move-btn rb-move-dn\u0026quot; title=\u0026quot;Move down\u0026quot;\u0026gt;↓\u0026lt;/button\u0026gt;\n      \u0026lt;span class=\u0026quot;rb-sec-title\u0026quot;\u0026gt;Work Experience\u0026lt;/span\u0026gt;\n      \u0026lt;span class=\u0026quot;rb-sec-toggle\u0026quot;\u0026gt;▾\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;rb-section-body\u0026quot;\u0026gt;\n      \u0026lt;div id=\u0026quot;rb-exp-list\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n      \u0026lt;button class=\u0026quot;rb-add-btn\u0026quot; id=\u0026quot;rb-add-exp\u0026quot;\u0026gt;+ Add Experience\u0026lt;/button\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;!-- Education --\u0026gt;\n  \u0026lt;div class=\u0026quot;rb-section-card\u0026quot; data-section=\u0026quot;education\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;rb-section-header\u0026quot;\u0026gt;\n      \u0026lt;button class=\u0026quot;rb-move-btn rb-move-up\u0026quot; title=\u0026quot;Move up\u0026quot;\u0026gt;↑\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;rb-move-btn rb-move-dn\u0026quot; title=\u0026quot;Move down\u0026quot;\u0026gt;↓\u0026lt;/button\u0026gt;\n      \u0026lt;span class=\u0026quot;rb-sec-title\u0026quot;\u0026gt;Education\u0026lt;/span\u0026gt;\n      \u0026lt;span class=\u0026quot;rb-sec-toggle\u0026quot;\u0026gt;▾\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;rb-section-body\u0026quot;\u0026gt;\n      \u0026lt;div id=\u0026quot;rb-edu-list\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n      \u0026lt;button class=\u0026quot;rb-add-btn\u0026quot; id=\u0026quot;rb-add-edu\u0026quot;\u0026gt;+ Add Education\u0026lt;/button\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;!-- Skills --\u0026gt;\n  \u0026lt;div class=\u0026quot;rb-section-card\u0026quot; data-section=\u0026quot;skills\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;rb-section-header\u0026quot;\u0026gt;\n      \u0026lt;button class=\u0026quot;rb-move-btn rb-move-up\u0026quot; title=\u0026quot;Move up\u0026quot;\u0026gt;↑\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;rb-move-btn rb-move-dn\u0026quot; title=\u0026quot;Move down\u0026quot;\u0026gt;↓\u0026lt;/button\u0026gt;\n      \u0026lt;span class=\u0026quot;rb-sec-title\u0026quot;\u0026gt;Skills\u0026lt;/span\u0026gt;\n      \u0026lt;span class=\u0026quot;rb-sec-toggle\u0026quot;\u0026gt;▾\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;rb-section-body\u0026quot;\u0026gt;\n      \u0026lt;div class=\u0026quot;rb-tags-input\u0026quot;\u0026gt;\n        \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;rb-skill-input\u0026quot; placeholder=\u0026quot;e.g. JavaScript\u0026quot;\u0026gt;\n        \u0026lt;button id=\u0026quot;rb-skill-add\u0026quot;\u0026gt;Add\u0026lt;/button\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;div id=\u0026quot;rb-tags-wrap\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;!-- Languages --\u0026gt;\n  \u0026lt;div class=\u0026quot;rb-section-card\u0026quot; data-section=\u0026quot;languages\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;rb-section-header\u0026quot;\u0026gt;\n      \u0026lt;button class=\u0026quot;rb-move-btn rb-move-up\u0026quot; title=\u0026quot;Move up\u0026quot;\u0026gt;↑\u0026lt;/button\u0026gt;\n      \u0026lt;button class=\u0026quot;rb-move-btn rb-move-dn\u0026quot; title=\u0026quot;Move down\u0026quot;\u0026gt;↓\u0026lt;/button\u0026gt;\n      \u0026lt;span class=\u0026quot;rb-sec-title\u0026quot;\u0026gt;Languages\u0026lt;/span\u0026gt;\n      \u0026lt;span class=\u0026quot;rb-sec-toggle\u0026quot;\u0026gt;▾\u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;rb-section-body\u0026quot;\u0026gt;\n      \u0026lt;div class=\u0026quot;rb-tags-input\u0026quot;\u0026gt;\n        \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;rb-lang-input\u0026quot; placeholder=\u0026quot;e.g. English (Native)\u0026quot;\u0026gt;\n        \u0026lt;button id=\u0026quot;rb-lang-add\u0026quot;\u0026gt;Add\u0026lt;/button\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;div id=\u0026quot;rb-lang-tags-wrap\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n  \u003c!-- Preview --\u003e\n  \u003cdiv id=\"rb-preview-wrap\"\u003e\n    \u003ch2\u003eLive Preview\u003c/h2\u003e\n    \u003cdiv id=\"rb-preview-box\"\u003e\n      \u003cdiv id=\"rb-preview-inner\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  'use strict';\n\n  // State\n  var state = {\n    name: '', email: '', phone: '', location: '', linkedin: '', website: '',\n    summary: '',\n    experience: [],\n    education: [],\n    skills: [],\n    languages: [],\n    template: 'classic',\n    color: '#2563eb',\n    sectionOrder: ['personal','summary','experience','education','skills','languages']\n  };\n\n  var expId = 0, eduId = 0;\n\n  // Helpers\n  function qs(sel, ctx) { return (ctx || document).querySelector(sel); }\n  function qsa(sel, ctx) { return (ctx || document).querySelectorAll(sel); }\n  function byId(id) { return document.getElementById(id); }\n  function esc(s) {\n    return String(s || '').replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;').replace(/\"/g,'\u0026quot;');\n  }\n\n  // Collapse / expand sections\n  qsa('#rb-sections-list .rb-section-header').forEach(function(hdr) {\n    hdr.addEventListener('click', function(e) {\n      if (e.target.classList.contains('rb-move-btn')) return;\n      var body = hdr.nextElementSibling;\n      var tog = qs('.rb-sec-toggle', hdr);\n      body.classList.toggle('hidden');\n      tog.textContent = body.classList.contains('hidden') ? '▸' : '▾';\n    });\n  });\n\n  // Move up/down\n  function rebuildSectionOrder() {\n    state.sectionOrder = Array.from(qsa('#rb-sections-list .rb-section-card')).map(function(c) {\n      return c.getAttribute('data-section');\n    });\n  }\n  byId('rb-sections-list').addEventListener('click', function(e) {\n    var btn = e.target.closest('.rb-move-btn');\n    if (!btn) return;\n    var card = btn.closest('.rb-section-card');\n    var list = byId('rb-sections-list');\n    if (btn.classList.contains('rb-move-up')) {\n      var prev = card.previousElementSibling;\n      if (prev) list.insertBefore(card, prev);\n    } else {\n      var next = card.nextElementSibling;\n      if (next) list.insertBefore(next, card);\n    }\n    rebuildSectionOrder();\n    render();\n  });\n\n  // Experience entries\n  function addExp(data) {\n    data = data || {};\n    var id = ++expId;\n    state.experience.push({ id: id, title: data.title||'', company: data.company||'', start: data.start||'', end: data.end||'', desc: data.desc||'' });\n    renderExpList();\n  }\n  function removeExp(id) {\n    state.experience = state.experience.filter(function(e) { return e.id !== id; });\n    renderExpList();\n    render();\n  }\n  function renderExpList() {\n    var list = byId('rb-exp-list');\n    list.innerHTML = '';\n    state.experience.forEach(function(e) {\n      var div = document.createElement('div');\n      div.className = 'rb-entry';\n      div.innerHTML = '\u003cdiv class=\"rb-entry-header\"\u003e\u003cspan class=\"rb-entry-label\"\u003eExperience #' + e.id + '\u003c/span\u003e\u003cbutton class=\"rb-remove-entry\" data-id=\"' + e.id + '\"\u003eRemove\u003c/button\u003e\u003c/div\u003e' +\n        '\u003cdiv class=\"rb-field\"\u003e\u003clabel\u003eJob Title\u003c/label\u003e\u003cinput type=\"text\" class=\"rb-exp-f\" data-id=\"' + e.id + '\" data-f=\"title\" value=\"' + esc(e.title) + '\" placeholder=\"Software Engineer\"\u003e\u003c/div\u003e' +\n        '\u003cdiv class=\"rb-field\"\u003e\u003clabel\u003eCompany\u003c/label\u003e\u003cinput type=\"text\" class=\"rb-exp-f\" data-id=\"' + e.id + '\" data-f=\"company\" value=\"' + esc(e.company) + '\" placeholder=\"Acme Corp\"\u003e\u003c/div\u003e' +\n        '\u003cdiv class=\"rb-field-row\"\u003e' +\n        '\u003cdiv class=\"rb-field\"\u003e\u003clabel\u003eStart\u003c/label\u003e\u003cinput type=\"text\" class=\"rb-exp-f\" data-id=\"' + e.id + '\" data-f=\"start\" value=\"' + esc(e.start) + '\" placeholder=\"Jan 2022\"\u003e\u003c/div\u003e' +\n        '\u003cdiv class=\"rb-field\"\u003e\u003clabel\u003eEnd\u003c/label\u003e\u003cinput type=\"text\" class=\"rb-exp-f\" data-id=\"' + e.id + '\" data-f=\"end\" value=\"' + esc(e.end) + '\" placeholder=\"Present\"\u003e\u003c/div\u003e' +\n        '\u003c/div\u003e' +\n        '\u003cdiv class=\"rb-field\"\u003e\u003clabel\u003eDescription\u003c/label\u003e\u003ctextarea class=\"rb-exp-f\" data-id=\"' + e.id + '\" data-f=\"desc\" placeholder=\"Key responsibilities and achievements…\"\u003e' + esc(e.desc) + '\u003c/textarea\u003e\u003c/div\u003e';\n\u003cpre\u003e\u003ccode\u003e  list.appendChild(div);\n});\n// Events\nqsa('.rb-remove-entry', list).forEach(function(btn) {\n  btn.addEventListener('click', function() { removeExp(parseInt(btn.getAttribute('data-id'))); });\n});\nqsa('.rb-exp-f', list).forEach(function(inp) {\n  inp.addEventListener('input', function() {\n    var id = parseInt(inp.getAttribute('data-id'));\n    var f = inp.getAttribute('data-f');\n    var entry = state.experience.find(function(e) { return e.id === id; });\n    if (entry) entry[f] = inp.value;\n    render();\n  });\n});\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e}\nbyId(\u0026lsquo;rb-add-exp\u0026rsquo;).addEventListener(\u0026lsquo;click\u0026rsquo;, function() { addExp(); render(); });\u003c/p\u003e","title":"Resume Builder - Free CV Creator"},{"content":" Instantly convert colors between RGB, HEX, and HSL. Use the color picker, type a HEX code, or drag the sliders — all values update live. Click any output to copy the CSS-ready value.\nColor Input RGB Sliders R G B \u0026lt;!-- HSL sliders --\u0026gt; \u0026lt;div class=\u0026quot;rhc-panel\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;HSL Sliders\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;rhc-hsl-group\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;rhc-hsl-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;rhc-hsl-label\u0026quot;\u0026gt;H\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; class=\u0026quot;rhc-slider slider-h\u0026quot; id=\u0026quot;rhcH\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;360\u0026quot; value=\u0026quot;243\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;rhc-hsl-val\u0026quot; id=\u0026quot;rhcHVal\u0026quot;\u0026gt;243°\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rhc-hsl-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;rhc-hsl-label\u0026quot;\u0026gt;S\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; class=\u0026quot;rhc-slider slider-s\u0026quot; id=\u0026quot;rhcS\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;100\u0026quot; value=\u0026quot;100\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;rhc-hsl-val\u0026quot; id=\u0026quot;rhcSVal\u0026quot;\u0026gt;100%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;rhc-hsl-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;rhc-hsl-label\u0026quot;\u0026gt;L\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; class=\u0026quot;rhc-slider slider-l\u0026quot; id=\u0026quot;rhcL\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;100\u0026quot; value=\u0026quot;70\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;rhc-hsl-val\u0026quot; id=\u0026quot;rhcLVal\u0026quot;\u0026gt;70%\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; HEX #6C63FF Copy RGB rgb(108, 99, 255) Copy HSL hsl(243, 100%, 70%) Copy Color History (last 10) No history yet — pick a color to start. Related Tools Generate color palettes → Color Palette Generator Check contrast → Contrast Checker Simulate color blindness → Color Blindness Simulator ","permalink":"https://productivity-works.com/tools/rgb-hex-converter/","summary":"\u003cdiv id=\"rhc-app\"\u003e\n\u003cstyle\u003e\n#rhc-app *,#rhc-app *::before,#rhc-app *::after{box-sizing:border-box;margin:0;padding:0}\n#rhc-app{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:#1a1a2e;--accent:#6c63ff;--radius:10px;--shadow:0 4px 24px rgba(0,0,0,.10)}\n#rhc-app .rhc-wrap{max-width:760px;margin:0 auto;padding:0 0 48px}\n#rhc-app .rhc-lead{font-size:.97rem;color:#555;margin-bottom:28px;line-height:1.6}\n\n/* Layout */\n#rhc-app .rhc-grid{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-bottom:24px}\n@media(max-width:600px){#rhc-app .rhc-grid{grid-template-columns:1fr}}\n\n/* Panels */\n#rhc-app .rhc-panel{background:#f7f7fc;border-radius:var(--radius);padding:20px}\n#rhc-app .rhc-panel h3{font-size:.78rem;font-weight:700;color:#444;text-transform:uppercase;letter-spacing:.06em;margin-bottom:14px}\n\n/* Color picker */\n#rhc-app .rhc-picker-row{display:flex;align-items:center;gap:14px;margin-bottom:18px}\n#rhc-app .rhc-color-input{width:72px;height:72px;border:3px solid #ddd;border-radius:10px;cursor:pointer;padding:2px;background:#fff}\n#rhc-app .rhc-hex-input{flex:1;height:44px;padding:0 14px;border:2px solid #ddd;border-radius:8px;font-size:1rem;font-family:monospace;background:#fff;outline:none;transition:border-color .2s;text-transform:uppercase}\n#rhc-app .rhc-hex-input:focus{border-color:var(--accent)}\n\n/* Preview swatch */\n#rhc-app .rhc-swatch{width:100%;height:120px;border-radius:var(--radius);margin-bottom:20px;transition:background .15s;border:1px solid rgba(0,0,0,.07);box-shadow:var(--shadow)}\n\n/* Sliders */\n#rhc-app .rhc-slider-group{display:flex;flex-direction:column;gap:12px}\n#rhc-app .rhc-slider-row{display:grid;grid-template-columns:18px 1fr 52px;align-items:center;gap:10px}\n#rhc-app .rhc-slider-label{font-size:.85rem;font-weight:700;color:#444;text-align:center}\n#rhc-app .rhc-slider{-webkit-appearance:none;appearance:none;width:100%;height:8px;border-radius:4px;outline:none;cursor:pointer}\n#rhc-app .rhc-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:20px;height:20px;border-radius:50%;background:#fff;border:2px solid #aaa;box-shadow:0 1px 4px rgba(0,0,0,.2);cursor:pointer}\n#rhc-app .rhc-slider::-moz-range-thumb{width:20px;height:20px;border-radius:50%;background:#fff;border:2px solid #aaa;box-shadow:0 1px 4px rgba(0,0,0,.2);cursor:pointer}\n#rhc-app .slider-r{background:linear-gradient(to right,#000,#ff0000)}\n#rhc-app .slider-g{background:linear-gradient(to right,#000,#00ff00)}\n#rhc-app .slider-b{background:linear-gradient(to right,#000,#0000ff)}\n#rhc-app .rhc-num-input{width:52px;height:34px;border:2px solid #ddd;border-radius:6px;text-align:center;font-size:.88rem;font-weight:600;outline:none;transition:border-color .2s}\n#rhc-app .rhc-num-input:focus{border-color:var(--accent)}\n\n/* Output values */\n#rhc-app .rhc-outputs{display:flex;flex-direction:column;gap:10px;margin-bottom:24px}\n#rhc-app .rhc-output-row{display:flex;align-items:center;gap:10px;background:#fff;border:1.5px solid #e5e7eb;border-radius:8px;padding:10px 14px}\n#rhc-app .rhc-output-label{font-size:.75rem;font-weight:700;color:#6c63ff;text-transform:uppercase;letter-spacing:.05em;min-width:38px}\n#rhc-app .rhc-output-val{flex:1;font-family:monospace;font-size:.92rem;color:#1a1a2e;word-break:break-all}\n#rhc-app .rhc-copy-btn{flex-shrink:0;padding:5px 12px;border:none;border-radius:6px;background:#6c63ff;color:#fff;font-size:.78rem;font-weight:700;cursor:pointer;transition:opacity .2s,transform .1s}\n#rhc-app .rhc-copy-btn:hover{opacity:.85}\n#rhc-app .rhc-copy-btn:active{transform:scale(.95)}\n#rhc-app .rhc-copy-btn.copied{background:#22c55e}\n\n/* HSL sliders panel */\n#rhc-app .rhc-hsl-panel{background:#f7f7fc;border-radius:var(--radius);padding:20px;margin-bottom:24px}\n#rhc-app .rhc-hsl-panel h3{font-size:.78rem;font-weight:700;color:#444;text-transform:uppercase;letter-spacing:.06em;margin-bottom:14px}\n#rhc-app .rhc-hsl-group{display:flex;flex-direction:column;gap:12px}\n#rhc-app .rhc-hsl-row{display:grid;grid-template-columns:22px 1fr 60px;align-items:center;gap:10px}\n#rhc-app .rhc-hsl-label{font-size:.78rem;font-weight:700;color:#444;text-align:center}\n#rhc-app .slider-h{background:linear-gradient(to right,#f00,#ff0,#0f0,#0ff,#00f,#f0f,#f00)}\n#rhc-app .slider-s{background:linear-gradient(to right,#888,#f00)}\n#rhc-app .slider-l{background:linear-gradient(to right,#000,#888,#fff)}\n#rhc-app .rhc-hsl-val{font-size:.82rem;font-weight:600;color:#555;text-align:right}\n\n/* Color history */\n#rhc-app .rhc-history{margin-bottom:0}\n#rhc-app .rhc-history h3{font-size:.78rem;font-weight:700;color:#444;text-transform:uppercase;letter-spacing:.06em;margin-bottom:12px}\n#rhc-app .rhc-history-swatches{display:flex;flex-wrap:wrap;gap:8px}\n#rhc-app .rhc-hist-swatch{width:42px;height:42px;border-radius:8px;cursor:pointer;border:2px solid #ddd;transition:transform .15s,border-color .15s;position:relative}\n#rhc-app .rhc-hist-swatch:hover{transform:scale(1.12);border-color:#6c63ff}\n#rhc-app .rhc-hist-swatch .rhc-hist-tip{display:none;position:absolute;bottom:calc(100% + 6px);left:50%;transform:translateX(-50%);background:#1a1a2e;color:#fff;font-size:.7rem;white-space:nowrap;padding:3px 7px;border-radius:5px;pointer-events:none;z-index:10}\n#rhc-app .rhc-hist-swatch:hover .rhc-hist-tip{display:block}\n#rhc-app .rhc-history-empty{font-size:.85rem;color:#999;font-style:italic}\n\u003c/style\u003e\n\u003cdiv class=\"rhc-wrap\"\u003e\n  \u003cp class=\"rhc-lead\"\u003eInstantly convert colors between RGB, HEX, and HSL. Use the color picker, type a HEX code, or drag the sliders — all values update live. Click any output to copy the CSS-ready value.\u003c/p\u003e","title":"RGB ↔ HEX Color Converter — Free Online Tool"},{"content":"Build a robots.txt file with a visual editor — no coding required. Add user-agent blocks, allow/disallow paths, set crawl delays, and include your sitemap. Choose from presets to block AI crawlers, apply WordPress defaults, or start fresh.\nAllow All Block All Block AI Crawlers WordPress Default Standard SEO Clear All Global Settings Sitemap URL \u0026lt;!-- UA blocks --\u0026gt; \u0026lt;div id=\u0026quot;robots-ua-list\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;btn-add-ua\u0026quot; onclick=\u0026quot;robotsAddUA()\u0026quot;\u0026gt;+ Add User-agent Block\u0026lt;/button\u0026gt; Live Preview robots.txt Copy Download .txt Related Tools Generate meta tags → Meta Tag Generator Format SQL queries → SQL Formatter Build cron expressions → Cron Expression Generator Related Articles Best Web Hosting 2026: Compared for Speed, Price \u0026amp; WordPress ","permalink":"https://productivity-works.com/tools/robots-txt-generator/","summary":"\u003cp\u003eBuild a \u003ccode\u003erobots.txt\u003c/code\u003e file with a visual editor — no coding required. Add user-agent blocks, allow/disallow paths, set crawl delays, and include your sitemap. Choose from presets to block AI crawlers, apply WordPress defaults, or start fresh.\u003c/p\u003e\n\u003cdiv id=\"robots-app\"\u003e\n\u003cstyle\u003e\n#robots-app *,\n#robots-app *::before,\n#robots-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\n#robots-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 15px;\n  color: #1a1a2e;\n  line-height: 1.5;\n  max-width: 900px;\n  margin: 0 auto;\n}\n\n#robots-app h2 {\n  font-size: 1.1rem;\n  font-weight: 700;\n  margin-bottom: 12px;\n  color: #1a1a2e;\n}\n\n#robots-app h3 {\n  font-size: 0.95rem;\n  font-weight: 700;\n  margin-bottom: 8px;\n  color: #333;\n}\n\n#robots-app .robots-layout {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 20px;\n}\n\n@media (max-width: 680px) {\n  #robots-app .robots-layout {\n    grid-template-columns: 1fr;\n  }\n}\n\n#robots-app .panel {\n  background: #f8f9fc;\n  border: 1px solid #e0e4ef;\n  border-radius: 10px;\n  padding: 18px;\n}\n\n#robots-app .panel-full {\n  background: #f8f9fc;\n  border: 1px solid #e0e4ef;\n  border-radius: 10px;\n  padding: 18px;\n  margin-top: 20px;\n}\n\n/* Presets */\n#robots-app .preset-grid {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 20px;\n}\n\n#robots-app .preset-btn {\n  padding: 7px 14px;\n  border-radius: 20px;\n  border: 1.5px solid #5a6acf;\n  background: #fff;\n  color: #5a6acf;\n  font-size: 0.82rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s;\n  white-space: nowrap;\n}\n\n#robots-app .preset-btn:hover {\n  background: #5a6acf;\n  color: #fff;\n}\n\n/* Global settings */\n#robots-app .global-settings {\n  margin-bottom: 20px;\n}\n\n#robots-app label {\n  display: block;\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #555;\n  margin-bottom: 4px;\n}\n\n#robots-app input[type=\"text\"],\n#robots-app input[type=\"number\"],\n#robots-app input[type=\"url\"] {\n  width: 100%;\n  padding: 8px 10px;\n  border: 1.5px solid #d0d5e8;\n  border-radius: 6px;\n  font-size: 0.9rem;\n  background: #fff;\n  color: #1a1a2e;\n  outline: none;\n  transition: border-color 0.15s;\n}\n\n#robots-app input:focus {\n  border-color: #5a6acf;\n}\n\n#robots-app .field-row {\n  margin-bottom: 12px;\n}\n\n/* User-agent blocks */\n#robots-app .ua-block {\n  background: #fff;\n  border: 1.5px solid #d0d5e8;\n  border-radius: 8px;\n  padding: 14px;\n  margin-bottom: 12px;\n}\n\n#robots-app .ua-block-header {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin-bottom: 10px;\n}\n\n#robots-app .ua-block-header input {\n  flex: 1;\n}\n\n#robots-app .btn-icon {\n  background: none;\n  border: none;\n  cursor: pointer;\n  font-size: 1.1rem;\n  padding: 2px 6px;\n  border-radius: 4px;\n  line-height: 1;\n  color: #888;\n  transition: color 0.15s, background 0.15s;\n}\n\n#robots-app .btn-icon:hover {\n  color: #e05252;\n  background: #fdecea;\n}\n\n#robots-app .rules-list {\n  margin-bottom: 8px;\n}\n\n#robots-app .rule-row {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  margin-bottom: 6px;\n}\n\n#robots-app .rule-type-select {\n  padding: 6px 8px;\n  border: 1.5px solid #d0d5e8;\n  border-radius: 6px;\n  font-size: 0.85rem;\n  background: #fff;\n  color: #1a1a2e;\n  cursor: pointer;\n  outline: none;\n}\n\n#robots-app .rule-type-select:focus {\n  border-color: #5a6acf;\n}\n\n#robots-app .rule-row input {\n  flex: 1;\n}\n\n#robots-app .btn-add-rule {\n  display: inline-flex;\n  align-items: center;\n  gap: 4px;\n  padding: 5px 12px;\n  border-radius: 6px;\n  border: 1.5px solid #5a6acf;\n  background: #fff;\n  color: #5a6acf;\n  font-size: 0.8rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s;\n}\n\n#robots-app .btn-add-rule:hover {\n  background: #5a6acf;\n  color: #fff;\n}\n\n#robots-app .crawl-delay-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin-top: 8px;\n}\n\n#robots-app .crawl-delay-row label {\n  margin-bottom: 0;\n  white-space: nowrap;\n  font-size: 0.82rem;\n}\n\n#robots-app .crawl-delay-row input {\n  width: 80px;\n}\n\n#robots-app .btn-add-ua {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  padding: 9px 18px;\n  border-radius: 7px;\n  border: 2px dashed #5a6acf;\n  background: #fff;\n  color: #5a6acf;\n  font-size: 0.88rem;\n  font-weight: 600;\n  cursor: pointer;\n  width: 100%;\n  justify-content: center;\n  transition: background 0.15s;\n  margin-top: 4px;\n}\n\n#robots-app .btn-add-ua:hover {\n  background: #eef0fb;\n}\n\n/* Preview */\n#robots-app .preview-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 10px;\n  flex-wrap: wrap;\n  gap: 8px;\n}\n\n#robots-app .preview-actions {\n  display: flex;\n  gap: 8px;\n}\n\n#robots-app .btn-copy,\n#robots-app .btn-download {\n  padding: 7px 16px;\n  border-radius: 6px;\n  border: none;\n  font-size: 0.85rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, opacity 0.15s;\n}\n\n#robots-app .btn-copy {\n  background: #5a6acf;\n  color: #fff;\n}\n\n#robots-app .btn-copy:hover {\n  background: #4757b8;\n}\n\n#robots-app .btn-download {\n  background: #27ae60;\n  color: #fff;\n}\n\n#robots-app .btn-download:hover {\n  background: #1e9150;\n}\n\n#robots-app .btn-copy.copied {\n  background: #27ae60;\n}\n\n#robots-app pre#robots-preview {\n  background: #0f1117;\n  color: #a8ff78;\n  padding: 16px;\n  border-radius: 8px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 0.82rem;\n  line-height: 1.7;\n  white-space: pre;\n  overflow-x: auto;\n  min-height: 120px;\n  tab-size: 2;\n}\n\n/* Syntax colors */\n#robots-app .syn-comment { color: #6b8fa0; }\n#robots-app .syn-key     { color: #79b8ff; }\n#robots-app .syn-colon   { color: #e0e0e0; }\n#robots-app .syn-val-allow   { color: #a8ff78; }\n#robots-app .syn-val-disallow { color: #ff7b7b; }\n#robots-app .syn-val-sitemap { color: #ffd580; }\n#robots-app .syn-val-neutral { color: #e0e0e0; }\n\n#robots-app .info-badge {\n  display: inline-block;\n  background: #eef0fb;\n  color: #5a6acf;\n  border-radius: 4px;\n  font-size: 0.75rem;\n  padding: 2px 8px;\n  margin-left: 8px;\n  font-weight: 600;\n}\n\u003c/style\u003e\n\u003c!-- Preset buttons --\u003e\n\u003cdiv class=\"preset-grid\"\u003e\n  \u003cbutton class=\"preset-btn\" onclick=\"robotsApplyPreset('allow-all')\"\u003eAllow All\u003c/button\u003e\n  \u003cbutton class=\"preset-btn\" onclick=\"robotsApplyPreset('block-all')\"\u003eBlock All\u003c/button\u003e\n  \u003cbutton class=\"preset-btn\" onclick=\"robotsApplyPreset('block-ai')\"\u003eBlock AI Crawlers\u003c/button\u003e\n  \u003cbutton class=\"preset-btn\" onclick=\"robotsApplyPreset('wordpress')\"\u003eWordPress Default\u003c/button\u003e\n  \u003cbutton class=\"preset-btn\" onclick=\"robotsApplyPreset('standard-seo')\"\u003eStandard SEO\u003c/button\u003e\n  \u003cbutton class=\"preset-btn\" onclick=\"robotsApplyPreset('clear')\"\u003eClear All\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"robots-layout\"\u003e\n  \u003c!-- Left: editor --\u003e\n  \u003cdiv\u003e\n    \u003c!-- Global settings --\u003e\n    \u003cdiv class=\"panel global-settings\"\u003e\n      \u003ch2\u003eGlobal Settings\u003c/h2\u003e\n      \u003cdiv class=\"field-row\"\u003e\n        \u003clabel for=\"robots-sitemap\"\u003eSitemap URL\u003c/label\u003e\n        \u003cinput type=\"url\" id=\"robots-sitemap\" placeholder=\"https://example.com/sitemap.xml\" oninput=\"robotsRender()\"\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;!-- UA blocks --\u0026gt;\n\u0026lt;div id=\u0026quot;robots-ua-list\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n\u0026lt;button class=\u0026quot;btn-add-ua\u0026quot; onclick=\u0026quot;robotsAddUA()\u0026quot;\u0026gt;+ Add User-agent Block\u0026lt;/button\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n  \u003c!-- Right: preview --\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"panel-full\" style=\"margin-top:0; height: 100%;\"\u003e\n      \u003cdiv class=\"preview-header\"\u003e\n        \u003ch2\u003eLive Preview \u003cspan class=\"info-badge\"\u003erobots.txt\u003c/span\u003e\u003c/h2\u003e\n        \u003cdiv class=\"preview-actions\"\u003e\n          \u003cbutton class=\"btn-copy\" id=\"robots-copy-btn\" onclick=\"robotsCopy()\"\u003eCopy\u003c/button\u003e\n          \u003cbutton class=\"btn-download\" onclick=\"robotsDownload()\"\u003eDownload .txt\u003c/button\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n      \u003cpre id=\"robots-preview\"\u003e\u003c/pre\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  // State\n  var state = {\n    sitemap: '',\n    blocks: []\n  };\n\n  var blockCounter = 0;\n\n  function uid() {\n    return 'b' + (++blockCounter);\n  }\n\n  function makeBlock(ua, rules, crawlDelay) {\n    return {\n      id: uid(),\n      ua: ua || '*',\n      rules: rules || [],\n      crawlDelay: crawlDelay || ''\n    };\n  }\n\n  function makeRule(type, path) {\n    return { type: type || 'Disallow', path: path || '' };\n  }\n\n  // Presets\n  var PRESETS = {\n    'allow-all': function() {\n      state.sitemap = '';\n      state.blocks = [makeBlock('*', [makeRule('Allow', '/')], '')];\n    },\n    'block-all': function() {\n      state.sitemap = '';\n      state.blocks = [makeBlock('*', [makeRule('Disallow', '/')], '')];\n    },\n    'block-ai': function() {\n      state.sitemap = '';\n      var aiAgents = [\n        'GPTBot', 'CCBot', 'ChatGPT-User', 'anthropic-ai',\n        'Claude-Web', 'Omgilibot', 'FacebookBot', 'Google-Extended',\n        'Bytespider', 'PerplexityBot', 'YouBot'\n      ];\n      state.blocks = aiAgents.map(function(a) {\n        return makeBlock(a, [makeRule('Disallow', '/')], '');\n      });\n      state.blocks.unshift(makeBlock('*', [makeRule('Allow', '/')], ''));\n    },\n    'wordpress': function() {\n      state.sitemap = 'https://example.com/sitemap.xml';\n      state.blocks = [\n        makeBlock('*', [\n          makeRule('Disallow', '/wp-admin/'),\n          makeRule('Allow', '/wp-admin/admin-ajax.php')\n        ], '')\n      ];\n    },\n    'standard-seo': function() {\n      state.sitemap = 'https://example.com/sitemap.xml';\n      state.blocks = [\n        makeBlock('*', [\n          makeRule('Allow', '/'),\n          makeRule('Disallow', '/admin/'),\n          makeRule('Disallow', '/private/'),\n          makeRule('Disallow', '/*.pdf$')\n        ], '10')\n      ];\n    },\n    'clear': function() {\n      state.sitemap = '';\n      state.blocks = [];\n    }\n  };\n\n  window.robotsApplyPreset = function(name) {\n    if (PRESETS[name]) {\n      PRESETS[name]();\n      syncFormFromState();\n      robotsRender();\n    }\n  };\n\n  // ---- DOM rendering ----\n\n  function syncFormFromState() {\n    document.getElementById('robots-sitemap').value = state.sitemap;\n    var list = document.getElementById('robots-ua-list');\n    list.innerHTML = '';\n    state.blocks.forEach(function(block) {\n      list.appendChild(buildBlockEl(block));\n    });\n  }\n\n  function buildBlockEl(block) {\n    var div = document.createElement('div');\n    div.className = 'panel';\n    div.style.marginBottom = '12px';\n    div.dataset.id = block.id;\n\n    div.innerHTML =\n      '\u003cdiv class=\"ua-block\"\u003e' +\n        '\u003cdiv class=\"ua-block-header\"\u003e' +\n          '\u003cspan style=\"font-size:0.78rem;font-weight:700;color:#888;white-space:nowrap\"\u003eUser-agent\u003c/span\u003e' +\n          '\u003cinput type=\"text\" value=\"' + esc(block.ua) + '\" placeholder=\"e.g. * or GPTBot\"' +\n            ' oninput=\"robotsUAChange(\\'' + block.id + '\\', this.value)\"\u003e' +\n          '\u003cbutton class=\"btn-icon\" title=\"Remove block\" onclick=\"robotsRemoveBlock(\\'' + block.id + '\\')\"\u003e\u0026times;\u003c/button\u003e' +\n        '\u003c/div\u003e' +\n        '\u003cdiv class=\"rules-list\" id=\"rules-' + block.id + '\"\u003e' +\n        '\u003c/div\u003e' +\n        '\u003cbutton class=\"btn-add-rule\" onclick=\"robotsAddRule(\\'' + block.id + '\\')\"\u003e+ Add Rule\u003c/button\u003e' +\n        '\u003cdiv class=\"crawl-delay-row\"\u003e' +\n          '\u003clabel\u003eCrawl-delay (sec):\u003c/label\u003e' +\n          '\u003cinput type=\"number\" min=\"0\" value=\"' + esc(block.crawlDelay) + '\" placeholder=\"none\"' +\n            ' oninput=\"robotsCrawlDelayChange(\\'' + block.id + '\\', this.value)\"\u003e' +\n        '\u003c/div\u003e' +\n      '\u003c/div\u003e';\n\n    var rulesEl = div.querySelector('#rules-' + block.id);\n    block.rules.forEach(function(rule, i) {\n      rulesEl.appendChild(buildRuleEl(block.id, rule, i));\n    });\n\n    return div;\n  }\n\n  function buildRuleEl(blockId, rule, idx) {\n    var row = document.createElement('div');\n    row.className = 'rule-row';\n    row.dataset.idx = idx;\n\n    var sel = document.createElement('select');\n    sel.className = 'rule-type-select';\n    ['Allow', 'Disallow'].forEach(function(t) {\n      var opt = document.createElement('option');\n      opt.value = t;\n      opt.textContent = t;\n      if (rule.type === t) opt.selected = true;\n      sel.appendChild(opt);\n    });\n    sel.addEventListener('change', function() {\n      robotsRuleTypeChange(blockId, idx, this.value);\n    });\n\n    var inp = document.createElement('input');\n    inp.type = 'text';\n    inp.value = rule.path;\n    inp.placeholder = '/path/';\n    inp.addEventListener('input', function() {\n      robotsRulePathChange(blockId, idx, this.value);\n    });\n\n    var del = document.createElement('button');\n    del.className = 'btn-icon';\n    del.title = 'Remove rule';\n    del.innerHTML = '\u0026times;';\n    del.addEventListener('click', function() {\n      robotsRemoveRule(blockId, idx);\n    });\n\n    row.appendChild(sel);\n    row.appendChild(inp);\n    row.appendChild(del);\n    return row;\n  }\n\n  function esc(s) {\n    return String(s || '').replace(/\"/g, '\u0026quot;').replace(/\u003c/g, '\u0026lt;').replace(/\u003e/g, '\u0026gt;');\n  }\n\n  // ---- State mutation ----\n\n  function findBlock(id) {\n    return state.blocks.find(function(b) { return b.id === id; });\n  }\n\n  window.robotsAddUA = function() {\n    var block = makeBlock('*', [makeRule('Disallow', '/')], '');\n    state.blocks.push(block);\n    var list = document.getElementById('robots-ua-list');\n    list.appendChild(buildBlockEl(block));\n    robotsRender();\n  };\n\n  window.robotsRemoveBlock = function(id) {\n    state.blocks = state.blocks.filter(function(b) { return b.id !== id; });\n    var el = document.querySelector('[data-id=\"' + id + '\"]');\n    if (el) el.remove();\n    robotsRender();\n  };\n\n  window.robotsUAChange = function(id, val) {\n    var b = findBlock(id);\n    if (b) { b.ua = val; robotsRender(); }\n  };\n\n  window.robotsCrawlDelayChange = function(id, val) {\n    var b = findBlock(id);\n    if (b) { b.crawlDelay = val; robotsRender(); }\n  };\n\n  window.robotsAddRule = function(id) {\n    var b = findBlock(id);\n    if (!b) return;\n    var rule = makeRule('Disallow', '');\n    b.rules.push(rule);\n    var rulesEl = document.getElementById('rules-' + id);\n    if (rulesEl) rulesEl.appendChild(buildRuleEl(id, rule, b.rules.length - 1));\n    robotsRender();\n  };\n\n  window.robotsRemoveRule = function(blockId, idx) {\n    var b = findBlock(blockId);\n    if (!b) return;\n    b.rules.splice(idx, 1);\n    // Re-render just the block\n    var blockEl = document.querySelector('[data-id=\"' + blockId + '\"]');\n    if (blockEl) {\n      var newEl = buildBlockEl(b);\n      blockEl.replaceWith(newEl);\n    }\n    robotsRender();\n  };\n\n  window.robotsRuleTypeChange = function(blockId, idx, val) {\n    var b = findBlock(blockId);\n    if (b \u0026\u0026 b.rules[idx]) { b.rules[idx].type = val; robotsRender(); }\n  };\n\n  window.robotsRulePathChange = function(blockId, idx, val) {\n    var b = findBlock(blockId);\n    if (b \u0026\u0026 b.rules[idx]) { b.rules[idx].path = val; robotsRender(); }\n  };\n\n  window.robotsRender = function() {\n    state.sitemap = document.getElementById('robots-sitemap').value.trim();\n    var lines = [];\n\n    if (state.blocks.length === 0 \u0026\u0026 !state.sitemap) {\n      document.getElementById('robots-preview').innerHTML =\n        '\u003cspan class=\"syn-comment\"\u003e# Your robots.txt will appear here...\u003c/span\u003e';\n      return;\n    }\n\n    state.blocks.forEach(function(block, bi) {\n      if (bi \u003e 0) lines.push('');\n      lines.push({ key: 'User-agent', val: block.ua });\n      block.rules.forEach(function(rule) {\n        lines.push({ key: rule.type, val: rule.path });\n      });\n      if (block.crawlDelay \u0026\u0026 block.crawlDelay !== '') {\n        lines.push({ key: 'Crawl-delay', val: block.crawlDelay });\n      }\n    });\n\n    if (state.sitemap) {\n      lines.push('');\n      lines.push({ key: 'Sitemap', val: state.sitemap });\n    }\n\n    // Build highlighted HTML\n    var html = lines.map(function(line) {\n      if (line === '') return '';\n      if (typeof line === 'string') {\n        return '\u003cspan class=\"syn-comment\"\u003e' + htmlEsc(line) + '\u003c/span\u003e';\n      }\n      var keyClass = 'syn-key';\n      var valClass = 'syn-val-neutral';\n      if (line.key === 'Allow') valClass = 'syn-val-allow';\n      else if (line.key === 'Disallow') valClass = 'syn-val-disallow';\n      else if (line.key === 'Sitemap') valClass = 'syn-val-sitemap';\n\n      return '\u003cspan class=\"' + keyClass + '\"\u003e' + htmlEsc(line.key) + '\u003c/span\u003e' +\n             '\u003cspan class=\"syn-colon\"\u003e: \u003c/span\u003e' +\n             '\u003cspan class=\"' + valClass + '\"\u003e' + htmlEsc(line.val) + '\u003c/span\u003e';\n    }).join('\\n');\n\n    document.getElementById('robots-preview').innerHTML = html || '\u003cspan class=\"syn-comment\"\u003e# Empty\u003c/span\u003e';\n  };\n\n  function htmlEsc(s) {\n    return String(s || '').replace(/\u0026/g, '\u0026amp;').replace(/\u003c/g, '\u0026lt;').replace(/\u003e/g, '\u0026gt;');\n  }\n\n  function getRawText() {\n    var lines = [];\n    state.sitemap = document.getElementById('robots-sitemap').value.trim();\n\n    state.blocks.forEach(function(block, bi) {\n      if (bi \u003e 0) lines.push('');\n      lines.push('User-agent: ' + block.ua);\n      block.rules.forEach(function(rule) {\n        lines.push(rule.type + ': ' + rule.path);\n      });\n      if (block.crawlDelay \u0026\u0026 block.crawlDelay !== '') {\n        lines.push('Crawl-delay: ' + block.crawlDelay);\n      }\n    });\n\n    if (state.sitemap) {\n      lines.push('');\n      lines.push('Sitemap: ' + state.sitemap);\n    }\n\n    return lines.join('\\n');\n  }\n\n  window.robotsCopy = function() {\n    var text = getRawText();\n    navigator.clipboard.writeText(text).then(function() {\n      var btn = document.getElementById('robots-copy-btn');\n      btn.textContent = 'Copied!';\n      btn.classList.add('copied');\n      setTimeout(function() {\n        btn.textContent = 'Copy';\n        btn.classList.remove('copied');\n      }, 2000);\n    }).catch(function() {\n      var ta = document.createElement('textarea');\n      ta.value = text;\n      ta.style.position = 'fixed';\n      ta.style.opacity = '0';\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n    });\n  };\n\n  window.robotsDownload = function() {\n    var text = getRawText();\n    var blob = new Blob([text], { type: 'text/plain' });\n    var url = URL.createObjectURL(blob);\n    var a = document.createElement('a');\n    a.href = url;\n    a.download = 'robots.txt';\n    a.click();\n    URL.revokeObjectURL(url);\n  };\n\n  // Init with Allow All preset\n  PRESETS['allow-all']();\n  syncFormFromState();\n  robotsRender();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch3 id=\"related-tools\"\u003eRelated Tools\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003eGenerate meta tags → \u003ca href=\"https://productivity-works.com/tools/meta-tag-generator/\"\u003eMeta Tag Generator\u003c/a\u003e\n\u003c/p\u003e","title":"Robots.txt Generator — Free Online Tool"},{"content":"Enter your investment figures below to instantly calculate ROI, annualized return, NPV, IRR, and payback period — no account or sign-up needed.\nSimple ROI Advanced ROI Comparison Initial Investment ($) Value Type Final Value ($) Gain / Loss ($) Final Value ($) Total value at end of period Gain / Loss ($) Positive = profit, negative = loss Investment Period Period Unit Years Months Calculate Reset Results Break-even Progress Investment vs. Returns Initial Investment ($) Annual Revenue ($) Investment Period (years) Discount Rate (% per year) Annual Costs Remove + Add Cost Calculate Reset Results Break-even Progress Cumulative Cash Flow by Year Enter 2–3 investments to compare side by side.\n+ Add Investment Compare Reset Comparison Table ROI % Comparison \u003e Calculate compound interest \u0026rarr; Compound Interest Calculator\n\u003e Plan your retirement \u0026rarr; Pension Simulator\n\u003e Manage your budget \u0026rarr; 50/30/20 Budget Calculator\nCalculate compound interest → Compound Interest Calculator Plan your retirement → Pension Simulator Manage your budget → 50/30/20 Budget Calculator Related Articles Real Estate Investment in Japan for Salaried Workers: How It Compares to NISA and iDeCo RENOSY Review for Expats: Is Real Estate Investment in Japan Worth It? (2026) How to Make Money With AI in 2026: 15 Realistic Ways That Work ","permalink":"https://productivity-works.com/tools/roi-calculator/","summary":"\u003cp\u003eEnter your investment figures below to instantly calculate ROI, annualized return, NPV, IRR, and payback period — no account or sign-up needed.\u003c/p\u003e\n\u003cdiv id=\"roi-app\"\u003e\n\u003cstyle\u003e\n#roi-app {\n  font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n  color: #1e293b;\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 0 0 2rem;\n}\n#roi-app *, #roi-app *::before, #roi-app *::after {\n  box-sizing: border-box;\n}\n\u003cp\u003e/* Tabs */\n#roi-app .tab-bar {\ndisplay: flex;\ngap: 0;\nborder-bottom: 2px solid #cbd5e1;\nmargin-bottom: 1.5rem;\nflex-wrap: wrap;\n}\n#roi-app .tab-btn {\npadding: 0.6rem 1.2rem;\nbackground: none;\nborder: none;\nborder-bottom: 3px solid transparent;\nmargin-bottom: -2px;\ncursor: pointer;\nfont-size: 0.95rem;\nfont-weight: 600;\ncolor: #64748b;\ntransition: color 0.15s, border-color 0.15s;\nwhite-space: nowrap;\n}\n#roi-app .tab-btn:hover { color: #334155; }\n#roi-app .tab-btn.active {\ncolor: #0f172a;\nborder-bottom-color: #3b82f6;\n}\n#roi-app .tab-panel { display: none; }\n#roi-app .tab-panel.active { display: block; }\u003c/p\u003e","title":"ROI Calculator"},{"content":" ROT13 / Caesar Cipher ROT13 Cipher Mode ROT13 ROT5 ROT47 Caesar Direction Encode Decode View Normal Brute Force Shift 13 Input Output Copy Output Brute Force — All 25 Caesar Shifts ShiftOutput How It Works ROT13 rotates each letter by 13 positions in the alphabet. Since the English alphabet has 26 letters, ROT13 is its own inverse — applying it twice returns the original text. It is commonly used to obscure spoilers or puzzle answers online.\nCaesar cipher is the generalized form: rotate by any shift from 1 to 25. Julius Caesar reportedly used a shift of 3. The Decode direction reverses the shift automatically.\nROT5 applies the same rotation concept to digits (0–9) with a shift of 5. It is often combined with ROT13 as ROT18.\nROT47 operates on all 94 printable ASCII characters (! through ~), rotating by 47. It encodes punctuation and symbols as well as letters and digits.\nBrute force mode runs all 25 possible Caesar shifts at once, which is useful when you have an unknown shift and want to scan for readable plaintext.\nAlphabet Mapping Reference Plain A B C D E F G H I J K L M ROT13 N O P Q R S T U V W X Y Z Plain N O P Q R S T U V W X Y Z ROT13 A B C D E F G H I J K L M Related: Hash data → Hash Generator ","permalink":"https://productivity-works.com/tools/rot13-encoder/","summary":"\u003cstyle\u003e\n#rot-app *,\n#rot-app *::before,\n#rot-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\n#rot-app {\n  font-family: 'Courier New', Courier, monospace;\n  background: #1a2e1a;\n  color: #ffb300;\n  border-radius: 8px;\n  padding: 24px;\n  max-width: 820px;\n  margin: 0 auto 2rem auto;\n  border: 1px solid #2e4d2e;\n}\n\n#rot-app h2 {\n  font-size: 1.1rem;\n  letter-spacing: 0.12em;\n  text-transform: uppercase;\n  color: #ffd54f;\n  margin-bottom: 18px;\n  border-bottom: 1px solid #2e4d2e;\n  padding-bottom: 10px;\n}\n\n#rot-app .row {\n  display: flex;\n  gap: 12px;\n  flex-wrap: wrap;\n  margin-bottom: 14px;\n  align-items: center;\n}\n\n#rot-app label {\n  font-size: 0.78rem;\n  letter-spacing: 0.08em;\n  color: #ffd54f;\n  text-transform: uppercase;\n  display: block;\n  margin-bottom: 5px;\n}\n\n#rot-app select,\n#rot-app button {\n  background: #0f1f0f;\n  color: #ffb300;\n  border: 1px solid #3a5c3a;\n  border-radius: 4px;\n  padding: 7px 14px;\n  font-family: inherit;\n  font-size: 0.88rem;\n  cursor: pointer;\n  transition: border-color 0.15s, background 0.15s;\n}\n\n#rot-app select:focus,\n#rot-app button:focus {\n  outline: 2px solid #ffb300;\n  outline-offset: 2px;\n}\n\n#rot-app button:hover {\n  background: #1e3d1e;\n  border-color: #ffb300;\n}\n\n#rot-app button.active,\n#rot-app button[data-active=\"true\"] {\n  background: #ffb300;\n  color: #0f1f0f;\n  border-color: #ffb300;\n  font-weight: 700;\n}\n\n#rot-app .btn-group {\n  display: flex;\n  gap: 6px;\n}\n\n#rot-app textarea {\n  width: 100%;\n  background: #0f1f0f;\n  color: #ffb300;\n  border: 1px solid #3a5c3a;\n  border-radius: 4px;\n  padding: 12px;\n  font-family: inherit;\n  font-size: 0.92rem;\n  resize: vertical;\n  min-height: 110px;\n  line-height: 1.55;\n  transition: border-color 0.15s;\n}\n\n#rot-app textarea:focus {\n  outline: none;\n  border-color: #ffb300;\n}\n\n#rot-app textarea[readonly] {\n  color: #ffd54f;\n  cursor: default;\n}\n\n#rot-app .slider-wrap {\n  width: 100%;\n  margin-bottom: 14px;\n}\n\n#rot-app .slider-row {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  margin-bottom: 8px;\n}\n\n#rot-app input[type=range] {\n  -webkit-appearance: none;\n  appearance: none;\n  flex: 1;\n  height: 6px;\n  background: #2e4d2e;\n  border-radius: 3px;\n  outline: none;\n  cursor: pointer;\n}\n\n#rot-app input[type=range]::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 18px;\n  height: 18px;\n  border-radius: 50%;\n  background: #ffb300;\n  cursor: pointer;\n  border: 2px solid #0f1f0f;\n}\n\n#rot-app input[type=range]::-moz-range-thumb {\n  width: 18px;\n  height: 18px;\n  border-radius: 50%;\n  background: #ffb300;\n  cursor: pointer;\n  border: 2px solid #0f1f0f;\n}\n\n#rot-app .shift-badge {\n  background: #ffb300;\n  color: #0f1f0f;\n  font-weight: 700;\n  border-radius: 4px;\n  padding: 2px 10px;\n  font-size: 1rem;\n  min-width: 38px;\n  text-align: center;\n}\n\n#rot-app .alpha-map {\n  font-size: 0.72rem;\n  letter-spacing: 0.05em;\n  color: #8fbc8f;\n  background: #0f1f0f;\n  border: 1px solid #2e4d2e;\n  border-radius: 4px;\n  padding: 7px 10px;\n  overflow-x: auto;\n  white-space: nowrap;\n  line-height: 1.8;\n}\n\n#rot-app .alpha-map span.hi {\n  color: #ffb300;\n  font-weight: 700;\n}\n\n#rot-app .section-label {\n  font-size: 0.72rem;\n  letter-spacing: 0.1em;\n  text-transform: uppercase;\n  color: #8fbc8f;\n  margin-bottom: 4px;\n}\n\n#rot-app .copy-btn {\n  margin-top: 8px;\n  padding: 6px 18px;\n  font-size: 0.82rem;\n}\n\n#rot-app .copy-btn.copied {\n  background: #388e3c;\n  color: #fff;\n  border-color: #388e3c;\n}\n\n#rot-app .brute-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.8rem;\n  margin-top: 8px;\n}\n\n#rot-app .brute-table th {\n  background: #2e4d2e;\n  color: #ffd54f;\n  text-align: left;\n  padding: 5px 8px;\n  font-weight: 700;\n  letter-spacing: 0.06em;\n}\n\n#rot-app .brute-table td {\n  padding: 4px 8px;\n  border-bottom: 1px solid #1e3d1e;\n  color: #ffb300;\n  word-break: break-all;\n}\n\n#rot-app .brute-table tr:hover td {\n  background: #1e3d1e;\n}\n\n#rot-app .brute-table td:first-child {\n  color: #8fbc8f;\n  font-weight: 700;\n  width: 40px;\n  white-space: nowrap;\n}\n\n#rot-app .hidden {\n  display: none;\n}\n\n#rot-app .divider {\n  border: none;\n  border-top: 1px solid #2e4d2e;\n  margin: 18px 0;\n}\n\n#rot-app .mode-tag {\n  font-size: 0.7rem;\n  letter-spacing: 0.12em;\n  text-transform: uppercase;\n  color: #8fbc8f;\n  background: #1e3d1e;\n  border-radius: 3px;\n  padding: 2px 8px;\n  margin-left: 6px;\n  vertical-align: middle;\n}\n\u003c/style\u003e\n\u003cdiv id=\"rot-app\"\u003e\n\u003ch2\u003eROT13 / Caesar Cipher \u003cspan class=\"mode-tag\" id=\"modeTag\"\u003eROT13\u003c/span\u003e\u003c/h2\u003e\n\u003c!-- Mode selector --\u003e\n\u003cdiv class=\"row\"\u003e\n  \u003cdiv\u003e\n    \u003clabel\u003eCipher Mode\u003c/label\u003e\n    \u003cdiv class=\"btn-group\"\u003e\n      \u003cbutton id=\"btnRot13\" data-active=\"true\" onclick=\"rotSetMode('rot13')\"\u003eROT13\u003c/button\u003e\n      \u003cbutton id=\"btnRot5\" onclick=\"rotSetMode('rot5')\"\u003eROT5\u003c/button\u003e\n      \u003cbutton id=\"btnRot47\" onclick=\"rotSetMode('rot47')\"\u003eROT47\u003c/button\u003e\n      \u003cbutton id=\"btnCaesar\" onclick=\"rotSetMode('caesar')\"\u003eCaesar\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv\u003e\n    \u003clabel\u003eDirection\u003c/label\u003e\n    \u003cdiv class=\"btn-group\"\u003e\n      \u003cbutton id=\"btnEncode\" data-active=\"true\" onclick=\"rotSetDir('encode')\"\u003eEncode\u003c/button\u003e\n      \u003cbutton id=\"btnDecode\" onclick=\"rotSetDir('decode')\"\u003eDecode\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv\u003e\n    \u003clabel\u003eView\u003c/label\u003e\n    \u003cdiv class=\"btn-group\"\u003e\n      \u003cbutton id=\"btnNormal\" data-active=\"true\" onclick=\"rotSetView('normal')\"\u003eNormal\u003c/button\u003e\n      \u003cbutton id=\"btnBrute\" onclick=\"rotSetView('brute')\"\u003eBrute Force\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Caesar shift slider --\u003e\n\u003cdiv class=\"slider-wrap\" id=\"sliderWrap\" style=\"display:none\"\u003e\n  \u003cdiv class=\"slider-row\"\u003e\n    \u003clabel style=\"margin:0;white-space:nowrap\"\u003eShift\u003c/label\u003e\n    \u003cinput type=\"range\" id=\"shiftSlider\" min=\"1\" max=\"25\" value=\"13\" oninput=\"rotUpdateShift(this.value)\"\u003e\n    \u003cspan class=\"shift-badge\" id=\"shiftBadge\"\u003e13\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"alpha-map\" id=\"alphaMap\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Input --\u003e\n\u003cdiv class=\"section-label\"\u003eInput\u003c/div\u003e\n\u003ctextarea id=\"rotInput\" placeholder=\"Type or paste text here...\" oninput=\"rotProcess()\"\u003e\u003c/textarea\u003e\n\u003chr class=\"divider\"\u003e\n\u003c!-- Normal output --\u003e\n\u003cdiv id=\"normalView\"\u003e\n  \u003cdiv class=\"section-label\"\u003eOutput\u003c/div\u003e\n  \u003ctextarea id=\"rotOutput\" readonly placeholder=\"Output will appear here...\"\u003e\u003c/textarea\u003e\n  \u003cbutton class=\"copy-btn\" id=\"copyBtn\" onclick=\"rotCopy()\"\u003eCopy Output\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Brute force output --\u003e\n\u003cdiv id=\"bruteView\" class=\"hidden\"\u003e\n  \u003cdiv class=\"section-label\"\u003eBrute Force — All 25 Caesar Shifts\u003c/div\u003e\n  \u003cdiv style=\"overflow-x:auto\"\u003e\n    \u003ctable class=\"brute-table\" id=\"bruteTable\"\u003e\n      \u003cthead\u003e\u003ctr\u003e\u003cth\u003eShift\u003c/th\u003e\u003cth\u003eOutput\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e\n      \u003ctbody id=\"bruteTbody\"\u003e\u003c/tbody\u003e\n    \u003c/table\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n  var state = {\n    mode: 'rot13',\n    dir: 'encode',\n    shift: 13,\n    view: 'normal'\n  };\n\n  window.rotSetMode = function(m) {\n    state.mode = m;\n    ['rot13','rot5','rot47','caesar'].forEach(function(x){\n      var b = document.getElementById('btn' + x.charAt(0).toUpperCase() + x.slice(1));\n      if(b) b.dataset.active = (x === m) ? 'true' : 'false';\n    });\n    document.getElementById('sliderWrap').style.display = (m === 'caesar') ? 'block' : 'none';\n    if(m === 'caesar') rotRenderAlphaMap(state.shift, state.dir);\n    document.getElementById('modeTag').textContent = m.toUpperCase();\n    rotProcess();\n  };\n\n  window.rotSetDir = function(d) {\n    state.dir = d;\n    document.getElementById('btnEncode').dataset.active = (d === 'encode') ? 'true' : 'false';\n    document.getElementById('btnDecode').dataset.active = (d === 'decode') ? 'true' : 'false';\n    if(state.mode === 'caesar') rotRenderAlphaMap(state.shift, d);\n    rotProcess();\n  };\n\n  window.rotSetView = function(v) {\n    state.view = v;\n    document.getElementById('btnNormal').dataset.active = (v === 'normal') ? 'true' : 'false';\n    document.getElementById('btnBrute').dataset.active = (v === 'brute') ? 'true' : 'false';\n    document.getElementById('normalView').classList.toggle('hidden', v !== 'normal');\n    document.getElementById('bruteView').classList.toggle('hidden', v !== 'brute');\n    rotProcess();\n  };\n\n  window.rotUpdateShift = function(v) {\n    state.shift = parseInt(v, 10);\n    document.getElementById('shiftBadge').textContent = v;\n    rotRenderAlphaMap(state.shift, state.dir);\n    rotProcess();\n  };\n\n  function rotRenderAlphaMap(shift, dir) {\n    var plain = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';\n    var s = (dir === 'decode') ? (26 - shift) % 26 : shift;\n    var cipher = plain.split('').map(function(c){\n      return plain[(plain.indexOf(c) + s) % 26];\n    }).join('');\n    var html = '\u003cb style=\"color:#8fbc8f\"\u003ePlain:\u0026nbsp;\u003c/b\u003e';\n    for(var i=0;i\u003c26;i++){\n      html += '\u003cspan\u003e' + plain[i] + '\u003c/span\u003e';\n      if(i\u003c25) html += ' ';\n    }\n    html += '\u003cbr\u003e\u003cb style=\"color:#8fbc8f\"\u003eCiph:\u0026nbsp;\u0026nbsp;\u003c/b\u003e';\n    for(var j=0;j\u003c26;j++){\n      html += '\u003cspan class=\"hi\"\u003e' + cipher[j] + '\u003c/span\u003e';\n      if(j\u003c25) html += ' ';\n    }\n    document.getElementById('alphaMap').innerHTML = html;\n  }\n\n  function rot13char(c) {\n    if(c \u003e= 'a' \u0026\u0026 c \u003c= 'z') return String.fromCharCode(((c.charCodeAt(0) - 97 + 13) % 26) + 97);\n    if(c \u003e= 'A' \u0026\u0026 c \u003c= 'Z') return String.fromCharCode(((c.charCodeAt(0) - 65 + 13) % 26) + 65);\n    return c;\n  }\n\n  function rot5char(c) {\n    if(c \u003e= '0' \u0026\u0026 c \u003c= '9') return String.fromCharCode(((c.charCodeAt(0) - 48 + 5) % 10) + 48);\n    return c;\n  }\n\n  function rot47char(c) {\n    var code = c.charCodeAt(0);\n    if(code \u003e= 33 \u0026\u0026 code \u003c= 126) return String.fromCharCode(((code - 33 + 47) % 94) + 33);\n    return c;\n  }\n\n  function caesarChar(c, shift) {\n    if(c \u003e= 'a' \u0026\u0026 c \u003c= 'z') return String.fromCharCode(((c.charCodeAt(0) - 97 + shift + 26) % 26) + 97);\n    if(c \u003e= 'A' \u0026\u0026 c \u003c= 'Z') return String.fromCharCode(((c.charCodeAt(0) - 65 + shift + 26) % 26) + 65);\n    return c;\n  }\n\n  function applyMode(text, mode, dir, shift) {\n    var effectiveShift = shift;\n    if(mode === 'caesar' \u0026\u0026 dir === 'decode') effectiveShift = (26 - shift) % 26;\n    return text.split('').map(function(c){\n      if(mode === 'rot13') return rot13char(c);\n      if(mode === 'rot5') return rot5char(c);\n      if(mode === 'rot47') return rot47char(c);\n      if(mode === 'caesar') return caesarChar(c, effectiveShift);\n      return c;\n    }).join('');\n  }\n\n  window.rotProcess = function() {\n    var input = document.getElementById('rotInput').value;\n    if(state.view === 'normal') {\n      var out = applyMode(input, state.mode, state.dir, state.shift);\n      document.getElementById('rotOutput').value = out;\n    } else {\n      var tbody = document.getElementById('bruteTbody');\n      var rows = '';\n      for(var s = 1; s \u003c= 25; s++) {\n        var result = applyMode(input, 'caesar', 'encode', s);\n        rows += '\u003ctr\u003e\u003ctd\u003e+' + s + '\u003c/td\u003e\u003ctd\u003e' + escHtml(result) + '\u003c/td\u003e\u003c/tr\u003e';\n      }\n      tbody.innerHTML = rows;\n    }\n  };\n\n  function escHtml(s) {\n    return s.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n  }\n\n  window.rotCopy = function() {\n    var out = document.getElementById('rotOutput').value;\n    if(!out) return;\n    navigator.clipboard.writeText(out).then(function(){\n      var btn = document.getElementById('copyBtn');\n      btn.textContent = 'Copied!';\n      btn.classList.add('copied');\n      setTimeout(function(){ btn.textContent = 'Copy Output'; btn.classList.remove('copied'); }, 1800);\n    }).catch(function(){\n      var ta = document.createElement('textarea');\n      ta.value = out;\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n    });\n  };\n})();\n\u003c/script\u003e\n\u003ch2 id=\"how-it-works\"\u003eHow It Works\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eROT13\u003c/strong\u003e rotates each letter by 13 positions in the alphabet. Since the English alphabet has 26 letters, ROT13 is its own inverse — applying it twice returns the original text. It is commonly used to obscure spoilers or puzzle answers online.\u003c/p\u003e","title":"ROT13 / Caesar Cipher Encoder \u0026 Decoder"},{"content":" Savings Goal Calculator Calculate how long to reach your savings goal — or how much you need to save each month.\nHow long to reach goal? How much to save monthly? Savings Goal ($) Current Savings ($) Monthly Contribution ($) Annual Interest Rate (%) Target Years Compound Frequency Monthly Quarterly Semi-annually Annually \u0026#9654; Calculate Total Contributed Interest Earned Final Amount Current Progress toward Goal 0% Principal vs. Interest Over Time Principal Contributed Interest Earned Track expenses → Expense Tracker Calculate ROI → ROI Calculator Related Articles Investing for Beginners: Start with $100 in 2026 7 Best Budgeting Apps in 2026 (Free \u0026amp; Paid Compared) Best High-Yield Savings Accounts 2026: Where to Earn 4-5% APY ","permalink":"https://productivity-works.com/tools/savings-goal-calculator/","summary":"\u003cdiv id=\"sg-app\"\u003e\n\u003cstyle\u003e\n#sg-app *,#sg-app *::before,#sg-app *::after{box-sizing:border-box;margin:0;padding:0}\n#sg-app{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:#1e293b;max-width:780px;margin:0 auto;padding:0 4px}\n#sg-app h2.sg-h{font-size:1.35rem;font-weight:700;color:#0f172a;margin-bottom:6px}\n#sg-app p.sg-sub{font-size:.92rem;color:#64748b;margin-bottom:18px}\n#sg-app .sg-card{background:#fff;border:1.5px solid #e2e8f0;border-radius:12px;padding:22px 22px 18px;margin-bottom:20px;box-shadow:0 1px 4px rgba(0,0,0,.06)}\n#sg-app .sg-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}\n@media(max-width:540px){#sg-app .sg-grid{grid-template-columns:1fr}}\n#sg-app .sg-field{display:flex;flex-direction:column;gap:5px}\n#sg-app .sg-field label{font-size:.82rem;font-weight:600;color:#475569}\n#sg-app .sg-field input[type=number],#sg-app .sg-field select{border:1.5px solid #cbd5e1;border-radius:8px;padding:9px 12px;font-size:.95rem;color:#0f172a;background:#f8fafc;width:100%;outline:none;transition:border-color .2s}\n#sg-app .sg-field input[type=number]:focus,#sg-app .sg-field select:focus{border-color:#3b82f6;background:#fff}\n#sg-app .sg-mode-toggle{display:flex;gap:0;border:1.5px solid #cbd5e1;border-radius:9px;overflow:hidden;margin-bottom:16px}\n#sg-app .sg-mode-toggle button{flex:1;padding:9px 8px;border:none;background:#f8fafc;color:#64748b;font-size:.85rem;font-weight:600;cursor:pointer;transition:background .18s,color .18s}\n#sg-app .sg-mode-toggle button.sg-active{background:#3b82f6;color:#fff}\n#sg-app .sg-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:11px 28px;background:linear-gradient(135deg,#2563eb 0%,#3b82f6 100%);color:#fff;border:none;border-radius:9px;font-size:.97rem;font-weight:700;cursor:pointer;width:100%;margin-top:4px;transition:opacity .18s}\n#sg-app .sg-btn:hover{opacity:.9}\n#sg-app .sg-result-card{background:linear-gradient(135deg,#eff6ff 0%,#dbeafe 100%);border:1.5px solid #bfdbfe;border-radius:12px;padding:22px;margin-bottom:20px}\n#sg-app .sg-result-headline{font-size:1.55rem;font-weight:800;color:#1d4ed8;margin-bottom:4px}\n#sg-app .sg-result-sub{font-size:.9rem;color:#3730a3;margin-bottom:18px}\n#sg-app .sg-summary-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:18px}\n@media(max-width:480px){#sg-app .sg-summary-grid{grid-template-columns:1fr 1fr}}\n#sg-app .sg-stat{background:#fff;border-radius:9px;padding:12px 10px;text-align:center;border:1px solid #bfdbfe}\n#sg-app .sg-stat-val{font-size:1.1rem;font-weight:800;color:#1d4ed8;display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n#sg-app .sg-stat-lbl{font-size:.74rem;color:#64748b;margin-top:2px;display:block}\n#sg-app .sg-progress-wrap{margin-bottom:18px}\n#sg-app .sg-progress-lbl{display:flex;justify-content:space-between;font-size:.8rem;color:#64748b;margin-bottom:5px}\n#sg-app .sg-progress-track{height:20px;background:#dbeafe;border-radius:10px;overflow:hidden;position:relative}\n#sg-app .sg-progress-bar{height:100%;border-radius:10px;background:linear-gradient(90deg,#2563eb,#60a5fa);transition:width .5s ease}\n#sg-app .sg-milestone-row{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}\n#sg-app .sg-info-tag{background:#eff6ff;color:#1d4ed8;border-radius:6px;padding:4px 10px;font-size:.78rem;font-weight:600}\n#sg-app .sg-chart-wrap{margin-bottom:8px}\n#sg-app .sg-chart-title{font-size:.85rem;font-weight:700;color:#374151;margin-bottom:8px}\n#sg-app canvas.sg-canvas{width:100%;border-radius:8px;display:block}\n#sg-app .sg-legend{display:flex;gap:16px;margin-top:7px;flex-wrap:wrap}\n#sg-app .sg-legend-item{display:flex;align-items:center;gap:5px;font-size:.78rem;color:#64748b}\n#sg-app .sg-legend-dot{width:12px;height:12px;border-radius:3px;flex-shrink:0}\n#sg-app .sg-hidden{display:none}\n#sg-app .sg-error{background:#fef2f2;border:1.5px solid #fca5a5;border-radius:8px;padding:10px 14px;color:#b91c1c;font-size:.87rem;margin-top:8px}\n\u003c/style\u003e\n\u003cdiv class=\"sg-card\"\u003e\n  \u003ch2 class=\"sg-h\"\u003eSavings Goal Calculator\u003c/h2\u003e\n  \u003cp class=\"sg-sub\"\u003eCalculate how long to reach your savings goal — or how much you need to save each month.\u003c/p\u003e","title":"Savings Goal Calculator"},{"content":" 0 DEG RAD MC MR M+ M− ⌫ sin cos tan log ln asin acos atan √ ∛ x² x³ xʸ 1/x eˣ π e n! ( ) 7 8 9 ÷ AC 4 5 6 × % 1 2 3 − = 0 . + History No calculations yet. Calculate percentages → Percentage Calculator\nConvert units → Unit Converter ","permalink":"https://productivity-works.com/tools/scientific-calculator/","summary":"\u003cdiv id=\"sc-app\"\u003e\n\u003cstyle\u003e\n#sc-app * { box-sizing: border-box; margin: 0; padding: 0; }\n#sc-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n  max-width: 420px;\n  margin: 0 auto;\n  padding: 16px;\n}\n#sc-app .sc-calc-wrap {\n  background: #1a1a2e;\n  border-radius: 16px;\n  padding: 20px;\n  box-shadow: 0 8px 32px rgba(0,0,0,0.3);\n}\n#sc-app .sc-display {\n  background: #0f0f1a;\n  border-radius: 10px;\n  padding: 14px 16px;\n  margin-bottom: 14px;\n  min-height: 80px;\n  display: flex;\n  flex-direction: column;\n  align-items: flex-end;\n  justify-content: flex-end;\n  gap: 4px;\n}\n#sc-app .sc-expr {\n  font-size: 13px;\n  color: #8888aa;\n  min-height: 18px;\n  word-break: break-all;\n  text-align: right;\n  max-width: 100%;\n  overflow-x: auto;\n  white-space: nowrap;\n}\n#sc-app .sc-result {\n  font-size: 28px;\n  color: #e8e8ff;\n  font-weight: 300;\n  word-break: break-all;\n  text-align: right;\n  max-width: 100%;\n  overflow-x: auto;\n  white-space: nowrap;\n}\n#sc-app .sc-mode-row {\n  display: flex;\n  gap: 8px;\n  margin-bottom: 12px;\n  justify-content: flex-end;\n}\n#sc-app .sc-mode-btn {\n  padding: 4px 12px;\n  border-radius: 20px;\n  border: 1.5px solid #4444aa;\n  background: transparent;\n  color: #8888cc;\n  font-size: 12px;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#sc-app .sc-mode-btn.active {\n  background: #4444aa;\n  color: #fff;\n}\n#sc-app .sc-grid {\n  display: grid;\n  grid-template-columns: repeat(5, 1fr);\n  gap: 8px;\n}\n#sc-app .sc-btn {\n  padding: 0;\n  height: 46px;\n  border-radius: 8px;\n  border: none;\n  font-size: 14px;\n  font-weight: 500;\n  cursor: pointer;\n  transition: all 0.1s;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  user-select: none;\n  -webkit-tap-highlight-color: transparent;\n}\n#sc-app .sc-btn:active { transform: scale(0.93); }\n#sc-app .sc-btn.num { background: #2a2a4a; color: #ddeeff; }\n#sc-app .sc-btn.num:hover { background: #33336a; }\n#sc-app .sc-btn.op { background: #1e3a5f; color: #7ec8e3; }\n#sc-app .sc-btn.op:hover { background: #254e80; }\n#sc-app .sc-btn.fn { background: #1a2a3a; color: #aaccee; font-size: 12px; }\n#sc-app .sc-btn.fn:hover { background: #22384a; }\n#sc-app .sc-btn.eq { background: #0066cc; color: #fff; font-size: 18px; }\n#sc-app .sc-btn.eq:hover { background: #0077ee; }\n#sc-app .sc-btn.ac { background: #c0392b; color: #fff; }\n#sc-app .sc-btn.ac:hover { background: #e74c3c; }\n#sc-app .sc-btn.cl { background: #7f3c3c; color: #ffcccc; }\n#sc-app .sc-btn.cl:hover { background: #a04040; }\n#sc-app .sc-btn.mem { background: #1a3a2a; color: #88ddaa; font-size: 12px; }\n#sc-app .sc-btn.mem:hover { background: #224a36; }\n#sc-app .sc-btn.const { background: #2a1a3a; color: #cc88ff; }\n#sc-app .sc-btn.const:hover { background: #3a2a50; }\n#sc-app .sc-history {\n  margin-top: 20px;\n  background: #0f0f1a;\n  border-radius: 10px;\n  padding: 12px 14px;\n}\n#sc-app .sc-history h3 {\n  font-size: 13px;\n  color: #6666aa;\n  margin-bottom: 8px;\n  font-weight: 500;\n  letter-spacing: 0.04em;\n}\n#sc-app .sc-history-list {\n  list-style: none;\n  max-height: 180px;\n  overflow-y: auto;\n}\n#sc-app .sc-history-list li {\n  font-size: 13px;\n  color: #8888bb;\n  padding: 4px 0;\n  border-bottom: 1px solid #1e1e30;\n  display: flex;\n  justify-content: space-between;\n  gap: 8px;\n  cursor: pointer;\n  transition: color 0.1s;\n}\n#sc-app .sc-history-list li:last-child { border-bottom: none; }\n#sc-app .sc-history-list li:hover { color: #ccccff; }\n#sc-app .sc-history-list .hi-expr { color: #555588; }\n#sc-app .sc-history-list .hi-val { color: #aaaadd; font-weight: 600; }\n#sc-app .sc-crosslinks {\n  margin-top: 20px;\n  font-size: 13px;\n  color: #6c757d;\n  line-height: 1.7;\n}\n#sc-app .sc-crosslinks a { color: #0066cc; text-decoration: none; }\n#sc-app .sc-crosslinks a:hover { text-decoration: underline; }\n@media (max-width: 480px) {\n  #sc-app .sc-btn { height: 42px; font-size: 13px; }\n  #sc-app .sc-btn.fn { font-size: 11px; }\n  #sc-app .sc-result { font-size: 22px; }\n}\n\u003c/style\u003e\n\u003cdiv class=\"sc-calc-wrap\"\u003e\n  \u003cdiv class=\"sc-display\"\u003e\n    \u003cdiv class=\"sc-expr\" id=\"sc-expr\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"sc-result\" id=\"sc-result\"\u003e0\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"sc-mode-row\"\u003e\n    \u003cbutton class=\"sc-mode-btn active\" id=\"sc-deg-btn\" onclick=\"scSetMode('deg')\"\u003eDEG\u003c/button\u003e\n    \u003cbutton class=\"sc-mode-btn\" id=\"sc-rad-btn\" onclick=\"scSetMode('rad')\"\u003eRAD\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"sc-grid\"\u003e\n    \u003c!-- Row 1: Memory --\u003e\n    \u003cbutton class=\"sc-btn mem\" onclick=\"scMem('mc')\"\u003eMC\u003c/button\u003e\n    \u003cbutton class=\"sc-btn mem\" onclick=\"scMem('mr')\"\u003eMR\u003c/button\u003e\n    \u003cbutton class=\"sc-btn mem\" onclick=\"scMem('mp')\"\u003eM+\u003c/button\u003e\n    \u003cbutton class=\"sc-btn mem\" onclick=\"scMem('mm')\"\u003eM−\u003c/button\u003e\n    \u003cbutton class=\"sc-btn cl\" onclick=\"scBack()\"\u003e⌫\u003c/button\u003e\n    \u003c!-- Row 2: Scientific --\u003e\n    \u003cbutton class=\"sc-btn fn\" onclick=\"scFn('sin')\"\u003esin\u003c/button\u003e\n    \u003cbutton class=\"sc-btn fn\" onclick=\"scFn('cos')\"\u003ecos\u003c/button\u003e\n    \u003cbutton class=\"sc-btn fn\" onclick=\"scFn('tan')\"\u003etan\u003c/button\u003e\n    \u003cbutton class=\"sc-btn fn\" onclick=\"scFn('log')\"\u003elog\u003c/button\u003e\n    \u003cbutton class=\"sc-btn fn\" onclick=\"scFn('ln')\"\u003eln\u003c/button\u003e\n    \u003c!-- Row 3: Scientific --\u003e\n    \u003cbutton class=\"sc-btn fn\" onclick=\"scFn('asin')\"\u003easin\u003c/button\u003e\n    \u003cbutton class=\"sc-btn fn\" onclick=\"scFn('acos')\"\u003eacos\u003c/button\u003e\n    \u003cbutton class=\"sc-btn fn\" onclick=\"scFn('atan')\"\u003eatan\u003c/button\u003e\n    \u003cbutton class=\"sc-btn fn\" onclick=\"scFn('sqrt')\"\u003e√\u003c/button\u003e\n    \u003cbutton class=\"sc-btn fn\" onclick=\"scFn('cbrt')\"\u003e∛\u003c/button\u003e\n    \u003c!-- Row 4: Power / special --\u003e\n    \u003cbutton class=\"sc-btn fn\" onclick=\"scFn('sq')\"\u003ex²\u003c/button\u003e\n    \u003cbutton class=\"sc-btn fn\" onclick=\"scFn('cb')\"\u003ex³\u003c/button\u003e\n    \u003cbutton class=\"sc-btn fn\" onclick=\"scFn('pow')\"\u003exʸ\u003c/button\u003e\n    \u003cbutton class=\"sc-btn fn\" onclick=\"scFn('inv')\"\u003e1/x\u003c/button\u003e\n    \u003cbutton class=\"sc-btn fn\" onclick=\"scFn('exp')\"\u003eeˣ\u003c/button\u003e\n    \u003c!-- Row 5: Constants / factorial / parens --\u003e\n    \u003cbutton class=\"sc-btn const\" onclick=\"scConst('pi')\"\u003eπ\u003c/button\u003e\n    \u003cbutton class=\"sc-btn const\" onclick=\"scConst('e')\"\u003ee\u003c/button\u003e\n    \u003cbutton class=\"sc-btn fn\" onclick=\"scFn('fact')\"\u003en!\u003c/button\u003e\n    \u003cbutton class=\"sc-btn op\" onclick=\"scOp('(')\"\u003e(\u003c/button\u003e\n    \u003cbutton class=\"sc-btn op\" onclick=\"scOp(')')\"\u003e)\u003c/button\u003e\n    \u003c!-- Row 6: Numbers + ops --\u003e\n    \u003cbutton class=\"sc-btn num\" onclick=\"scNum('7')\"\u003e7\u003c/button\u003e\n    \u003cbutton class=\"sc-btn num\" onclick=\"scNum('8')\"\u003e8\u003c/button\u003e\n    \u003cbutton class=\"sc-btn num\" onclick=\"scNum('9')\"\u003e9\u003c/button\u003e\n    \u003cbutton class=\"sc-btn op\" onclick=\"scOp('÷')\"\u003e÷\u003c/button\u003e\n    \u003cbutton class=\"sc-btn ac\" onclick=\"scAC()\"\u003eAC\u003c/button\u003e\n    \u003c!-- Row 7 --\u003e\n    \u003cbutton class=\"sc-btn num\" onclick=\"scNum('4')\"\u003e4\u003c/button\u003e\n    \u003cbutton class=\"sc-btn num\" onclick=\"scNum('5')\"\u003e5\u003c/button\u003e\n    \u003cbutton class=\"sc-btn num\" onclick=\"scNum('6')\"\u003e6\u003c/button\u003e\n    \u003cbutton class=\"sc-btn op\" onclick=\"scOp('×')\"\u003e×\u003c/button\u003e\n    \u003cbutton class=\"sc-btn op\" onclick=\"scOp('%')\"\u003e%\u003c/button\u003e\n    \u003c!-- Row 8 --\u003e\n    \u003cbutton class=\"sc-btn num\" onclick=\"scNum('1')\"\u003e1\u003c/button\u003e\n    \u003cbutton class=\"sc-btn num\" onclick=\"scNum('2')\"\u003e2\u003c/button\u003e\n    \u003cbutton class=\"sc-btn num\" onclick=\"scNum('3')\"\u003e3\u003c/button\u003e\n    \u003cbutton class=\"sc-btn op\" onclick=\"scOp('−')\"\u003e−\u003c/button\u003e\n    \u003cbutton class=\"sc-btn eq\" onclick=\"scEq()\" style=\"grid-row: span 2;\"\u003e=\u003c/button\u003e\n    \u003c!-- Row 9 --\u003e\n    \u003cbutton class=\"sc-btn num\" onclick=\"scNum('0')\" style=\"grid-column: span 2;\"\u003e0\u003c/button\u003e\n    \u003cbutton class=\"sc-btn num\" onclick=\"scNum('.')\"\u003e.\u003c/button\u003e\n    \u003cbutton class=\"sc-btn op\" onclick=\"scOp('+')\"\u003e+\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"sc-history\"\u003e\n  \u003ch3\u003eHistory\u003c/h3\u003e\n  \u003cul class=\"sc-history-list\" id=\"sc-history-list\"\u003e\n    \u003cli style=\"color:#444466;font-style:italic;\"\u003eNo calculations yet.\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cdiv class=\"sc-crosslinks\"\u003e\n  Calculate percentages → \u003ca href=\"/tools/percentage-calculator/\"\u003ePercentage Calculator\u003c/a\u003e\u003cbr\u003e\n  Convert units → \u003ca href=\"/tools/unit-converter/\"\u003eUnit Converter\u003c/a\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  'use strict';\n\n  var SC = {\n    expr: '',\n    pendingFn: null,\n    memory: 0,\n    history: [],\n    angleMode: 'deg',\n    justEvaled: false,\n    lastResult: '0'\n  };\n\n  function display() {\n    document.getElementById('sc-expr').textContent = SC.expr;\n    document.getElementById('sc-result').textContent = SC.lastResult;\n  }\n\n  function toRad(x) {\n    return SC.angleMode === 'deg' ? x * Math.PI / 180 : x;\n  }\n\n  function fromRad(x) {\n    return SC.angleMode === 'deg' ? x * 180 / Math.PI : x;\n  }\n\n  function fmtNum(n) {\n    if (!isFinite(n)) return n.toString();\n    var s = parseFloat(n.toPrecision(12)).toString();\n    return s;\n  }\n\n  function evalExpr(raw) {\n    var s = raw\n      .replace(/×/g, '*')\n      .replace(/÷/g, '/')\n      .replace(/−/g, '-')\n      .replace(/π/g, '(' + Math.PI + ')')\n      .replace(/\\be\\b/g, '(' + Math.E + ')');\n    var result = Function('\"use strict\"; return (' + s + ')')();\n    return result;\n  }\n\n  window.scSetMode = function(mode) {\n    SC.angleMode = mode;\n    document.getElementById('sc-deg-btn').classList.toggle('active', mode === 'deg');\n    document.getElementById('sc-rad-btn').classList.toggle('active', mode === 'rad');\n  };\n\n  window.scNum = function(n) {\n    if (SC.justEvaled \u0026\u0026 n !== '.') {\n      SC.expr = '';\n      SC.justEvaled = false;\n    }\n    SC.expr += n;\n    SC.lastResult = SC.expr;\n    display();\n  };\n\n  window.scOp = function(op) {\n    SC.justEvaled = false;\n    SC.expr += op;\n    SC.lastResult = SC.expr;\n    display();\n  };\n\n  window.scConst = function(c) {\n    if (SC.justEvaled) { SC.expr = ''; SC.justEvaled = false; }\n    if (c === 'pi') SC.expr += 'π';\n    else SC.expr += 'e';\n    SC.lastResult = fmtNum(c === 'pi' ? Math.PI : Math.E);\n    display();\n  };\n\n  window.scFn = function(fn) {\n    if (SC.justEvaled) { SC.justEvaled = false; }\n    var cur = SC.expr;\n    if (fn === 'sq') {\n      SC.expr = '(' + (cur || '0') + ')^2';\n      try { SC.lastResult = fmtNum(Math.pow(evalExpr(cur || '0'), 2)); } catch(e) { SC.lastResult = 'Error'; }\n    } else if (fn === 'cb') {\n      SC.expr = '(' + (cur || '0') + ')^3';\n      try { SC.lastResult = fmtNum(Math.pow(evalExpr(cur || '0'), 3)); } catch(e) { SC.lastResult = 'Error'; }\n    } else if (fn === 'pow') {\n      SC.expr = (cur || '0') + '^';\n      SC.lastResult = SC.expr;\n    } else if (fn === 'inv') {\n      try {\n        var v = evalExpr(cur || '0');\n        SC.lastResult = fmtNum(1 / v);\n        SC.expr = '1/(' + cur + ')';\n      } catch(e) { SC.lastResult = 'Error'; }\n    } else if (fn === 'fact') {\n      try {\n        var v = evalExpr(cur || '0');\n        SC.lastResult = fmtNum(scFactorial(Math.round(v)));\n        SC.expr = cur + '!';\n      } catch(e) { SC.lastResult = 'Error'; }\n    } else if (fn === 'exp') {\n      try {\n        var v = evalExpr(cur || '0');\n        SC.lastResult = fmtNum(Math.exp(v));\n        SC.expr = 'e^(' + cur + ')';\n      } catch(e) { SC.lastResult = 'Error'; }\n    } else if (fn === 'sqrt') {\n      try {\n        var v = evalExpr(cur || '0');\n        SC.lastResult = fmtNum(Math.sqrt(v));\n        SC.expr = '√(' + cur + ')';\n      } catch(e) { SC.lastResult = 'Error'; }\n    } else if (fn === 'cbrt') {\n      try {\n        var v = evalExpr(cur || '0');\n        SC.lastResult = fmtNum(Math.cbrt(v));\n        SC.expr = '∛(' + cur + ')';\n      } catch(e) { SC.lastResult = 'Error'; }\n    } else if (fn === 'log') {\n      try {\n        var v = evalExpr(cur || '0');\n        SC.lastResult = fmtNum(Math.log10(v));\n        SC.expr = 'log(' + cur + ')';\n      } catch(e) { SC.lastResult = 'Error'; }\n    } else if (fn === 'ln') {\n      try {\n        var v = evalExpr(cur || '0');\n        SC.lastResult = fmtNum(Math.log(v));\n        SC.expr = 'ln(' + cur + ')';\n      } catch(e) { SC.lastResult = 'Error'; }\n    } else if (fn === 'sin') {\n      try {\n        var v = evalExpr(cur || '0');\n        SC.lastResult = fmtNum(Math.sin(toRad(v)));\n        SC.expr = 'sin(' + cur + ')';\n      } catch(e) { SC.lastResult = 'Error'; }\n    } else if (fn === 'cos') {\n      try {\n        var v = evalExpr(cur || '0');\n        SC.lastResult = fmtNum(Math.cos(toRad(v)));\n        SC.expr = 'cos(' + cur + ')';\n      } catch(e) { SC.lastResult = 'Error'; }\n    } else if (fn === 'tan') {\n      try {\n        var v = evalExpr(cur || '0');\n        SC.lastResult = fmtNum(Math.tan(toRad(v)));\n        SC.expr = 'tan(' + cur + ')';\n      } catch(e) { SC.lastResult = 'Error'; }\n    } else if (fn === 'asin') {\n      try {\n        var v = evalExpr(cur || '0');\n        SC.lastResult = fmtNum(fromRad(Math.asin(v)));\n        SC.expr = 'asin(' + cur + ')';\n      } catch(e) { SC.lastResult = 'Error'; }\n    } else if (fn === 'acos') {\n      try {\n        var v = evalExpr(cur || '0');\n        SC.lastResult = fmtNum(fromRad(Math.acos(v)));\n        SC.expr = 'acos(' + cur + ')';\n      } catch(e) { SC.lastResult = 'Error'; }\n    } else if (fn === 'atan') {\n      try {\n        var v = evalExpr(cur || '0');\n        SC.lastResult = fmtNum(fromRad(Math.atan(v)));\n        SC.expr = 'atan(' + cur + ')';\n      } catch(e) { SC.lastResult = 'Error'; }\n    }\n    display();\n  };\n\n  function scFactorial(n) {\n    if (n \u003c 0) return NaN;\n    if (n \u003e 170) return Infinity;\n    var r = 1;\n    for (var i = 2; i \u003c= n; i++) r *= i;\n    return r;\n  }\n\n  window.scEq = function() {\n    if (!SC.expr) return;\n    var raw = SC.expr;\n    try {\n      var s = raw\n        .replace(/\\^/g, '**')\n        .replace(/×/g, '*')\n        .replace(/÷/g, '/')\n        .replace(/−/g, '-')\n        .replace(/π/g, '(' + Math.PI + ')')\n        .replace(/e(?!\\^)/g, '(' + Math.E + ')');\n      var result = Function('\"use strict\"; return (' + s + ')')();\n      var res = fmtNum(result);\n      addHistory(raw, res);\n      SC.expr = res;\n      SC.lastResult = res;\n      SC.justEvaled = true;\n    } catch(e) {\n      SC.lastResult = 'Error';\n    }\n    display();\n  };\n\n  function addHistory(expr, val) {\n    SC.history.unshift({ expr: expr, val: val });\n    if (SC.history.length \u003e 10) SC.history.pop();\n    renderHistory();\n  }\n\n  function renderHistory() {\n    var ul = document.getElementById('sc-history-list');\n    if (!SC.history.length) {\n      ul.innerHTML = '\u003cli style=\"color:#444466;font-style:italic;\"\u003eNo calculations yet.\u003c/li\u003e';\n      return;\n    }\n    ul.innerHTML = SC.history.map(function(h) {\n      return '\u003cli onclick=\"scUseHistory(\\'' + h.val.replace(/'/g, \"\\\\'\") + '\\')\"\u003e' +\n        '\u003cspan class=\"hi-expr\"\u003e' + escHtml(h.expr) + '\u003c/span\u003e' +\n        '\u003cspan class=\"hi-val\"\u003e' + escHtml(h.val) + '\u003c/span\u003e' +\n        '\u003c/li\u003e';\n    }).join('');\n  }\n\n  function escHtml(s) {\n    return String(s).replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n  }\n\n  window.scUseHistory = function(val) {\n    SC.expr = val;\n    SC.lastResult = val;\n    SC.justEvaled = false;\n    display();\n  };\n\n  window.scAC = function() {\n    SC.expr = '';\n    SC.lastResult = '0';\n    SC.justEvaled = false;\n    display();\n  };\n\n  window.scBack = function() {\n    if (SC.justEvaled) { scAC(); return; }\n    SC.expr = SC.expr.slice(0, -1);\n    SC.lastResult = SC.expr || '0';\n    display();\n  };\n\n  window.scMem = function(op) {\n    if (op === 'mc') {\n      SC.memory = 0;\n    } else if (op === 'mr') {\n      SC.expr = String(SC.memory);\n      SC.lastResult = String(SC.memory);\n      SC.justEvaled = false;\n      display();\n      return;\n    } else if (op === 'mp') {\n      try { SC.memory += parseFloat(SC.lastResult) || 0; } catch(e) {}\n    } else if (op === 'mm') {\n      try { SC.memory -= parseFloat(SC.lastResult) || 0; } catch(e) {}\n    }\n  };\n\n  document.addEventListener('keydown', function(e) {\n    if (e.target \u0026\u0026 e.target.closest \u0026\u0026 e.target.closest('#sc-app')) {\n      // only trap if focus is inside\n    }\n    var k = e.key;\n    if (k \u003e= '0' \u0026\u0026 k \u003c= '9') scNum(k);\n    else if (k === '.') scNum('.');\n    else if (k === '+') scOp('+');\n    else if (k === '-') scOp('−');\n    else if (k === '*') scOp('×');\n    else if (k === '/') { e.preventDefault(); scOp('÷'); }\n    else if (k === '(') scOp('(');\n    else if (k === ')') scOp(')');\n    else if (k === '%') scOp('%');\n    else if (k === 'Enter' || k === '=') scEq();\n    else if (k === 'Backspace') scBack();\n    else if (k === 'Escape') scAC();\n  });\n\n})();\n\u003c/script\u003e\n\u003c/div\u003e","title":"Scientific Calculator"},{"content":" ↻ Refresh Info\nScreen \u0026 Display Screen Resolution— Viewport Size— Device Pixel Ratio— Color Depth— Orientation— Touch Support— Browser \u0026 System Browser— Operating System— Language— Online Status— Cookies Enabled— Hardware Threads— Refresh Rate Estimation Click \"Measure\" to estimate your display refresh rate using requestAnimationFrame. — Hz Measure Refresh Rate Display Test Patterns Click any test to go full-screen. Click anywhere on screen to exit.\nColor Bars Grayscale Gradient Dead Pixel (Black) Dead Pixel (White) Dead Pixel (Red) Dead Pixel (Green) Dead Pixel (Blue) Click anywhere to exit Check resolution → Screen Resolution ","permalink":"https://productivity-works.com/tools/screen-recorder-info/","summary":"\u003cdiv id=\"si-app\"\u003e\n\u003cstyle\u003e\n#si-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 680px;\n  margin: 0 auto;\n  color: #1e293b;\n  box-sizing: border-box;\n}\n#si-app *, #si-app *::before, #si-app *::after {\n  box-sizing: border-box;\n}\n#si-app h2 {\n  font-size: 1.1rem;\n  font-weight: 700;\n  margin: 0 0 12px 0;\n  color: #0f172a;\n}\n#si-app .si-card {\n  background: #fff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 20px;\n  margin-bottom: 16px;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.05);\n}\n#si-app .si-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 10px 16px;\n}\n#si-app .si-row {\n  display: flex;\n  flex-direction: column;\n  gap: 2px;\n}\n#si-app .si-label {\n  font-size: 11px;\n  font-weight: 600;\n  color: #94a3b8;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#si-app .si-value {\n  font-size: 15px;\n  font-weight: 700;\n  color: #0f172a;\n  word-break: break-all;\n}\n#si-app .si-value.good { color: #059669; }\n#si-app .si-value.warn { color: #d97706; }\n#si-app .si-section-title {\n  font-size: 13px;\n  font-weight: 700;\n  color: #475569;\n  margin-bottom: 12px;\n  padding-bottom: 6px;\n  border-bottom: 1.5px solid #f1f5f9;\n}\n#si-app .si-btn-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-top: 4px;\n}\n#si-app .si-btn {\n  padding: 8px 16px;\n  border: 1.5px solid #cbd5e1;\n  background: #f8fafc;\n  border-radius: 8px;\n  font-size: 13px;\n  font-weight: 600;\n  color: #334155;\n  cursor: pointer;\n  transition: background 0.15s, border-color 0.15s;\n}\n#si-app .si-btn:hover {\n  background: #e0f2fe;\n  border-color: #7dd3fc;\n  color: #0369a1;\n}\n#si-app .si-btn.danger {\n  border-color: #fca5a5;\n  color: #dc2626;\n  background: #fff5f5;\n}\n#si-app .si-btn.danger:hover {\n  background: #fee2e2;\n}\n#si-app .si-refresh-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  padding: 7px 14px;\n  background: #6366f1;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 13px;\n  font-weight: 700;\n  cursor: pointer;\n  margin-bottom: 14px;\n  transition: background 0.15s;\n}\n#si-app .si-refresh-btn:hover { background: #4f46e5; }\n#si-app .si-fps-bar {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-top: 8px;\n}\n#si-app .si-fps-track {\n  flex: 1;\n  height: 8px;\n  background: #e2e8f0;\n  border-radius: 4px;\n  overflow: hidden;\n}\n#si-app .si-fps-fill {\n  height: 100%;\n  background: #22c55e;\n  border-radius: 4px;\n  transition: width 0.5s;\n}\n#si-app .si-overlay {\n  display: none;\n  position: fixed;\n  top: 0; left: 0; right: 0; bottom: 0;\n  z-index: 999999;\n  cursor: pointer;\n}\n#si-app .si-overlay-label {\n  position: fixed;\n  bottom: 24px;\n  left: 50%;\n  transform: translateX(-50%);\n  background: rgba(0,0,0,0.7);\n  color: #fff;\n  padding: 8px 20px;\n  border-radius: 30px;\n  font-size: 14px;\n  font-weight: 600;\n  pointer-events: none;\n  z-index: 1000000;\n}\n#si-app .si-colorbar {\n  display: flex;\n  height: 80px;\n  border-radius: 8px;\n  overflow: hidden;\n  margin-top: 8px;\n}\n#si-app .si-colorbar div {\n  flex: 1;\n}\n#si-app .si-gray {\n  height: 40px;\n  border-radius: 8px;\n  margin-top: 8px;\n  background: linear-gradient(to right, #000 0%, #fff 100%);\n}\n@media (max-width: 480px) {\n  #si-app .si-grid { grid-template-columns: 1fr; }\n}\n\u003c/style\u003e\n\u003cp\u003e\u003cbutton class=\"si-refresh-btn\" onclick=\"siRefresh()\"\u003e↻ Refresh Info\u003c/button\u003e\u003c/p\u003e","title":"Screen Info \u0026 Display Tester"},{"content":" Recording Mode\nScreen only Camera only Screen + Camera (PiP) Audio\nSystem audio (if supported) Microphone \u0026#9679; Start Recording \u0026#9632; Stop \u0026#10074;\u0026#10074; Pause 00:00 Preview will appear here when recording starts Download Recording\n\u0026#8595; Download WebM \u0026gt; Draw on whiteboard \u0026rarr; Whiteboard Drawing Tool\n\u0026gt; Create pixel art \u0026rarr; Pixel Art Editor\n\u0026gt; Generate placeholder images \u0026rarr; Placeholder Image Generator ","permalink":"https://productivity-works.com/tools/screen-recorder/","summary":"\u003cdiv id=\"sr-app\"\u003e\n\u003cstyle\u003e\n#sr-app {\n  font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n  max-width: 800px;\n  margin: 0 auto;\n  color: #e2e8f0;\n}\n\n#sr-app * {\n  box-sizing: border-box;\n}\n\n#sr-app .sr-card {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 12px;\n  padding: 20px 24px;\n  margin-bottom: 16px;\n}\n\n#sr-app .sr-section-title {\n  font-size: 0.75rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #94a3b8;\n  margin: 0 0 14px 0;\n}\n\n/* Mode \u0026 Audio options */\n#sr-app .sr-options {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n}\n\n#sr-app .sr-radio-label,\n#sr-app .sr-check-label {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  cursor: pointer;\n  padding: 8px 14px;\n  border-radius: 8px;\n  border: 1px solid #334155;\n  background: #0f172a;\n  font-size: 0.875rem;\n  color: #cbd5e1;\n  transition: border-color 0.15s, background 0.15s;\n  user-select: none;\n}\n\n#sr-app .sr-radio-label:hover,\n#sr-app .sr-check-label:hover {\n  border-color: #64748b;\n  background: #1e293b;\n}\n\n#sr-app .sr-radio-label input,\n#sr-app .sr-check-label input {\n  accent-color: #6366f1;\n  width: 15px;\n  height: 15px;\n  cursor: pointer;\n}\n\n#sr-app .sr-radio-label.sr-selected {\n  border-color: #6366f1;\n  background: #1e1b4b;\n  color: #c7d2fe;\n}\n\n/* Controls row */\n#sr-app .sr-controls {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  gap: 10px;\n}\n\n#sr-app .sr-btn {\n  padding: 10px 20px;\n  border: none;\n  border-radius: 8px;\n  font-size: 0.875rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: opacity 0.15s, transform 0.1s;\n  line-height: 1;\n}\n\n#sr-app .sr-btn:active {\n  transform: scale(0.97);\n}\n\n#sr-app .sr-btn:disabled {\n  opacity: 0.35;\n  cursor: not-allowed;\n  transform: none;\n}\n\n#sr-app .sr-btn-start {\n  background: #6366f1;\n  color: #fff;\n}\n\n#sr-app .sr-btn-start:hover:not(:disabled) {\n  background: #4f46e5;\n}\n\n#sr-app .sr-btn-stop {\n  background: #ef4444;\n  color: #fff;\n}\n\n#sr-app .sr-btn-stop:hover:not(:disabled) {\n  background: #dc2626;\n}\n\n#sr-app .sr-btn-pause {\n  background: #f59e0b;\n  color: #1c1917;\n}\n\n#sr-app .sr-btn-pause:hover:not(:disabled) {\n  background: #d97706;\n}\n\n#sr-app .sr-btn-download {\n  background: #10b981;\n  color: #fff;\n}\n\n#sr-app .sr-btn-download:hover:not(:disabled) {\n  background: #059669;\n}\n\n/* Timer \u0026 indicator */\n#sr-app .sr-timer-wrap {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-left: auto;\n}\n\n#sr-app .sr-dot {\n  width: 12px;\n  height: 12px;\n  border-radius: 50%;\n  background: #ef4444;\n  flex-shrink: 0;\n  opacity: 0;\n}\n\n#sr-app .sr-dot.sr-active {\n  opacity: 1;\n  animation: sr-blink 1s step-start infinite;\n}\n\n#sr-app .sr-dot.sr-paused {\n  opacity: 1;\n  animation: none;\n  background: #f59e0b;\n}\n\n@keyframes sr-blink {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0; }\n}\n\n#sr-app .sr-timer {\n  font-size: 1.25rem;\n  font-weight: 700;\n  font-variant-numeric: tabular-nums;\n  color: #f1f5f9;\n  letter-spacing: 0.05em;\n  min-width: 64px;\n}\n\n/* Video area */\n#sr-app .sr-video-wrap {\n  position: relative;\n  background: #0f172a;\n  border-radius: 10px;\n  overflow: hidden;\n  aspect-ratio: 16 / 9;\n  border: 1px solid #334155;\n}\n\n#sr-app .sr-video-placeholder {\n  position: absolute;\n  inset: 0;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  gap: 10px;\n  color: #475569;\n  font-size: 0.875rem;\n}\n\n#sr-app .sr-video-placeholder svg {\n  opacity: 0.4;\n}\n\n#sr-app #sr-preview,\n#sr-app #sr-playback {\n  width: 100%;\n  height: 100%;\n  object-fit: contain;\n  display: none;\n  background: #000;\n}\n\n#sr-app #sr-preview.sr-visible,\n#sr-app #sr-playback.sr-visible {\n  display: block;\n}\n\n/* PiP camera overlay */\n#sr-app #sr-cam-overlay {\n  position: absolute;\n  bottom: 12px;\n  right: 12px;\n  width: 22%;\n  aspect-ratio: 4/3;\n  border-radius: 8px;\n  overflow: hidden;\n  border: 2px solid #6366f1;\n  background: #000;\n  display: none;\n}\n\n#sr-app #sr-cam-overlay.sr-visible {\n  display: block;\n}\n\n#sr-app #sr-cam-overlay video {\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n}\n\n/* Download info */\n#sr-app .sr-dl-info {\n  display: flex;\n  align-items: center;\n  gap: 14px;\n  flex-wrap: wrap;\n}\n\n#sr-app .sr-filesize {\n  font-size: 0.8rem;\n  color: #94a3b8;\n}\n\n/* Status message */\n#sr-app .sr-status {\n  border-radius: 8px;\n  padding: 10px 14px;\n  font-size: 0.85rem;\n  line-height: 1.5;\n  display: none;\n}\n\n#sr-app .sr-status.sr-visible {\n  display: block;\n}\n\n#sr-app .sr-status.sr-info {\n  background: #1e3a5f;\n  border: 1px solid #2563eb;\n  color: #93c5fd;\n}\n\n#sr-app .sr-status.sr-error {\n  background: #3b1515;\n  border: 1px solid #ef4444;\n  color: #fca5a5;\n}\n\n#sr-app .sr-status.sr-success {\n  background: #052e16;\n  border: 1px solid #10b981;\n  color: #6ee7b7;\n}\n\n/* Cross-links */\n#sr-app .sr-links {\n  border-top: 1px solid #334155;\n  padding-top: 16px;\n  font-size: 0.85rem;\n  color: #94a3b8;\n  line-height: 2;\n}\n\n#sr-app .sr-links a {\n  color: #818cf8;\n  text-decoration: none;\n}\n\n#sr-app .sr-links a:hover {\n  text-decoration: underline;\n  color: #a5b4fc;\n}\n\n@media (max-width: 520px) {\n  #sr-app .sr-timer-wrap {\n    margin-left: 0;\n    width: 100%;\n    justify-content: flex-end;\n  }\n\n  #sr-app .sr-btn {\n    flex: 1 1 auto;\n    text-align: center;\n  }\n}\n\u003c/style\u003e\n\u003c!-- Mode selection --\u003e\n\u003cdiv class=\"sr-card\"\u003e\n  \u003cp class=\"sr-section-title\"\u003eRecording Mode\u003c/p\u003e","title":"Screen Recorder"},{"content":"Check your screen resolution, viewport size, pixel ratio, and active CSS breakpoints in real time. Resize your window to see live updates.\nYour Display Info LIVE Screen vs Viewport (Visual) Active CSS Breakpoints Common Resolution Comparison NameWidthHeightRatio Browser \u0026amp; OS Info \u0026#128203; Copy All Info Related Tools Resize images to exact pixel dimensions → Image Resizer Pick and convert colors for your web designs → Color Picker Generate CSS gradients for your site backgrounds → CSS Gradient Generator ","permalink":"https://productivity-works.com/tools/screen-resolution/","summary":"\u003cp\u003eCheck your screen resolution, viewport size, pixel ratio, and active CSS breakpoints in real time. Resize your window to see live updates.\u003c/p\u003e\n\u003cdiv id=\"screen-app\"\u003e\n\u003cstyle\u003e\n#screen-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  color: #1e293b;\n  max-width: 860px;\n  margin: 0 auto;\n}\n#screen-app * { box-sizing: border-box; }\n\n#screen-app h2 {\n  font-size: 1.1rem;\n  font-weight: 700;\n  color: #0891b2;\n  margin: 1.6rem 0 0.6rem;\n  border-left: 4px solid #0891b2;\n  padding-left: 0.6rem;\n}\n\n#screen-app .sa-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\n  gap: 0.75rem;\n  margin-bottom: 1rem;\n}\n\n#screen-app .sa-card {\n  background: #f0f9ff;\n  border: 1px solid #bae6fd;\n  border-radius: 10px;\n  padding: 0.9rem 1rem;\n}\n#screen-app .sa-card .sa-label {\n  font-size: 0.72rem;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  color: #0e7490;\n  margin-bottom: 0.25rem;\n}\n#screen-app .sa-card .sa-value {\n  font-size: 1.35rem;\n  font-weight: 700;\n  color: #0891b2;\n}\n#screen-app .sa-card .sa-unit {\n  font-size: 0.8rem;\n  color: #64748b;\n  margin-left: 2px;\n}\n\n/* Visual area */\n#screen-app .sa-visual-wrap {\n  background: #0f172a;\n  border-radius: 12px;\n  padding: 1.5rem;\n  margin-bottom: 1rem;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  min-height: 200px;\n  position: relative;\n}\n#screen-app .sa-screen-rect {\n  position: relative;\n  background: #1e293b;\n  border: 2px solid #0891b2;\n  border-radius: 4px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n#screen-app .sa-screen-label {\n  position: absolute;\n  top: -20px;\n  left: 0;\n  font-size: 0.7rem;\n  color: #0891b2;\n  white-space: nowrap;\n}\n#screen-app .sa-viewport-rect {\n  position: absolute;\n  background: rgba(8, 145, 178, 0.25);\n  border: 2px dashed #06b6d4;\n  border-radius: 3px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n#screen-app .sa-viewport-label {\n  font-size: 0.65rem;\n  color: #67e8f9;\n  text-align: center;\n  pointer-events: none;\n}\n\n/* Breakpoints */\n#screen-app .sa-bp-list {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem;\n  margin-bottom: 1rem;\n}\n#screen-app .sa-bp-tag {\n  padding: 0.3rem 0.75rem;\n  border-radius: 999px;\n  font-size: 0.78rem;\n  font-weight: 600;\n  background: #e2e8f0;\n  color: #64748b;\n  border: 2px solid transparent;\n  transition: all 0.2s;\n}\n#screen-app .sa-bp-tag.active {\n  background: #0891b2;\n  color: #fff;\n  border-color: #0891b2;\n}\n\n/* Resolution comparison */\n#screen-app .sa-res-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.85rem;\n  margin-bottom: 1rem;\n}\n#screen-app .sa-res-table th {\n  background: #0891b2;\n  color: #fff;\n  padding: 0.5rem 0.75rem;\n  text-align: left;\n  font-weight: 600;\n}\n#screen-app .sa-res-table td {\n  padding: 0.45rem 0.75rem;\n  border-bottom: 1px solid #e2e8f0;\n}\n#screen-app .sa-res-table tr:nth-child(even) td {\n  background: #f8fafc;\n}\n#screen-app .sa-res-table tr.sa-res-match td {\n  background: #ecfeff;\n  font-weight: 700;\n  color: #0891b2;\n}\n#screen-app .sa-res-table tr.sa-res-match td:first-child::before {\n  content: \"► \";\n}\n\n/* Browser/OS */\n#screen-app .sa-info-box {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 8px;\n  padding: 0.9rem 1rem;\n  font-size: 0.84rem;\n  line-height: 1.7;\n  margin-bottom: 1rem;\n}\n#screen-app .sa-info-box strong {\n  color: #0891b2;\n}\n\n/* Copy button */\n#screen-app .sa-copy-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 0.4rem;\n  background: #0891b2;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  padding: 0.6rem 1.2rem;\n  font-size: 0.88rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.2s;\n  margin-bottom: 1rem;\n}\n#screen-app .sa-copy-btn:hover { background: #0e7490; }\n#screen-app .sa-copy-btn.copied { background: #059669; }\n\n#screen-app .sa-live-badge {\n  display: inline-block;\n  background: #0891b2;\n  color: #fff;\n  font-size: 0.65rem;\n  font-weight: 700;\n  letter-spacing: 0.08em;\n  border-radius: 999px;\n  padding: 0.1rem 0.5rem;\n  vertical-align: middle;\n  margin-left: 0.4rem;\n  animation: sa-pulse 2s infinite;\n}\n@keyframes sa-pulse {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0.5; }\n}\n\u003c/style\u003e\n\u003ch2\u003eYour Display Info \u003cspan class=\"sa-live-badge\"\u003eLIVE\u003c/span\u003e\u003c/h2\u003e\n\u003cdiv class=\"sa-grid\" id=\"sa-metrics\"\u003e\u003c/div\u003e\n\u003ch2\u003eScreen vs Viewport (Visual)\u003c/h2\u003e\n\u003cdiv class=\"sa-visual-wrap\" id=\"sa-visual\"\u003e\n  \u003cdiv class=\"sa-screen-rect\" id=\"sa-screen-rect\"\u003e\n    \u003cspan class=\"sa-screen-label\" id=\"sa-screen-rect-label\"\u003e\u003c/span\u003e\n    \u003cdiv class=\"sa-viewport-rect\" id=\"sa-viewport-rect\"\u003e\n      \u003cspan class=\"sa-viewport-label\" id=\"sa-vp-label\"\u003e\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003ch2\u003eActive CSS Breakpoints\u003c/h2\u003e\n\u003cdiv class=\"sa-bp-list\" id=\"sa-breakpoints\"\u003e\u003c/div\u003e\n\u003ch2\u003eCommon Resolution Comparison\u003c/h2\u003e\n\u003ctable class=\"sa-res-table\"\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\u003cth\u003eName\u003c/th\u003e\u003cth\u003eWidth\u003c/th\u003e\u003cth\u003eHeight\u003c/th\u003e\u003cth\u003eRatio\u003c/th\u003e\u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody id=\"sa-res-tbody\"\u003e\u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2\u003eBrowser \u0026amp; OS Info\u003c/h2\u003e\n\u003cdiv class=\"sa-info-box\" id=\"sa-browser-info\"\u003e\u003c/div\u003e\n\u003cbutton class=\"sa-copy-btn\" id=\"sa-copy-btn\" onclick=\"saCopyAll()\"\u003e\n  \u0026#128203; Copy All Info\n\u003c/button\u003e\n\u003cscript\u003e\n(function() {\n  var RESOLUTIONS = [\n    { name: \"nHD\",       w: 640,  h: 360  },\n    { name: \"HD (720p)\", w: 1280, h: 720  },\n    { name: \"Full HD (1080p)\", w: 1920, h: 1080 },\n    { name: \"QHD (1440p)\",    w: 2560, h: 1440 },\n    { name: \"4K UHD\",         w: 3840, h: 2160 },\n    { name: \"5K\",             w: 5120, h: 2880 },\n    { name: \"8K UHD\",         w: 7680, h: 4320 },\n  ];\n\n  var BREAKPOINTS = [\n    { label: \"sm (≥640px)\",  mq: \"(min-width: 640px)\"  },\n    { label: \"md (≥768px)\",  mq: \"(min-width: 768px)\"  },\n    { label: \"lg (≥1024px)\", mq: \"(min-width: 1024px)\" },\n    { label: \"xl (≥1280px)\", mq: \"(min-width: 1280px)\" },\n    { label: \"2xl (≥1536px)\",mq: \"(min-width: 1536px)\" },\n  ];\n\n  function gcd(a, b) { return b === 0 ? a : gcd(b, a % b); }\n  function aspectRatio(w, h) {\n    var d = gcd(w, h);\n    return (w / d) + \":\" + (h / d);\n  }\n\n  function detectBrowser(ua) {\n    if (/Edg\\//.test(ua))     return \"Microsoft Edge\";\n    if (/OPR\\/|Opera/.test(ua)) return \"Opera\";\n    if (/Chrome\\//.test(ua))  return \"Google Chrome\";\n    if (/Firefox\\//.test(ua)) return \"Mozilla Firefox\";\n    if (/Safari\\//.test(ua))  return \"Apple Safari\";\n    return \"Unknown Browser\";\n  }\n  function detectOS(ua) {\n    if (/Windows NT 10/.test(ua)) return \"Windows 10/11\";\n    if (/Windows NT/.test(ua))    return \"Windows\";\n    if (/Mac OS X/.test(ua))      return \"macOS\";\n    if (/Android/.test(ua))       return \"Android\";\n    if (/iPhone|iPad/.test(ua))   return \"iOS\";\n    if (/Linux/.test(ua))         return \"Linux\";\n    return \"Unknown OS\";\n  }\n  function detectDevice(ua) {\n    if (/Mobile|Android|iPhone/.test(ua)) return \"Mobile\";\n    if (/iPad|Tablet/.test(ua))           return \"Tablet\";\n    return \"Desktop\";\n  }\n\n  function getData() {\n    var sw = window.screen.width;\n    var sh = window.screen.height;\n    var vw = window.innerWidth;\n    var vh = window.innerHeight;\n    var dpr = window.devicePixelRatio || 1;\n    var cd = window.screen.colorDepth || 24;\n    var orient = (sw \u003e sh) ? \"Landscape\" : \"Portrait\";\n    if (window.screen.orientation \u0026\u0026 window.screen.orientation.type) {\n      orient = window.screen.orientation.type.replace(/-primary|-secondary/, '');\n      orient = orient.charAt(0).toUpperCase() + orient.slice(1);\n    }\n    return { sw: sw, sh: sh, vw: vw, vh: vh, dpr: dpr, cd: cd, orient: orient };\n  }\n\n  function renderMetrics(d) {\n    var cards = [\n      { label: \"Screen Width\",  value: d.sw, unit: \"px\" },\n      { label: \"Screen Height\", value: d.sh, unit: \"px\" },\n      { label: \"Screen Ratio\",  value: aspectRatio(d.sw, d.sh), unit: \"\" },\n      { label: \"Viewport Width\",  value: d.vw, unit: \"px\" },\n      { label: \"Viewport Height\", value: d.vh, unit: \"px\" },\n      { label: \"Viewport Ratio\",  value: aspectRatio(d.vw, d.vh), unit: \"\" },\n      { label: \"Device Pixel Ratio\", value: d.dpr.toFixed(2), unit: \"x\" },\n      { label: \"Color Depth\",     value: d.cd, unit: \"bit\" },\n      { label: \"Orientation\",     value: d.orient, unit: \"\" },\n    ];\n    var el = document.getElementById(\"sa-metrics\");\n    el.innerHTML = cards.map(function(c) {\n      return '\u003cdiv class=\"sa-card\"\u003e' +\n        '\u003cdiv class=\"sa-label\"\u003e' + c.label + '\u003c/div\u003e' +\n        '\u003cdiv class=\"sa-value\"\u003e' + c.value + '\u003cspan class=\"sa-unit\"\u003e' + c.unit + '\u003c/span\u003e\u003c/div\u003e' +\n        '\u003c/div\u003e';\n    }).join(\"\");\n  }\n\n  function renderVisual(d) {\n    var container = document.getElementById(\"sa-visual\");\n    var cw = container.offsetWidth - 48;\n    var ch = Math.max(160, container.offsetHeight - 48);\n    var scaleX = cw / d.sw;\n    var scaleY = ch / d.sh;\n    var scale = Math.min(scaleX, scaleY, 1);\n\n    var srW = Math.round(d.sw * scale);\n    var srH = Math.round(d.sh * scale);\n    var vpW = Math.round(d.vw * scale);\n    var vpH = Math.round(d.vh * scale);\n\n    var sr = document.getElementById(\"sa-screen-rect\");\n    sr.style.width  = srW + \"px\";\n    sr.style.height = srH + \"px\";\n\n    document.getElementById(\"sa-screen-rect-label\").textContent =\n      \"Screen: \" + d.sw + \" x \" + d.sh;\n\n    var vp = document.getElementById(\"sa-viewport-rect\");\n    vp.style.width  = Math.min(vpW, srW) + \"px\";\n    vp.style.height = Math.min(vpH, srH) + \"px\";\n\n    document.getElementById(\"sa-vp-label\").textContent =\n      \"Viewport\\n\" + d.vw + \"x\" + d.vh;\n  }\n\n  function renderBreakpoints() {\n    var el = document.getElementById(\"sa-breakpoints\");\n    el.innerHTML = BREAKPOINTS.map(function(bp) {\n      var active = window.matchMedia(bp.mq).matches;\n      return '\u003cspan class=\"sa-bp-tag' + (active ? \" active\" : \"\") + '\"\u003e' + bp.label + '\u003c/span\u003e';\n    }).join(\"\");\n  }\n\n  function renderResTable(d) {\n    var tbody = document.getElementById(\"sa-res-tbody\");\n    var match = -1;\n    var minDiff = Infinity;\n    RESOLUTIONS.forEach(function(r, i) {\n      var diff = Math.abs(r.w - d.sw) + Math.abs(r.h - d.sh);\n      if (diff \u003c minDiff) { minDiff = diff; match = i; }\n    });\n    tbody.innerHTML = RESOLUTIONS.map(function(r, i) {\n      var cls = (i === match \u0026\u0026 minDiff \u003c 300) ? ' class=\"sa-res-match\"' : '';\n      return '\u003ctr' + cls + '\u003e\u003ctd\u003e' + r.name + '\u003c/td\u003e\u003ctd\u003e' + r.w + 'px\u003c/td\u003e\u003ctd\u003e' + r.h + 'px\u003c/td\u003e\u003ctd\u003e' + aspectRatio(r.w, r.h) + '\u003c/td\u003e\u003c/tr\u003e';\n    }).join(\"\");\n  }\n\n  function renderBrowser() {\n    var ua = navigator.userAgent;\n    var el = document.getElementById(\"sa-browser-info\");\n    el.innerHTML =\n      '\u003cstrong\u003eBrowser:\u003c/strong\u003e ' + detectBrowser(ua) + '\u003cbr\u003e' +\n      '\u003cstrong\u003eOS:\u003c/strong\u003e ' + detectOS(ua) + '\u003cbr\u003e' +\n      '\u003cstrong\u003eDevice Type:\u003c/strong\u003e ' + detectDevice(ua) + '\u003cbr\u003e' +\n      '\u003cstrong\u003eTouch:\u003c/strong\u003e ' + ('ontouchstart' in window ? 'Yes' : 'No') + '\u003cbr\u003e' +\n      '\u003cstrong\u003eUser Agent:\u003c/strong\u003e \u003cspan style=\"word-break:break-all;color:#334155\"\u003e' + ua + '\u003c/span\u003e';\n  }\n\n  function renderAll() {\n    var d = getData();\n    renderMetrics(d);\n    renderVisual(d);\n    renderBreakpoints();\n    renderResTable(d);\n  }\n\n  window.saCopyAll = function() {\n    var d = getData();\n    var ua = navigator.userAgent;\n    var lines = [\n      \"=== Screen Resolution Info ===\",\n      \"Screen: \" + d.sw + \" x \" + d.sh + \" (\" + aspectRatio(d.sw, d.sh) + \")\",\n      \"Viewport: \" + d.vw + \" x \" + d.vh + \" (\" + aspectRatio(d.vw, d.vh) + \")\",\n      \"Device Pixel Ratio: \" + d.dpr.toFixed(2) + \"x\",\n      \"Color Depth: \" + d.cd + \" bit\",\n      \"Orientation: \" + d.orient,\n      \"Browser: \" + detectBrowser(ua),\n      \"OS: \" + detectOS(ua),\n      \"Device: \" + detectDevice(ua),\n      \"User Agent: \" + ua,\n    ];\n    var bp = BREAKPOINTS.filter(function(b) { return window.matchMedia(b.mq).matches; });\n    lines.push(\"Active Breakpoints: \" + (bp.length ? bp.map(function(b){return b.label;}).join(\", \") : \"none\"));\n    lines.push(\"Captured: \" + new Date().toLocaleString());\n\n    navigator.clipboard.writeText(lines.join(\"\\n\")).then(function() {\n      var btn = document.getElementById(\"sa-copy-btn\");\n      btn.textContent = \"Copied!\";\n      btn.classList.add(\"copied\");\n      setTimeout(function() {\n        btn.textContent = \"\\uD83D\\uDCCB Copy All Info\";\n        btn.classList.remove(\"copied\");\n      }, 2000);\n    }).catch(function() {\n      alert(lines.join(\"\\n\"));\n    });\n  };\n\n  renderAll();\n  renderBrowser();\n\n  var resizeTimer;\n  window.addEventListener(\"resize\", function() {\n    clearTimeout(resizeTimer);\n    resizeTimer = setTimeout(renderAll, 80);\n  });\n\n  window.addEventListener(\"orientationchange\", function() {\n    setTimeout(renderAll, 300);\n  });\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003eResize images to exact pixel dimensions → \u003ca href=\"https://productivity-works.com/tools/image-resizer/\"\u003eImage Resizer\u003c/a\u003e\n\u003c/p\u003e","title":"Screen Resolution Checker — Instant Display Info"},{"content":" Screen Time Calculator Find out exactly how much of your life is spent looking at screens — and what you could do with that time instead.\nEnter Your Daily Screen Time Drag each slider to match your average hours per day for each activity.\n💼 Work / School 8h 📱 Social Media 2h 🎬 Streaming (TV/Video) 2h 🎮 Gaming 1h 🌐 Web Browsing 1h 📲 Other Screens 0.5h Your current age (for lifetime projection):\nCalculate My Screen Time\nYour Screen Time Summary - Hours / Day - Hours / Week - Hours / Month - Hours / Year - Full Days / Year - % of Waking Hours Breakdown by Activity Lifetime Screen Time Projection - - Tips to Reduce Screen Time What Else Could You Do With That Time? Based on your - hours of non-work screen time per year:\nReset Calculator\nRelated Free Tools Pomodoro Timer — Focus in 25-minute sprints Sleep Debt Calculator — Find out if you're sleep-deprived Work Hours Tracker — Log and analyze your working time Distraction Cost Calculator — See how much interruptions cost you Habit Streak Tracker — Build screen-free routines ","permalink":"https://productivity-works.com/tools/screen-time-calculator/","summary":"\u003cdiv id=\"st-app\"\u003e\n\u003cstyle\u003e\n#st-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  padding: 24px 16px;\n  color: #1a1a2e;\n}\n#st-app h2 {\n  font-size: 1.5rem;\n  font-weight: 700;\n  margin: 32px 0 12px;\n  color: #1a1a2e;\n}\n#st-app h3 {\n  font-size: 1.15rem;\n  font-weight: 600;\n  margin: 20px 0 8px;\n  color: #16213e;\n}\n#st-app p {\n  margin: 0 0 12px;\n  line-height: 1.6;\n}\n#st-app .intro-box {\n  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n  color: #fff;\n  border-radius: 14px;\n  padding: 28px 24px;\n  margin-bottom: 32px;\n}\n#st-app .intro-box h1 {\n  font-size: 1.6rem;\n  font-weight: 800;\n  margin: 0 0 8px;\n  color: #fff;\n}\n#st-app .intro-box p {\n  margin: 0;\n  opacity: 0.9;\n  font-size: 0.97rem;\n}\n#st-app .inputs-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));\n  gap: 16px;\n  margin-bottom: 24px;\n}\n#st-app .input-card {\n  background: #f8f9ff;\n  border: 2px solid #e8eaf6;\n  border-radius: 12px;\n  padding: 16px 18px;\n  transition: border-color 0.2s;\n}\n#st-app .input-card:focus-within {\n  border-color: #667eea;\n}\n#st-app .input-card label {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  font-size: 0.9rem;\n  font-weight: 600;\n  color: #444;\n  margin-bottom: 10px;\n}\n#st-app .input-card .icon {\n  font-size: 1.3rem;\n}\n#st-app .input-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n#st-app .input-card input[type=\"range\"] {\n  flex: 1;\n  -webkit-appearance: none;\n  height: 6px;\n  border-radius: 3px;\n  background: #d1d5f0;\n  outline: none;\n  cursor: pointer;\n}\n#st-app .input-card input[type=\"range\"]::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  width: 18px;\n  height: 18px;\n  border-radius: 50%;\n  background: #667eea;\n  cursor: pointer;\n  box-shadow: 0 1px 4px rgba(102,126,234,0.4);\n}\n#st-app .val-badge {\n  min-width: 48px;\n  text-align: center;\n  background: #667eea;\n  color: #fff;\n  border-radius: 8px;\n  padding: 3px 8px;\n  font-size: 0.85rem;\n  font-weight: 700;\n}\n#st-app .calc-btn {\n  display: block;\n  width: 100%;\n  padding: 15px;\n  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n  color: #fff;\n  font-size: 1.05rem;\n  font-weight: 700;\n  border: none;\n  border-radius: 12px;\n  cursor: pointer;\n  letter-spacing: 0.5px;\n  margin-bottom: 32px;\n  transition: opacity 0.2s, transform 0.1s;\n}\n#st-app .calc-btn:hover { opacity: 0.92; transform: translateY(-1px); }\n#st-app .calc-btn:active { transform: translateY(0); }\n#st-app .results-section {\n  display: none;\n}\n#st-app .results-section.visible {\n  display: block;\n}\n#st-app .stats-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n  gap: 14px;\n  margin-bottom: 28px;\n}\n#st-app .stat-card {\n  background: #fff;\n  border: 2px solid #e8eaf6;\n  border-radius: 12px;\n  padding: 18px 14px;\n  text-align: center;\n}\n#st-app .stat-card .stat-val {\n  font-size: 1.9rem;\n  font-weight: 800;\n  color: #667eea;\n  line-height: 1.1;\n}\n#st-app .stat-card .stat-label {\n  font-size: 0.78rem;\n  color: #888;\n  margin-top: 4px;\n  font-weight: 500;\n  text-transform: uppercase;\n  letter-spacing: 0.5px;\n}\n#st-app .stat-card.highlight {\n  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n  border-color: transparent;\n}\n#st-app .stat-card.highlight .stat-val,\n#st-app .stat-card.highlight .stat-label {\n  color: #fff;\n}\n#st-app .chart-wrap {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 20px;\n  margin-bottom: 32px;\n}\n@media (min-width: 560px) {\n  #st-app .chart-wrap { flex-direction: row; align-items: flex-start; }\n}\n#st-app canvas#st-pie {\n  max-width: 220px;\n  max-height: 220px;\n}\n#st-app .legend {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n}\n#st-app .legend-item {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  font-size: 0.88rem;\n}\n#st-app .legend-dot {\n  width: 13px;\n  height: 13px;\n  border-radius: 50%;\n  flex-shrink: 0;\n}\n#st-app .legend-label { flex: 1; color: #444; font-weight: 500; }\n#st-app .legend-pct { font-weight: 700; color: #222; }\n#st-app .recommend-box {\n  border-radius: 12px;\n  padding: 20px 22px;\n  margin-bottom: 24px;\n}\n#st-app .recommend-box.ok { background: #e8f8f0; border: 2px solid #34d399; }\n#st-app .recommend-box.warn { background: #fff8e1; border: 2px solid #fbbf24; }\n#st-app .recommend-box.danger { background: #fef2f2; border: 2px solid #f87171; }\n#st-app .recommend-box .rec-title {\n  font-size: 1.05rem;\n  font-weight: 700;\n  margin-bottom: 6px;\n}\n#st-app .recommend-box.ok .rec-title { color: #059669; }\n#st-app .recommend-box.warn .rec-title { color: #d97706; }\n#st-app .recommend-box.danger .rec-title { color: #dc2626; }\n#st-app .lifetime-box {\n  background: #1a1a2e;\n  color: #fff;\n  border-radius: 14px;\n  padding: 24px 22px;\n  margin-bottom: 28px;\n}\n#st-app .lifetime-box h3 { color: #a78bfa; margin-top: 0; }\n#st-app .lifetime-box .life-stat {\n  font-size: 2.2rem;\n  font-weight: 800;\n  color: #c4b5fd;\n  margin: 4px 0;\n}\n#st-app .lifetime-box p { color: #ccc; font-size: 0.9rem; }\n#st-app .tips-list {\n  list-style: none;\n  padding: 0;\n  margin: 0 0 28px;\n}\n#st-app .tips-list li {\n  display: flex;\n  align-items: flex-start;\n  gap: 10px;\n  padding: 10px 14px;\n  background: #f8f9ff;\n  border-radius: 10px;\n  margin-bottom: 8px;\n  font-size: 0.92rem;\n  line-height: 1.5;\n}\n#st-app .tips-list li .tip-icon { font-size: 1.2rem; flex-shrink: 0; margin-top: 1px; }\n#st-app .instead-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));\n  gap: 12px;\n  margin-bottom: 32px;\n}\n#st-app .instead-card {\n  background: #f0f4ff;\n  border-radius: 12px;\n  padding: 16px;\n  text-align: center;\n}\n#st-app .instead-card .inst-icon { font-size: 2rem; margin-bottom: 6px; }\n#st-app .instead-card .inst-val { font-size: 1.4rem; font-weight: 800; color: #667eea; }\n#st-app .instead-card .inst-label { font-size: 0.82rem; color: #555; margin-top: 2px; }\n#st-app .related-links {\n  background: #f8f9ff;\n  border-radius: 12px;\n  padding: 20px 22px;\n  margin-top: 32px;\n}\n#st-app .related-links h3 { margin-top: 0; }\n#st-app .related-links ul {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n}\n#st-app .related-links ul li {\n  padding: 6px 0;\n  border-bottom: 1px solid #e8eaf6;\n}\n#st-app .related-links ul li:last-child { border-bottom: none; }\n#st-app .related-links a {\n  color: #667eea;\n  text-decoration: none;\n  font-weight: 500;\n  font-size: 0.93rem;\n}\n#st-app .related-links a:hover { text-decoration: underline; }\n#st-app .reset-btn {\n  background: none;\n  border: 2px solid #667eea;\n  color: #667eea;\n  border-radius: 10px;\n  padding: 10px 24px;\n  font-size: 0.92rem;\n  font-weight: 600;\n  cursor: pointer;\n  margin-top: 8px;\n  transition: background 0.2s, color 0.2s;\n}\n#st-app .reset-btn:hover { background: #667eea; color: #fff; }\n\u003c/style\u003e\n\u003cdiv class=\"intro-box\"\u003e\n  \u003ch1\u003eScreen Time Calculator\u003c/h1\u003e\n  \u003cp\u003eFind out exactly how much of your life is spent looking at screens — and what you could do with that time instead.\u003c/p\u003e","title":"Screen Time Calculator - Track Your Daily Screen Usage"},{"content":"Generate a valid XML sitemap for your website. Add URLs one-by-one or paste them in bulk, set priorities and change frequencies, then download or copy your sitemap.xml file.\nPresets Blog E-commerce Portfolio Clear All Default Values (applied to new entries) Default Change Freq always hourly daily weekly monthly yearly never Default Priority Individual URLs Bulk Input + Add URL Each URL entry supports: Last Modified date, Change Frequency, and Priority (0.0–1.0).\nPaste URLs (one per line) Import URLs Default change frequency and priority will be applied to all imported URLs. Switch to Individual URLs to customize each entry.\nGenerated Sitemap XML URLs: 0 \u0026#10003; Valid XML Copy XML Download sitemap.xml Copied! How to Use Add URLs — Click \u0026ldquo;Add URL\u0026rdquo; to add entries one by one, or use \u0026ldquo;Bulk Input\u0026rdquo; to paste a list. Set fields — For each URL, optionally set the Last Modified date, Change Frequency, and Priority. Use a preset — Choose Blog, E-commerce, or Portfolio to load a starter template. Copy or Download — Click \u0026ldquo;Copy XML\u0026rdquo; to copy to clipboard, or \u0026ldquo;Download sitemap.xml\u0026rdquo; to save the file. Submit to Google — Upload sitemap.xml to your server root and submit via Google Search Console . What is a Sitemap XML? An XML sitemap is a file that lists all the important pages of your website, helping search engines like Google and Bing discover and index your content more efficiently. The standard format is defined by sitemaps.org .\nEach \u0026lt;url\u0026gt; entry can include:\nField Description \u0026lt;loc\u0026gt; Full URL of the page (required) \u0026lt;lastmod\u0026gt; Date the page was last modified (YYYY-MM-DD) \u0026lt;changefreq\u0026gt; How often the page changes (daily, weekly, etc.) \u0026lt;priority\u0026gt; Importance relative to other pages (0.0–1.0) Related Tools Build robots.txt → Robots.txt Generator Generate meta tags → Meta Tag Generator Create privacy policy → Privacy Policy Generator ","permalink":"https://productivity-works.com/tools/sitemap-generator/","summary":"\u003cp\u003eGenerate a valid XML sitemap for your website. Add URLs one-by-one or paste them in bulk, set priorities and change frequencies, then download or copy your \u003ccode\u003esitemap.xml\u003c/code\u003e file.\u003c/p\u003e\n\u003cdiv id=\"sitemap-app\"\u003e\n\u003cstyle\u003e\n  #sitemap-app *,\n  #sitemap-app *::before,\n  #sitemap-app *::after {\n    box-sizing: border-box;\n    margin: 0;\n    padding: 0;\n  }\n\u003cp\u003e#sitemap-app {\nfont-family: -apple-system, BlinkMacSystemFont, \u0026ldquo;Segoe UI\u0026rdquo;, Roboto, Helvetica, Arial, sans-serif;\nfont-size: 15px;\ncolor: #1a1a1a;\nline-height: 1.5;\nmax-width: 860px;\nmargin: 0 auto;\n}\u003c/p\u003e\n\u003cp\u003e#sitemap-app .sa-section {\nbackground: #fff;\nborder: 1px solid #e0e0e0;\nborder-radius: 10px;\npadding: 20px;\nmargin-bottom: 16px;\n}\u003c/p\u003e","title":"Sitemap XML Generator — Free Online Tool"},{"content":"Enter your wake-up time or bedtime and instantly find the ideal sleep schedule based on 90-minute sleep cycles — so you wake up between cycles feeling alert, not groggy.\nI want to wake up at… I'm going to bed at… Target wake-up time We'll calculate the best bedtimes based on 90-min sleep cycles.\nBedtime (when you plan to sleep) We'll calculate the best wake-up times based on 90-min sleep cycles.\nCalculate Sleep Times Recommended bedtimes\nSleep cycle timeline (selected option)\nFall asleep (~14 min) 90-min cycle Sleep Quality Tips\n🌙Stick to a schedule: Going to bed and waking at the same time every day — even on weekends — anchors your circadian rhythm. 📵Screen-free wind-down: Avoid bright screens for 30–60 minutes before bed. Blue light suppresses melatonin production. ❄️Cool your room: The ideal sleep temperature is around 65–68°F (18–20°C). A cooler room helps your core body temperature drop, triggering sleep. ☕Cut caffeine early: Caffeine has a half-life of ~5 hours. Stop consuming it at least 6 hours before bedtime for best results. 🛏️Bed = sleep only: Reserve your bed for sleep. Working or watching TV in bed trains your brain to stay alert there. 💡5–6 cycles = optimal: Most adults feel best with 5 or 6 full 90-minute cycles (7.5–9 hours). Waking mid-cycle causes grogginess (sleep inertia). Disclaimer: Sleep needs vary by individual age and health. This calculator is for general guidance only and is not medical advice.\nRelated Tools Track your daily nutrition → Calorie Calculator Check your body mass index → BMI Calculator Find out your exact age → Age Calculator ","permalink":"https://productivity-works.com/tools/sleep-calculator/","summary":"\u003cp\u003eEnter your wake-up time or bedtime and instantly find the ideal sleep schedule based on 90-minute sleep cycles — so you wake up between cycles feeling alert, not groggy.\u003c/p\u003e\n\u003cdiv id=\"sl-app\"\u003e\n\u003cstyle\u003e\n#sl-app *,\n#sl-app *::before,\n#sl-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#sl-app {\n  font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n  color: #1e293b;\n  max-width: 700px;\n  margin: 0 auto;\n  padding: 8px 0;\n  font-size: 15px;\n  line-height: 1.6;\n}\n#sl-app h2 {\n  font-size: 18px;\n  font-weight: 700;\n  color: #0f172a;\n  margin-bottom: 16px;\n}\n#sl-app .sl-card {\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 22px 24px;\n  margin-bottom: 20px;\n}\n#sl-app .sl-tabs {\n  display: flex;\n  gap: 8px;\n  margin-bottom: 22px;\n}\n#sl-app .sl-tab {\n  flex: 1;\n  padding: 10px 14px;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 8px;\n  background: #fff;\n  color: #475569;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  text-align: center;\n  transition: all 0.15s;\n}\n#sl-app .sl-tab:hover {\n  border-color: #6366f1;\n  color: #6366f1;\n}\n#sl-app .sl-tab.active {\n  background: #6366f1;\n  border-color: #6366f1;\n  color: #fff;\n}\n#sl-app .sl-label {\n  display: block;\n  font-size: 13px;\n  font-weight: 600;\n  color: #334155;\n  margin-bottom: 7px;\n}\n#sl-app .sl-time-input {\n  width: 100%;\n  max-width: 220px;\n  padding: 10px 14px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 22px;\n  font-weight: 700;\n  color: #0f172a;\n  background: #fff;\n  outline: none;\n  transition: border-color 0.15s;\n}\n#sl-app .sl-time-input:focus {\n  border-color: #6366f1;\n}\n#sl-app .sl-hint {\n  font-size: 12px;\n  color: #94a3b8;\n  margin-top: 6px;\n}\n#sl-app .sl-btn {\n  display: inline-block;\n  margin-top: 16px;\n  padding: 11px 28px;\n  background: #6366f1;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 15px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n#sl-app .sl-btn:hover {\n  background: #4f46e5;\n}\n#sl-app .sl-results {\n  display: none;\n}\n#sl-app .sl-results.visible {\n  display: block;\n}\n#sl-app .sl-results-title {\n  font-size: 15px;\n  font-weight: 700;\n  color: #334155;\n  margin-bottom: 14px;\n  padding-bottom: 8px;\n  border-bottom: 2px solid #e2e8f0;\n}\n#sl-app .sl-cycle-list {\n  list-style: none;\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n}\n#sl-app .sl-cycle-item {\n  display: flex;\n  align-items: center;\n  gap: 14px;\n  padding: 14px 16px;\n  border-radius: 10px;\n  border: 1.5px solid #e2e8f0;\n  background: #fff;\n  position: relative;\n}\n#sl-app .sl-cycle-item.best {\n  border-color: #6366f1;\n  background: #eef2ff;\n}\n#sl-app .sl-cycle-item.good {\n  border-color: #34d399;\n  background: #f0fdf4;\n}\n#sl-app .sl-cycle-time {\n  font-size: 24px;\n  font-weight: 800;\n  color: #0f172a;\n  min-width: 80px;\n}\n#sl-app .sl-cycle-meta {\n  display: flex;\n  flex-direction: column;\n  gap: 2px;\n}\n#sl-app .sl-cycle-cycles {\n  font-size: 13px;\n  font-weight: 600;\n  color: #475569;\n}\n#sl-app .sl-cycle-duration {\n  font-size: 12px;\n  color: #94a3b8;\n}\n#sl-app .sl-badge {\n  margin-left: auto;\n  padding: 4px 10px;\n  border-radius: 20px;\n  font-size: 12px;\n  font-weight: 700;\n  white-space: nowrap;\n}\n#sl-app .sl-badge-best {\n  background: #6366f1;\n  color: #fff;\n}\n#sl-app .sl-badge-good {\n  background: #34d399;\n  color: #fff;\n}\n#sl-app .sl-badge-ok {\n  background: #f1f5f9;\n  color: #64748b;\n}\n#sl-app .sl-timeline {\n  margin-top: 8px;\n  padding: 16px;\n  background: #fff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n}\n#sl-app .sl-timeline-title {\n  font-size: 13px;\n  font-weight: 700;\n  color: #334155;\n  margin-bottom: 12px;\n}\n#sl-app .sl-tl-bar {\n  display: flex;\n  height: 28px;\n  border-radius: 6px;\n  overflow: hidden;\n  margin-bottom: 8px;\n}\n#sl-app .sl-tl-seg {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 10px;\n  font-weight: 700;\n  color: #fff;\n  transition: flex 0.3s;\n}\n#sl-app .sl-tl-seg.fall-asleep { background: #94a3b8; }\n#sl-app .sl-tl-seg.cycle { background: #6366f1; }\n#sl-app .sl-tl-seg.cycle:nth-child(even) { background: #818cf8; }\n#sl-app .sl-tl-legend {\n  display: flex;\n  gap: 14px;\n  flex-wrap: wrap;\n}\n#sl-app .sl-tl-legend-item {\n  display: flex;\n  align-items: center;\n  gap: 5px;\n  font-size: 11px;\n  color: #64748b;\n}\n#sl-app .sl-tl-dot {\n  width: 10px;\n  height: 10px;\n  border-radius: 3px;\n  flex-shrink: 0;\n}\n#sl-app .sl-section-label {\n  font-size: 13px;\n  font-weight: 700;\n  color: #334155;\n  margin-bottom: 10px;\n  margin-top: 18px;\n}\n#sl-app .sl-tips {\n  list-style: none;\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n}\n#sl-app .sl-tip {\n  display: flex;\n  gap: 10px;\n  align-items: flex-start;\n  font-size: 13px;\n  color: #475569;\n  padding: 10px 14px;\n  background: #f8fafc;\n  border-radius: 8px;\n  border: 1px solid #e2e8f0;\n}\n#sl-app .sl-tip-icon {\n  font-size: 16px;\n  flex-shrink: 0;\n  line-height: 1.4;\n}\n@media (max-width: 520px) {\n  #sl-app .sl-tabs {\n    flex-direction: column;\n  }\n  #sl-app .sl-cycle-time {\n    font-size: 20px;\n    min-width: 64px;\n  }\n}\n\u003c/style\u003e\n\u003cdiv class=\"sl-tabs\"\u003e\n  \u003cbutton class=\"sl-tab active\" id=\"sl-tab-wake\" onclick=\"(function(){document.getElementById('sl-tab-wake').classList.add('active');document.getElementById('sl-tab-bed').classList.remove('active');document.getElementById('sl-panel-wake').style.display='block';document.getElementById('sl-panel-bed').style.display='none';document.getElementById('sl-results').classList.remove('visible');})()\"\u003eI want to wake up at…\u003c/button\u003e\n  \u003cbutton class=\"sl-tab\" id=\"sl-tab-bed\" onclick=\"(function(){document.getElementById('sl-tab-bed').classList.add('active');document.getElementById('sl-tab-wake').classList.remove('active');document.getElementById('sl-panel-bed').style.display='block';document.getElementById('sl-panel-wake').style.display='none';document.getElementById('sl-results').classList.remove('visible');})()\"\u003eI'm going to bed at…\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"sl-card\"\u003e\n  \u003cdiv id=\"sl-panel-wake\"\u003e\n    \u003clabel class=\"sl-label\" for=\"sl-wake-time\"\u003eTarget wake-up time\u003c/label\u003e\n    \u003cinput type=\"time\" id=\"sl-wake-time\" class=\"sl-time-input\"\u003e\n    \u003cp class=\"sl-hint\"\u003eWe'll calculate the best bedtimes based on 90-min sleep cycles.\u003c/p\u003e","title":"Sleep Cycle Calculator"},{"content":" Enter your ideal sleep hours and how much you actually slept each night this week. The calculator will show your accumulated sleep debt and how long recovery will take. Step 1: Your Ideal Sleep Ideal hours of sleep per night 8.0 hrs Most adults need 7–9 hours. Adults over 65 often need 7–8 hours.\nStep 2: This Week's Sleep Log Enter hours slept (decimals OK, e.g. 6.5)\nMon Tue Wed Thu Fri Sat Sun Calculate My Sleep Debt Your Results \u0026lt;div class=\u0026quot;sd-summary-grid\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;sd-stat\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;sd-stat-val\u0026quot; id=\u0026quot;sd-total-debt\u0026quot;\u0026gt;0.0\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sd-stat-lbl\u0026quot;\u0026gt;Weekly Sleep Debt (hrs)\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sd-stat\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;sd-stat-val\u0026quot; id=\u0026quot;sd-avg-actual\u0026quot;\u0026gt;0.0\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sd-stat-lbl\u0026quot;\u0026gt;Avg Actual Sleep (hrs/night)\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sd-stat\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;sd-stat-val\u0026quot; id=\u0026quot;sd-monthly-debt\u0026quot;\u0026gt;0.0\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sd-stat-lbl\u0026quot;\u0026gt;Projected Monthly Debt (hrs)\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sd-stat\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;sd-stat-val\u0026quot; id=\u0026quot;sd-recovery-days\u0026quot;\u0026gt;0\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sd-stat-lbl\u0026quot;\u0026gt;Recovery Days Needed\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Bar chart --\u0026gt; \u0026lt;h3 style=\u0026quot;font-size:0.95rem;font-weight:700;color:#374151;margin:0 0 0.5rem\u0026quot;\u0026gt;Ideal vs. Actual Sleep per Night\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;sd-legend\u0026quot;\u0026gt; \u0026lt;span\u0026gt;\u0026lt;span class=\u0026quot;sd-legend-dot\u0026quot; style=\u0026quot;background:#c7d2fe\u0026quot;\u0026gt;\u0026lt;/span\u0026gt;Ideal\u0026lt;/span\u0026gt; \u0026lt;span\u0026gt;\u0026lt;span class=\u0026quot;sd-legend-dot\u0026quot; style=\u0026quot;background:#4f46e5\u0026quot;\u0026gt;\u0026lt;/span\u0026gt;Actual\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sd-chart-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;sd-chart\u0026quot; id=\u0026quot;sd-chart\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Recovery Plan Tips to Reduce Sleep Debt Related Free Tools Pomodoro Timer - Focus in 25-minute blocks Deep Work Planner - Schedule your peak focus hours Energy Level Tracker - Map your daily energy curve Related Tools Pomodoro Timer Sleep Calculator ","permalink":"https://productivity-works.com/tools/sleep-debt-calculator/","summary":"\u003cdiv id=\"sd-app\"\u003e\n\u003cstyle\u003e\n#sd-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 760px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#sd-app h2 {\n  font-size: 1.4rem;\n  font-weight: 700;\n  margin: 1.6rem 0 0.8rem;\n  color: #1a1a2e;\n}\n#sd-app .sd-intro {\n  background: #eef2ff;\n  border-left: 4px solid #4f46e5;\n  border-radius: 6px;\n  padding: 1rem 1.2rem;\n  margin-bottom: 1.6rem;\n  font-size: 0.95rem;\n  color: #3730a3;\n}\n#sd-app .sd-card {\n  background: #fff;\n  border: 1px solid #e5e7eb;\n  border-radius: 12px;\n  padding: 1.4rem 1.6rem;\n  margin-bottom: 1.2rem;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.06);\n}\n#sd-app label {\n  display: block;\n  font-size: 0.88rem;\n  font-weight: 600;\n  color: #374151;\n  margin-bottom: 0.3rem;\n}\n#sd-app .sd-ideal-row {\n  display: flex;\n  align-items: center;\n  gap: 1rem;\n}\n#sd-app .sd-ideal-row input[type=range] {\n  flex: 1;\n  accent-color: #4f46e5;\n}\n#sd-app .sd-ideal-val {\n  font-size: 1.3rem;\n  font-weight: 700;\n  color: #4f46e5;\n  min-width: 3.5rem;\n  text-align: center;\n}\n#sd-app .sd-days-grid {\n  display: grid;\n  grid-template-columns: repeat(7, 1fr);\n  gap: 0.6rem;\n}\n@media (max-width: 540px) {\n  #sd-app .sd-days-grid {\n    grid-template-columns: repeat(4, 1fr);\n  }\n}\n#sd-app .sd-day-cell {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 0.3rem;\n}\n#sd-app .sd-day-cell span {\n  font-size: 0.78rem;\n  font-weight: 600;\n  color: #6b7280;\n}\n#sd-app .sd-day-cell input[type=number] {\n  width: 100%;\n  padding: 0.4rem 0.2rem;\n  border: 1px solid #d1d5db;\n  border-radius: 6px;\n  font-size: 1rem;\n  text-align: center;\n  outline: none;\n  transition: border-color 0.2s;\n}\n#sd-app .sd-day-cell input[type=number]:focus {\n  border-color: #4f46e5;\n  box-shadow: 0 0 0 2px rgba(79,70,229,0.15);\n}\n#sd-app .sd-btn {\n  display: block;\n  width: 100%;\n  padding: 0.85rem;\n  background: #4f46e5;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 1.05rem;\n  font-weight: 700;\n  cursor: pointer;\n  margin-top: 1rem;\n  transition: background 0.2s;\n}\n#sd-app .sd-btn:hover { background: #4338ca; }\n#sd-app .sd-result {\n  display: none;\n}\n#sd-app .sd-result.visible { display: block; }\n#sd-app .sd-summary-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n  gap: 0.8rem;\n  margin-bottom: 1.2rem;\n}\n#sd-app .sd-stat {\n  background: #f9fafb;\n  border: 1px solid #e5e7eb;\n  border-radius: 10px;\n  padding: 0.9rem 1rem;\n  text-align: center;\n}\n#sd-app .sd-stat .sd-stat-val {\n  font-size: 1.8rem;\n  font-weight: 800;\n  color: #4f46e5;\n  line-height: 1.1;\n}\n#sd-app .sd-stat .sd-stat-lbl {\n  font-size: 0.78rem;\n  color: #6b7280;\n  margin-top: 0.3rem;\n}\n#sd-app .sd-badge {\n  display: inline-block;\n  padding: 0.35rem 0.9rem;\n  border-radius: 999px;\n  font-size: 0.9rem;\n  font-weight: 700;\n  margin-bottom: 1rem;\n}\n#sd-app .sd-badge.none    { background: #d1fae5; color: #065f46; }\n#sd-app .sd-badge.mild    { background: #fef3c7; color: #92400e; }\n#sd-app .sd-badge.moderate{ background: #ffe4e6; color: #9f1239; }\n#sd-app .sd-badge.severe  { background: #fce7f3; color: #831843; }\n#sd-app .sd-chart-wrap {\n  overflow-x: auto;\n  margin-bottom: 1.2rem;\n}\n#sd-app .sd-chart {\n  display: flex;\n  align-items: flex-end;\n  gap: 0.5rem;\n  height: 160px;\n  padding: 0.5rem 0.2rem 0;\n  min-width: 320px;\n}\n#sd-app .sd-bar-group {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 2px;\n  height: 100%;\n  justify-content: flex-end;\n}\n#sd-app .sd-bars {\n  display: flex;\n  align-items: flex-end;\n  gap: 3px;\n  width: 100%;\n  height: 130px;\n}\n#sd-app .sd-bar {\n  flex: 1;\n  border-radius: 4px 4px 0 0;\n  min-height: 4px;\n  transition: height 0.4s ease;\n}\n#sd-app .sd-bar.ideal  { background: #c7d2fe; }\n#sd-app .sd-bar.actual { background: #4f46e5; }\n#sd-app .sd-bar-lbl {\n  font-size: 0.7rem;\n  color: #6b7280;\n  margin-top: 4px;\n}\n#sd-app .sd-legend {\n  display: flex;\n  gap: 1rem;\n  font-size: 0.8rem;\n  color: #374151;\n  margin-bottom: 0.8rem;\n}\n#sd-app .sd-legend-dot {\n  width: 12px; height: 12px;\n  border-radius: 3px;\n  display: inline-block;\n  margin-right: 4px;\n  vertical-align: middle;\n}\n#sd-app .sd-recovery {\n  background: #f0fdf4;\n  border: 1px solid #bbf7d0;\n  border-radius: 10px;\n  padding: 1rem 1.2rem;\n  margin-bottom: 1.2rem;\n}\n#sd-app .sd-recovery h3 {\n  font-size: 1rem;\n  font-weight: 700;\n  color: #166534;\n  margin: 0 0 0.4rem;\n}\n#sd-app .sd-recovery p {\n  font-size: 0.9rem;\n  color: #166534;\n  margin: 0;\n}\n#sd-app .sd-tips { list-style: none; padding: 0; margin: 0; }\n#sd-app .sd-tips li {\n  padding: 0.5rem 0;\n  border-bottom: 1px solid #f3f4f6;\n  font-size: 0.9rem;\n  color: #374151;\n  padding-left: 1.4rem;\n  position: relative;\n}\n#sd-app .sd-tips li:last-child { border-bottom: none; }\n#sd-app .sd-tips li::before {\n  content: \"•\";\n  color: #4f46e5;\n  font-weight: 700;\n  position: absolute;\n  left: 0.3rem;\n}\n#sd-app .sd-related {\n  background: #f9fafb;\n  border: 1px solid #e5e7eb;\n  border-radius: 10px;\n  padding: 1rem 1.2rem;\n  margin-top: 1.6rem;\n}\n#sd-app .sd-related h3 {\n  font-size: 0.95rem;\n  font-weight: 700;\n  color: #374151;\n  margin: 0 0 0.5rem;\n}\n#sd-app .sd-related ul {\n  list-style: none;\n  padding: 0; margin: 0;\n}\n#sd-app .sd-related ul li {\n  padding: 0.3rem 0;\n  font-size: 0.88rem;\n}\n#sd-app .sd-related ul li a {\n  color: #4f46e5;\n  text-decoration: none;\n}\n#sd-app .sd-related ul li a:hover { text-decoration: underline; }\n\u003c/style\u003e\n\u003cdiv class=\"sd-intro\"\u003e\nEnter your ideal sleep hours and how much you actually slept each night this week. The calculator will show your accumulated sleep debt and how long recovery will take.\n\u003c/div\u003e\n\u003c!-- Ideal Sleep --\u003e\n\u003cdiv class=\"sd-card\"\u003e\n  \u003ch2 style=\"margin-top:0\"\u003eStep 1: Your Ideal Sleep\u003c/h2\u003e\n  \u003clabel for=\"sd-ideal\"\u003eIdeal hours of sleep per night\u003c/label\u003e\n  \u003cdiv class=\"sd-ideal-row\"\u003e\n    \u003cinput type=\"range\" id=\"sd-ideal\" min=\"6\" max=\"10\" step=\"0.5\" value=\"8\" oninput=\"sdUpdateIdeal(this.value)\"\u003e\n    \u003cdiv class=\"sd-ideal-val\" id=\"sd-ideal-display\"\u003e8.0 hrs\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cp style=\"font-size:0.8rem;color:#6b7280;margin:0.5rem 0 0\"\u003eMost adults need 7–9 hours. Adults over 65 often need 7–8 hours.\u003c/p\u003e","title":"Sleep Debt Calculator - Track Your Sleep Deficit"},{"content":" Social Media Image Resizer Resize and crop images for any platform — Instagram, Facebook, Twitter/X, LinkedIn, YouTube, Pinterest. Pick a preset, drag to reposition, and download. No upload to servers. All processing happens in your browser.\n🖼️ Drop an image here or click to upload JPG, PNG, WebP, GIF, BMP — up to 30 MB Platform\nInstagram Facebook Twitter / X LinkedIn YouTube Pinterest Fit Mode Cover (crop to fill) Contain (letterbox) Stretch (distort) Cover crops the image to exactly fill the frame. Contain adds padding bars. Stretch distorts to fit. Background Color (for Contain mode) Used only when Contain mode leaves empty areas. Preview — drag to reposition drag to reposition — — KB ⬇ Download Image PNG JPEG How to Use STEP 1 — Upload your image: Drag and drop any image onto the upload area, or click to browse. JPG, PNG, WebP, GIF, and BMP up to 30 MB are supported.\nSTEP 2 — Choose a platform: Click a platform tab (Instagram, Facebook, Twitter/X, LinkedIn, YouTube, Pinterest) and then select the specific format from the preset grid.\nSTEP 3 — Pick a fit mode: Cover crops the image to fill the frame exactly — drag the preview to reposition. Contain adds letterbox bars using your chosen background color. Stretch distorts the image to fill the frame.\nSTEP 4 — Adjust background color: In Contain mode, select the bar color using the color picker or by typing a hex value.\nSTEP 5 — Download: Choose PNG or JPEG, then click Download Image. The file is named automatically with the platform, format name, and dimensions.\nPlatform Image Size Reference (2025) Platform Format Size Instagram Post 1080 × 1080 Instagram Story / Reel 1080 × 1920 Instagram Profile 320 × 320 Facebook Post 1200 × 630 Facebook Cover 820 × 312 Facebook Story 1080 × 1920 Twitter / X Post 1600 × 900 Twitter / X Header 1500 × 500 LinkedIn Post 1200 × 627 LinkedIn Cover 1584 × 396 YouTube Thumbnail 1280 × 720 YouTube Channel Art 2560 × 1440 Pinterest Pin 1000 × 1500 Fit Mode Explained Cover scales the image up so that both dimensions meet or exceed the target size, then crops the overflow. You can drag the preview to control which part is kept. This is the best choice for most social media images.\nContain scales the image down so it fits entirely within the target frame, adding bars (in your chosen background color) to fill the remaining space. Use this when you need the full image visible with no cropping.\nStretch forces the image to exactly match the target dimensions, which may distort the proportions. Useful when your source image is already close in shape to the target.\nRelated Tools Resize images to any custom dimension → Image Resizer Generate placeholder images at exact sizes → Placeholder Image Generator ","permalink":"https://productivity-works.com/tools/social-media-image-resizer/","summary":"\u003cdiv id=\"sr-app\"\u003e\n\u003cstyle\u003e\n#sr-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 0 0 48px 0;\n  color: #1a202c;\n}\n\n#sr-app * {\n  box-sizing: border-box;\n}\n\n/* Hero */\n.sr-hero {\n  background: linear-gradient(135deg, #7c3aed 0%, #5b21b6 50%, #4c1d95 100%);\n  border-radius: 16px;\n  padding: 32px 28px;\n  margin-bottom: 24px;\n  color: #fff;\n}\n\n.sr-hero h2 {\n  margin: 0 0 6px 0;\n  font-size: 24px;\n  font-weight: 800;\n}\n\n.sr-hero p {\n  margin: 0;\n  font-size: 14px;\n  opacity: 0.88;\n  line-height: 1.6;\n}\n\n/* Upload area */\n.sr-upload {\n  border: 3px dashed #7c3aed;\n  border-radius: 14px;\n  padding: 40px 24px;\n  text-align: center;\n  background: #f5f3ff;\n  cursor: pointer;\n  transition: background 0.2s, border-color 0.2s;\n  margin-bottom: 20px;\n  position: relative;\n}\n\n.sr-upload:hover, .sr-upload.drag-over {\n  background: #ede9fe;\n  border-color: #5b21b6;\n}\n\n.sr-upload-icon {\n  font-size: 48px;\n  line-height: 1;\n  margin-bottom: 10px;\n  display: block;\n}\n\n.sr-upload-title {\n  font-size: 17px;\n  font-weight: 700;\n  color: #4c1d95;\n  margin-bottom: 4px;\n}\n\n.sr-upload-sub {\n  font-size: 13px;\n  color: #6b7280;\n}\n\n#sr-file-input {\n  position: absolute;\n  inset: 0;\n  opacity: 0;\n  cursor: pointer;\n  width: 100%;\n  height: 100%;\n}\n\n/* Platform tabs */\n.sr-platforms {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 16px;\n}\n\n.sr-platform-btn {\n  padding: 8px 14px;\n  border-radius: 8px;\n  border: 2px solid #e5e7eb;\n  background: #fff;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n  color: #374151;\n}\n\n.sr-platform-btn:hover {\n  border-color: #7c3aed;\n  color: #7c3aed;\n}\n\n.sr-platform-btn.active {\n  border-color: #7c3aed;\n  background: #7c3aed;\n  color: #fff;\n}\n\n/* Presets grid */\n.sr-presets {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));\n  gap: 8px;\n  margin-bottom: 20px;\n}\n\n.sr-preset-btn {\n  padding: 10px 12px;\n  border-radius: 10px;\n  border: 2px solid #e5e7eb;\n  background: #fff;\n  cursor: pointer;\n  text-align: left;\n  transition: all 0.15s;\n}\n\n.sr-preset-btn:hover {\n  border-color: #7c3aed;\n  background: #f5f3ff;\n}\n\n.sr-preset-btn.active {\n  border-color: #7c3aed;\n  background: #ede9fe;\n}\n\n.sr-preset-name {\n  font-size: 12px;\n  font-weight: 700;\n  color: #1a202c;\n  display: block;\n  margin-bottom: 2px;\n}\n\n.sr-preset-dim {\n  font-size: 11px;\n  color: #6b7280;\n}\n\n/* Controls row */\n.sr-controls {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 16px;\n  margin-bottom: 20px;\n}\n\n@media (max-width: 540px) {\n  .sr-controls { grid-template-columns: 1fr; }\n}\n\n.sr-control-group {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n}\n\n.sr-label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #374151;\n}\n\n.sr-select, .sr-color-input {\n  padding: 8px 12px;\n  border-radius: 8px;\n  border: 2px solid #e5e7eb;\n  font-size: 14px;\n  background: #fff;\n  color: #1a202c;\n  transition: border-color 0.15s;\n  width: 100%;\n}\n\n.sr-select:focus, .sr-color-input:focus {\n  outline: none;\n  border-color: #7c3aed;\n}\n\n.sr-color-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n\n.sr-color-swatch {\n  width: 36px;\n  height: 36px;\n  border-radius: 8px;\n  border: 2px solid #e5e7eb;\n  cursor: pointer;\n  padding: 2px;\n}\n\n/* Canvas preview */\n.sr-preview-wrap {\n  margin-bottom: 20px;\n  background: #f9fafb;\n  border-radius: 14px;\n  padding: 16px;\n  display: none;\n}\n\n.sr-preview-wrap.visible { display: block; }\n\n.sr-preview-label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #6b7280;\n  margin-bottom: 10px;\n}\n\n.sr-canvas-container {\n  position: relative;\n  display: inline-block;\n  cursor: move;\n  border-radius: 8px;\n  overflow: hidden;\n  max-width: 100%;\n  border: 2px solid #e5e7eb;\n  background: #fff;\n  touch-action: none;\n}\n\n#sr-canvas {\n  display: block;\n  max-width: 100%;\n  height: auto;\n}\n\n/* Info bar */\n.sr-info-bar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 12px;\n  align-items: center;\n  margin-bottom: 20px;\n  font-size: 13px;\n  color: #6b7280;\n}\n\n.sr-info-badge {\n  background: #f3f4f6;\n  border-radius: 6px;\n  padding: 4px 10px;\n  font-weight: 600;\n  color: #374151;\n}\n\n/* Download buttons */\n.sr-dl-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  align-items: center;\n}\n\n.sr-dl-btn {\n  padding: 12px 24px;\n  border-radius: 10px;\n  border: none;\n  font-size: 15px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: all 0.15s;\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n}\n\n.sr-dl-btn.primary {\n  background: #7c3aed;\n  color: #fff;\n}\n\n.sr-dl-btn.primary:hover {\n  background: #5b21b6;\n}\n\n.sr-dl-btn.primary:disabled {\n  background: #c4b5fd;\n  cursor: not-allowed;\n}\n\n.sr-dl-btn.secondary {\n  background: #f3f4f6;\n  color: #374151;\n  font-size: 13px;\n  padding: 10px 16px;\n}\n\n.sr-dl-btn.secondary:hover {\n  background: #e5e7eb;\n}\n\n.sr-fmt-toggle {\n  display: flex;\n  gap: 6px;\n  margin-left: auto;\n}\n\n.sr-fmt-btn {\n  padding: 8px 14px;\n  border-radius: 8px;\n  border: 2px solid #e5e7eb;\n  background: #fff;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  color: #374151;\n  transition: all 0.15s;\n}\n\n.sr-fmt-btn.active {\n  border-color: #7c3aed;\n  background: #7c3aed;\n  color: #fff;\n}\n\n/* Section heading */\n.sr-section-title {\n  font-size: 14px;\n  font-weight: 700;\n  color: #374151;\n  margin: 0 0 10px 0;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n\n/* Hint */\n.sr-hint {\n  font-size: 12px;\n  color: #9ca3af;\n  margin-top: 8px;\n  line-height: 1.5;\n}\n\n/* Drag hint overlay */\n.sr-drag-hint {\n  position: absolute;\n  bottom: 8px;\n  right: 8px;\n  background: rgba(0,0,0,0.55);\n  color: #fff;\n  font-size: 11px;\n  padding: 3px 8px;\n  border-radius: 6px;\n  pointer-events: none;\n}\n\u003c/style\u003e\n\u003cdiv class=\"sr-hero\"\u003e\n  \u003ch2\u003eSocial Media Image Resizer\u003c/h2\u003e\n  \u003cp\u003eResize and crop images for any platform — Instagram, Facebook, Twitter/X, LinkedIn, YouTube, Pinterest. Pick a preset, drag to reposition, and download. No upload to servers. All processing happens in your browser.\u003c/p\u003e","title":"Social Media Image Resizer"},{"content":" Bill Details Total Bill Amount ($) Number of People Tip Percentage No tip (0%) 10% 15% 18% 20% Custom… Custom Tip (%) Tax Amount ($) (optional) Please enter a valid bill amount greater than 0.\nSplit Mode Equal Split Custom Split Enter each person's share amount. Remaining balance is shown below. + Add Person Custom amounts exceed the total bill.\nCalculate Split Reset Summary Total Bill — Tip Total — Tax Total — Grand Total — \u0026lt;h2\u0026gt;Per Person Breakdown\u0026lt;/h2\u0026gt; \u0026lt;div style=\u0026quot;overflow-x:auto;\u0026quot;\u0026gt; \u0026lt;table class=\u0026quot;sb-breakdown-table\u0026quot;\u0026gt; \u0026lt;thead\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;th\u0026gt;Person\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;Bill Share\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;Tip\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;Tax\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;Total\u0026lt;/th\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;/thead\u0026gt; \u0026lt;tbody id=\u0026quot;res-table-body\u0026quot;\u0026gt;\u0026lt;/tbody\u0026gt; \u0026lt;/table\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sb-reset-wrap\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;sb-reset-link\u0026quot; onclick=\u0026quot;sbReset()\u0026quot;\u0026gt;Start over\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; Calculate tips \u0026rarr; Tip Calculator\nPlan your budget \u0026rarr; 50/30/20 Budget Calculator\nCalculate percentages \u0026rarr; Percentage Calculator ","permalink":"https://productivity-works.com/tools/split-bill-calculator/","summary":"\u003cdiv id=\"sb-app\"\u003e\n\u003cstyle\u003e\n#sb-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  max-width: 680px;\n  margin: 0 auto;\n  color: #e2e8f0;\n}\n\n#sb-app *,\n#sb-app *::before,\n#sb-app *::after {\n  box-sizing: border-box;\n}\n\n#sb-app h2 {\n  font-size: 1.15rem;\n  font-weight: 700;\n  color: #94a3b8;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  margin: 0 0 1rem 0;\n  padding-bottom: 0.5rem;\n  border-bottom: 1px solid #334155;\n}\n\n/* Cards */\n.sb-card {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 12px;\n  padding: 1.5rem;\n  margin-bottom: 1.25rem;\n}\n\n/* Form rows */\n.sb-row {\n  display: flex;\n  gap: 1rem;\n  margin-bottom: 1rem;\n  flex-wrap: wrap;\n}\n\n.sb-field {\n  display: flex;\n  flex-direction: column;\n  gap: 0.35rem;\n  flex: 1 1 180px;\n}\n\n.sb-field label {\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #94a3b8;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n\n.sb-field input[type=\"number\"],\n.sb-field input[type=\"text\"],\n.sb-field select {\n  background: #0f172a;\n  border: 1px solid #475569;\n  border-radius: 8px;\n  color: #e2e8f0;\n  font-size: 1rem;\n  padding: 0.55rem 0.75rem;\n  width: 100%;\n  transition: border-color 0.15s;\n  appearance: none;\n  -webkit-appearance: none;\n}\n\n.sb-field input[type=\"number\"]:focus,\n.sb-field input[type=\"text\"]:focus,\n.sb-field select:focus {\n  outline: none;\n  border-color: #38bdf8;\n}\n\n.sb-field select {\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2394a3b8' d='M6 8L1 3h10z'/%3E%3C/svg%3E\");\n  background-repeat: no-repeat;\n  background-position: right 0.75rem center;\n  padding-right: 2rem;\n}\n\n/* Custom tip row */\n#sb-custom-tip-wrap {\n  display: none;\n}\n#sb-custom-tip-wrap.visible {\n  display: flex;\n}\n\n/* Mode toggle */\n.sb-toggle {\n  display: flex;\n  background: #0f172a;\n  border: 1px solid #334155;\n  border-radius: 8px;\n  overflow: hidden;\n  margin-bottom: 1.25rem;\n  width: 100%;\n}\n\n.sb-toggle button {\n  flex: 1;\n  padding: 0.6rem 1rem;\n  border: none;\n  background: transparent;\n  color: #94a3b8;\n  font-size: 0.9rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s;\n}\n\n.sb-toggle button.active {\n  background: #38bdf8;\n  color: #0f172a;\n}\n\n/* Custom split table */\n#sb-people-list {\n  display: flex;\n  flex-direction: column;\n  gap: 0.6rem;\n  margin-bottom: 1rem;\n}\n\n.sb-person-row {\n  display: flex;\n  gap: 0.6rem;\n  align-items: center;\n}\n\n.sb-person-row input[type=\"text\"] {\n  background: #0f172a;\n  border: 1px solid #475569;\n  border-radius: 8px;\n  color: #e2e8f0;\n  font-size: 0.95rem;\n  padding: 0.5rem 0.65rem;\n  flex: 1 1 120px;\n}\n\n.sb-person-row input[type=\"number\"] {\n  background: #0f172a;\n  border: 1px solid #475569;\n  border-radius: 8px;\n  color: #e2e8f0;\n  font-size: 0.95rem;\n  padding: 0.5rem 0.65rem;\n  width: 110px;\n}\n\n.sb-person-row input:focus {\n  outline: none;\n  border-color: #38bdf8;\n}\n\n.sb-person-row button.sb-remove {\n  background: #334155;\n  border: none;\n  border-radius: 6px;\n  color: #94a3b8;\n  font-size: 1rem;\n  width: 32px;\n  height: 32px;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  flex-shrink: 0;\n  transition: background 0.15s, color 0.15s;\n}\n\n.sb-person-row button.sb-remove:hover {\n  background: #dc2626;\n  color: #fff;\n}\n\n/* Buttons */\n.sb-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 0.4rem;\n  background: #334155;\n  border: none;\n  border-radius: 8px;\n  color: #e2e8f0;\n  font-size: 0.9rem;\n  font-weight: 600;\n  padding: 0.55rem 1rem;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n\n.sb-btn:hover {\n  background: #475569;\n}\n\n.sb-btn.primary {\n  background: #0ea5e9;\n  color: #fff;\n}\n\n.sb-btn.primary:hover {\n  background: #38bdf8;\n  color: #0f172a;\n}\n\n.sb-btn-row {\n  display: flex;\n  gap: 0.75rem;\n  flex-wrap: wrap;\n  margin-top: 0.5rem;\n}\n\n/* Error */\n.sb-error {\n  color: #f87171;\n  font-size: 0.85rem;\n  margin-top: 0.25rem;\n  display: none;\n}\n\n.sb-error.visible {\n  display: block;\n}\n\n/* Results */\n#sb-results {\n  display: none;\n}\n\n#sb-results.visible {\n  display: block;\n}\n\n.sb-summary-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));\n  gap: 0.75rem;\n  margin-bottom: 1.25rem;\n}\n\n.sb-stat {\n  background: #0f172a;\n  border: 1px solid #334155;\n  border-radius: 10px;\n  padding: 0.85rem 1rem;\n  text-align: center;\n}\n\n.sb-stat .stat-label {\n  font-size: 0.75rem;\n  font-weight: 600;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 0.3rem;\n}\n\n.sb-stat .stat-value {\n  font-size: 1.4rem;\n  font-weight: 700;\n  color: #38bdf8;\n}\n\n.sb-stat .stat-value.accent-green {\n  color: #34d399;\n}\n\n.sb-stat .stat-value.accent-amber {\n  color: #fbbf24;\n}\n\n/* Per-person breakdown */\n.sb-breakdown-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.92rem;\n}\n\n.sb-breakdown-table th {\n  text-align: left;\n  padding: 0.5rem 0.75rem;\n  color: #64748b;\n  font-weight: 600;\n  font-size: 0.78rem;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  border-bottom: 1px solid #334155;\n}\n\n.sb-breakdown-table th:not(:first-child),\n.sb-breakdown-table td:not(:first-child) {\n  text-align: right;\n}\n\n.sb-breakdown-table td {\n  padding: 0.55rem 0.75rem;\n  border-bottom: 1px solid #1e293b;\n  color: #cbd5e1;\n}\n\n.sb-breakdown-table tr:last-child td {\n  border-bottom: none;\n}\n\n.sb-breakdown-table tr:hover td {\n  background: #1e293b;\n}\n\n.sb-breakdown-table td.person-name {\n  font-weight: 600;\n  color: #e2e8f0;\n}\n\n.sb-breakdown-table td.person-total {\n  font-weight: 700;\n  color: #38bdf8;\n}\n\n/* Reset link */\n.sb-reset-wrap {\n  text-align: center;\n  margin-top: 1rem;\n}\n\n.sb-reset-link {\n  color: #64748b;\n  font-size: 0.85rem;\n  cursor: pointer;\n  background: none;\n  border: none;\n  text-decoration: underline;\n  padding: 0;\n}\n\n.sb-reset-link:hover {\n  color: #94a3b8;\n}\n\n/* Cross-links */\n.sb-crosslinks {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 10px;\n  padding: 1rem 1.25rem;\n  margin-top: 1.5rem;\n  font-size: 0.9rem;\n  color: #94a3b8;\n  line-height: 2;\n}\n\n.sb-crosslinks a {\n  color: #38bdf8;\n  text-decoration: none;\n}\n\n.sb-crosslinks a:hover {\n  text-decoration: underline;\n}\n\n@media (max-width: 480px) {\n  .sb-card {\n    padding: 1.1rem;\n  }\n  .sb-field {\n    flex: 1 1 100%;\n  }\n  .sb-stat .stat-value {\n    font-size: 1.2rem;\n  }\n}\n\u003c/style\u003e\n\u003c!-- ===== INPUTS ===== --\u003e\n\u003cdiv class=\"sb-card\"\u003e\n  \u003ch2\u003eBill Details\u003c/h2\u003e\n  \u003cdiv class=\"sb-row\"\u003e\n    \u003cdiv class=\"sb-field\"\u003e\n      \u003clabel for=\"sb-total\"\u003eTotal Bill Amount ($)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"sb-total\" min=\"0\" step=\"0.01\" placeholder=\"e.g. 120.00\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"sb-field\"\u003e\n      \u003clabel for=\"sb-people\"\u003eNumber of People\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"sb-people\" min=\"1\" max=\"50\" step=\"1\" value=\"2\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"sb-row\"\u003e\n    \u003cdiv class=\"sb-field\"\u003e\n      \u003clabel for=\"sb-tip-select\"\u003eTip Percentage\u003c/label\u003e\n      \u003cselect id=\"sb-tip-select\"\u003e\n        \u003coption value=\"0\"\u003eNo tip (0%)\u003c/option\u003e\n        \u003coption value=\"10\"\u003e10%\u003c/option\u003e\n        \u003coption value=\"15\" selected\u003e15%\u003c/option\u003e\n        \u003coption value=\"18\"\u003e18%\u003c/option\u003e\n        \u003coption value=\"20\"\u003e20%\u003c/option\u003e\n        \u003coption value=\"custom\"\u003eCustom…\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"sb-field\" id=\"sb-custom-tip-wrap\"\u003e\n      \u003clabel for=\"sb-tip-custom\"\u003eCustom Tip (%)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"sb-tip-custom\" min=\"0\" max=\"100\" step=\"0.1\" placeholder=\"e.g. 12\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"sb-row\"\u003e\n    \u003cdiv class=\"sb-field\"\u003e\n      \u003clabel for=\"sb-tax\"\u003eTax Amount ($) \u003cspan style=\"color:#64748b;font-weight:400;\"\u003e(optional)\u003c/span\u003e\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"sb-tax\" min=\"0\" step=\"0.01\" placeholder=\"0.00\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"sb-field\" style=\"justify-content:flex-end;\"\u003e\n      \u003c!-- spacer --\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cp id=\"sb-input-error\" class=\"sb-error\"\u003ePlease enter a valid bill amount greater than 0.\u003c/p\u003e","title":"Split Bill Calculator"},{"content":"Paste your SQL and get it back formatted with proper indentation, uppercased keywords, and optional syntax highlighting — all running locally in your browser. No data is ever sent to a server.\nSQL Formatter Format SQL Minify Clear Syntax highlight Tab size: 2 spaces 4 spaces Input SQL Formatted Output Copy Related: Format JSON — JSON Formatter ","permalink":"https://productivity-works.com/tools/sql-formatter/","summary":"\u003cp\u003ePaste your SQL and get it back formatted with proper indentation, uppercased keywords, and optional syntax highlighting — all running locally in your browser. No data is ever sent to a server.\u003c/p\u003e\n\u003cstyle\u003e\n#sql-app *,\n#sql-app *::before,\n#sql-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#sql-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  background: #1a1a2e;\n  color: #e2e8f0;\n  border-radius: 12px;\n  padding: 24px;\n  max-width: 900px;\n  margin: 0 auto 2rem auto;\n}\n#sql-app h2 {\n  font-size: 1.1rem;\n  color: #93c5fd;\n  margin-bottom: 12px;\n  letter-spacing: 0.03em;\n}\n#sql-app .sql-controls {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  margin-bottom: 16px;\n  align-items: center;\n}\n#sql-app .sql-btn {\n  background: #2563eb;\n  color: #fff;\n  border: none;\n  border-radius: 6px;\n  padding: 8px 18px;\n  font-size: 0.92rem;\n  cursor: pointer;\n  transition: background 0.15s;\n  font-weight: 600;\n}\n#sql-app .sql-btn:hover { background: #1d4ed8; }\n#sql-app .sql-btn.secondary {\n  background: #374151;\n  color: #e2e8f0;\n}\n#sql-app .sql-btn.secondary:hover { background: #4b5563; }\n#sql-app .sql-btn.success {\n  background: #059669;\n}\n#sql-app .sql-btn.success:hover { background: #047857; }\n#sql-app .sql-label {\n  font-size: 0.85rem;\n  color: #94a3b8;\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n#sql-app .sql-label select {\n  background: #0f172a;\n  color: #e2e8f0;\n  border: 1px solid #334155;\n  border-radius: 5px;\n  padding: 5px 8px;\n  font-size: 0.88rem;\n}\n#sql-app .sql-label input[type=\"checkbox\"] {\n  accent-color: #60a5fa;\n  width: 15px;\n  height: 15px;\n}\n#sql-app .sql-pane {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 14px;\n  margin-bottom: 14px;\n}\n@media (max-width: 620px) {\n  #sql-app .sql-pane { grid-template-columns: 1fr; }\n}\n#sql-app .sql-section label {\n  display: block;\n  font-size: 0.78rem;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #64748b;\n  margin-bottom: 6px;\n}\n#sql-app textarea {\n  width: 100%;\n  height: 220px;\n  background: #0f172a;\n  color: #e2e8f0;\n  border: 1px solid #334155;\n  border-radius: 8px;\n  padding: 12px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 0.85rem;\n  resize: vertical;\n  line-height: 1.6;\n  outline: none;\n  transition: border-color 0.15s;\n}\n#sql-app textarea:focus { border-color: #60a5fa; }\n#sql-app .sql-output-wrap {\n  position: relative;\n}\n#sql-app .sql-output {\n  width: 100%;\n  min-height: 220px;\n  background: #0f172a;\n  border: 1px solid #334155;\n  border-radius: 8px;\n  padding: 12px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 0.85rem;\n  line-height: 1.6;\n  white-space: pre;\n  overflow: auto;\n  color: #e2e8f0;\n}\n#sql-app .sql-copy-btn {\n  position: absolute;\n  top: 8px;\n  right: 8px;\n  background: #1e293b;\n  color: #94a3b8;\n  border: 1px solid #334155;\n  border-radius: 5px;\n  padding: 4px 10px;\n  font-size: 0.78rem;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#sql-app .sql-copy-btn:hover { background: #334155; color: #e2e8f0; }\n#sql-app .sql-status {\n  font-size: 0.82rem;\n  color: #4ade80;\n  min-height: 1.2em;\n  margin-top: 4px;\n}\n/* Syntax highlight classes */\n#sql-app .kw  { color: #60a5fa; font-weight: 700; }\n#sql-app .str { color: #4ade80; }\n#sql-app .num { color: #fb923c; }\n#sql-app .cmt { color: #6b7280; font-style: italic; }\n\u003c/style\u003e\n\u003cdiv id=\"sql-app\"\u003e\n  \u003ch2\u003eSQL Formatter\u003c/h2\u003e\n  \u003cdiv class=\"sql-controls\"\u003e\n    \u003cbutton class=\"sql-btn\" id=\"sql-format-btn\"\u003eFormat SQL\u003c/button\u003e\n    \u003cbutton class=\"sql-btn secondary\" id=\"sql-minify-btn\"\u003eMinify\u003c/button\u003e\n    \u003cbutton class=\"sql-btn secondary\" id=\"sql-clear-btn\"\u003eClear\u003c/button\u003e\n    \u003clabel class=\"sql-label\"\u003e\n      \u003cinput type=\"checkbox\" id=\"sql-highlight-chk\" checked\u003e\n      Syntax highlight\n    \u003c/label\u003e\n    \u003clabel class=\"sql-label\"\u003e\n      Tab size:\n      \u003cselect id=\"sql-tabsize-sel\"\u003e\n        \u003coption value=\"2\"\u003e2 spaces\u003c/option\u003e\n        \u003coption value=\"4\" selected\u003e4 spaces\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/label\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"sql-pane\"\u003e\n    \u003cdiv class=\"sql-section\"\u003e\n      \u003clabel\u003eInput SQL\u003c/label\u003e\n      \u003ctextarea id=\"sql-input\" placeholder=\"Paste your SQL here…\" spellcheck=\"false\"\u003e\u003c/textarea\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"sql-section\"\u003e\n      \u003clabel\u003eFormatted Output\u003c/label\u003e\n      \u003cdiv class=\"sql-output-wrap\"\u003e\n        \u003cdiv class=\"sql-output\" id=\"sql-output\"\u003e\u003c/div\u003e\n        \u003cbutton class=\"sql-copy-btn\" id=\"sql-copy-btn\"\u003eCopy\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"sql-status\" id=\"sql-status\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  'use strict';\n\n  const KEYWORDS = [\n    'SELECT','FROM','WHERE','AND','OR',\n    'LEFT JOIN','RIGHT JOIN','INNER JOIN','OUTER JOIN','FULL JOIN',\n    'JOIN','ON','GROUP BY','ORDER BY','HAVING','LIMIT','OFFSET',\n    'INSERT INTO','INSERT','UPDATE','DELETE','CREATE TABLE','CREATE INDEX',\n    'CREATE','ALTER TABLE','ALTER','DROP TABLE','DROP',\n    'TABLE','INDEX','VALUES','SET','AS','IN','NOT IN','NOT',\n    'NULL','IS NULL','IS NOT NULL','IS',\n    'LIKE','BETWEEN','UNION ALL','UNION',\n    'ALL','DISTINCT','COUNT','SUM','AVG','MAX','MIN',\n    'CASE','WHEN','THEN','ELSE','END',\n    'WITH','EXISTS','RETURNING'\n  ];\n\n  // Clause-level keywords that get their own line\n  const CLAUSE_KW = [\n    'SELECT','FROM','WHERE','LEFT JOIN','RIGHT JOIN','INNER JOIN',\n    'OUTER JOIN','FULL JOIN','JOIN','ON','GROUP BY','ORDER BY',\n    'HAVING','LIMIT','OFFSET','INSERT INTO','INSERT','UPDATE',\n    'DELETE','CREATE TABLE','CREATE INDEX','CREATE','ALTER TABLE',\n    'ALTER','DROP TABLE','DROP','SET','VALUES','UNION ALL','UNION',\n    'WITH','RETURNING'\n  ];\n\n  function tokenize(sql) {\n    const tokens = [];\n    let i = 0;\n    while (i \u003c sql.length) {\n      // Line comment\n      if (sql[i] === '-' \u0026\u0026 sql[i+1] === '-') {\n        let j = i;\n        while (j \u003c sql.length \u0026\u0026 sql[j] !== '\\n') j++;\n        tokens.push({ type: 'comment', val: sql.slice(i, j) });\n        i = j;\n        continue;\n      }\n      // Block comment\n      if (sql[i] === '/' \u0026\u0026 sql[i+1] === '*') {\n        let j = i + 2;\n        while (j \u003c sql.length \u0026\u0026 !(sql[j] === '*' \u0026\u0026 sql[j+1] === '/')) j++;\n        j += 2;\n        tokens.push({ type: 'comment', val: sql.slice(i, j) });\n        i = j;\n        continue;\n      }\n      // String single\n      if (sql[i] === \"'\") {\n        let j = i + 1;\n        while (j \u003c sql.length) {\n          if (sql[j] === \"'\" \u0026\u0026 sql[j+1] === \"'\") { j += 2; continue; }\n          if (sql[j] === \"'\") { j++; break; }\n          j++;\n        }\n        tokens.push({ type: 'string', val: sql.slice(i, j) });\n        i = j;\n        continue;\n      }\n      // String double-quoted identifier\n      if (sql[i] === '\"') {\n        let j = i + 1;\n        while (j \u003c sql.length \u0026\u0026 sql[j] !== '\"') j++;\n        j++;\n        tokens.push({ type: 'ident', val: sql.slice(i, j) });\n        i = j;\n        continue;\n      }\n      // Backtick identifier\n      if (sql[i] === '`') {\n        let j = i + 1;\n        while (j \u003c sql.length \u0026\u0026 sql[j] !== '`') j++;\n        j++;\n        tokens.push({ type: 'ident', val: sql.slice(i, j) });\n        i = j;\n        continue;\n      }\n      // Number\n      if (/[0-9]/.test(sql[i]) || (sql[i] === '.' \u0026\u0026 /[0-9]/.test(sql[i+1]))) {\n        let j = i;\n        while (j \u003c sql.length \u0026\u0026 /[0-9.\\-eExX_a-fA-F]/.test(sql[j])) j++;\n        tokens.push({ type: 'number', val: sql.slice(i, j) });\n        i = j;\n        continue;\n      }\n      // Whitespace\n      if (/\\s/.test(sql[i])) {\n        let j = i;\n        while (j \u003c sql.length \u0026\u0026 /\\s/.test(sql[j])) j++;\n        tokens.push({ type: 'ws', val: sql.slice(i, j) });\n        i = j;\n        continue;\n      }\n      // Punctuation / operators\n      if (/[(),;*=\u003c\u003e!+\\-\\/%\u0026|^~]/.test(sql[i])) {\n        tokens.push({ type: 'punct', val: sql[i] });\n        i++;\n        continue;\n      }\n      // Word\n      let j = i;\n      while (j \u003c sql.length \u0026\u0026 /[A-Za-z0-9_$#@]/.test(sql[j])) j++;\n      if (j === i) { tokens.push({ type: 'other', val: sql[i] }); i++; continue; }\n      tokens.push({ type: 'word', val: sql.slice(i, j) });\n      i = j;\n    }\n    return tokens;\n  }\n\n  function upperKeywords(tokens) {\n    // Multi-word keyword matching across consecutive word/ws tokens\n    const out = tokens.map(t =\u003e Object.assign({}, t));\n    // Single-word keywords\n    const single = new Set(KEYWORDS.filter(k =\u003e !k.includes(' ')));\n    for (let i = 0; i \u003c out.length; i++) {\n      if (out[i].type === 'word' \u0026\u0026 single.has(out[i].val.toUpperCase())) {\n        out[i].val = out[i].val.toUpperCase();\n        out[i].type = 'keyword';\n      }\n    }\n    // Multi-word keywords: rebuild as single keyword tokens\n    const multi = KEYWORDS.filter(k =\u003e k.includes(' ')).sort((a,b) =\u003e b.length - a.length);\n    for (const kw of multi) {\n      const parts = kw.split(' ');\n      for (let i = 0; i \u003c= out.length - parts.length; i++) {\n        const slice = out.slice(i, i + parts.length * 2 - 1);\n        // Check words match (allow ws between)\n        let pi = 0, si = 0, match = true;\n        while (pi \u003c parts.length \u0026\u0026 si \u003c slice.length) {\n          if (slice[si].type === 'ws') { si++; continue; }\n          if ((slice[si].type === 'word' || slice[si].type === 'keyword') \u0026\u0026\n              slice[si].val.toUpperCase() === parts[pi]) {\n            pi++; si++;\n          } else { match = false; break; }\n        }\n        if (match \u0026\u0026 pi === parts.length) {\n          const consumed = si;\n          out.splice(i, consumed, { type: 'keyword', val: kw });\n        }\n      }\n    }\n    return out;\n  }\n\n  function formatSQL(sql, tabSize) {\n    const indent = ' '.repeat(tabSize);\n    const tokens = upperKeywords(tokenize(sql));\n    const clauseSet = new Set(CLAUSE_KW);\n\n    let result = '';\n    let depth = 0;\n    let newlineNeeded = false;\n    let firstToken = true;\n\n    function pad() { return indent.repeat(depth); }\n\n    for (let i = 0; i \u003c tokens.length; i++) {\n      const t = tokens[i];\n      if (t.type === 'ws') continue; // we control whitespace\n\n      if (t.type === 'keyword' \u0026\u0026 clauseSet.has(t.val)) {\n        if (!firstToken) result += '\\n';\n        result += pad() + t.val;\n        newlineNeeded = false;\n        firstToken = false;\n        // peek ahead for content on same line\n        result += ' ';\n        continue;\n      }\n\n      if (t.val === '(') {\n        result += '(';\n        depth++;\n        // If next non-ws token is SELECT, it's a subquery → newline\n        let ni = i + 1;\n        while (ni \u003c tokens.length \u0026\u0026 tokens[ni].type === 'ws') ni++;\n        if (ni \u003c tokens.length \u0026\u0026 tokens[ni].val === 'SELECT') {\n          result += '\\n' + pad();\n        }\n        firstToken = false;\n        continue;\n      }\n\n      if (t.val === ')') {\n        depth = Math.max(0, depth - 1);\n        result = result.trimEnd();\n        result += '\\n' + pad() + ')';\n        firstToken = false;\n        continue;\n      }\n\n      if (t.val === ',') {\n        result = result.trimEnd();\n        result += ',\\n' + pad();\n        firstToken = false;\n        continue;\n      }\n\n      if (t.val === ';') {\n        result = result.trimEnd();\n        result += ';\\n';\n        firstToken = false;\n        continue;\n      }\n\n      result += t.val;\n      if (t.type !== 'punct') result += ' ';\n      firstToken = false;\n    }\n\n    // Clean up: collapse multiple blank lines, trailing spaces\n    return result\n      .split('\\n')\n      .map(l =\u003e l.trimEnd())\n      .join('\\n')\n      .replace(/\\n{3,}/g, '\\n\\n')\n      .trim();\n  }\n\n  function minifySQL(sql) {\n    const tokens = upperKeywords(tokenize(sql));\n    return tokens\n      .filter(t =\u003e t.type !== 'ws')\n      .map((t, i, arr) =\u003e {\n        const prev = arr[i-1];\n        const needSpace = prev \u0026\u0026 !['punct'].includes(prev.type) \u0026\u0026 !['punct'].includes(t.type) \u0026\u0026\n                          !(prev.val === '(') \u0026\u0026 !(t.val === ')') \u0026\u0026 !(t.val === ',') \u0026\u0026\n                          !(t.val === ';') \u0026\u0026 !(prev.val === ')' \u0026\u0026 t.val === '(');\n        return (needSpace ? ' ' : '') + t.val;\n      })\n      .join('')\n      .trim();\n  }\n\n  function escapeHtml(s) {\n    return s.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n  }\n\n  function highlightSQL(text) {\n    const tokens = upperKeywords(tokenize(text));\n    return tokens.map(t =\u003e {\n      const v = escapeHtml(t.val);\n      switch (t.type) {\n        case 'keyword': return '\u003cspan class=\"kw\"\u003e' + v + '\u003c/span\u003e';\n        case 'string':  return '\u003cspan class=\"str\"\u003e' + v + '\u003c/span\u003e';\n        case 'number':  return '\u003cspan class=\"num\"\u003e' + v + '\u003c/span\u003e';\n        case 'comment': return '\u003cspan class=\"cmt\"\u003e' + v + '\u003c/span\u003e';\n        default: return v;\n      }\n    }).join('');\n  }\n\n  const inputEl   = document.getElementById('sql-input');\n  const outputEl  = document.getElementById('sql-output');\n  const formatBtn = document.getElementById('sql-format-btn');\n  const minifyBtn = document.getElementById('sql-minify-btn');\n  const clearBtn  = document.getElementById('sql-clear-btn');\n  const copyBtn   = document.getElementById('sql-copy-btn');\n  const hlChk     = document.getElementById('sql-highlight-chk');\n  const tabSel    = document.getElementById('sql-tabsize-sel');\n  const statusEl  = document.getElementById('sql-status');\n\n  let lastFormatted = '';\n\n  function renderOutput(text, highlight) {\n    lastFormatted = text;\n    if (highlight) {\n      outputEl.innerHTML = highlightSQL(text);\n    } else {\n      outputEl.textContent = text;\n    }\n  }\n\n  function setStatus(msg, ok) {\n    statusEl.textContent = msg;\n    statusEl.style.color = ok === false ? '#f87171' : '#4ade80';\n  }\n\n  formatBtn.addEventListener('click', () =\u003e {\n    const sql = inputEl.value.trim();\n    if (!sql) { setStatus('Paste SQL into the input first.', false); return; }\n    const tab = parseInt(tabSel.value, 10);\n    try {\n      const out = formatSQL(sql, tab);\n      renderOutput(out, hlChk.checked);\n      setStatus('Formatted successfully.');\n    } catch(e) {\n      setStatus('Error formatting SQL.', false);\n    }\n  });\n\n  minifyBtn.addEventListener('click', () =\u003e {\n    const sql = inputEl.value.trim();\n    if (!sql) { setStatus('Paste SQL into the input first.', false); return; }\n    try {\n      const out = minifySQL(sql);\n      renderOutput(out, false);\n      setStatus('Minified successfully.');\n    } catch(e) {\n      setStatus('Error minifying SQL.', false);\n    }\n  });\n\n  clearBtn.addEventListener('click', () =\u003e {\n    inputEl.value = '';\n    outputEl.textContent = '';\n    lastFormatted = '';\n    statusEl.textContent = '';\n  });\n\n  copyBtn.addEventListener('click', () =\u003e {\n    if (!lastFormatted) { setStatus('Nothing to copy.', false); return; }\n    navigator.clipboard.writeText(lastFormatted).then(() =\u003e {\n      copyBtn.textContent = 'Copied!';\n      setStatus('Copied to clipboard.');\n      setTimeout(() =\u003e { copyBtn.textContent = 'Copy'; }, 1800);\n    }).catch(() =\u003e {\n      setStatus('Copy failed — please select and copy manually.', false);\n    });\n  });\n\n  hlChk.addEventListener('change', () =\u003e {\n    if (lastFormatted) renderOutput(lastFormatted, hlChk.checked);\n  });\n\n  // Demo SQL on load\n  inputEl.value = \"select u.id,u.name,o.total from users u inner join orders o on u.id=o.user_id where o.total\u003e100 and u.active=1 order by o.total desc limit 10;\";\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cstrong\u003eRelated:\u003c/strong\u003e Format JSON — \u003ca href=\"https://productivity-works.com/tools/json-formatter/\"\u003eJSON Formatter\u003c/a\u003e\n\u003c/p\u003e","title":"SQL Formatter \u0026 Beautifier — Free Online Tool"},{"content":" SQL Input MongoDB Output Copied to clipboard! Convert Copy Output Clear Quick Examples SELECT * WHERE SELECT fields WHERE AND LIKE IN ORDER + LIMIT INSERT UPDATE DELETE ","permalink":"https://productivity-works.com/tools/sql-to-mongodb/","summary":"\u003cdiv id=\"stm-app\"\u003e\n  \u003cstyle\u003e\n    #stm-app {\n      font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n      max-width: 900px;\n      margin: 0 auto;\n      color: #e2e8f0;\n    }\n    #stm-app .stm-layout {\n      display: grid;\n      grid-template-columns: 1fr 1fr;\n      gap: 16px;\n    }\n    @media (max-width: 640px) {\n      #stm-app .stm-layout { grid-template-columns: 1fr; }\n    }\n    #stm-app .stm-panel {\n      display: flex;\n      flex-direction: column;\n      gap: 8px;\n    }\n    #stm-app .stm-panel-label {\n      font-size: 12px;\n      font-weight: 600;\n      letter-spacing: 0.08em;\n      text-transform: uppercase;\n      color: #94a3b8;\n    }\n    #stm-app textarea {\n      width: 100%;\n      height: 260px;\n      padding: 14px 16px;\n      font-size: 13.5px;\n      font-family: \"Fira Code\", \"Cascadia Code\", \"Consolas\", \"Menlo\", monospace;\n      line-height: 1.65;\n      border: 1.5px solid #334155;\n      border-radius: 10px;\n      resize: vertical;\n      box-sizing: border-box;\n      background: #0f172a;\n      color: #e2e8f0;\n      outline: none;\n      transition: border-color 0.2s;\n    }\n    #stm-app textarea:focus {\n      border-color: #38bdf8;\n    }\n    #stm-app textarea::placeholder {\n      color: #475569;\n    }\n    #stm-app textarea[readonly] {\n      background: #0a1120;\n      cursor: default;\n      color: #7dd3fc;\n    }\n    #stm-app .stm-btn-row {\n      display: flex;\n      gap: 10px;\n      justify-content: center;\n      margin: 6px 0;\n      flex-wrap: wrap;\n    }\n    #stm-app .stm-btn {\n      padding: 10px 32px;\n      font-size: 14px;\n      font-weight: 600;\n      border: none;\n      border-radius: 8px;\n      cursor: pointer;\n      transition: background 0.15s, transform 0.1s;\n    }\n    #stm-app .stm-btn:active { transform: scale(0.97); }\n    #stm-app .stm-btn-convert {\n      background: linear-gradient(135deg, #0ea5e9, #6366f1);\n      color: #fff;\n    }\n    #stm-app .stm-btn-convert:hover { background: linear-gradient(135deg, #38bdf8, #818cf8); }\n    #stm-app .stm-btn-copy {\n      background: #1e293b;\n      color: #94a3b8;\n      border: 1.5px solid #334155;\n    }\n    #stm-app .stm-btn-copy:hover { background: #334155; color: #e2e8f0; }\n    #stm-app .stm-btn-clear {\n      background: #1e293b;\n      color: #94a3b8;\n      border: 1.5px solid #334155;\n    }\n    #stm-app .stm-btn-clear:hover { background: #334155; color: #e2e8f0; }\n    #stm-app .stm-error {\n      background: #1e0a0a;\n      border: 1.5px solid #7f1d1d;\n      border-radius: 8px;\n      color: #fca5a5;\n      font-size: 13px;\n      padding: 10px 14px;\n      font-family: \"Fira Code\", monospace;\n      display: none;\n    }\n    #stm-app .stm-examples {\n      margin-top: 8px;\n      border: 1.5px solid #1e293b;\n      border-radius: 10px;\n      overflow: hidden;\n    }\n    #stm-app .stm-examples-title {\n      background: #1e293b;\n      padding: 8px 14px;\n      font-size: 12px;\n      font-weight: 600;\n      color: #64748b;\n      letter-spacing: 0.06em;\n      text-transform: uppercase;\n    }\n    #stm-app .stm-example-list {\n      display: flex;\n      flex-wrap: wrap;\n      gap: 0;\n    }\n    #stm-app .stm-example-btn {\n      background: none;\n      border: none;\n      border-right: 1px solid #1e293b;\n      border-bottom: 1px solid #1e293b;\n      color: #38bdf8;\n      font-size: 12px;\n      font-family: \"Fira Code\", monospace;\n      padding: 7px 12px;\n      cursor: pointer;\n      text-align: left;\n      transition: background 0.12s;\n    }\n    #stm-app .stm-example-btn:hover { background: #0f172a; color: #7dd3fc; }\n    #stm-app .stm-copy-toast {\n      display: none;\n      font-size: 12px;\n      color: #4ade80;\n      text-align: center;\n      margin-top: 4px;\n    }\n  \u003c/style\u003e\n  \u003cdiv class=\"stm-layout\"\u003e\n    \u003cdiv class=\"stm-panel\"\u003e\n      \u003cdiv class=\"stm-panel-label\"\u003eSQL Input\u003c/div\u003e\n      \u003ctextarea id=\"stm-sql\" placeholder=\"SELECT * FROM users WHERE age \u003e 25\"\u003e\u003c/textarea\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"stm-panel\"\u003e\n      \u003cdiv class=\"stm-panel-label\"\u003eMongoDB Output\u003c/div\u003e\n      \u003ctextarea id=\"stm-mongo\" readonly placeholder=\"MongoDB query will appear here...\"\u003e\u003c/textarea\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv id=\"stm-error\" class=\"stm-error\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"stm-copy-toast\" id=\"stm-copy-toast\"\u003eCopied to clipboard!\u003c/div\u003e\n  \u003cdiv class=\"stm-btn-row\"\u003e\n    \u003cbutton class=\"stm-btn stm-btn-convert\" id=\"stm-convert-btn\"\u003eConvert\u003c/button\u003e\n    \u003cbutton class=\"stm-btn stm-btn-copy\" id=\"stm-copy-btn\"\u003eCopy Output\u003c/button\u003e\n    \u003cbutton class=\"stm-btn stm-btn-clear\" id=\"stm-clear-btn\"\u003eClear\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"stm-examples\"\u003e\n    \u003cdiv class=\"stm-examples-title\"\u003eQuick Examples\u003c/div\u003e\n    \u003cdiv class=\"stm-example-list\"\u003e\n      \u003cbutton class=\"stm-example-btn\" data-sql=\"SELECT * FROM users WHERE age \u003e 25\"\u003eSELECT * WHERE\u003c/button\u003e\n      \u003cbutton class=\"stm-example-btn\" data-sql=\"SELECT name, age FROM users\"\u003eSELECT fields\u003c/button\u003e\n      \u003cbutton class=\"stm-example-btn\" data-sql=\"SELECT * FROM orders WHERE status = 'active' AND total \u003e 100\"\u003eWHERE AND\u003c/button\u003e\n      \u003cbutton class=\"stm-example-btn\" data-sql=\"SELECT * FROM users WHERE name LIKE '%john%'\"\u003eLIKE\u003c/button\u003e\n      \u003cbutton class=\"stm-example-btn\" data-sql=\"SELECT * FROM products WHERE category IN ('books', 'music')\"\u003eIN\u003c/button\u003e\n      \u003cbutton class=\"stm-example-btn\" data-sql=\"SELECT * FROM users ORDER BY age DESC LIMIT 10\"\u003eORDER + LIMIT\u003c/button\u003e\n      \u003cbutton class=\"stm-example-btn\" data-sql=\"INSERT INTO users (name, age, email) VALUES ('John', 30, 'john@example.com')\"\u003eINSERT\u003c/button\u003e\n      \u003cbutton class=\"stm-example-btn\" data-sql=\"UPDATE users SET age = 31, email = 'new@example.com' WHERE name = 'John'\"\u003eUPDATE\u003c/button\u003e\n      \u003cbutton class=\"stm-example-btn\" data-sql=\"DELETE FROM users WHERE name = 'John'\"\u003eDELETE\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cscript\u003e\n  (function() {\n    var sqlEl    = document.getElementById('stm-sql');\n    var mongoEl  = document.getElementById('stm-mongo');\n    var errorEl  = document.getElementById('stm-error');\n    var copyToast= document.getElementById('stm-copy-toast');\n    var convertBtn = document.getElementById('stm-convert-btn');\n    var copyBtn  = document.getElementById('stm-copy-btn');\n    var clearBtn = document.getElementById('stm-clear-btn');\n\n    // ---- Helpers ----\n    function showError(msg) {\n      errorEl.textContent = msg;\n      errorEl.style.display = 'block';\n      mongoEl.value = '';\n    }\n    function clearError() {\n      errorEl.style.display = 'none';\n    }\n\n    // Parse a value token into JS representation\n    function parseValue(v) {\n      v = v.trim();\n      if (/^'.*'$/.test(v)) return JSON.stringify(v.slice(1, -1));\n      if (/^\".*\"$/.test(v)) return JSON.stringify(v.slice(1, -1));\n      if (!isNaN(Number(v))) return v;\n      if (v.toUpperCase() === 'NULL') return 'null';\n      if (v.toUpperCase() === 'TRUE') return 'true';\n      if (v.toUpperCase() === 'FALSE') return 'false';\n      return JSON.stringify(v);\n    }\n\n    // Parse WHERE clause into MongoDB filter object string\n    function parseWhere(where) {\n      where = where.trim();\n      if (!where) return '{}';\n\n      // Split by OR first (lowest precedence)\n      var orParts = splitByKeyword(where, 'OR');\n      if (orParts.length \u003e 1) {\n        var orClauses = orParts.map(function(p) { return parseAndClause(p.trim()); });\n        return '{ $or: [ ' + orClauses.join(', ') + ' ] }';\n      }\n      return parseAndClause(where);\n    }\n\n    function parseAndClause(clause) {\n      var parts = splitByKeyword(clause, 'AND');\n      if (parts.length === 1) return parseSingleCondition(clause.trim());\n      var conditions = parts.map(function(p) { return parseSingleCondition(p.trim()); });\n      return '{ $and: [ ' + conditions.join(', ') + ' ] }';\n    }\n\n    function splitByKeyword(str, kw) {\n      // Split by keyword (case-insensitive) but not inside quotes\n      var parts = [];\n      var re = new RegExp('\\\\b' + kw + '\\\\b', 'gi');\n      var last = 0;\n      var inQuote = false;\n      var qChar = '';\n      for (var i = 0; i \u003c str.length; i++) {\n        var c = str[i];\n        if (!inQuote \u0026\u0026 (c === \"'\" || c === '\"')) { inQuote = true; qChar = c; continue; }\n        if (inQuote \u0026\u0026 c === qChar) { inQuote = false; continue; }\n        if (!inQuote) {\n          var sub = str.slice(i).match(new RegExp('^' + kw + '\\\\b', 'i'));\n          if (sub) {\n            parts.push(str.slice(last, i));\n            i += sub[0].length;\n            last = i;\n            i--; // compensate for loop i++\n          }\n        }\n      }\n      parts.push(str.slice(last));\n      return parts.filter(function(p) { return p.trim().length \u003e 0; });\n    }\n\n    function parseSingleCondition(cond) {\n      cond = cond.trim();\n\n      // IS NULL / IS NOT NULL\n      var isNullM = cond.match(/^(\\w+)\\s+IS\\s+NOT\\s+NULL$/i);\n      if (isNullM) return '{ ' + isNullM[1] + ': { $ne: null } }';\n      var isNullM2 = cond.match(/^(\\w+)\\s+IS\\s+NULL$/i);\n      if (isNullM2) return '{ ' + isNullM2[1] + ': null }';\n\n      // LIKE\n      var likeM = cond.match(/^(\\w+)\\s+(?:NOT\\s+)?LIKE\\s+('([^']*)'|\"([^\"]*)\")/i);\n      if (likeM) {\n        var likeNot = /NOT\\s+LIKE/i.test(cond);\n        var pattern = (likeM[3] !== undefined ? likeM[3] : likeM[4]);\n        var regexStr = pattern.replace(/%/g, '.*').replace(/_/g, '.');\n        var mongoRegex = '/' + regexStr + '/i';\n        if (likeNot) return '{ ' + likeM[1] + ': { $not: ' + mongoRegex + ' } }';\n        return '{ ' + likeM[1] + ': { $regex: ' + mongoRegex + ' } }';\n      }\n\n      // IN / NOT IN\n      var inM = cond.match(/^(\\w+)\\s+(NOT\\s+)?IN\\s*\\(([^)]+)\\)/i);\n      if (inM) {\n        var inNot = !!inM[2];\n        var vals = inM[3].split(',').map(function(v) { return parseValue(v.trim()); });\n        if (inNot) return '{ ' + inM[1] + ': { $nin: [ ' + vals.join(', ') + ' ] } }';\n        return '{ ' + inM[1] + ': { $in: [ ' + vals.join(', ') + ' ] } }';\n      }\n\n      // Comparison operators\n      var compM = cond.match(/^(\\w+)\\s*(!=|\u003c\u003e|\u003c=|\u003e=|\u003c|\u003e|=)\\s*(.+)$/i);\n      if (compM) {\n        var field = compM[1];\n        var op    = compM[2];\n        var val   = parseValue(compM[3]);\n        var opMap = { '=': null, '!=': '$ne', '\u003c\u003e': '$ne', '\u003c': '$lt', '\u003c=': '$lte', '\u003e': '$gt', '\u003e=': '$gte' };\n        var mongoOp = opMap[op];\n        if (mongoOp === null) return '{ ' + field + ': ' + val + ' }';\n        return '{ ' + field + ': { ' + mongoOp + ': ' + val + ' } }';\n      }\n\n      return '{ /* unsupported: ' + cond + ' */ }';\n    }\n\n    // Parse field list for projection\n    function parseFields(fieldStr) {\n      fieldStr = fieldStr.trim();\n      if (fieldStr === '*') return null;\n      var fields = fieldStr.split(',').map(function(f) { return f.trim().replace(/\\s+AS\\s+\\w+/i, ''); });\n      // exclude _id by default? No, just list fields as 1\n      return '{ ' + fields.map(function(f) { return f + ': 1'; }).join(', ') + ' }';\n    }\n\n    // Parse INSERT columns+values\n    function parseInsert(cols, vals) {\n      var colArr = cols.split(',').map(function(c) { return c.trim(); });\n      var valArr = splitValues(vals);\n      var pairs = colArr.map(function(c, i) {\n        return c + ': ' + parseValue(valArr[i] || 'null');\n      });\n      return '{ ' + pairs.join(', ') + ' }';\n    }\n\n    // Split values respecting quoted strings\n    function splitValues(str) {\n      var result = [];\n      var cur = '';\n      var depth = 0;\n      var inQ = false;\n      var qC = '';\n      for (var i = 0; i \u003c str.length; i++) {\n        var c = str[i];\n        if (!inQ \u0026\u0026 (c === \"'\" || c === '\"')) { inQ = true; qC = c; cur += c; continue; }\n        if (inQ \u0026\u0026 c === qC) { inQ = false; cur += c; continue; }\n        if (!inQ \u0026\u0026 c === '(') { depth++; cur += c; continue; }\n        if (!inQ \u0026\u0026 c === ')') { depth--; cur += c; continue; }\n        if (!inQ \u0026\u0026 depth === 0 \u0026\u0026 c === ',') { result.push(cur.trim()); cur = ''; continue; }\n        cur += c;\n      }\n      if (cur.trim()) result.push(cur.trim());\n      return result;\n    }\n\n    // Parse SET clause for UPDATE\n    function parseSet(setStr) {\n      var pairs = splitValues(setStr);\n      var obj = pairs.map(function(p) {\n        var m = p.match(/^(\\w+)\\s*=\\s*(.+)$/);\n        if (!m) return '/* ' + p + ' */';\n        return m[1] + ': ' + parseValue(m[2]);\n      });\n      return '{ ' + obj.join(', ') + ' }';\n    }\n\n    // ---- Main converter ----\n    function convertSQL(sql) {\n      sql = sql.trim().replace(/;$/, '').trim();\n\n      // SELECT\n      var selM = sql.match(/^SELECT\\s+([\\s\\S]+?)\\s+FROM\\s+(\\w+)([\\s\\S]*)$/i);\n      if (selM) {\n        var fields   = selM[1].trim();\n        var table    = selM[2].trim();\n        var rest     = selM[3].trim();\n\n        var whereStr = '';\n        var orderStr = '';\n        var limitStr = '';\n\n        // Extract LIMIT\n        var limitM = rest.match(/\\bLIMIT\\s+(\\d+)\\b/i);\n        if (limitM) { limitStr = limitM[1]; rest = rest.replace(limitM[0], '').trim(); }\n\n        // Extract ORDER BY\n        var orderM = rest.match(/\\bORDER\\s+BY\\s+([\\w\\s,]+?)(?=\\bLIMIT\\b|$)/i);\n        if (orderM) {\n          var orderRaw = orderM[1].trim();\n          rest = rest.replace(orderM[0], '').trim();\n          var orderParts = orderRaw.split(',').map(function(o) {\n            var om = o.trim().match(/^(\\w+)(?:\\s+(ASC|DESC))?$/i);\n            if (!om) return '';\n            var dir = (om[2] \u0026\u0026 om[2].toUpperCase() === 'DESC') ? -1 : 1;\n            return om[1] + ': ' + dir;\n          }).filter(Boolean);\n          orderStr = '{ ' + orderParts.join(', ') + ' }';\n        }\n\n        // Extract WHERE\n        var whereM = rest.match(/\\bWHERE\\s+([\\s\\S]+?)(?=\\bORDER\\s+BY\\b|\\bLIMIT\\b|\\bGROUP\\s+BY\\b|$)/i);\n        if (whereM) whereStr = whereM[1].trim();\n\n        var filter = whereStr ? parseWhere(whereStr) : '{}';\n        var proj   = parseFields(fields);\n\n        var query = 'db.' + table + '.find(' + filter;\n        if (proj) query += ', ' + proj;\n        query += ')';\n        if (orderStr) query += '.sort(' + orderStr + ')';\n        if (limitStr) query += '.limit(' + limitStr + ')';\n        return query;\n      }\n\n      // INSERT\n      var insM = sql.match(/^INSERT\\s+INTO\\s+(\\w+)\\s*\\(([^)]+)\\)\\s*VALUES\\s*\\(([^)]+)\\)/i);\n      if (insM) {\n        var table = insM[1];\n        var doc = parseInsert(insM[2], insM[3]);\n        return 'db.' + table + '.insertOne(' + doc + ')';\n      }\n\n      // UPDATE\n      var updM = sql.match(/^UPDATE\\s+(\\w+)\\s+SET\\s+([\\s\\S]+?)(?:\\s+WHERE\\s+([\\s\\S]+))?$/i);\n      if (updM) {\n        var table  = updM[1];\n        var setStr = updM[2].trim();\n        var whereStr2 = updM[3] ? updM[3].trim() : '';\n        // Remove trailing WHERE if any leaked\n        setStr = setStr.replace(/\\s+WHERE\\s+[\\s\\S]+$/i, '').trim();\n        var filter2 = whereStr2 ? parseWhere(whereStr2) : '{}';\n        var update  = '{ $set: ' + parseSet(setStr) + ' }';\n        return 'db.' + table + '.updateOne(' + filter2 + ', ' + update + ')';\n      }\n\n      // DELETE\n      var delM = sql.match(/^DELETE\\s+FROM\\s+(\\w+)(?:\\s+WHERE\\s+([\\s\\S]+))?$/i);\n      if (delM) {\n        var table    = delM[1];\n        var whereStr3 = delM[2] ? delM[2].trim() : '';\n        var filter3  = whereStr3 ? parseWhere(whereStr3) : '{}';\n        return 'db.' + table + '.deleteOne(' + filter3 + ')';\n      }\n\n      throw new Error('Unsupported SQL statement. Supported: SELECT, INSERT INTO, UPDATE, DELETE FROM.');\n    }\n\n    // ---- Events ----\n    convertBtn.addEventListener('click', function() {\n      clearError();\n      var sql = sqlEl.value.trim();\n      if (!sql) { showError('Please enter a SQL statement.'); return; }\n      try {\n        mongoEl.value = convertSQL(sql);\n      } catch (e) {\n        showError('Error: ' + e.message);\n      }\n    });\n\n    sqlEl.addEventListener('keydown', function(e) {\n      if ((e.ctrlKey || e.metaKey) \u0026\u0026 e.key === 'Enter') convertBtn.click();\n    });\n\n    copyBtn.addEventListener('click', function() {\n      var text = mongoEl.value;\n      if (!text) return;\n      navigator.clipboard.writeText(text).then(function() {\n        copyToast.style.display = 'block';\n        setTimeout(function() { copyToast.style.display = 'none'; }, 1800);\n      });\n    });\n\n    clearBtn.addEventListener('click', function() {\n      sqlEl.value = '';\n      mongoEl.value = '';\n      clearError();\n    });\n\n    // Example buttons\n    document.querySelectorAll('#stm-app .stm-example-btn').forEach(function(btn) {\n      btn.addEventListener('click', function() {\n        sqlEl.value = btn.getAttribute('data-sql');\n        clearError();\n        convertBtn.click();\n      });\n    });\n  })();\n  \u003c/script\u003e\n\u003c/div\u003e","title":"SQL to MongoDB Converter"},{"content":" Stopwatch Countdown h m s 00:00:00.000 Start Lap Reset \u0026#x26F6; Space Start / Stop \u0026nbsp;\u0026nbsp; L Lap \u0026nbsp;\u0026nbsp; R Reset \u0026nbsp;\u0026nbsp; F Fullscreen Lap Times Clear Lap Lap Time Total Time Related Tools Boost focus with the Pomodoro Technique → Pomodoro Timer Count down to a deadline or event → Countdown Timer Generate random numbers for games or decisions → Random Number Generator ","permalink":"https://productivity-works.com/tools/stopwatch/","summary":"\u003cdiv id=\"sw-app\"\u003e\n\u003cstyle\u003e\n  #sw-app {\n    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, sans-serif;\n    max-width: 720px;\n    margin: 0 auto;\n    padding: 16px;\n    color: #1a2e2a;\n  }\n\n  /* Mode tabs */\n  #sw-app .sw-mode-tabs {\n    display: flex;\n    justify-content: center;\n    gap: 8px;\n    margin-bottom: 32px;\n    flex-wrap: wrap;\n  }\n  #sw-app .sw-mode-tab {\n    padding: 8px 24px;\n    border: 2px solid #10b981;\n    border-radius: 24px;\n    background: transparent;\n    color: #10b981;\n    font-size: 14px;\n    font-weight: 600;\n    cursor: pointer;\n    transition: all 0.2s;\n  }\n  #sw-app .sw-mode-tab:hover {\n    background: #d1fae5;\n  }\n  #sw-app .sw-mode-tab.active {\n    background: #10b981;\n    color: #fff;\n  }\n\n  /* Display */\n  #sw-app .sw-display-wrap {\n    text-align: center;\n    margin-bottom: 32px;\n  }\n  #sw-app .sw-display {\n    font-size: clamp(48px, 10vw, 96px);\n    font-variant-numeric: tabular-nums;\n    font-weight: 700;\n    letter-spacing: 2px;\n    color: #065f46;\n    line-height: 1;\n    font-family: 'Courier New', 'Roboto Mono', monospace;\n  }\n  #sw-app .sw-display .sw-ms {\n    font-size: 0.45em;\n    color: #10b981;\n    vertical-align: baseline;\n  }\n\n  /* Countdown input */\n  #sw-app .sw-countdown-setup {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    gap: 8px;\n    margin-bottom: 24px;\n    flex-wrap: wrap;\n  }\n  #sw-app .sw-countdown-setup label {\n    font-size: 13px;\n    color: #6b7280;\n    font-weight: 500;\n  }\n  #sw-app .sw-countdown-setup .sw-cd-field {\n    display: flex;\n    align-items: center;\n    gap: 4px;\n  }\n  #sw-app .sw-cd-input {\n    width: 64px;\n    padding: 8px 10px;\n    border: 2px solid #10b981;\n    border-radius: 8px;\n    font-size: 18px;\n    font-weight: 700;\n    text-align: center;\n    color: #065f46;\n    outline: none;\n    font-family: monospace;\n  }\n  #sw-app .sw-cd-input:focus {\n    border-color: #059669;\n    box-shadow: 0 0 0 3px rgba(16,185,129,0.15);\n  }\n  #sw-app .sw-cd-sep {\n    font-size: 22px;\n    font-weight: 700;\n    color: #10b981;\n  }\n\n  /* Buttons */\n  #sw-app .sw-buttons {\n    display: flex;\n    justify-content: center;\n    gap: 12px;\n    flex-wrap: wrap;\n    margin-bottom: 28px;\n  }\n  #sw-app .sw-btn {\n    padding: 13px 32px;\n    border: none;\n    border-radius: 12px;\n    font-size: 16px;\n    font-weight: 700;\n    cursor: pointer;\n    transition: transform 0.1s, box-shadow 0.1s;\n    box-shadow: 0 2px 8px rgba(0,0,0,0.12);\n    letter-spacing: 0.5px;\n  }\n  #sw-app .sw-btn:active {\n    transform: scale(0.96);\n    box-shadow: 0 1px 4px rgba(0,0,0,0.1);\n  }\n  #sw-app .sw-btn-start {\n    background: #10b981;\n    color: #fff;\n    min-width: 120px;\n  }\n  #sw-app .sw-btn-start:hover {\n    background: #059669;\n  }\n  #sw-app .sw-btn-stop {\n    background: #ef4444;\n    color: #fff;\n    min-width: 120px;\n  }\n  #sw-app .sw-btn-stop:hover {\n    background: #dc2626;\n  }\n  #sw-app .sw-btn-lap {\n    background: #f0fdf4;\n    color: #065f46;\n    border: 2px solid #10b981;\n  }\n  #sw-app .sw-btn-lap:hover {\n    background: #d1fae5;\n  }\n  #sw-app .sw-btn-reset {\n    background: #f3f4f6;\n    color: #374151;\n    border: 2px solid #d1d5db;\n  }\n  #sw-app .sw-btn-reset:hover {\n    background: #e5e7eb;\n  }\n  #sw-app .sw-btn-fs {\n    background: #f0fdf4;\n    color: #065f46;\n    border: 2px solid #10b981;\n    padding: 13px 18px;\n    font-size: 18px;\n  }\n  #sw-app .sw-btn-fs:hover {\n    background: #d1fae5;\n  }\n  #sw-app .sw-btn:disabled {\n    opacity: 0.4;\n    cursor: not-allowed;\n    transform: none;\n  }\n\n  /* Keyboard shortcuts hint */\n  #sw-app .sw-shortcuts {\n    text-align: center;\n    font-size: 12px;\n    color: #9ca3af;\n    margin-bottom: 28px;\n  }\n  #sw-app .sw-shortcuts kbd {\n    display: inline-block;\n    padding: 2px 6px;\n    background: #f3f4f6;\n    border: 1px solid #d1d5db;\n    border-radius: 4px;\n    font-family: monospace;\n    font-size: 11px;\n    color: #374151;\n  }\n\n  /* Lap table */\n  #sw-app .sw-lap-section {\n    margin-top: 8px;\n  }\n  #sw-app .sw-lap-header {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    margin-bottom: 10px;\n  }\n  #sw-app .sw-lap-title {\n    font-size: 15px;\n    font-weight: 700;\n    color: #065f46;\n  }\n  #sw-app .sw-lap-clear {\n    font-size: 12px;\n    color: #10b981;\n    background: none;\n    border: none;\n    cursor: pointer;\n    padding: 4px 8px;\n    border-radius: 6px;\n    transition: background 0.15s;\n  }\n  #sw-app .sw-lap-clear:hover {\n    background: #d1fae5;\n  }\n  #sw-app .sw-lap-table {\n    width: 100%;\n    border-collapse: collapse;\n    font-size: 14px;\n  }\n  #sw-app .sw-lap-table thead th {\n    padding: 10px 12px;\n    background: #ecfdf5;\n    color: #065f46;\n    font-weight: 700;\n    text-align: left;\n    border-bottom: 2px solid #10b981;\n    font-size: 12px;\n    text-transform: uppercase;\n    letter-spacing: 0.5px;\n  }\n  #sw-app .sw-lap-table tbody tr {\n    border-bottom: 1px solid #f0fdf4;\n    transition: background 0.12s;\n  }\n  #sw-app .sw-lap-table tbody tr:hover {\n    background: #f0fdf4;\n  }\n  #sw-app .sw-lap-table td {\n    padding: 10px 12px;\n    font-variant-numeric: tabular-nums;\n    font-family: monospace;\n    font-size: 14px;\n  }\n  #sw-app .sw-lap-table td:first-child {\n    font-family: inherit;\n    font-weight: 600;\n    color: #374151;\n    font-size: 13px;\n  }\n  #sw-app .sw-lap-best {\n    background: #dcfce7 !important;\n    color: #166534;\n    font-weight: 700;\n  }\n  #sw-app .sw-lap-worst {\n    background: #fee2e2 !important;\n    color: #991b1b;\n    font-weight: 700;\n  }\n  #sw-app .sw-lap-best td,\n  #sw-app .sw-lap-worst td {\n    font-weight: 700;\n  }\n  #sw-app .sw-lap-badge {\n    display: inline-block;\n    padding: 1px 7px;\n    border-radius: 10px;\n    font-size: 10px;\n    font-family: sans-serif;\n    letter-spacing: 0.5px;\n    text-transform: uppercase;\n    vertical-align: middle;\n    margin-left: 6px;\n  }\n  #sw-app .sw-badge-best {\n    background: #10b981;\n    color: #fff;\n  }\n  #sw-app .sw-badge-worst {\n    background: #ef4444;\n    color: #fff;\n  }\n\n  /* Countdown finished state */\n  #sw-app .sw-cd-done {\n    color: #ef4444 !important;\n    animation: sw-pulse 0.6s ease-in-out infinite alternate;\n  }\n  @keyframes sw-pulse {\n    from { opacity: 1; }\n    to { opacity: 0.5; }\n  }\n\n  /* Fullscreen overlay */\n  #sw-app.sw-fullscreen {\n    position: fixed;\n    inset: 0;\n    z-index: 99999;\n    max-width: 100%;\n    background: #fff;\n    display: flex;\n    flex-direction: column;\n    justify-content: center;\n    align-items: center;\n    padding: 32px;\n    overflow-y: auto;\n  }\n  #sw-app.sw-fullscreen .sw-display {\n    font-size: clamp(72px, 18vw, 160px);\n  }\n  #sw-app.sw-fullscreen .sw-lap-section {\n    width: 100%;\n    max-width: 700px;\n  }\n\n  /* Responsive */\n  @media (max-width: 480px) {\n    #sw-app .sw-btn {\n      padding: 11px 20px;\n      font-size: 14px;\n    }\n    #sw-app .sw-lap-table {\n      font-size: 12px;\n    }\n  }\n\u003c/style\u003e\n\u003c!-- Mode tabs --\u003e\n\u003cdiv class=\"sw-mode-tabs\"\u003e\n  \u003cbutton class=\"sw-mode-tab active\" data-mode=\"stopwatch\" onclick=\"swSetMode('stopwatch')\"\u003eStopwatch\u003c/button\u003e\n  \u003cbutton class=\"sw-mode-tab\" data-mode=\"countdown\" onclick=\"swSetMode('countdown')\"\u003eCountdown\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Countdown setup (hidden in stopwatch mode) --\u003e\n\u003cdiv class=\"sw-countdown-setup\" id=\"sw-cd-setup\" style=\"display:none;\"\u003e\n  \u003cspan class=\"sw-cd-field\"\u003e\n    \u003cinput class=\"sw-cd-input\" id=\"sw-cd-h\" type=\"number\" min=\"0\" max=\"99\" value=\"0\" placeholder=\"hh\"\u003e\n    \u003cspan class=\"sw-cd-sep\"\u003eh\u003c/span\u003e\n  \u003c/span\u003e\n  \u003cspan class=\"sw-cd-field\"\u003e\n    \u003cinput class=\"sw-cd-input\" id=\"sw-cd-m\" type=\"number\" min=\"0\" max=\"59\" value=\"5\" placeholder=\"mm\"\u003e\n    \u003cspan class=\"sw-cd-sep\"\u003em\u003c/span\u003e\n  \u003c/span\u003e\n  \u003cspan class=\"sw-cd-field\"\u003e\n    \u003cinput class=\"sw-cd-input\" id=\"sw-cd-s\" type=\"number\" min=\"0\" max=\"59\" value=\"0\" placeholder=\"ss\"\u003e\n    \u003cspan class=\"sw-cd-sep\"\u003es\u003c/span\u003e\n  \u003c/span\u003e\n\u003c/div\u003e\n\u003c!-- Main display --\u003e\n\u003cdiv class=\"sw-display-wrap\"\u003e\n  \u003cdiv class=\"sw-display\" id=\"sw-display\"\u003e\n    \u003cspan id=\"sw-hms\"\u003e00:00:00\u003c/span\u003e\u003cspan class=\"sw-ms\"\u003e.\u003cspan id=\"sw-ms\"\u003e000\u003c/span\u003e\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Buttons --\u003e\n\u003cdiv class=\"sw-buttons\"\u003e\n  \u003cbutton class=\"sw-btn sw-btn-start\" id=\"sw-btn-startstop\" onclick=\"swToggle()\"\u003eStart\u003c/button\u003e\n  \u003cbutton class=\"sw-btn sw-btn-lap\" id=\"sw-btn-lap\" onclick=\"swLap()\" disabled\u003eLap\u003c/button\u003e\n  \u003cbutton class=\"sw-btn sw-btn-reset\" onclick=\"swReset()\"\u003eReset\u003c/button\u003e\n  \u003cbutton class=\"sw-btn sw-btn-fs\" id=\"sw-btn-fs\" onclick=\"swToggleFs()\" title=\"Fullscreen\"\u003e\u0026#x26F6;\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Keyboard shortcuts --\u003e\n\u003cdiv class=\"sw-shortcuts\"\u003e\n  \u003ckbd\u003eSpace\u003c/kbd\u003e Start / Stop \u0026nbsp;\u0026nbsp;\n  \u003ckbd\u003eL\u003c/kbd\u003e Lap \u0026nbsp;\u0026nbsp;\n  \u003ckbd\u003eR\u003c/kbd\u003e Reset \u0026nbsp;\u0026nbsp;\n  \u003ckbd\u003eF\u003c/kbd\u003e Fullscreen\n\u003c/div\u003e\n\u003c!-- Lap section --\u003e\n\u003cdiv class=\"sw-lap-section\" id=\"sw-lap-section\" style=\"display:none;\"\u003e\n  \u003cdiv class=\"sw-lap-header\"\u003e\n    \u003cspan class=\"sw-lap-title\"\u003eLap Times\u003c/span\u003e\n    \u003cbutton class=\"sw-lap-clear\" onclick=\"swClearLaps()\"\u003eClear\u003c/button\u003e\n  \u003c/div\u003e\n  \u003ctable class=\"sw-lap-table\"\u003e\n    \u003cthead\u003e\n      \u003ctr\u003e\n        \u003cth\u003eLap\u003c/th\u003e\n        \u003cth\u003eLap Time\u003c/th\u003e\n        \u003cth\u003eTotal Time\u003c/th\u003e\n      \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody id=\"sw-lap-tbody\"\u003e\u003c/tbody\u003e\n  \u003c/table\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  // State\n  var SW = {\n    mode: 'stopwatch',      // 'stopwatch' | 'countdown'\n    running: false,\n    startTime: 0,           // performance.now() reference\n    elapsed: 0,             // ms accumulated before last pause\n    lapOffset: 0,           // elapsed at start of current lap\n    laps: [],               // [{lap, total}] in ms\n    cdTotal: 0,             // countdown total ms\n    cdDone: false,\n    raf: null,\n    fs: false\n  };\n\n  // Elements\n  var elHMS   = document.getElementById('sw-hms');\n  var elMS    = document.getElementById('sw-ms');\n  var elDisp  = document.getElementById('sw-display');\n  var elBtn   = document.getElementById('sw-btn-startstop');\n  var elLapBtn= document.getElementById('sw-btn-lap');\n  var elLapSec= document.getElementById('sw-lap-section');\n  var elTbody = document.getElementById('sw-lap-tbody');\n  var elCdSet = document.getElementById('sw-cd-setup');\n  var elApp   = document.getElementById('sw-app');\n  var elCdH   = document.getElementById('sw-cd-h');\n  var elCdM   = document.getElementById('sw-cd-m');\n  var elCdS   = document.getElementById('sw-cd-s');\n\n  // Format helpers\n  function pad2(n) { return n \u003c 10 ? '0' + n : '' + n; }\n  function pad3(n) { return n \u003c 10 ? '00' + n : n \u003c 100 ? '0' + n : '' + n; }\n\n  function msToDisplay(ms) {\n    ms = Math.max(0, Math.floor(ms));\n    var mil = ms % 1000;\n    var s   = Math.floor(ms / 1000) % 60;\n    var m   = Math.floor(ms / 60000) % 60;\n    var h   = Math.floor(ms / 3600000);\n    return { hms: pad2(h) + ':' + pad2(m) + ':' + pad2(s), ms: pad3(mil) };\n  }\n\n  function msToString(ms) {\n    var d = msToDisplay(ms);\n    return d.hms + '.' + d.ms;\n  }\n\n  // Render current time\n  function render() {\n    var now = SW.running ? (performance.now() - SW.startTime + SW.elapsed) : SW.elapsed;\n    if (SW.mode === 'countdown') {\n      var remaining = SW.cdTotal - now;\n      if (remaining \u003c= 0) {\n        remaining = 0;\n        if (!SW.cdDone) {\n          SW.cdDone = true;\n          swStop();\n          elDisp.classList.add('sw-cd-done');\n          // Browser beep via oscillator\n          try {\n            var ctx = new (window.AudioContext || window.webkitAudioContext)();\n            for (var i = 0; i \u003c 3; i++) {\n              (function(delay) {\n                var osc = ctx.createOscillator();\n                var gain = ctx.createGain();\n                osc.connect(gain); gain.connect(ctx.destination);\n                osc.frequency.value = 880;\n                osc.type = 'sine';\n                gain.gain.setValueAtTime(0.5, ctx.currentTime + delay);\n                gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + delay + 0.3);\n                osc.start(ctx.currentTime + delay);\n                osc.stop(ctx.currentTime + delay + 0.35);\n              })(i * 0.4);\n            }\n          } catch(e) {}\n        }\n      }\n      var d = msToDisplay(remaining);\n      elHMS.textContent = d.hms;\n      elMS.textContent  = d.ms;\n    } else {\n      var d2 = msToDisplay(now);\n      elHMS.textContent = d2.hms;\n      elMS.textContent  = d2.ms;\n    }\n  }\n\n  function loop() {\n    render();\n    SW.raf = requestAnimationFrame(loop);\n  }\n\n  // Controls\n  function swStart() {\n    if (SW.mode === 'countdown' \u0026\u0026 !SW.running \u0026\u0026 SW.elapsed === 0) {\n      // Load countdown value\n      var h = parseInt(elCdH.value) || 0;\n      var m = parseInt(elCdM.value) || 0;\n      var s = parseInt(elCdS.value) || 0;\n      SW.cdTotal = (h * 3600 + m * 60 + s) * 1000;\n      if (SW.cdTotal \u003c= 0) return;\n      SW.cdDone = false;\n      elDisp.classList.remove('sw-cd-done');\n    }\n    SW.running = true;\n    SW.startTime = performance.now();\n    elBtn.textContent = 'Stop';\n    elBtn.className = 'sw-btn sw-btn-stop';\n    elLapBtn.disabled = (SW.mode === 'countdown');\n    SW.raf = requestAnimationFrame(loop);\n  }\n\n  function swStop() {\n    if (!SW.running) return;\n    SW.elapsed += performance.now() - SW.startTime;\n    SW.running = false;\n    cancelAnimationFrame(SW.raf);\n    render();\n    elBtn.textContent = 'Start';\n    elBtn.className = 'sw-btn sw-btn-start';\n    elLapBtn.disabled = true;\n  }\n\n  window.swToggle = function() {\n    if (SW.running) swStop(); else swStart();\n  };\n\n  window.swLap = function() {\n    if (!SW.running || SW.mode === 'countdown') return;\n    var total = SW.elapsed + (performance.now() - SW.startTime);\n    var lap   = total - SW.lapOffset;\n    SW.lapOffset = total;\n    SW.laps.unshift({ lap: lap, total: total, num: SW.laps.length + 1 });\n    renderLaps();\n    elLapSec.style.display = '';\n  };\n\n  window.swReset = function() {\n    swStop();\n    SW.elapsed   = 0;\n    SW.lapOffset = 0;\n    SW.laps      = [];\n    SW.cdDone    = false;\n    elDisp.classList.remove('sw-cd-done');\n    elBtn.textContent = 'Start';\n    elBtn.className = 'sw-btn sw-btn-start';\n    elLapBtn.disabled = true;\n    render();\n    elTbody.innerHTML = '';\n    elLapSec.style.display = 'none';\n  };\n\n  window.swClearLaps = function() {\n    SW.laps = [];\n    SW.lapOffset = SW.elapsed + (SW.running ? (performance.now() - SW.startTime) : 0);\n    elTbody.innerHTML = '';\n    elLapSec.style.display = 'none';\n  };\n\n  window.swSetMode = function(mode) {\n    swReset();\n    SW.mode = mode;\n    document.querySelectorAll('#sw-app .sw-mode-tab').forEach(function(t) {\n      t.classList.toggle('active', t.dataset.mode === mode);\n    });\n    elCdSet.style.display = mode === 'countdown' ? 'flex' : 'none';\n    // Lap button: only show in stopwatch\n    elLapBtn.style.display = mode === 'countdown' ? 'none' : '';\n    elLapSec.style.display = 'none';\n    render();\n  };\n\n  window.swToggleFs = function() {\n    SW.fs = !SW.fs;\n    elApp.classList.toggle('sw-fullscreen', SW.fs);\n    document.getElementById('sw-btn-fs').title = SW.fs ? 'Exit Fullscreen' : 'Fullscreen';\n    document.getElementById('sw-btn-fs').innerHTML = SW.fs ? '\u0026#x2715;' : '\u0026#x26F6;';\n  };\n\n  // Render lap table\n  function renderLaps() {\n    if (SW.laps.length === 0) return;\n    // Find best and worst by lap time\n    var lapTimes = SW.laps.map(function(l){ return l.lap; });\n    var minLap = Math.min.apply(null, lapTimes);\n    var maxLap = Math.max.apply(null, lapTimes);\n    var onlyOne = SW.laps.length === 1;\n\n    elTbody.innerHTML = SW.laps.map(function(l, i) {\n      var isBest  = !onlyOne \u0026\u0026 l.lap === minLap;\n      var isWorst = !onlyOne \u0026\u0026 l.lap === maxLap;\n      var rowCls  = isBest ? 'sw-lap-best' : isWorst ? 'sw-lap-worst' : '';\n      var badge   = isBest\n        ? '\u003cspan class=\"sw-lap-badge sw-badge-best\"\u003eBest\u003c/span\u003e'\n        : isWorst\n        ? '\u003cspan class=\"sw-lap-badge sw-badge-worst\"\u003eWorst\u003c/span\u003e'\n        : '';\n      var lapNum  = SW.laps.length - i;\n      return '\u003ctr class=\"' + rowCls + '\"\u003e'\n        + '\u003ctd\u003eLap ' + lapNum + badge + '\u003c/td\u003e'\n        + '\u003ctd\u003e' + msToString(l.lap) + '\u003c/td\u003e'\n        + '\u003ctd\u003e' + msToString(l.total) + '\u003c/td\u003e'\n        + '\u003c/tr\u003e';\n    }).join('');\n  }\n\n  // Keyboard shortcuts\n  document.addEventListener('keydown', function(e) {\n    // Ignore if focus is on an input field\n    if (document.activeElement \u0026\u0026 (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA')) return;\n    // Check the stopwatch app is present on the page\n    if (!document.getElementById('sw-app')) return;\n    if (e.code === 'Space' || e.key === ' ') {\n      e.preventDefault();\n      swToggle();\n    } else if (e.key === 'l' || e.key === 'L') {\n      swLap();\n    } else if (e.key === 'r' || e.key === 'R') {\n      swReset();\n    } else if (e.key === 'f' || e.key === 'F') {\n      swToggleFs();\n    }\n  });\n\n  // Escape exits fullscreen\n  document.addEventListener('keydown', function(e) {\n    if ((e.key === 'Escape') \u0026\u0026 SW.fs) swToggleFs();\n  });\n\n  // Init render\n  render();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003eBoost focus with the Pomodoro Technique → \u003ca href=\"https://productivity-works.com/tools/pomodoro-timer/\"\u003ePomodoro Timer\u003c/a\u003e\n\u003c/p\u003e","title":"Stopwatch - Free Online Stopwatch with Lap Timer"},{"content":" Stopwatch Timer 🌙 ⏰ Time's up! Click to dismiss. 00:00:00.00 Start Lap Reset Space Start/Stop \u0026nbsp; L Lap \u0026nbsp; R Reset Lap Times #Lap TimeTotal No laps yet Hours : Min : Sec 00:05:00.00 Start Reset Space Start/Stop \u0026nbsp; R Reset Plan your day → Daily Planner Event countdown → Event Countdown ","permalink":"https://productivity-works.com/tools/stopwatch-timer/","summary":"\u003cdiv id=\"sw-app\"\u003e\n\u003cstyle\u003e\n#sw-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 600px;\n  margin: 0 auto;\n  padding: 16px;\n  box-sizing: border-box;\n}\n#sw-app * {\n  box-sizing: border-box;\n}\n#sw-app .sw-card {\n  background: var(--sw-bg, #1a1a2e);\n  border-radius: 16px;\n  padding: 24px;\n  box-shadow: 0 8px 32px rgba(0,0,0,0.18);\n  transition: background 0.3s;\n}\n#sw-app.sw-light .sw-card {\n  background: #f8fafc;\n  box-shadow: 0 4px 24px rgba(0,0,0,0.10);\n}\n#sw-app .sw-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin-bottom: 20px;\n}\n#sw-app .sw-mode-tabs {\n  display: flex;\n  background: rgba(255,255,255,0.08);\n  border-radius: 10px;\n  padding: 3px;\n  gap: 2px;\n}\n#sw-app.sw-light .sw-mode-tabs {\n  background: #e2e8f0;\n}\n#sw-app .sw-tab {\n  padding: 7px 20px;\n  border: none;\n  border-radius: 8px;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  background: transparent;\n  color: rgba(255,255,255,0.55);\n  transition: all 0.2s;\n}\n#sw-app.sw-light .sw-tab {\n  color: #64748b;\n}\n#sw-app .sw-tab.active {\n  background: #6366f1;\n  color: #fff;\n}\n#sw-app .sw-theme-btn {\n  background: rgba(255,255,255,0.1);\n  border: none;\n  border-radius: 8px;\n  padding: 7px 12px;\n  color: #fff;\n  cursor: pointer;\n  font-size: 18px;\n  transition: background 0.2s;\n}\n#sw-app.sw-light .sw-theme-btn {\n  background: #e2e8f0;\n  color: #334155;\n}\n#sw-app .sw-theme-btn:hover {\n  background: rgba(255,255,255,0.2);\n}\n#sw-app.sw-light .sw-theme-btn:hover {\n  background: #cbd5e1;\n}\n#sw-app .sw-display {\n  text-align: center;\n  margin: 20px 0 24px;\n}\n#sw-app .sw-time {\n  font-size: clamp(48px, 12vw, 72px);\n  font-weight: 700;\n  font-variant-numeric: tabular-nums;\n  letter-spacing: 2px;\n  color: #e2e8f0;\n  line-height: 1.1;\n  transition: color 0.3s;\n}\n#sw-app.sw-light .sw-time {\n  color: #1e293b;\n}\n#sw-app .sw-ms {\n  font-size: clamp(24px, 6vw, 36px);\n  color: #6366f1;\n  font-weight: 700;\n  font-variant-numeric: tabular-nums;\n}\n#sw-app .sw-time-alarm {\n  animation: sw-pulse 0.5s ease infinite alternate;\n}\n@keyframes sw-pulse {\n  from { color: #e2e8f0; }\n  to { color: #f43f5e; }\n}\n#sw-app.sw-light .sw-time-alarm {\n  animation: sw-pulse-light 0.5s ease infinite alternate;\n}\n@keyframes sw-pulse-light {\n  from { color: #1e293b; }\n  to { color: #e11d48; }\n}\n#sw-app .sw-timer-inputs {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  gap: 4px;\n  margin-bottom: 20px;\n}\n#sw-app .sw-time-input-wrap {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 4px;\n}\n#sw-app .sw-time-input-label {\n  font-size: 11px;\n  color: rgba(255,255,255,0.45);\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 1px;\n}\n#sw-app.sw-light .sw-time-input-label {\n  color: #94a3b8;\n}\n#sw-app .sw-time-input {\n  width: 72px;\n  text-align: center;\n  font-size: 32px;\n  font-weight: 700;\n  font-variant-numeric: tabular-nums;\n  background: rgba(255,255,255,0.08);\n  border: 2px solid rgba(255,255,255,0.12);\n  border-radius: 10px;\n  color: #e2e8f0;\n  padding: 8px 4px;\n  outline: none;\n  transition: border-color 0.2s;\n  -moz-appearance: textfield;\n}\n#sw-app .sw-time-input::-webkit-outer-spin-button,\n#sw-app .sw-time-input::-webkit-inner-spin-button {\n  -webkit-appearance: none;\n  margin: 0;\n}\n#sw-app.sw-light .sw-time-input {\n  background: #fff;\n  border-color: #cbd5e1;\n  color: #1e293b;\n}\n#sw-app .sw-time-input:focus {\n  border-color: #6366f1;\n}\n#sw-app .sw-time-sep {\n  font-size: 32px;\n  font-weight: 700;\n  color: rgba(255,255,255,0.4);\n  padding-bottom: 18px;\n}\n#sw-app.sw-light .sw-time-sep {\n  color: #94a3b8;\n}\n#sw-app .sw-controls {\n  display: flex;\n  justify-content: center;\n  gap: 12px;\n  flex-wrap: wrap;\n  margin-bottom: 20px;\n}\n#sw-app .sw-btn {\n  padding: 12px 28px;\n  border: none;\n  border-radius: 50px;\n  font-size: 15px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: transform 0.1s, opacity 0.2s, box-shadow 0.2s;\n  outline: none;\n}\n#sw-app .sw-btn:active {\n  transform: scale(0.96);\n}\n#sw-app .sw-btn-start {\n  background: linear-gradient(135deg, #6366f1, #8b5cf6);\n  color: #fff;\n  box-shadow: 0 4px 14px rgba(99,102,241,0.4);\n  min-width: 120px;\n}\n#sw-app .sw-btn-stop {\n  background: linear-gradient(135deg, #f43f5e, #e11d48);\n  color: #fff;\n  box-shadow: 0 4px 14px rgba(244,63,94,0.4);\n  min-width: 120px;\n}\n#sw-app .sw-btn-lap {\n  background: rgba(255,255,255,0.1);\n  color: #e2e8f0;\n}\n#sw-app.sw-light .sw-btn-lap {\n  background: #e2e8f0;\n  color: #334155;\n}\n#sw-app .sw-btn-reset {\n  background: rgba(255,255,255,0.07);\n  color: rgba(255,255,255,0.7);\n}\n#sw-app.sw-light .sw-btn-reset {\n  background: #f1f5f9;\n  color: #64748b;\n}\n#sw-app .sw-btn:disabled {\n  opacity: 0.35;\n  cursor: not-allowed;\n}\n#sw-app .sw-shortcuts {\n  text-align: center;\n  font-size: 12px;\n  color: rgba(255,255,255,0.3);\n  margin-bottom: 16px;\n  letter-spacing: 0.5px;\n}\n#sw-app.sw-light .sw-shortcuts {\n  color: #94a3b8;\n}\n#sw-app .sw-shortcuts kbd {\n  background: rgba(255,255,255,0.1);\n  border-radius: 4px;\n  padding: 1px 5px;\n  font-family: monospace;\n  font-size: 11px;\n}\n#sw-app.sw-light .sw-shortcuts kbd {\n  background: #e2e8f0;\n}\n#sw-app .sw-laps {\n  margin-top: 8px;\n}\n#sw-app .sw-laps-title {\n  font-size: 13px;\n  font-weight: 700;\n  color: rgba(255,255,255,0.5);\n  text-transform: uppercase;\n  letter-spacing: 1px;\n  margin-bottom: 10px;\n}\n#sw-app.sw-light .sw-laps-title {\n  color: #94a3b8;\n}\n#sw-app .sw-laps-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 14px;\n}\n#sw-app .sw-laps-table th {\n  text-align: left;\n  padding: 6px 10px;\n  color: rgba(255,255,255,0.35);\n  font-weight: 600;\n  font-size: 12px;\n  border-bottom: 1px solid rgba(255,255,255,0.07);\n}\n#sw-app.sw-light .sw-laps-table th {\n  color: #94a3b8;\n  border-bottom-color: #e2e8f0;\n}\n#sw-app .sw-laps-table td {\n  padding: 8px 10px;\n  color: #e2e8f0;\n  border-bottom: 1px solid rgba(255,255,255,0.05);\n  font-variant-numeric: tabular-nums;\n}\n#sw-app.sw-light .sw-laps-table td {\n  color: #334155;\n  border-bottom-color: #f1f5f9;\n}\n#sw-app .sw-laps-table tr:last-child td {\n  border-bottom: none;\n}\n#sw-app .sw-laps-table .sw-lap-fastest {\n  color: #34d399;\n}\n#sw-app .sw-laps-table .sw-lap-slowest {\n  color: #f87171;\n}\n#sw-app .sw-laps-empty {\n  text-align: center;\n  color: rgba(255,255,255,0.2);\n  font-size: 13px;\n  padding: 18px 0;\n}\n#sw-app.sw-light .sw-laps-empty {\n  color: #cbd5e1;\n}\n#sw-app .sw-alarm-banner {\n  display: none;\n  background: linear-gradient(135deg, #f43f5e, #e11d48);\n  color: #fff;\n  text-align: center;\n  padding: 12px 16px;\n  border-radius: 10px;\n  font-size: 15px;\n  font-weight: 700;\n  margin-bottom: 16px;\n  cursor: pointer;\n  animation: sw-banner-in 0.3s ease;\n}\n@keyframes sw-banner-in {\n  from { opacity: 0; transform: translateY(-10px); }\n  to { opacity: 1; transform: translateY(0); }\n}\n#sw-app .sw-alarm-banner.show {\n  display: block;\n}\n#sw-app .sw-progress-ring {\n  display: flex;\n  justify-content: center;\n  margin-bottom: 8px;\n}\n#sw-app .sw-progress-ring svg {\n  transform: rotate(-90deg);\n}\n#sw-app .sw-progress-track {\n  fill: none;\n  stroke: rgba(255,255,255,0.08);\n  stroke-width: 6;\n}\n#sw-app.sw-light .sw-progress-track {\n  stroke: #e2e8f0;\n}\n#sw-app .sw-progress-fill {\n  fill: none;\n  stroke: #6366f1;\n  stroke-width: 6;\n  stroke-linecap: round;\n  transition: stroke-dashoffset 0.1s linear;\n}\n@media (max-width: 480px) {\n  #sw-app .sw-btn {\n    padding: 11px 18px;\n    font-size: 14px;\n  }\n  #sw-app .sw-time-input {\n    width: 60px;\n    font-size: 26px;\n  }\n}\n\u003c/style\u003e\n\u003cdiv class=\"sw-card\"\u003e\n  \u003cdiv class=\"sw-header\"\u003e\n    \u003cdiv class=\"sw-mode-tabs\"\u003e\n      \u003cbutton class=\"sw-tab active\" id=\"sw-tab-stopwatch\"\u003eStopwatch\u003c/button\u003e\n      \u003cbutton class=\"sw-tab\" id=\"sw-tab-timer\"\u003eTimer\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cbutton class=\"sw-theme-btn\" id=\"sw-theme-btn\" title=\"Toggle theme\"\u003e🌙\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv id=\"sw-alarm-banner\" class=\"sw-alarm-banner\"\u003e\n    ⏰ Time's up! Click to dismiss.\n  \u003c/div\u003e\n  \u003c!-- Stopwatch panel --\u003e\n  \u003cdiv id=\"sw-stopwatch-panel\"\u003e\n    \u003cdiv class=\"sw-display\"\u003e\n      \u003cdiv class=\"sw-time\" id=\"sw-sw-display\"\u003e\n        \u003cspan id=\"sw-sw-hms\"\u003e00:00:00\u003c/span\u003e\u003cspan class=\"sw-ms\"\u003e.\u003cspan id=\"sw-sw-ms\"\u003e00\u003c/span\u003e\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"sw-controls\"\u003e\n      \u003cbutton class=\"sw-btn sw-btn-start\" id=\"sw-sw-startstop\"\u003eStart\u003c/button\u003e\n      \u003cbutton class=\"sw-btn sw-btn-lap\" id=\"sw-sw-lap\" disabled\u003eLap\u003c/button\u003e\n      \u003cbutton class=\"sw-btn sw-btn-reset\" id=\"sw-sw-reset\"\u003eReset\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"sw-shortcuts\"\u003e\n      \u003ckbd\u003eSpace\u003c/kbd\u003e Start/Stop \u0026nbsp; \u003ckbd\u003eL\u003c/kbd\u003e Lap \u0026nbsp; \u003ckbd\u003eR\u003c/kbd\u003e Reset\n    \u003c/div\u003e\n    \u003cdiv class=\"sw-laps\" id=\"sw-laps-section\"\u003e\n      \u003cdiv class=\"sw-laps-title\"\u003eLap Times\u003c/div\u003e\n      \u003ctable class=\"sw-laps-table\" id=\"sw-laps-table\"\u003e\n        \u003cthead\u003e\u003ctr\u003e\u003cth\u003e#\u003c/th\u003e\u003cth\u003eLap Time\u003c/th\u003e\u003cth\u003eTotal\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e\n        \u003ctbody id=\"sw-laps-body\"\u003e\n          \u003ctr\u003e\u003ctd colspan=\"3\" class=\"sw-laps-empty\"\u003eNo laps yet\u003c/td\u003e\u003c/tr\u003e\n        \u003c/tbody\u003e\n      \u003c/table\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Timer panel --\u003e\n  \u003cdiv id=\"sw-timer-panel\" style=\"display:none;\"\u003e\n    \u003cdiv id=\"sw-timer-inputs-wrap\"\u003e\n      \u003cdiv class=\"sw-timer-inputs\"\u003e\n        \u003cdiv class=\"sw-time-input-wrap\"\u003e\n          \u003cspan class=\"sw-time-input-label\"\u003eHours\u003c/span\u003e\n          \u003cinput type=\"number\" class=\"sw-time-input\" id=\"sw-t-h\" value=\"0\" min=\"0\" max=\"99\"\u003e\n        \u003c/div\u003e\n        \u003cdiv class=\"sw-time-sep\"\u003e:\u003c/div\u003e\n        \u003cdiv class=\"sw-time-input-wrap\"\u003e\n          \u003cspan class=\"sw-time-input-label\"\u003eMin\u003c/span\u003e\n          \u003cinput type=\"number\" class=\"sw-time-input\" id=\"sw-t-m\" value=\"5\" min=\"0\" max=\"59\"\u003e\n        \u003c/div\u003e\n        \u003cdiv class=\"sw-time-sep\"\u003e:\u003c/div\u003e\n        \u003cdiv class=\"sw-time-input-wrap\"\u003e\n          \u003cspan class=\"sw-time-input-label\"\u003eSec\u003c/span\u003e\n          \u003cinput type=\"number\" class=\"sw-time-input\" id=\"sw-t-s\" value=\"0\" min=\"0\" max=\"59\"\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"sw-progress-ring\" id=\"sw-progress-ring\" style=\"display:none;\"\u003e\n      \u003csvg width=\"160\" height=\"160\" viewBox=\"0 0 160 160\"\u003e\n        \u003ccircle class=\"sw-progress-track\" cx=\"80\" cy=\"80\" r=\"70\"/\u003e\n        \u003ccircle class=\"sw-progress-fill\" cx=\"80\" cy=\"80\" r=\"70\" id=\"sw-progress-fill\"\n          stroke-dasharray=\"439.82\"\n          stroke-dashoffset=\"0\"/\u003e\n      \u003c/svg\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"sw-display\" id=\"sw-timer-display-wrap\" style=\"display:none;\"\u003e\n      \u003cdiv class=\"sw-time\" id=\"sw-t-display\"\u003e\n        \u003cspan id=\"sw-t-hms\"\u003e00:05:00\u003c/span\u003e\u003cspan class=\"sw-ms\"\u003e.\u003cspan id=\"sw-t-ms\"\u003e00\u003c/span\u003e\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"sw-controls\"\u003e\n      \u003cbutton class=\"sw-btn sw-btn-start\" id=\"sw-t-startstop\"\u003eStart\u003c/button\u003e\n      \u003cbutton class=\"sw-btn sw-btn-reset\" id=\"sw-t-reset\"\u003eReset\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"sw-shortcuts\"\u003e\n      \u003ckbd\u003eSpace\u003c/kbd\u003e Start/Stop \u0026nbsp; \u003ckbd\u003eR\u003c/kbd\u003e Reset\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var app = document.getElementById('sw-app');\n\n  // --- Theme ---\n  var themeBtn = document.getElementById('sw-theme-btn');\n  var isLight = false;\n  themeBtn.addEventListener('click', function() {\n    isLight = !isLight;\n    app.classList.toggle('sw-light', isLight);\n    themeBtn.textContent = isLight ? '🌙' : '☀️';\n  });\n\n  // --- Tab switching ---\n  var tabSW = document.getElementById('sw-tab-stopwatch');\n  var tabTimer = document.getElementById('sw-tab-timer');\n  var panelSW = document.getElementById('sw-stopwatch-panel');\n  var panelTimer = document.getElementById('sw-timer-panel');\n  var currentMode = 'stopwatch';\n\n  tabSW.addEventListener('click', function() {\n    if (currentMode === 'stopwatch') return;\n    currentMode = 'stopwatch';\n    tabSW.classList.add('active');\n    tabTimer.classList.remove('active');\n    panelSW.style.display = '';\n    panelTimer.style.display = 'none';\n  });\n  tabTimer.addEventListener('click', function() {\n    if (currentMode === 'timer') return;\n    currentMode = 'timer';\n    tabTimer.classList.add('active');\n    tabSW.classList.remove('active');\n    panelSW.style.display = 'none';\n    panelTimer.style.display = '';\n  });\n\n  // ============================================================\n  // STOPWATCH\n  // ============================================================\n  var swRunning = false;\n  var swStartTime = 0;\n  var swElapsed = 0;\n  var swLapStart = 0;\n  var swLaps = [];\n  var swRaf = null;\n\n  var swDisplay = document.getElementById('sw-sw-hms');\n  var swMs = document.getElementById('sw-sw-ms');\n  var swDisplayWrap = document.getElementById('sw-sw-display');\n  var swBtnSS = document.getElementById('sw-sw-startstop');\n  var swBtnLap = document.getElementById('sw-sw-lap');\n  var swBtnReset = document.getElementById('sw-sw-reset');\n  var swLapsBody = document.getElementById('sw-laps-body');\n\n  function pad2(n) { return String(Math.floor(n)).padStart(2, '0'); }\n\n  function formatTime(ms) {\n    var totalSec = Math.floor(ms / 1000);\n    var h = Math.floor(totalSec / 3600);\n    var m = Math.floor((totalSec % 3600) / 60);\n    var s = totalSec % 60;\n    return pad2(h) + ':' + pad2(m) + ':' + pad2(s);\n  }\n\n  function formatMs(ms) {\n    return pad2((ms % 1000) / 10);\n  }\n\n  function swTick() {\n    var now = performance.now();\n    var total = swElapsed + (now - swStartTime);\n    swDisplay.textContent = formatTime(total);\n    swMs.textContent = formatMs(total);\n    swRaf = requestAnimationFrame(swTick);\n  }\n\n  swBtnSS.addEventListener('click', swToggle);\n  swBtnLap.addEventListener('click', swLap);\n  swBtnReset.addEventListener('click', swReset);\n\n  function swToggle() {\n    if (swRunning) {\n      swRunning = false;\n      swElapsed += performance.now() - swStartTime;\n      cancelAnimationFrame(swRaf);\n      swBtnSS.textContent = 'Start';\n      swBtnSS.className = 'sw-btn sw-btn-start';\n    } else {\n      swRunning = true;\n      swStartTime = performance.now();\n      if (swLaps.length === 0) swLapStart = 0;\n      swRaf = requestAnimationFrame(swTick);\n      swBtnSS.textContent = 'Stop';\n      swBtnSS.className = 'sw-btn sw-btn-stop';\n      swBtnLap.disabled = false;\n    }\n  }\n\n  function swLap() {\n    if (!swRunning) return;\n    var now = performance.now();\n    var total = swElapsed + (now - swStartTime);\n    var lapTime = total - swLapStart;\n    swLapStart = total;\n    swLaps.push({ lap: lapTime, total: total });\n    swRenderLaps();\n  }\n\n  function swReset() {\n    swRunning = false;\n    cancelAnimationFrame(swRaf);\n    swElapsed = 0;\n    swLapStart = 0;\n    swLaps = [];\n    swDisplay.textContent = '00:00:00';\n    swMs.textContent = '00';\n    swBtnSS.textContent = 'Start';\n    swBtnSS.className = 'sw-btn sw-btn-start';\n    swBtnLap.disabled = true;\n    swRenderLaps();\n  }\n\n  function swRenderLaps() {\n    if (swLaps.length === 0) {\n      swLapsBody.innerHTML = '\u003ctr\u003e\u003ctd colspan=\"3\" class=\"sw-laps-empty\"\u003eNo laps yet\u003c/td\u003e\u003c/tr\u003e';\n      return;\n    }\n    var lapTimes = swLaps.map(function(l) { return l.lap; });\n    var minLap = Math.min.apply(null, lapTimes);\n    var maxLap = Math.max.apply(null, lapTimes);\n    var html = '';\n    for (var i = swLaps.length - 1; i \u003e= 0; i--) {\n      var cls = '';\n      if (swLaps.length \u003e 1) {\n        if (swLaps[i].lap === minLap) cls = ' class=\"sw-lap-fastest\"';\n        else if (swLaps[i].lap === maxLap) cls = ' class=\"sw-lap-slowest\"';\n      }\n      html += '\u003ctr' + cls + '\u003e\u003ctd\u003e' + (i + 1) + '\u003c/td\u003e\u003ctd\u003e' +\n        formatTime(swLaps[i].lap) + '.' + formatMs(swLaps[i].lap) +\n        '\u003c/td\u003e\u003ctd\u003e' +\n        formatTime(swLaps[i].total) + '.' + formatMs(swLaps[i].total) +\n        '\u003c/td\u003e\u003c/tr\u003e';\n    }\n    swLapsBody.innerHTML = html;\n  }\n\n  // ============================================================\n  // TIMER\n  // ============================================================\n  var tRunning = false;\n  var tStartTime = 0;\n  var tTotalMs = 0;\n  var tRemaining = 0;\n  var tRaf = null;\n  var tFinished = false;\n  var tAudioCtx = null;\n  var tAlarmInterval = null;\n\n  var tH = document.getElementById('sw-t-h');\n  var tM = document.getElementById('sw-t-m');\n  var tS = document.getElementById('sw-t-s');\n  var tDisplay = document.getElementById('sw-t-hms');\n  var tDisplayMs = document.getElementById('sw-t-ms');\n  var tDisplayWrap = document.getElementById('sw-t-display');\n  var tDisplaySection = document.getElementById('sw-timer-display-wrap');\n  var tInputsWrap = document.getElementById('sw-timer-inputs-wrap');\n  var tProgressRing = document.getElementById('sw-progress-ring');\n  var tProgressFill = document.getElementById('sw-progress-fill');\n  var tBtnSS = document.getElementById('sw-t-startstop');\n  var tBtnReset = document.getElementById('sw-t-reset');\n  var alarmBanner = document.getElementById('sw-alarm-banner');\n  var CIRCUMFERENCE = 2 * Math.PI * 70; // 439.82\n\n  tBtnSS.addEventListener('click', tToggle);\n  tBtnReset.addEventListener('click', tReset);\n  alarmBanner.addEventListener('click', tDismissAlarm);\n\n  function tGetInputMs() {\n    var h = parseInt(tH.value) || 0;\n    var m = parseInt(tM.value) || 0;\n    var s = parseInt(tS.value) || 0;\n    return (h * 3600 + m * 60 + s) * 1000;\n  }\n\n  function tToggle() {\n    if (tFinished) { tReset(); return; }\n    if (tRunning) {\n      tRunning = false;\n      tRemaining -= performance.now() - tStartTime;\n      if (tRemaining \u003c 0) tRemaining = 0;\n      cancelAnimationFrame(tRaf);\n      tBtnSS.textContent = 'Resume';\n      tBtnSS.className = 'sw-btn sw-btn-start';\n    } else {\n      if (tRemaining === 0 \u0026\u0026 !tFinished) {\n        tTotalMs = tGetInputMs();\n        if (tTotalMs \u003c= 0) return;\n        tRemaining = tTotalMs;\n        tInputsWrap.style.display = 'none';\n        tProgressRing.style.display = 'flex';\n        tDisplaySection.style.display = '';\n      }\n      tRunning = true;\n      tStartTime = performance.now();\n      tRaf = requestAnimationFrame(tTick);\n      tBtnSS.textContent = 'Pause';\n      tBtnSS.className = 'sw-btn sw-btn-stop';\n    }\n  }\n\n  function tTick() {\n    var now = performance.now();\n    var elapsed = now - tStartTime;\n    var left = tRemaining - elapsed;\n    if (left \u003c= 0) {\n      left = 0;\n      tRunning = false;\n      tFinished = true;\n      cancelAnimationFrame(tRaf);\n      tRenderTimer(0);\n      tFireAlarm();\n      return;\n    }\n    tRenderTimer(left);\n    tRaf = requestAnimationFrame(tTick);\n  }\n\n  function tRenderTimer(ms) {\n    tDisplay.textContent = formatTime(ms);\n    tDisplayMs.textContent = formatMs(ms);\n    var fraction = tTotalMs \u003e 0 ? ms / tTotalMs : 0;\n    var offset = CIRCUMFERENCE * (1 - fraction);\n    tProgressFill.setAttribute('stroke-dashoffset', offset.toFixed(2));\n  }\n\n  function tReset() {\n    tRunning = false;\n    tFinished = false;\n    cancelAnimationFrame(tRaf);\n    tDismissAlarm();\n    tRemaining = 0;\n    tInputsWrap.style.display = '';\n    tProgressRing.style.display = 'none';\n    tDisplaySection.style.display = 'none';\n    tBtnSS.textContent = 'Start';\n    tBtnSS.className = 'sw-btn sw-btn-start';\n    tProgressFill.setAttribute('stroke-dashoffset', '0');\n    document.getElementById('sw-t-display').classList.remove('sw-time-alarm');\n  }\n\n  function tFireAlarm() {\n    alarmBanner.classList.add('show');\n    document.getElementById('sw-t-display').classList.add('sw-time-alarm');\n    tBtnSS.textContent = 'Reset';\n    tBtnSS.className = 'sw-btn sw-btn-reset';\n    tPlayAlarmSound();\n    tAlarmInterval = setInterval(tPlayAlarmSound, 2000);\n  }\n\n  function tDismissAlarm() {\n    alarmBanner.classList.remove('show');\n    clearInterval(tAlarmInterval);\n    tAlarmInterval = null;\n  }\n\n  function tPlayAlarmSound() {\n    try {\n      var AudioContext = window.AudioContext || window.webkitAudioContext;\n      if (!AudioContext) return;\n      var ctx = new AudioContext();\n      function beep(freq, start, dur) {\n        var osc = ctx.createOscillator();\n        var gain = ctx.createGain();\n        osc.connect(gain);\n        gain.connect(ctx.destination);\n        osc.type = 'sine';\n        osc.frequency.value = freq;\n        gain.gain.setValueAtTime(0.4, ctx.currentTime + start);\n        gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + start + dur);\n        osc.start(ctx.currentTime + start);\n        osc.stop(ctx.currentTime + start + dur + 0.05);\n      }\n      beep(880, 0, 0.15);\n      beep(1100, 0.18, 0.15);\n      beep(1320, 0.36, 0.3);\n    } catch(e) {}\n  }\n\n  // ============================================================\n  // KEYBOARD SHORTCUTS\n  // ============================================================\n  document.addEventListener('keydown', function(e) {\n    var tag = e.target.tagName;\n    if (tag === 'INPUT' || tag === 'TEXTAREA') return;\n    if (e.key === ' ' || e.code === 'Space') {\n      e.preventDefault();\n      if (currentMode === 'stopwatch') swToggle();\n      else tToggle();\n    } else if (e.key === 'l' || e.key === 'L') {\n      if (currentMode === 'stopwatch') swLap();\n    } else if (e.key === 'r' || e.key === 'R') {\n      if (currentMode === 'stopwatch') swReset();\n      else tReset();\n    }\n  });\n\n  // Clamp timer inputs\n  [tH, tM, tS].forEach(function(inp) {\n    inp.addEventListener('change', function() {\n      var max = inp === tH ? 99 : 59;\n      var val = parseInt(inp.value) || 0;\n      if (val \u003c 0) val = 0;\n      if (val \u003e max) val = max;\n      inp.value = val;\n    });\n  });\n\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003cblockquote\u003e\n\u003cp\u003ePlan your day → \u003ca href=\"https://productivity-works.com/tools/daily-planner/\"\u003eDaily Planner\u003c/a\u003e\n\u003c/p\u003e","title":"Stopwatch \u0026 Timer"},{"content":" Escape and unescape strings instantly for 10 language modes: JSON, JavaScript/TypeScript, Python, Java, C#, SQL, HTML, XML, CSV, and Regex. Paste raw text, pick a mode, and hit Escape — or paste escaped text and hit Unescape to recover the original. No data leaves your browser. Select Language / Context Bulk mode (process each line independently) Input Output Escape Unescape Copy Output Clear Escape Sequence Reference How to Use Select a mode — choose the target language or format from the buttons above. Paste input — enter the raw string you want to escape (or an already-escaped string to unescape). Escape / Unescape — click the appropriate button; the result appears instantly on the right. Bulk mode — when enabled, each line of input is escaped/unescaped independently, making it easy to process lists of strings. Copy Output — copies the result to your clipboard with one click. What Each Mode Handles JSON — \", \\, /, control chars, unicode (\\uXXXX) JavaScript / TypeScript — single \u0026amp; double quotes, backtick, \\n \\t \\r \\0, unicode Python — single \u0026amp; double quotes, \\n \\t \\r \\\\ \\0, raw-string awareness Java / C# — standard C-style escapes plus unicode \\uXXXX SQL — single-quote doubling, backslash mode toggle HTML — named entities: \u0026amp;amp; \u0026amp;lt; \u0026amp;gt; \u0026amp;quot; \u0026amp;apos; XML — same five entities as HTML, strictly per XML spec CSV — field quoting, embedded quote doubling, newline handling Regex — escapes all regex metacharacters: . * + ? ^ $ { } [ ] | ( ) \\ Related Tools JSON Formatter HTML Entity Encoder URL Encoder Related Tools Case Converter Character Counter Regex Cheatsheet ","permalink":"https://productivity-works.com/tools/string-escape-tool/","summary":"\u003cdiv id=\"esc-app\"\u003e\n\u003cstyle\u003e\n#esc-app *,\n#esc-app *::before,\n#esc-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\n#esc-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 15px;\n  color: #1a1a2e;\n  max-width: 860px;\n  margin: 0 auto;\n  padding: 0 8px 48px;\n}\n\n#esc-app h2 {\n  font-size: 1.1rem;\n  font-weight: 700;\n  margin-bottom: 12px;\n  color: #1a1a2e;\n}\n\n#esc-app .esc-intro {\n  background: #f0f4ff;\n  border-left: 4px solid #3b6ef8;\n  border-radius: 4px;\n  padding: 14px 16px;\n  margin-bottom: 24px;\n  font-size: 0.93rem;\n  color: #333;\n  line-height: 1.6;\n}\n\n/* Mode Selector */\n#esc-app .esc-mode-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 20px;\n}\n\n#esc-app .esc-mode-btn {\n  padding: 6px 14px;\n  border-radius: 20px;\n  border: 2px solid #d0d5e8;\n  background: #fff;\n  color: #555;\n  font-size: 0.85rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: border-color 0.15s, background 0.15s, color 0.15s;\n  white-space: nowrap;\n}\n\n#esc-app .esc-mode-btn:hover {\n  border-color: #3b6ef8;\n  color: #3b6ef8;\n}\n\n#esc-app .esc-mode-btn.active {\n  background: #3b6ef8;\n  border-color: #3b6ef8;\n  color: #fff;\n}\n\n/* Bulk toggle */\n#esc-app .esc-options-row {\n  display: flex;\n  align-items: center;\n  gap: 20px;\n  margin-bottom: 16px;\n  flex-wrap: wrap;\n}\n\n#esc-app .esc-toggle-label {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  cursor: pointer;\n  font-size: 0.88rem;\n  color: #444;\n  user-select: none;\n}\n\n#esc-app .esc-toggle-label input[type=\"checkbox\"] {\n  width: 16px;\n  height: 16px;\n  accent-color: #3b6ef8;\n  cursor: pointer;\n}\n\n#esc-app .esc-mode-desc {\n  font-size: 0.82rem;\n  color: #777;\n  font-style: italic;\n}\n\n/* Textareas */\n#esc-app .esc-panels {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 16px;\n  margin-bottom: 16px;\n}\n\n@media (max-width: 600px) {\n  #esc-app .esc-panels {\n    grid-template-columns: 1fr;\n  }\n}\n\n#esc-app .esc-panel {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n}\n\n#esc-app .esc-panel-label {\n  font-size: 0.82rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  color: #555;\n}\n\n#esc-app .esc-textarea {\n  width: 100%;\n  min-height: 200px;\n  padding: 12px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 0.88rem;\n  line-height: 1.6;\n  border: 2px solid #d0d5e8;\n  border-radius: 8px;\n  background: #fafbff;\n  color: #1a1a2e;\n  resize: vertical;\n  transition: border-color 0.15s;\n  outline: none;\n}\n\n#esc-app .esc-textarea:focus {\n  border-color: #3b6ef8;\n  background: #fff;\n}\n\n#esc-app .esc-textarea.output {\n  background: #f4f7ff;\n  color: #1a3a6e;\n}\n\n/* Action buttons */\n#esc-app .esc-action-row {\n  display: flex;\n  gap: 10px;\n  margin-bottom: 8px;\n  flex-wrap: wrap;\n  align-items: center;\n}\n\n#esc-app .esc-btn {\n  padding: 10px 24px;\n  border-radius: 8px;\n  border: none;\n  font-size: 0.9rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s;\n  letter-spacing: 0.02em;\n}\n\n#esc-app .esc-btn:active {\n  transform: scale(0.97);\n}\n\n#esc-app .esc-btn-escape {\n  background: #3b6ef8;\n  color: #fff;\n}\n\n#esc-app .esc-btn-escape:hover {\n  background: #2550d0;\n}\n\n#esc-app .esc-btn-unescape {\n  background: #12b886;\n  color: #fff;\n}\n\n#esc-app .esc-btn-unescape:hover {\n  background: #0a9a70;\n}\n\n#esc-app .esc-btn-copy {\n  background: #f0f4ff;\n  color: #3b6ef8;\n  border: 2px solid #3b6ef8;\n  padding: 8px 18px;\n}\n\n#esc-app .esc-btn-copy:hover {\n  background: #3b6ef8;\n  color: #fff;\n}\n\n#esc-app .esc-btn-clear {\n  background: #fff;\n  color: #999;\n  border: 2px solid #d0d5e8;\n  padding: 8px 14px;\n  font-size: 0.82rem;\n}\n\n#esc-app .esc-btn-clear:hover {\n  border-color: #e05c5c;\n  color: #e05c5c;\n}\n\n/* Status message */\n#esc-app .esc-status {\n  font-size: 0.82rem;\n  color: #12b886;\n  font-weight: 600;\n  min-height: 18px;\n  transition: opacity 0.3s;\n}\n\n#esc-app .esc-status.error {\n  color: #e05c5c;\n}\n\n/* Reference table */\n#esc-app .esc-ref {\n  margin-top: 32px;\n  background: #fafbff;\n  border: 1px solid #d0d5e8;\n  border-radius: 10px;\n  padding: 20px;\n}\n\n#esc-app .esc-ref h3 {\n  font-size: 0.95rem;\n  font-weight: 700;\n  margin-bottom: 14px;\n  color: #1a1a2e;\n}\n\n#esc-app .esc-ref-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\n  gap: 8px;\n}\n\n#esc-app .esc-ref-item {\n  background: #fff;\n  border: 1px solid #e0e5f0;\n  border-radius: 6px;\n  padding: 8px 12px;\n  font-size: 0.82rem;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  gap: 8px;\n}\n\n#esc-app .esc-ref-item code {\n  font-family: monospace;\n  background: #f0f4ff;\n  padding: 1px 5px;\n  border-radius: 3px;\n  color: #3b6ef8;\n  font-size: 0.88em;\n  white-space: nowrap;\n}\n\n#esc-app .esc-ref-item span {\n  color: #777;\n}\n\n/* How it works */\n#esc-app .esc-howto {\n  margin-top: 28px;\n}\n\n#esc-app .esc-howto h3 {\n  font-size: 1rem;\n  font-weight: 700;\n  margin-bottom: 10px;\n}\n\n#esc-app .esc-howto ul {\n  padding-left: 20px;\n  line-height: 1.85;\n  color: #444;\n  font-size: 0.92rem;\n}\n\n/* Related links */\n#esc-app .esc-related {\n  margin-top: 32px;\n  padding-top: 20px;\n  border-top: 1px solid #e0e5f0;\n}\n\n#esc-app .esc-related h3 {\n  font-size: 0.88rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  color: #777;\n  margin-bottom: 10px;\n}\n\n#esc-app .esc-related-links {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n}\n\n#esc-app .esc-related-links a {\n  display: inline-block;\n  padding: 7px 16px;\n  border-radius: 20px;\n  border: 2px solid #3b6ef8;\n  color: #3b6ef8;\n  font-size: 0.85rem;\n  font-weight: 600;\n  text-decoration: none;\n  transition: background 0.15s, color 0.15s;\n}\n\n#esc-app .esc-related-links a:hover {\n  background: #3b6ef8;\n  color: #fff;\n}\n\u003c/style\u003e\n\u003cdiv class=\"esc-intro\"\u003e\n  Escape and unescape strings instantly for 10 language modes: JSON, JavaScript/TypeScript, Python, Java, C#, SQL, HTML, XML, CSV, and Regex. Paste raw text, pick a mode, and hit \u003cstrong\u003eEscape\u003c/strong\u003e — or paste escaped text and hit \u003cstrong\u003eUnescape\u003c/strong\u003e to recover the original. No data leaves your browser.\n\u003c/div\u003e\n\u003ch2\u003eSelect Language / Context\u003c/h2\u003e\n\u003cdiv class=\"esc-mode-row\" id=\"escModeRow\"\u003e\u003c/div\u003e\n\u003cdiv class=\"esc-options-row\"\u003e\n  \u003clabel class=\"esc-toggle-label\"\u003e\n    \u003cinput type=\"checkbox\" id=\"escBulkMode\"\u003e\n    Bulk mode (process each line independently)\n  \u003c/label\u003e\n  \u003cspan class=\"esc-mode-desc\" id=\"escModeDesc\"\u003e\u003c/span\u003e\n\u003c/div\u003e\n\u003cdiv class=\"esc-panels\"\u003e\n  \u003cdiv class=\"esc-panel\"\u003e\n    \u003cdiv class=\"esc-panel-label\"\u003eInput\u003c/div\u003e\n    \u003ctextarea class=\"esc-textarea\" id=\"escInput\" placeholder=\"Paste your string here…\" spellcheck=\"false\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"esc-panel\"\u003e\n    \u003cdiv class=\"esc-panel-label\"\u003eOutput\u003c/div\u003e\n    \u003ctextarea class=\"esc-textarea output\" id=\"escOutput\" placeholder=\"Result will appear here…\" readonly spellcheck=\"false\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"esc-action-row\"\u003e\n  \u003cbutton class=\"esc-btn esc-btn-escape\" id=\"escEscapeBtn\"\u003eEscape\u003c/button\u003e\n  \u003cbutton class=\"esc-btn esc-btn-unescape\" id=\"escUnescapeBtn\"\u003eUnescape\u003c/button\u003e\n  \u003cbutton class=\"esc-btn esc-btn-copy\" id=\"escCopyBtn\"\u003eCopy Output\u003c/button\u003e\n  \u003cbutton class=\"esc-btn esc-btn-clear\" id=\"escClearBtn\"\u003eClear\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"esc-status\" id=\"escStatus\"\u003e\u003c/div\u003e\n\u003cdiv class=\"esc-ref\"\u003e\n  \u003ch3\u003eEscape Sequence Reference\u003c/h3\u003e\n  \u003cdiv class=\"esc-ref-grid\" id=\"escRefGrid\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"esc-howto\"\u003e\n  \u003ch3\u003eHow to Use\u003c/h3\u003e\n  \u003cul\u003e\n    \u003cli\u003e\u003cstrong\u003eSelect a mode\u003c/strong\u003e — choose the target language or format from the buttons above.\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003ePaste input\u003c/strong\u003e — enter the raw string you want to escape (or an already-escaped string to unescape).\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003eEscape / Unescape\u003c/strong\u003e — click the appropriate button; the result appears instantly on the right.\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003eBulk mode\u003c/strong\u003e — when enabled, each line of input is escaped/unescaped independently, making it easy to process lists of strings.\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003eCopy Output\u003c/strong\u003e — copies the result to your clipboard with one click.\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cdiv class=\"esc-howto\" style=\"margin-top:20px;\"\u003e\n  \u003ch3\u003eWhat Each Mode Handles\u003c/h3\u003e\n  \u003cul\u003e\n    \u003cli\u003e\u003cstrong\u003eJSON\u003c/strong\u003e — \u003ccode\u003e\"\u003c/code\u003e, \u003ccode\u003e\\\u003c/code\u003e, \u003ccode\u003e/\u003c/code\u003e, control chars, unicode (\u003ccode\u003e\\uXXXX\u003c/code\u003e)\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003eJavaScript / TypeScript\u003c/strong\u003e — single \u0026amp; double quotes, backtick, \u003ccode\u003e\\n \\t \\r \\0\u003c/code\u003e, unicode\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003ePython\u003c/strong\u003e — single \u0026amp; double quotes, \u003ccode\u003e\\n \\t \\r \\\\ \\0\u003c/code\u003e, raw-string awareness\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003eJava / C#\u003c/strong\u003e — standard C-style escapes plus unicode \u003ccode\u003e\\uXXXX\u003c/code\u003e\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003eSQL\u003c/strong\u003e — single-quote doubling, backslash mode toggle\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003eHTML\u003c/strong\u003e — named entities: \u003ccode\u003e\u0026amp;amp; \u0026amp;lt; \u0026amp;gt; \u0026amp;quot; \u0026amp;apos;\u003c/code\u003e\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003eXML\u003c/strong\u003e — same five entities as HTML, strictly per XML spec\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003eCSV\u003c/strong\u003e — field quoting, embedded quote doubling, newline handling\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003eRegex\u003c/strong\u003e — escapes all regex metacharacters: \u003ccode\u003e. * + ? ^ $ { } [ ] | ( ) \\\u003c/code\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cdiv class=\"esc-related\"\u003e\n  \u003ch3\u003eRelated Tools\u003c/h3\u003e\n  \u003cdiv class=\"esc-related-links\"\u003e\n    \u003ca href=\"/tools/json-formatter/\"\u003eJSON Formatter\u003c/a\u003e\n    \u003ca href=\"/tools/html-entity-encoder/\"\u003eHTML Entity Encoder\u003c/a\u003e\n    \u003ca href=\"/tools/url-encoder/\"\u003eURL Encoder\u003c/a\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  'use strict';\n\n  /* ── Mode definitions ── */\n  const MODES = [\n    {\n      id: 'json',\n      label: 'JSON',\n      desc: 'Escapes \\\\ \" / and control characters per RFC 8259',\n      refs: [\n        { seq: '\\\\\\\\', desc: 'Backslash' },\n        { seq: '\\\\\"', desc: 'Double quote' },\n        { seq: '\\\\/', desc: 'Forward slash' },\n        { seq: '\\\\n', desc: 'Newline' },\n        { seq: '\\\\r', desc: 'Carriage return' },\n        { seq: '\\\\t', desc: 'Tab' },\n        { seq: '\\\\b', desc: 'Backspace' },\n        { seq: '\\\\f', desc: 'Form feed' },\n        { seq: '\\\\uXXXX', desc: 'Unicode' },\n      ],\n      escape(s) {\n        return s\n          .replace(/\\\\/g, '\\\\\\\\')\n          .replace(/\"/g, '\\\\\"')\n          .replace(/\\//g, '\\\\/')\n          .replace(/\\n/g, '\\\\n')\n          .replace(/\\r/g, '\\\\r')\n          .replace(/\\t/g, '\\\\t')\n          .replace(/\\b/g, '\\\\b')\n          .replace(/\\f/g, '\\\\f')\n          .replace(/[\\x00-\\x1f\\x7f]/g, c =\u003e '\\\\u' + c.charCodeAt(0).toString(16).padStart(4, '0').toUpperCase());\n      },\n      unescape(s) {\n        return s\n          .replace(/\\\\u([0-9a-fA-F]{4})/g, (_, h) =\u003e String.fromCharCode(parseInt(h, 16)))\n          .replace(/\\\\\"/g, '\"')\n          .replace(/\\\\\\//g, '/')\n          .replace(/\\\\n/g, '\\n')\n          .replace(/\\\\r/g, '\\r')\n          .replace(/\\\\t/g, '\\t')\n          .replace(/\\\\b/g, '\\b')\n          .replace(/\\\\f/g, '\\f')\n          .replace(/\\\\\\\\/g, '\\\\');\n      },\n    },\n    {\n      id: 'js',\n      label: 'JS / TS',\n      desc: 'Escapes for JavaScript and TypeScript string literals',\n      refs: [\n        { seq: '\\\\\\\\', desc: 'Backslash' },\n        { seq: \"\\\\'\", desc: 'Single quote' },\n        { seq: '\\\\\"', desc: 'Double quote' },\n        { seq: '\\\\`', desc: 'Backtick' },\n        { seq: '\\\\n', desc: 'Newline' },\n        { seq: '\\\\t', desc: 'Tab' },\n        { seq: '\\\\r', desc: 'Carriage return' },\n        { seq: '\\\\0', desc: 'Null char' },\n        { seq: '\\\\uXXXX', desc: 'Unicode' },\n      ],\n      escape(s) {\n        return s\n          .replace(/\\\\/g, '\\\\\\\\')\n          .replace(/'/g, \"\\\\'\")\n          .replace(/\"/g, '\\\\\"')\n          .replace(/`/g, '\\\\`')\n          .replace(/\\n/g, '\\\\n')\n          .replace(/\\r/g, '\\\\r')\n          .replace(/\\t/g, '\\\\t')\n          .replace(/\\0/g, '\\\\0')\n          .replace(/[\\x00-\\x1f\\x7f]/g, c =\u003e '\\\\u' + c.charCodeAt(0).toString(16).padStart(4, '0').toUpperCase());\n      },\n      unescape(s) {\n        return s\n          .replace(/\\\\u([0-9a-fA-F]{4})/g, (_, h) =\u003e String.fromCharCode(parseInt(h, 16)))\n          .replace(/\\\\n/g, '\\n')\n          .replace(/\\\\r/g, '\\r')\n          .replace(/\\\\t/g, '\\t')\n          .replace(/\\\\0/g, '\\0')\n          .replace(/\\\\'/g, \"'\")\n          .replace(/\\\\\"/g, '\"')\n          .replace(/\\\\`/g, '`')\n          .replace(/\\\\\\\\/g, '\\\\');\n      },\n    },\n    {\n      id: 'python',\n      label: 'Python',\n      desc: 'Escapes for Python string literals',\n      refs: [\n        { seq: '\\\\\\\\', desc: 'Backslash' },\n        { seq: \"\\\\'\", desc: 'Single quote' },\n        { seq: '\\\\\"', desc: 'Double quote' },\n        { seq: '\\\\n', desc: 'Newline' },\n        { seq: '\\\\t', desc: 'Tab' },\n        { seq: '\\\\r', desc: 'Carriage return' },\n        { seq: '\\\\0', desc: 'Null char' },\n        { seq: '\\\\xHH', desc: 'Hex escape' },\n        { seq: '\\\\uXXXX', desc: 'Unicode' },\n      ],\n      escape(s) {\n        return s\n          .replace(/\\\\/g, '\\\\\\\\')\n          .replace(/'/g, \"\\\\'\")\n          .replace(/\"/g, '\\\\\"')\n          .replace(/\\n/g, '\\\\n')\n          .replace(/\\r/g, '\\\\r')\n          .replace(/\\t/g, '\\\\t')\n          .replace(/\\0/g, '\\\\0')\n          .replace(/[\\x01-\\x1f\\x7f]/g, c =\u003e '\\\\x' + c.charCodeAt(0).toString(16).padStart(2, '0'));\n      },\n      unescape(s) {\n        return s\n          .replace(/\\\\u([0-9a-fA-F]{4})/g, (_, h) =\u003e String.fromCharCode(parseInt(h, 16)))\n          .replace(/\\\\x([0-9a-fA-F]{2})/g, (_, h) =\u003e String.fromCharCode(parseInt(h, 16)))\n          .replace(/\\\\n/g, '\\n')\n          .replace(/\\\\r/g, '\\r')\n          .replace(/\\\\t/g, '\\t')\n          .replace(/\\\\0/g, '\\0')\n          .replace(/\\\\'/g, \"'\")\n          .replace(/\\\\\"/g, '\"')\n          .replace(/\\\\\\\\/g, '\\\\');\n      },\n    },\n    {\n      id: 'java',\n      label: 'Java',\n      desc: 'Java string literal escaping (also valid for Kotlin, Groovy)',\n      refs: [\n        { seq: '\\\\\\\\', desc: 'Backslash' },\n        { seq: '\\\\\"', desc: 'Double quote' },\n        { seq: \"\\\\'\", desc: 'Single quote' },\n        { seq: '\\\\n', desc: 'Newline' },\n        { seq: '\\\\t', desc: 'Tab' },\n        { seq: '\\\\r', desc: 'Carriage return' },\n        { seq: '\\\\f', desc: 'Form feed' },\n        { seq: '\\\\b', desc: 'Backspace' },\n        { seq: '\\\\uXXXX', desc: 'Unicode' },\n      ],\n      escape(s) {\n        return s\n          .replace(/\\\\/g, '\\\\\\\\')\n          .replace(/\"/g, '\\\\\"')\n          .replace(/'/g, \"\\\\'\")\n          .replace(/\\n/g, '\\\\n')\n          .replace(/\\r/g, '\\\\r')\n          .replace(/\\t/g, '\\\\t')\n          .replace(/\\f/g, '\\\\f')\n          .replace(/\\b/g, '\\\\b')\n          .replace(/[^\\x20-\\x7e]/g, c =\u003e '\\\\u' + c.charCodeAt(0).toString(16).padStart(4, '0').toUpperCase());\n      },\n      unescape(s) {\n        return s\n          .replace(/\\\\u([0-9a-fA-F]{4})/g, (_, h) =\u003e String.fromCharCode(parseInt(h, 16)))\n          .replace(/\\\\n/g, '\\n')\n          .replace(/\\\\r/g, '\\r')\n          .replace(/\\\\t/g, '\\t')\n          .replace(/\\\\f/g, '\\f')\n          .replace(/\\\\b/g, '\\b')\n          .replace(/\\\\\"/g, '\"')\n          .replace(/\\\\'/g, \"'\")\n          .replace(/\\\\\\\\/g, '\\\\');\n      },\n    },\n    {\n      id: 'csharp',\n      label: 'C#',\n      desc: 'C# verbatim and regular string escaping',\n      refs: [\n        { seq: '\\\\\\\\', desc: 'Backslash' },\n        { seq: '\\\\\"', desc: 'Double quote' },\n        { seq: '\\\\n', desc: 'Newline' },\n        { seq: '\\\\t', desc: 'Tab' },\n        { seq: '\\\\r', desc: 'Carriage return' },\n        { seq: '\\\\0', desc: 'Null char' },\n        { seq: '\\\\a', desc: 'Alert/bell' },\n        { seq: '\\\\uXXXX', desc: 'Unicode' },\n      ],\n      escape(s) {\n        return s\n          .replace(/\\\\/g, '\\\\\\\\')\n          .replace(/\"/g, '\\\\\"')\n          .replace(/\\n/g, '\\\\n')\n          .replace(/\\r/g, '\\\\r')\n          .replace(/\\t/g, '\\\\t')\n          .replace(/\\0/g, '\\\\0')\n          .replace(/\\x07/g, '\\\\a')\n          .replace(/[^\\x20-\\x7e]/g, c =\u003e '\\\\u' + c.charCodeAt(0).toString(16).padStart(4, '0').toUpperCase());\n      },\n      unescape(s) {\n        return s\n          .replace(/\\\\u([0-9a-fA-F]{4})/g, (_, h) =\u003e String.fromCharCode(parseInt(h, 16)))\n          .replace(/\\\\n/g, '\\n')\n          .replace(/\\\\r/g, '\\r')\n          .replace(/\\\\t/g, '\\t')\n          .replace(/\\\\0/g, '\\0')\n          .replace(/\\\\a/g, '\\x07')\n          .replace(/\\\\\"/g, '\"')\n          .replace(/\\\\\\\\/g, '\\\\');\n      },\n    },\n    {\n      id: 'sql',\n      label: 'SQL',\n      desc: \"Doubles single quotes (ANSI SQL standard)\",\n      refs: [\n        { seq: \"''\", desc: \"Single quote\" },\n        { seq: '\\\\n', desc: 'Newline (MySQL)' },\n        { seq: '\\\\t', desc: 'Tab (MySQL)' },\n        { seq: '\\\\\\\\', desc: 'Backslash (MySQL)' },\n      ],\n      escape(s) {\n        return s.replace(/'/g, \"''\");\n      },\n      unescape(s) {\n        return s.replace(/''/g, \"'\");\n      },\n    },\n    {\n      id: 'html',\n      label: 'HTML',\n      desc: 'Named HTML entities for safe embedding in HTML',\n      refs: [\n        { seq: '\u0026amp;', desc: '\u0026 Ampersand' },\n        { seq: '\u0026lt;', desc: '\u003c Less than' },\n        { seq: '\u0026gt;', desc: '\u003e Greater than' },\n        { seq: '\u0026quot;', desc: '\" Double quote' },\n        { seq: '\u0026#39;', desc: \"' Single quote\" },\n        { seq: '\u0026nbsp;', desc: 'Non-breaking space' },\n      ],\n      escape(s) {\n        return s\n          .replace(/\u0026/g, '\u0026amp;')\n          .replace(/\u003c/g, '\u0026lt;')\n          .replace(/\u003e/g, '\u0026gt;')\n          .replace(/\"/g, '\u0026quot;')\n          .replace(/'/g, '\u0026#39;');\n      },\n      unescape(s) {\n        return s\n          .replace(/\u0026quot;/g, '\"')\n          .replace(/\u0026#39;/g, \"'\")\n          .replace(/\u0026apos;/g, \"'\")\n          .replace(/\u0026lt;/g, '\u003c')\n          .replace(/\u0026gt;/g, '\u003e')\n          .replace(/\u0026nbsp;/g, '\\u00a0')\n          .replace(/\u0026amp;/g, '\u0026');\n      },\n    },\n    {\n      id: 'xml',\n      label: 'XML',\n      desc: 'XML entity encoding (five predefined entities)',\n      refs: [\n        { seq: '\u0026amp;', desc: '\u0026 Ampersand' },\n        { seq: '\u0026lt;', desc: '\u003c Less than' },\n        { seq: '\u0026gt;', desc: '\u003e Greater than' },\n        { seq: '\u0026quot;', desc: '\" Double quote' },\n        { seq: '\u0026apos;', desc: \"' Apostrophe\" },\n      ],\n      escape(s) {\n        return s\n          .replace(/\u0026/g, '\u0026amp;')\n          .replace(/\u003c/g, '\u0026lt;')\n          .replace(/\u003e/g, '\u0026gt;')\n          .replace(/\"/g, '\u0026quot;')\n          .replace(/'/g, '\u0026apos;');\n      },\n      unescape(s) {\n        return s\n          .replace(/\u0026quot;/g, '\"')\n          .replace(/\u0026apos;/g, \"'\")\n          .replace(/\u0026lt;/g, '\u003c')\n          .replace(/\u0026gt;/g, '\u003e')\n          .replace(/\u0026amp;/g, '\u0026');\n      },\n    },\n    {\n      id: 'csv',\n      label: 'CSV',\n      desc: 'RFC 4180 CSV field quoting',\n      refs: [\n        { seq: '\"\"', desc: 'Embedded double quote' },\n        { seq: '\"field\"', desc: 'Quoted field wrapper' },\n      ],\n      escape(s) {\n        if (/[\",\\r\\n]/.test(s)) {\n          return '\"' + s.replace(/\"/g, '\"\"') + '\"';\n        }\n        return s;\n      },\n      unescape(s) {\n        const t = s.trim();\n        if (t.startsWith('\"') \u0026\u0026 t.endsWith('\"')) {\n          return t.slice(1, -1).replace(/\"\"/g, '\"');\n        }\n        return s;\n      },\n    },\n    {\n      id: 'regex',\n      label: 'Regex',\n      desc: 'Escapes all regex metacharacters so a string can be used as a literal pattern',\n      refs: [\n        { seq: '\\\\.', desc: 'Any char (dot)' },\n        { seq: '\\\\*', desc: 'Quantifier *' },\n        { seq: '\\\\+', desc: 'Quantifier +' },\n        { seq: '\\\\?', desc: 'Quantifier ?' },\n        { seq: '\\\\^', desc: 'Start anchor' },\n        { seq: '\\\\$', desc: 'End anchor' },\n        { seq: '\\\\(\\\\)', desc: 'Group' },\n        { seq: '\\\\[\\\\]', desc: 'Character class' },\n        { seq: '\\\\{\\\\}', desc: 'Quantifier braces' },\n        { seq: '\\\\|', desc: 'Alternation' },\n      ],\n      escape(s) {\n        return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$\u0026');\n      },\n      unescape(s) {\n        return s.replace(/\\\\([.*+?^${}()|[\\]\\\\])/g, '$1');\n      },\n    },\n  ];\n\n  /* ── State ── */\n  let activeMode = MODES[0];\n\n  /* ── DOM refs ── */\n  const modeRow   = document.getElementById('escModeRow');\n  const modeDesc  = document.getElementById('escModeDesc');\n  const input     = document.getElementById('escInput');\n  const output    = document.getElementById('escOutput');\n  const bulkCheck = document.getElementById('escBulkMode');\n  const statusEl  = document.getElementById('escStatus');\n  const refGrid   = document.getElementById('escRefGrid');\n\n  /* ── Build mode buttons ── */\n  MODES.forEach(mode =\u003e {\n    const btn = document.createElement('button');\n    btn.className = 'esc-mode-btn' + (mode === activeMode ? ' active' : '');\n    btn.textContent = mode.label;\n    btn.addEventListener('click', () =\u003e {\n      activeMode = mode;\n      document.querySelectorAll('#esc-app .esc-mode-btn').forEach(b =\u003e b.classList.remove('active'));\n      btn.classList.add('active');\n      modeDesc.textContent = mode.desc;\n      renderRef();\n    });\n    modeRow.appendChild(btn);\n  });\n  modeDesc.textContent = activeMode.desc;\n\n  /* ── Render reference table ── */\n  function renderRef() {\n    refGrid.innerHTML = '';\n    activeMode.refs.forEach(r =\u003e {\n      const div = document.createElement('div');\n      div.className = 'esc-ref-item';\n      div.innerHTML = '\u003ccode\u003e' + r.seq + '\u003c/code\u003e\u003cspan\u003e' + r.desc + '\u003c/span\u003e';\n      refGrid.appendChild(div);\n    });\n  }\n  renderRef();\n\n  /* ── Process ── */\n  function process(fn) {\n    const text = input.value;\n    if (!text) { showStatus('Paste some text first.', true); return; }\n    try {\n      if (bulkCheck.checked) {\n        output.value = text.split('\\n').map(line =\u003e fn(line)).join('\\n');\n      } else {\n        output.value = fn(text);\n      }\n      showStatus('Done.');\n    } catch (e) {\n      showStatus('Error: ' + e.message, true);\n    }\n  }\n\n  document.getElementById('escEscapeBtn').addEventListener('click', () =\u003e process(s =\u003e activeMode.escape(s)));\n  document.getElementById('escUnescapeBtn').addEventListener('click', () =\u003e process(s =\u003e activeMode.unescape(s)));\n\n  /* ── Copy ── */\n  document.getElementById('escCopyBtn').addEventListener('click', () =\u003e {\n    const text = output.value;\n    if (!text) { showStatus('Nothing to copy.', true); return; }\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(text).then(() =\u003e showStatus('Copied to clipboard!')).catch(() =\u003e fallbackCopy(text));\n    } else {\n      fallbackCopy(text);\n    }\n  });\n\n  function fallbackCopy(text) {\n    output.select();\n    document.execCommand('copy');\n    showStatus('Copied to clipboard!');\n  }\n\n  /* ── Clear ── */\n  document.getElementById('escClearBtn').addEventListener('click', () =\u003e {\n    input.value = '';\n    output.value = '';\n    showStatus('Cleared.');\n  });\n\n  /* ── Status helper ── */\n  let statusTimer;\n  function showStatus(msg, isError) {\n    statusEl.textContent = msg;\n    statusEl.className = 'esc-status' + (isError ? ' error' : '');\n    clearTimeout(statusTimer);\n    statusTimer = setTimeout(() =\u003e { statusEl.textContent = ''; }, 3000);\n  }\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"https://productivity-works.com/tools/case-converter/\"\u003eCase Converter\u003c/a\u003e\n\n\u003ca href=\"https://productivity-works.com/tools/character-counter/\"\u003eCharacter Counter\u003c/a\u003e\n\n\u003ca href=\"https://productivity-works.com/tools/regex-cheatsheet/\"\u003eRegex Cheatsheet\u003c/a\u003e\n\u003c/p\u003e","title":"String Escape Tool — Multi-Language Escaper"},{"content":" Input Text Reverse Operations (select one) Reverse String Flip every character in the entire text Reverse Word Order Keep spellings intact, flip word positions Reverse Each Word Flip letters inside every individual word Case: Original UPPERCASE lowercase Title Case Sentence case Remove Extra Whitespace Result (live preview) Copy Result Result will appear here as you type... Characters: 0 Words: 0 Vowels: 0 Consonants: 0 Related Tools Count words → Word Counter ","permalink":"https://productivity-works.com/tools/string-reverser/","summary":"\u003cdiv id=\"sr-app\"\u003e\n\u003cstyle\u003e\n#sr-app {\n  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;\n  background: #0f0f13;\n  color: #e2e8f0;\n  border-radius: 12px;\n  padding: 24px;\n  margin: 0 auto;\n  max-width: 900px;\n  box-sizing: border-box;\n}\n\n#sr-app * {\n  box-sizing: border-box;\n}\n\n#sr-app h2 {\n  font-size: 1.05rem;\n  font-weight: 600;\n  color: #f1f5f9;\n  margin: 0 0 10px 0;\n}\n\n#sr-app textarea {\n  width: 100%;\n  min-height: 130px;\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  color: #e2e8f0;\n  font-size: 0.95rem;\n  padding: 12px 14px;\n  resize: vertical;\n  font-family: inherit;\n  transition: border-color 0.2s;\n  outline: none;\n  line-height: 1.6;\n}\n\n#sr-app textarea:focus {\n  border-color: #8b5cf6;\n}\n\n#sr-app .sr-label {\n  font-size: 0.75rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #8b5cf6;\n  margin-bottom: 6px;\n  display: block;\n}\n\n#sr-app .sr-section {\n  margin-bottom: 18px;\n}\n\n#sr-app .sr-ops {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\n  gap: 10px;\n  margin-bottom: 18px;\n}\n\n#sr-app .sr-op-card {\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  padding: 12px 14px;\n  cursor: pointer;\n  transition: all 0.18s;\n  user-select: none;\n}\n\n#sr-app .sr-op-card:hover {\n  border-color: #8b5cf6;\n}\n\n#sr-app .sr-op-card.active {\n  border-color: #8b5cf6;\n  background: #18122b;\n}\n\n#sr-app .sr-op-title {\n  font-size: 0.85rem;\n  font-weight: 600;\n  color: #f1f5f9;\n  margin-bottom: 3px;\n}\n\n#sr-app .sr-op-card.active .sr-op-title {\n  color: #a78bfa;\n}\n\n#sr-app .sr-op-desc {\n  font-size: 0.75rem;\n  color: #64748b;\n  line-height: 1.4;\n}\n\n#sr-app .sr-case-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 18px;\n  align-items: center;\n}\n\n#sr-app .sr-case-label {\n  font-size: 0.75rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #8b5cf6;\n  margin-right: 4px;\n}\n\n#sr-app .sr-btn {\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 6px;\n  color: #cbd5e1;\n  font-size: 0.82rem;\n  padding: 6px 14px;\n  cursor: pointer;\n  transition: all 0.18s;\n  font-family: inherit;\n  font-weight: 500;\n}\n\n#sr-app .sr-btn:hover {\n  background: #8b5cf6;\n  border-color: #8b5cf6;\n  color: #fff;\n}\n\n#sr-app .sr-btn.active {\n  background: #8b5cf6;\n  border-color: #8b5cf6;\n  color: #fff;\n}\n\n#sr-app .sr-btn.success {\n  background: #16a34a;\n  border-color: #16a34a;\n  color: #fff;\n}\n\n#sr-app .sr-toggle {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 0.82rem;\n  color: #94a3b8;\n  cursor: pointer;\n  user-select: none;\n  padding: 6px 10px;\n  border: 1px solid #2d2d3d;\n  border-radius: 6px;\n  background: #1a1a24;\n  transition: all 0.18s;\n}\n\n#sr-app .sr-toggle:hover {\n  border-color: #8b5cf6;\n  color: #e2e8f0;\n}\n\n#sr-app .sr-toggle input[type=\"checkbox\"] {\n  accent-color: #8b5cf6;\n  width: 14px;\n  height: 14px;\n  cursor: pointer;\n}\n\n#sr-app .sr-output-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin-bottom: 6px;\n}\n\n#sr-app .sr-stats-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 16px;\n  margin-top: 10px;\n  font-size: 0.82rem;\n  color: #94a3b8;\n}\n\n#sr-app .sr-stat-val {\n  color: #8b5cf6;\n  font-weight: 600;\n}\n\n#sr-app .sr-preview {\n  margin-top: 6px;\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  padding: 12px 14px;\n  min-height: 130px;\n  font-size: 0.95rem;\n  color: #cbd5e1;\n  line-height: 1.6;\n  white-space: pre-wrap;\n  word-break: break-word;\n}\n\n#sr-app .sr-placeholder {\n  color: #4a5568;\n  font-style: italic;\n}\n\u003c/style\u003e\n\u003c!-- Input --\u003e\n\u003cdiv class=\"sr-section\"\u003e\n  \u003cspan class=\"sr-label\"\u003eInput Text\u003c/span\u003e\n  \u003ctextarea id=\"sr-input\" placeholder=\"Type or paste your text here...\" oninput=\"srProcess()\"\u003e\u003c/textarea\u003e\n\u003c/div\u003e\n\u003c!-- Reverse operations --\u003e\n\u003cdiv class=\"sr-label\"\u003eReverse Operations (select one)\u003c/div\u003e\n\u003cdiv class=\"sr-ops\" id=\"sr-ops\"\u003e\n  \u003cdiv class=\"sr-op-card active\" data-op=\"reverse-string\" onclick=\"srSelectOp(this)\"\u003e\n    \u003cdiv class=\"sr-op-title\"\u003eReverse String\u003c/div\u003e\n    \u003cdiv class=\"sr-op-desc\"\u003eFlip every character in the entire text\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"sr-op-card\" data-op=\"reverse-words\" onclick=\"srSelectOp(this)\"\u003e\n    \u003cdiv class=\"sr-op-title\"\u003eReverse Word Order\u003c/div\u003e\n    \u003cdiv class=\"sr-op-desc\"\u003eKeep spellings intact, flip word positions\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"sr-op-card\" data-op=\"reverse-each-word\" onclick=\"srSelectOp(this)\"\u003e\n    \u003cdiv class=\"sr-op-title\"\u003eReverse Each Word\u003c/div\u003e\n    \u003cdiv class=\"sr-op-desc\"\u003eFlip letters inside every individual word\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Case toggle --\u003e\n\u003cdiv class=\"sr-case-row\"\u003e\n  \u003cspan class=\"sr-case-label\"\u003eCase:\u003c/span\u003e\n  \u003cbutton class=\"sr-btn active\" id=\"sr-case-none\" onclick=\"srSetCase('none')\"\u003eOriginal\u003c/button\u003e\n  \u003cbutton class=\"sr-btn\" id=\"sr-case-upper\" onclick=\"srSetCase('upper')\"\u003eUPPERCASE\u003c/button\u003e\n  \u003cbutton class=\"sr-btn\" id=\"sr-case-lower\" onclick=\"srSetCase('lower')\"\u003elowercase\u003c/button\u003e\n  \u003cbutton class=\"sr-btn\" id=\"sr-case-title\" onclick=\"srSetCase('title')\"\u003eTitle Case\u003c/button\u003e\n  \u003cbutton class=\"sr-btn\" id=\"sr-case-sentence\" onclick=\"srSetCase('sentence')\"\u003eSentence case\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Extra options --\u003e\n\u003cdiv style=\"display:flex;flex-wrap:wrap;gap:8px;margin-bottom:18px;\"\u003e\n  \u003clabel class=\"sr-toggle\"\u003e\n    \u003cinput type=\"checkbox\" id=\"sr-trim-ws\" onchange=\"srProcess()\"\u003e Remove Extra Whitespace\n  \u003c/label\u003e\n\u003c/div\u003e\n\u003c!-- Output --\u003e\n\u003cdiv class=\"sr-output-header\"\u003e\n  \u003cspan class=\"sr-label\"\u003eResult (live preview)\u003c/span\u003e\n  \u003cbutton class=\"sr-btn\" id=\"sr-copy-btn\" onclick=\"srCopy()\"\u003eCopy Result\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"sr-preview\" id=\"sr-preview\"\u003e\u003cspan class=\"sr-placeholder\"\u003eResult will appear here as you type...\u003c/span\u003e\u003c/div\u003e\n\u003c!-- Stats --\u003e\n\u003cdiv class=\"sr-stats-row\"\u003e\n  \u003cspan\u003eCharacters: \u003cspan class=\"sr-stat-val\" id=\"sr-chars\"\u003e0\u003c/span\u003e\u003c/span\u003e\n  \u003cspan\u003eWords: \u003cspan class=\"sr-stat-val\" id=\"sr-words\"\u003e0\u003c/span\u003e\u003c/span\u003e\n  \u003cspan\u003eVowels: \u003cspan class=\"sr-stat-val\" id=\"sr-vowels\"\u003e0\u003c/span\u003e\u003c/span\u003e\n  \u003cspan\u003eConsonants: \u003cspan class=\"sr-stat-val\" id=\"sr-cons\"\u003e0\u003c/span\u003e\u003c/span\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var srOp = 'reverse-string';\n  var srCase = 'none';\n\n  window.srSelectOp = function(card) {\n    document.querySelectorAll('#sr-ops .sr-op-card').forEach(function(c) { c.classList.remove('active'); });\n    card.classList.add('active');\n    srOp = card.getAttribute('data-op');\n    srProcess();\n  };\n\n  window.srSetCase = function(c) {\n    srCase = c;\n    ['none','upper','lower','title','sentence'].forEach(function(k) {\n      document.getElementById('sr-case-' + k).classList.toggle('active', k === c);\n    });\n    srProcess();\n  };\n\n  function reverseString(s) {\n    return s.split('').reverse().join('');\n  }\n\n  function reverseWords(s) {\n    return s.split(/(\\s+)/).reverse().join('');\n  }\n\n  function reverseEachWord(s) {\n    return s.replace(/\\S+/g, function(w) { return w.split('').reverse().join(''); });\n  }\n\n  function applyCase(s, c) {\n    if (c === 'upper') return s.toUpperCase();\n    if (c === 'lower') return s.toLowerCase();\n    if (c === 'title') return s.replace(/\\w\\S*/g, function(w) { return w.charAt(0).toUpperCase() + w.slice(1).toLowerCase(); });\n    if (c === 'sentence') return s.toLowerCase().replace(/(^\\s*\\w|[.!?]\\s+\\w)/g, function(c) { return c.toUpperCase(); });\n    return s;\n  }\n\n  function countVowels(s) {\n    return (s.match(/[aeiouAEIOU]/g) || []).length;\n  }\n\n  function countConsonants(s) {\n    return (s.match(/[bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ]/g) || []).length;\n  }\n\n  window.srProcess = function() {\n    var raw = document.getElementById('sr-input').value;\n    var trimWs = document.getElementById('sr-trim-ws').checked;\n    var previewEl = document.getElementById('sr-preview');\n\n    if (!raw) {\n      previewEl.innerHTML = '\u003cspan class=\"sr-placeholder\"\u003eResult will appear here as you type...\u003c/span\u003e';\n      document.getElementById('sr-chars').textContent = '0';\n      document.getElementById('sr-words').textContent = '0';\n      document.getElementById('sr-vowels').textContent = '0';\n      document.getElementById('sr-cons').textContent = '0';\n      return;\n    }\n\n    var s = raw;\n    if (trimWs) {\n      s = s.replace(/[ \\t]+/g, ' ').replace(/\\n{3,}/g, '\\n\\n').trim();\n    }\n\n    if (srOp === 'reverse-string') {\n      s = reverseString(s);\n    } else if (srOp === 'reverse-words') {\n      s = reverseWords(s);\n    } else if (srOp === 'reverse-each-word') {\n      s = reverseEachWord(s);\n    }\n\n    s = applyCase(s, srCase);\n\n    previewEl.textContent = s;\n\n    // Stats (based on result)\n    document.getElementById('sr-chars').textContent = s.length;\n    document.getElementById('sr-words').textContent = s.trim() ? s.trim().split(/\\s+/).length : 0;\n    document.getElementById('sr-vowels').textContent = countVowels(s);\n    document.getElementById('sr-cons').textContent = countConsonants(s);\n  };\n\n  window.srCopy = function() {\n    var text = document.getElementById('sr-preview').textContent;\n    if (!text || text === 'Result will appear here as you type...') return;\n    var btn = document.getElementById('sr-copy-btn');\n    var done = function() {\n      btn.textContent = 'Copied!';\n      btn.classList.add('success');\n      setTimeout(function() {\n        btn.textContent = 'Copy Result';\n        btn.classList.remove('success');\n      }, 1800);\n    };\n    if (navigator.clipboard) {\n      navigator.clipboard.writeText(text).then(done).catch(function() { fallbackCopy(text, done); });\n    } else {\n      fallbackCopy(text, done);\n    }\n  };\n\n  function fallbackCopy(text, cb) {\n    var ta = document.createElement('textarea');\n    ta.value = text;\n    ta.style.position = 'fixed';\n    ta.style.opacity = '0';\n    document.body.appendChild(ta);\n    ta.select();\n    document.execCommand('copy');\n    document.body.removeChild(ta);\n    cb();\n  }\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003eCount words → \u003ca href=\"https://productivity-works.com/tools/word-counter/\"\u003eWord Counter\u003c/a\u003e\n\u003c/p\u003e","title":"String Reverser \u0026 Text Tools - Free Online"},{"content":" SVG Icon Maker Draw custom SVG icons on a 24×24 grid — no software needed. Export clean SVG code instantly.\nPreset Icons \u0026lt;div class=\u0026quot;panel\u0026quot; style=\u0026quot;margin-bottom:12px\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Draw Tool\u0026lt;/h3\u0026gt; \u0026lt;div style=\u0026quot;display:flex;flex-direction:column;gap:6px\u0026quot; id=\u0026quot;tool-buttons\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;hr class=\u0026quot;section-sep\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Fill Color\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;fill-color\u0026quot; value=\u0026quot;#4f46e5\u0026quot;\u0026gt; \u0026lt;label style=\u0026quot;margin-top:6px\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;no-fill\u0026quot; style=\u0026quot;width:auto;margin-right:4px\u0026quot;\u0026gt; No Fill \u0026lt;/label\u0026gt; \u0026lt;label\u0026gt;Stroke Color\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;stroke-color\u0026quot; value=\u0026quot;#4f46e5\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Stroke Width\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;stroke-width\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;4\u0026quot; step=\u0026quot;0.5\u0026quot; value=\u0026quot;1.5\u0026quot;\u0026gt; \u0026lt;div style=\u0026quot;font-size:.78rem;color:#6b7280;text-align:right\u0026quot; id=\u0026quot;stroke-val\u0026quot;\u0026gt;1.5\u0026lt;/div\u0026gt; \u0026lt;label\u0026gt;Fill Opacity\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot; id=\u0026quot;fill-opacity\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;1\u0026quot; step=\u0026quot;0.05\u0026quot; value=\u0026quot;1\u0026quot;\u0026gt; \u0026lt;div style=\u0026quot;font-size:.78rem;color:#6b7280;text-align:right\u0026quot; id=\u0026quot;opacity-val\u0026quot;\u0026gt;1.0\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;panel\u0026quot;\u0026gt; \u0026lt;h3\u0026gt;Shapes (\u0026lt;span id=\u0026quot;shape-count\u0026quot;\u0026gt;0\u0026lt;/span\u0026gt;)\u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;shape-list\u0026quot; id=\u0026quot;shape-list\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div style=\u0026quot;margin-top:8px;display:flex;gap:6px\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;btn btn-danger btn-sm\u0026quot; id=\u0026quot;del-selected-btn\u0026quot;\u0026gt;Delete\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;btn btn-secondary btn-sm\u0026quot; id=\u0026quot;clear-btn\u0026quot;\u0026gt;Clear All\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Show Grid Snap Preview at: 16 32 64 Click \u0026amp; drag to draw. Click a shape to select it. SVG Code Output \u0026lt;!-- Draw shapes to generate SVG --\u0026gt; Copy SVG Download .svg Copied to clipboard! \u0026lt;hr class=\u0026quot;section-sep\u0026quot;\u0026gt; \u0026lt;h3 style=\u0026quot;margin-top:0\u0026quot;\u0026gt;Export Size\u0026lt;/h3\u0026gt; \u0026lt;label\u0026gt;Custom viewBox size\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;viewbox-size\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;24\u0026quot;\u0026gt;24×24 (standard)\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;16\u0026quot;\u0026gt;16×16\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;32\u0026quot;\u0026gt;32×32\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;48\u0026quot;\u0026gt;48×48\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;64\u0026quot;\u0026gt;64×64\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;hr class=\u0026quot;section-sep\u0026quot;\u0026gt; \u0026lt;h3 style=\u0026quot;margin-top:0\u0026quot;\u0026gt;Optimize\u0026lt;/h3\u0026gt; \u0026lt;label class=\u0026quot;grid-toggle\u0026quot; style=\u0026quot;margin-top:4px\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;round-coords\u0026quot; checked\u0026gt; Round coordinates (2dp) \u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; Related Tools SVG Path Editor — Edit and visualize SVG path data SVG to PNG Converter — Convert SVG files to PNG images Color Palette Generator — Generate harmonious color palettes Favicon Generator — Create favicons from scratch Pixel Art Editor — Draw pixel-perfect icons on a grid ","permalink":"https://productivity-works.com/tools/svg-icon-maker/","summary":"\u003cdiv id=\"sim-app\"\u003e\n\u003cstyle\u003e\n#sim-app *,#sim-app *::before,#sim-app *::after{box-sizing:border-box;margin:0;padding:0}\n#sim-app{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:#1f2937;background:#f9fafb;min-height:100vh;padding:24px 16px}\n#sim-app h1{font-size:1.6rem;font-weight:700;margin-bottom:6px;color:#111827}\n#sim-app .subtitle{color:#6b7280;font-size:.95rem;margin-bottom:20px}\n#sim-app .layout{display:grid;grid-template-columns:220px 1fr 260px;gap:16px;align-items:start}\n@media(max-width:900px){#sim-app .layout{grid-template-columns:1fr}}\n#sim-app .panel{background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:14px}\n#sim-app .panel h3{font-size:.8rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:#6b7280;margin-bottom:10px}\n#sim-app label{display:block;font-size:.82rem;color:#374151;margin-bottom:3px;margin-top:8px}\n#sim-app label:first-of-type{margin-top:0}\n#sim-app select,#sim-app input[type=number],#sim-app input[type=range]{width:100%;padding:5px 8px;border:1px solid #d1d5db;border-radius:6px;font-size:.85rem;color:#1f2937;background:#fff}\n#sim-app input[type=color]{width:100%;height:32px;border:1px solid #d1d5db;border-radius:6px;padding:2px;cursor:pointer;background:#fff}\n#sim-app .btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:8px 14px;border-radius:7px;font-size:.85rem;font-weight:600;cursor:pointer;border:none;transition:all .15s}\n#sim-app .btn-primary{background:#4f46e5;color:#fff}\n#sim-app .btn-primary:hover{background:#4338ca}\n#sim-app .btn-secondary{background:#f3f4f6;color:#374151;border:1px solid #d1d5db}\n#sim-app .btn-secondary:hover{background:#e5e7eb}\n#sim-app .btn-danger{background:#fee2e2;color:#dc2626;border:1px solid #fca5a5}\n#sim-app .btn-danger:hover{background:#fecaca}\n#sim-app .btn-sm{padding:5px 10px;font-size:.78rem}\n#sim-app .canvas-wrap{position:relative;display:flex;flex-direction:column;align-items:center;gap:12px}\n#sim-app #drawing-svg{border:2px solid #6366f1;border-radius:8px;cursor:crosshair;background:#fff;display:block;box-shadow:0 2px 8px rgba(99,102,241,.1)}\n#sim-app .canvas-controls{display:flex;gap:8px;flex-wrap:wrap;justify-content:center}\n#sim-app .tool-btn{padding:6px 12px;border-radius:6px;font-size:.82rem;font-weight:600;border:2px solid #e5e7eb;background:#fff;cursor:pointer;transition:all .15s;color:#374151}\n#sim-app .tool-btn.active{border-color:#4f46e5;background:#eef2ff;color:#4f46e5}\n#sim-app .tool-btn:hover:not(.active){border-color:#c7d2fe;background:#f5f3ff}\n#sim-app .preset-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:6px;margin-bottom:10px}\n#sim-app .preset-btn{display:flex;flex-direction:column;align-items:center;gap:3px;padding:6px 4px;border:1px solid #e5e7eb;border-radius:6px;background:#f9fafb;cursor:pointer;font-size:.7rem;color:#6b7280;transition:all .15s}\n#sim-app .preset-btn:hover{border-color:#6366f1;background:#eef2ff;color:#4f46e5}\n#sim-app .preset-btn svg{width:20px;height:20px}\n#sim-app .output-box{font-family:'Fira Mono','Consolas',monospace;font-size:.72rem;background:#0f172a;color:#a5f3fc;padding:12px;border-radius:8px;white-space:pre-wrap;word-break:break-all;max-height:220px;overflow-y:auto;line-height:1.5;border:1px solid #1e293b}\n#sim-app .btn-row{display:flex;gap:8px;flex-wrap:wrap;margin-top:8px}\n#sim-app .shape-list{max-height:180px;overflow-y:auto;margin-top:4px}\n#sim-app .shape-item{display:flex;align-items:center;justify-content:space-between;padding:5px 8px;border-radius:5px;font-size:.78rem;color:#374151;cursor:pointer;transition:background .1s}\n#sim-app .shape-item:hover{background:#f3f4f6}\n#sim-app .shape-item.selected{background:#eef2ff;color:#4338ca;font-weight:600}\n#sim-app .shape-dot{width:10px;height:10px;border-radius:50%;border:1px solid #9ca3af;margin-right:6px;flex-shrink:0}\n#sim-app .shape-item-left{display:flex;align-items:center}\n#sim-app .del-btn{background:none;border:none;color:#ef4444;cursor:pointer;font-size:.85rem;padding:1px 4px;border-radius:3px}\n#sim-app .del-btn:hover{background:#fee2e2}\n#sim-app .grid-toggle{display:flex;align-items:center;gap:6px;font-size:.82rem;color:#6b7280;cursor:pointer;user-select:none}\n#sim-app .grid-toggle input{margin:0}\n#sim-app .size-preview{display:flex;align-items:center;gap:8px;font-size:.8rem;color:#6b7280;margin-top:4px}\n#sim-app .size-preview canvas{border:1px solid #e5e7eb;border-radius:3px}\n#sim-app .alert-ok{background:#d1fae5;color:#065f46;border:1px solid #6ee7b7;border-radius:6px;padding:6px 12px;font-size:.82rem;display:none}\n#sim-app .section-sep{border:none;border-top:1px solid #f3f4f6;margin:10px 0}\n\u003c/style\u003e\n\u003ch1\u003eSVG Icon Maker\u003c/h1\u003e\n\u003cp class=\"subtitle\"\u003eDraw custom SVG icons on a 24×24 grid — no software needed. Export clean SVG code instantly.\u003c/p\u003e","title":"SVG Icon Maker - Create Custom SVG Icons"},{"content":" SVG Path Editor Path Data (d attribute)\n\u0026lt;span class=\u0026quot;svg-presets-label\u0026quot;\u0026gt;Shape Presets\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026quot;svg-presets-grid\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;svg-preset-btn\u0026quot; data-shape=\u0026quot;circle\u0026quot;\u0026gt;Circle\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;svg-preset-btn\u0026quot; data-shape=\u0026quot;star\u0026quot;\u0026gt;Star\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;svg-preset-btn\u0026quot; data-shape=\u0026quot;heart\u0026quot;\u0026gt;Heart\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;svg-preset-btn\u0026quot; data-shape=\u0026quot;arrow\u0026quot;\u0026gt;Arrow\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;svg-preset-btn\u0026quot; data-shape=\u0026quot;checkmark\u0026quot;\u0026gt;Checkmark\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;svg-preset-btn\u0026quot; data-shape=\u0026quot;xmark\u0026quot;\u0026gt;X Mark\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;svg-preset-btn\u0026quot; data-shape=\u0026quot;triangle\u0026quot;\u0026gt;Triangle\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;svg-preset-btn\u0026quot; data-shape=\u0026quot;hexagon\u0026quot;\u0026gt;Hexagon\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;svg-panel svg-section-sep\u0026quot;\u0026gt; \u0026lt;p class=\u0026quot;svg-panel-title\u0026quot;\u0026gt;Stroke \u0026amp;amp; Fill\u0026lt;/p\u0026gt; \u0026lt;div class=\u0026quot;svg-controls-row\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;svg-control-group\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Stroke Color\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;svg-stroke-color\u0026quot; value=\u0026quot;#4361ee\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;svg-control-group\u0026quot; style=\u0026quot;flex:1;min-width:100px;\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Stroke Width\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;svg-stroke-width\u0026quot; value=\u0026quot;2\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;20\u0026quot; step=\u0026quot;0.5\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;svg-control-group\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Fill Color\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;svg-fill-color\u0026quot; value=\u0026quot;#a8c8ff\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;svg-control-group\u0026quot; style=\u0026quot;flex:1;min-width:80px;\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Fill Opacity\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;svg-fill-opacity\u0026quot; value=\u0026quot;0.3\u0026quot; min=\u0026quot;0\u0026quot; max=\u0026quot;1\u0026quot; step=\u0026quot;0.05\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;svg-panel svg-section-sep\u0026quot;\u0026gt; \u0026lt;p class=\u0026quot;svg-panel-title\u0026quot;\u0026gt;Scale \u0026amp;amp; Translate\u0026lt;/p\u0026gt; \u0026lt;div class=\u0026quot;svg-transform-row\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;svg-control-group\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Scale X\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;svg-scale-x\u0026quot; value=\u0026quot;1\u0026quot; min=\u0026quot;0.1\u0026quot; max=\u0026quot;10\u0026quot; step=\u0026quot;0.1\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;svg-control-group\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Scale Y\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;svg-scale-y\u0026quot; value=\u0026quot;1\u0026quot; min=\u0026quot;0.1\u0026quot; max=\u0026quot;10\u0026quot; step=\u0026quot;0.1\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;svg-control-group\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Translate X\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;svg-translate-x\u0026quot; value=\u0026quot;0\u0026quot; step=\u0026quot;1\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;svg-control-group\u0026quot;\u0026gt; \u0026lt;label\u0026gt;Translate Y\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;svg-translate-y\u0026quot; value=\u0026quot;0\u0026quot; step=\u0026quot;1\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Preview\nLight Dark Checker \u0026lt;div class=\u0026quot;svg-info-box\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;svg-info-grid\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;svg-info-item\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;svg-info-key\u0026quot;\u0026gt;Commands\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;svg-info-val\u0026quot; id=\u0026quot;info-commands\u0026quot;\u0026gt;—\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;svg-info-item\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;svg-info-key\u0026quot;\u0026gt;Path Length\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;svg-info-val\u0026quot; id=\u0026quot;info-length\u0026quot;\u0026gt;—\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;svg-info-item\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;svg-info-key\u0026quot;\u0026gt;Bounding Box\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;svg-info-val\u0026quot; id=\u0026quot;info-bbox\u0026quot;\u0026gt;—\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;svg-info-item\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;svg-info-key\u0026quot;\u0026gt;Data Size\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026quot;svg-info-val\u0026quot; id=\u0026quot;info-size\u0026quot;\u0026gt;—\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;svg-panel svg-section-sep\u0026quot;\u0026gt; \u0026lt;p class=\u0026quot;svg-panel-title\u0026quot;\u0026gt;Output \u0026amp;amp; Actions\u0026lt;/p\u0026gt; \u0026lt;div class=\u0026quot;svg-actions-row\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;svg-action-btn\u0026quot; id=\u0026quot;svg-btn-minify\u0026quot;\u0026gt;Minify Path\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;svg-action-btn\u0026quot; id=\u0026quot;svg-btn-prettify\u0026quot;\u0026gt;Prettify Path\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;svg-action-btn primary\u0026quot; id=\u0026quot;svg-btn-copy-svg\u0026quot;\u0026gt;Copy SVG Code\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;svg-action-btn success\u0026quot; id=\u0026quot;svg-btn-copy-path\u0026quot;\u0026gt;Copy Path Data\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;svg-action-btn\u0026quot; id=\u0026quot;svg-btn-reset\u0026quot;\u0026gt;Reset\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;svg-output-area\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;svg-output-label\u0026quot;\u0026gt;Generated SVG Code\u0026lt;/div\u0026gt; \u0026lt;textarea id=\u0026quot;svg-output-code\u0026quot; readonly\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Copied! Related Tools Generate CSS gradients → CSS Gradient Generator Build color palettes → Color Palette Generator Create favicons → Favicon Generator ","permalink":"https://productivity-works.com/tools/svg-path-editor/","summary":"\u003cdiv id=\"svg-app\"\u003e\n\u003cstyle\u003e\n#svg-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 960px;\n  margin: 0 auto;\n  color: #1a1a1a;\n}\n#svg-app * {\n  box-sizing: border-box;\n}\n#svg-app h2.svg-tool-title {\n  font-size: 1.4rem;\n  font-weight: 700;\n  margin: 0 0 1rem 0;\n  color: #111;\n}\n#svg-app .svg-layout {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1.25rem;\n  align-items: start;\n}\n@media (max-width: 680px) {\n  #svg-app .svg-layout {\n    grid-template-columns: 1fr;\n  }\n}\n#svg-app .svg-panel {\n  background: #f8f9fa;\n  border: 1px solid #dee2e6;\n  border-radius: 10px;\n  padding: 1rem;\n}\n#svg-app .svg-panel-title {\n  font-size: 0.78rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  color: #6c757d;\n  margin: 0 0 0.6rem 0;\n}\n#svg-app textarea#svg-path-input {\n  width: 100%;\n  height: 120px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 0.82rem;\n  line-height: 1.5;\n  border: 1px solid #ced4da;\n  border-radius: 6px;\n  padding: 0.55rem 0.7rem;\n  resize: vertical;\n  background: #fff;\n  color: #212529;\n  outline: none;\n  transition: border-color 0.15s;\n}\n#svg-app textarea#svg-path-input:focus {\n  border-color: #4361ee;\n  box-shadow: 0 0 0 3px rgba(67,97,238,0.15);\n}\n#svg-app .svg-presets-label {\n  font-size: 0.78rem;\n  font-weight: 600;\n  color: #495057;\n  margin: 0.75rem 0 0.4rem 0;\n  display: block;\n}\n#svg-app .svg-presets-grid {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.35rem;\n}\n#svg-app .svg-preset-btn {\n  background: #fff;\n  border: 1px solid #ced4da;\n  border-radius: 5px;\n  padding: 0.28rem 0.65rem;\n  font-size: 0.78rem;\n  cursor: pointer;\n  color: #343a40;\n  transition: all 0.15s;\n  line-height: 1.4;\n}\n#svg-app .svg-preset-btn:hover {\n  background: #4361ee;\n  color: #fff;\n  border-color: #4361ee;\n}\n#svg-app .svg-controls-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem;\n  margin-top: 0.75rem;\n  align-items: flex-end;\n}\n#svg-app .svg-control-group {\n  display: flex;\n  flex-direction: column;\n  gap: 0.25rem;\n  min-width: 70px;\n}\n#svg-app .svg-control-group label {\n  font-size: 0.72rem;\n  font-weight: 600;\n  color: #6c757d;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#svg-app .svg-control-group input[type=\"number\"],\n#svg-app .svg-control-group input[type=\"range\"] {\n  border: 1px solid #ced4da;\n  border-radius: 5px;\n  padding: 0.3rem 0.4rem;\n  font-size: 0.82rem;\n  background: #fff;\n  color: #212529;\n  outline: none;\n  width: 100%;\n}\n#svg-app .svg-control-group input[type=\"number\"]:focus {\n  border-color: #4361ee;\n  box-shadow: 0 0 0 3px rgba(67,97,238,0.15);\n}\n#svg-app .svg-control-group input[type=\"color\"] {\n  border: 1px solid #ced4da;\n  border-radius: 5px;\n  padding: 0.15rem;\n  height: 34px;\n  width: 48px;\n  background: #fff;\n  cursor: pointer;\n}\n#svg-app .svg-preview-wrap {\n  position: relative;\n  border-radius: 8px;\n  overflow: hidden;\n  border: 1px solid #ced4da;\n  min-height: 260px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  background: #fff;\n  transition: background 0.2s;\n}\n#svg-app .svg-preview-wrap.dark-bg {\n  background: #1a1a2e;\n}\n#svg-app .svg-preview-wrap.checker-bg {\n  background-image:\n    linear-gradient(45deg, #ccc 25%, transparent 25%),\n    linear-gradient(-45deg, #ccc 25%, transparent 25%),\n    linear-gradient(45deg, transparent 75%, #ccc 75%),\n    linear-gradient(-45deg, transparent 75%, #ccc 75%);\n  background-size: 16px 16px;\n  background-position: 0 0, 0 8px, 8px -8px, -8px 0px;\n  background-color: #fff;\n}\n#svg-app svg#svg-preview {\n  max-width: 100%;\n  max-height: 280px;\n  display: block;\n}\n#svg-app .svg-preview-error {\n  color: #dc3545;\n  font-size: 0.82rem;\n  padding: 0.5rem;\n  text-align: center;\n}\n#svg-app .svg-bg-toggles {\n  display: flex;\n  gap: 0.4rem;\n  margin-bottom: 0.6rem;\n  flex-wrap: wrap;\n}\n#svg-app .svg-bg-btn {\n  border: 1px solid #ced4da;\n  border-radius: 5px;\n  padding: 0.22rem 0.6rem;\n  font-size: 0.75rem;\n  cursor: pointer;\n  background: #fff;\n  color: #343a40;\n  transition: all 0.15s;\n}\n#svg-app .svg-bg-btn.active {\n  background: #4361ee;\n  color: #fff;\n  border-color: #4361ee;\n}\n#svg-app .svg-info-box {\n  margin-top: 0.75rem;\n  background: #fff;\n  border: 1px solid #dee2e6;\n  border-radius: 6px;\n  padding: 0.6rem 0.8rem;\n}\n#svg-app .svg-info-grid {\n  display: grid;\n  grid-template-columns: repeat(2, 1fr);\n  gap: 0.3rem 1rem;\n}\n#svg-app .svg-info-item {\n  display: flex;\n  flex-direction: column;\n}\n#svg-app .svg-info-key {\n  font-size: 0.7rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  color: #6c757d;\n  letter-spacing: 0.05em;\n}\n#svg-app .svg-info-val {\n  font-size: 0.82rem;\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  color: #212529;\n}\n#svg-app .svg-actions-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.45rem;\n  margin-top: 0.75rem;\n}\n#svg-app .svg-action-btn {\n  padding: 0.4rem 0.85rem;\n  font-size: 0.82rem;\n  font-weight: 600;\n  border-radius: 6px;\n  border: 1px solid #ced4da;\n  cursor: pointer;\n  background: #fff;\n  color: #343a40;\n  transition: all 0.15s;\n}\n#svg-app .svg-action-btn:hover {\n  background: #4361ee;\n  color: #fff;\n  border-color: #4361ee;\n}\n#svg-app .svg-action-btn.primary {\n  background: #4361ee;\n  color: #fff;\n  border-color: #4361ee;\n}\n#svg-app .svg-action-btn.primary:hover {\n  background: #3451d1;\n  border-color: #3451d1;\n}\n#svg-app .svg-action-btn.success {\n  background: #2d9e6b;\n  color: #fff;\n  border-color: #2d9e6b;\n}\n#svg-app .svg-action-btn.success:hover {\n  background: #237a53;\n  border-color: #237a53;\n}\n#svg-app .svg-output-area {\n  margin-top: 1rem;\n}\n#svg-app .svg-output-label {\n  font-size: 0.78rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  color: #6c757d;\n  margin-bottom: 0.4rem;\n}\n#svg-app textarea#svg-output-code {\n  width: 100%;\n  height: 90px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 0.78rem;\n  line-height: 1.5;\n  border: 1px solid #ced4da;\n  border-radius: 6px;\n  padding: 0.5rem 0.65rem;\n  resize: vertical;\n  background: #f1f3f5;\n  color: #212529;\n  outline: none;\n}\n#svg-app .svg-toast {\n  position: fixed;\n  bottom: 1.5rem;\n  right: 1.5rem;\n  background: #212529;\n  color: #fff;\n  padding: 0.55rem 1.1rem;\n  border-radius: 8px;\n  font-size: 0.85rem;\n  font-weight: 500;\n  opacity: 0;\n  pointer-events: none;\n  transition: opacity 0.25s;\n  z-index: 9999;\n}\n#svg-app .svg-toast.show {\n  opacity: 1;\n}\n#svg-app .svg-section-sep {\n  margin: 1.25rem 0 0 0;\n}\n#svg-app .svg-transform-row {\n  display: grid;\n  grid-template-columns: 1fr 1fr 1fr 1fr;\n  gap: 0.5rem;\n  margin-top: 0.5rem;\n}\n@media (max-width: 480px) {\n  #svg-app .svg-transform-row {\n    grid-template-columns: 1fr 1fr;\n  }\n  #svg-app .svg-info-grid {\n    grid-template-columns: 1fr;\n  }\n}\n\u003c/style\u003e\n\u003ch2 class=\"svg-tool-title\"\u003eSVG Path Editor\u003c/h2\u003e\n\u003cdiv class=\"svg-layout\"\u003e\n  \u003c!-- Left: Input \u0026 Controls --\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"svg-panel\"\u003e\n      \u003cp class=\"svg-panel-title\"\u003ePath Data (d attribute)\u003c/p\u003e","title":"SVG Path Editor — Free Visual Tool"},{"content":" SVG to PNG Converter Upload or paste SVG code, set your scale and background, and download a high-resolution PNG — entirely in your browser. No files are ever sent to a server.\nInput SVG\n\u0026lt;div class=\u0026quot;sp-dropzone\u0026quot; id=\u0026quot;sp-dropzone\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;sp-dropzone-icon\u0026quot;\u0026gt;\u0026amp;#9653;\u0026lt;/div\u0026gt; \u0026lt;p class=\u0026quot;sp-dropzone-text\u0026quot;\u0026gt;Drop SVG file here\u0026lt;/p\u0026gt; \u0026lt;p class=\u0026quot;sp-dropzone-sub\u0026quot;\u0026gt;or click to browse\u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;input type=\u0026quot;file\u0026quot; id=\u0026quot;sp-file-input\u0026quot; accept=\u0026quot;.svg,image/svg+xml\u0026quot; style=\u0026quot;display:none;\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;sp-field\u0026quot;\u0026gt; \u0026lt;label class=\u0026quot;sp-label\u0026quot; for=\u0026quot;sp-code\u0026quot;\u0026gt;Or paste SVG code\u0026lt;/label\u0026gt; \u0026lt;textarea id=\u0026quot;sp-code\u0026quot; class=\u0026quot;sp-textarea\u0026quot; placeholder=\u0026quot;\u0026lt;svg xmlns=\u0026amp;quot;http://www.w3.org/2000/svg\u0026amp;quot; ...\u0026gt;\u0026amp;#10; ...\u0026amp;#10;\u0026lt;/svg\u0026gt;\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button class=\u0026quot;sp-btn-secondary\u0026quot; id=\u0026quot;sp-load-sample\u0026quot; style=\u0026quot;margin-top:10px;\u0026quot;\u0026gt;Load sample SVG\u0026lt;/button\u0026gt; Settings\n\u0026lt;div class=\u0026quot;sp-field\u0026quot;\u0026gt; \u0026lt;label class=\u0026quot;sp-label\u0026quot;\u0026gt;Scale (retina-ready)\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;sp-scale-row\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;sp-scale-btn active\u0026quot; data-scale=\u0026quot;1\u0026quot;\u0026gt;1x\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;sp-scale-btn\u0026quot; data-scale=\u0026quot;2\u0026quot;\u0026gt;2x\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;sp-scale-btn\u0026quot; data-scale=\u0026quot;3\u0026quot;\u0026gt;3x\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;sp-scale-btn\u0026quot; data-scale=\u0026quot;4\u0026quot;\u0026gt;4x\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sp-field\u0026quot;\u0026gt; \u0026lt;label class=\u0026quot;sp-label\u0026quot;\u0026gt;Output Dimensions (px)\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;sp-dim-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;sp-width\u0026quot; class=\u0026quot;sp-input\u0026quot; placeholder=\u0026quot;Width\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;8000\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;sp-lock-btn locked\u0026quot; id=\u0026quot;sp-lock-btn\u0026quot; title=\u0026quot;Lock aspect ratio\u0026quot;\u0026gt;\u0026amp;#128274;\u0026lt;/button\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;sp-height\u0026quot; class=\u0026quot;sp-input\u0026quot; placeholder=\u0026quot;Height\u0026quot; min=\u0026quot;1\u0026quot; max=\u0026quot;8000\u0026quot;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;p style=\u0026quot;font-size:11px;color:#9ca3af;margin:2px 0 0 0;\u0026quot;\u0026gt;Leave blank to use SVG's natural size × scale\u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sp-field\u0026quot;\u0026gt; \u0026lt;label class=\u0026quot;sp-label\u0026quot;\u0026gt;Background\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;sp-color-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;color\u0026quot; id=\u0026quot;sp-bg-color\u0026quot; class=\u0026quot;sp-color-input\u0026quot; value=\u0026quot;#ffffff\u0026quot; title=\u0026quot;Background color\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;sp-transparent-btn active\u0026quot; id=\u0026quot;sp-transparent-btn\u0026quot;\u0026gt;Transparent\u0026lt;/button\u0026gt; \u0026lt;span style=\u0026quot;font-size:12px;color:#6b7280;flex:1;\u0026quot; id=\u0026quot;sp-bg-label\u0026quot;\u0026gt;Transparent PNG\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Preview \u0026amp; Download\nYour SVG preview will appear here\nOutput: — \u0026times; — px Est. size: — Scale: 1x Convert \u0026amp; Download PNG\nHow it works Upload an SVG file or paste SVG code into the text area. Choose a scale multiplier (2x/3x for retina-sharp exports). Optionally set custom width/height — the aspect ratio lock keeps proportions intact. Pick a background color or leave it transparent. Click Convert \u0026amp; Download PNG — the SVG is rendered on an HTML Canvas and saved as PNG. Generate placeholder images → Placeholder Image Generator Resize images → Image Resizer ","permalink":"https://productivity-works.com/tools/svg-to-png/","summary":"\u003cdiv id=\"sp-app\"\u003e\n\u003cstyle\u003e\n#sp-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 0 0 48px 0;\n  color: #1a202c;\n}\n\n#sp-app * {\n  box-sizing: border-box;\n}\n\n/* Hero */\n.sp-hero {\n  background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 50%, #4c1d95 100%);\n  border-radius: 16px;\n  padding: 32px 28px;\n  margin-bottom: 24px;\n  color: #fff;\n}\n\n.sp-hero h2 {\n  margin: 0 0 6px 0;\n  font-size: 24px;\n  font-weight: 800;\n}\n\n.sp-hero p {\n  margin: 0;\n  font-size: 14px;\n  opacity: 0.88;\n  line-height: 1.6;\n}\n\n/* Layout */\n.sp-layout {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 20px;\n  margin-bottom: 20px;\n}\n\n@media (max-width: 640px) {\n  .sp-layout {\n    grid-template-columns: 1fr;\n  }\n}\n\n/* Panels */\n.sp-panel {\n  background: #fff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 20px;\n}\n\n.sp-panel-title {\n  font-size: 13px;\n  font-weight: 700;\n  color: #6d28d9;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  margin: 0 0 14px 0;\n}\n\n/* Drop Zone */\n.sp-dropzone {\n  border: 3px dashed #7c3aed;\n  border-radius: 12px;\n  padding: 36px 20px;\n  text-align: center;\n  background: #faf5ff;\n  cursor: pointer;\n  transition: background 0.2s, border-color 0.2s;\n  margin-bottom: 14px;\n}\n\n.sp-dropzone:hover,\n.sp-dropzone.sp-drag-over {\n  background: #ede9fe;\n  border-color: #6d28d9;\n}\n\n.sp-dropzone-icon {\n  font-size: 36px;\n  margin-bottom: 10px;\n}\n\n.sp-dropzone-text {\n  font-size: 15px;\n  font-weight: 600;\n  color: #4c1d95;\n  margin: 0 0 4px 0;\n}\n\n.sp-dropzone-sub {\n  font-size: 12px;\n  color: #7c3aed;\n  margin: 0;\n}\n\n/* Textarea */\n.sp-textarea {\n  width: 100%;\n  min-height: 140px;\n  border: 1.5px solid #d8b4fe;\n  border-radius: 8px;\n  padding: 10px 12px;\n  font-family: 'Menlo', 'Monaco', 'Courier New', monospace;\n  font-size: 12px;\n  color: #1a202c;\n  background: #fdfbff;\n  resize: vertical;\n  outline: none;\n  transition: border-color 0.2s;\n}\n\n.sp-textarea:focus {\n  border-color: #7c3aed;\n}\n\n/* Controls */\n.sp-controls {\n  display: flex;\n  flex-direction: column;\n  gap: 14px;\n}\n\n.sp-field {\n  display: flex;\n  flex-direction: column;\n  gap: 5px;\n}\n\n.sp-label {\n  font-size: 12px;\n  font-weight: 600;\n  color: #374151;\n}\n\n.sp-select,\n.sp-input {\n  border: 1.5px solid #d1d5db;\n  border-radius: 7px;\n  padding: 8px 10px;\n  font-size: 13px;\n  color: #1a202c;\n  background: #fff;\n  outline: none;\n  transition: border-color 0.2s;\n  width: 100%;\n}\n\n.sp-select:focus,\n.sp-input:focus {\n  border-color: #7c3aed;\n}\n\n.sp-dim-row {\n  display: flex;\n  gap: 8px;\n  align-items: center;\n}\n\n.sp-dim-row .sp-input {\n  flex: 1;\n}\n\n.sp-lock-btn {\n  background: #ede9fe;\n  border: 1.5px solid #c4b5fd;\n  border-radius: 7px;\n  width: 34px;\n  height: 34px;\n  cursor: pointer;\n  font-size: 16px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  flex-shrink: 0;\n  transition: background 0.15s;\n}\n\n.sp-lock-btn:hover {\n  background: #ddd6fe;\n}\n\n.sp-lock-btn.locked {\n  background: #7c3aed;\n  border-color: #7c3aed;\n  color: #fff;\n}\n\n.sp-color-row {\n  display: flex;\n  gap: 8px;\n  align-items: center;\n}\n\n.sp-color-input {\n  width: 40px;\n  height: 34px;\n  padding: 2px 3px;\n  border: 1.5px solid #d1d5db;\n  border-radius: 7px;\n  cursor: pointer;\n  flex-shrink: 0;\n}\n\n.sp-transparent-btn {\n  padding: 6px 12px;\n  font-size: 12px;\n  font-weight: 600;\n  border: 1.5px solid #d1d5db;\n  border-radius: 7px;\n  background: #fff;\n  cursor: pointer;\n  color: #374151;\n  transition: all 0.15s;\n}\n\n.sp-transparent-btn.active {\n  background: #7c3aed;\n  border-color: #7c3aed;\n  color: #fff;\n}\n\n/* Scale pills */\n.sp-scale-row {\n  display: flex;\n  gap: 6px;\n}\n\n.sp-scale-btn {\n  flex: 1;\n  padding: 7px 0;\n  border: 1.5px solid #d1d5db;\n  border-radius: 7px;\n  background: #fff;\n  font-size: 12px;\n  font-weight: 700;\n  color: #374151;\n  cursor: pointer;\n  transition: all 0.15s;\n  text-align: center;\n}\n\n.sp-scale-btn:hover {\n  border-color: #7c3aed;\n  color: #7c3aed;\n}\n\n.sp-scale-btn.active {\n  background: #7c3aed;\n  border-color: #7c3aed;\n  color: #fff;\n}\n\n/* Preview */\n.sp-preview-wrap {\n  background: repeating-conic-gradient(#e5e7eb 0% 25%, #fff 0% 50%) 0 0 / 16px 16px;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  min-height: 200px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  overflow: hidden;\n  margin-bottom: 16px;\n  position: relative;\n}\n\n.sp-preview-wrap img {\n  max-width: 100%;\n  max-height: 300px;\n  display: block;\n}\n\n.sp-preview-placeholder {\n  text-align: center;\n  color: #9ca3af;\n  padding: 40px 20px;\n}\n\n.sp-preview-placeholder svg {\n  opacity: 0.3;\n  margin-bottom: 10px;\n}\n\n.sp-preview-placeholder p {\n  margin: 0;\n  font-size: 13px;\n}\n\n/* Info bar */\n.sp-info-bar {\n  display: flex;\n  gap: 12px;\n  flex-wrap: wrap;\n  margin-bottom: 16px;\n}\n\n.sp-info-chip {\n  background: #f3f4f6;\n  border-radius: 6px;\n  padding: 4px 10px;\n  font-size: 12px;\n  color: #374151;\n  font-weight: 500;\n}\n\n.sp-info-chip span {\n  color: #7c3aed;\n  font-weight: 700;\n}\n\n/* Buttons */\n.sp-btn-primary {\n  display: block;\n  width: 100%;\n  padding: 13px;\n  background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%);\n  color: #fff;\n  font-size: 15px;\n  font-weight: 700;\n  border: none;\n  border-radius: 10px;\n  cursor: pointer;\n  transition: opacity 0.15s, transform 0.1s;\n  text-align: center;\n}\n\n.sp-btn-primary:hover {\n  opacity: 0.9;\n}\n\n.sp-btn-primary:active {\n  transform: scale(0.98);\n}\n\n.sp-btn-primary:disabled {\n  background: #9ca3af;\n  cursor: not-allowed;\n  transform: none;\n}\n\n.sp-btn-secondary {\n  display: block;\n  width: 100%;\n  padding: 10px;\n  background: #fff;\n  color: #7c3aed;\n  font-size: 13px;\n  font-weight: 600;\n  border: 1.5px solid #7c3aed;\n  border-radius: 10px;\n  cursor: pointer;\n  transition: background 0.15s;\n  text-align: center;\n  margin-top: 8px;\n}\n\n.sp-btn-secondary:hover {\n  background: #faf5ff;\n}\n\n/* Error */\n.sp-error {\n  background: #fef2f2;\n  border: 1.5px solid #fca5a5;\n  border-radius: 8px;\n  padding: 10px 14px;\n  font-size: 13px;\n  color: #dc2626;\n  margin-bottom: 14px;\n  display: none;\n}\n\n/* File name */\n.sp-filename {\n  font-size: 12px;\n  color: #6b7280;\n  text-align: center;\n  margin-bottom: 8px;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n/* Canvas (hidden) */\n#sp-canvas {\n  display: none;\n}\n\n/* How it works */\n.sp-how {\n  background: #faf5ff;\n  border: 1.5px solid #e9d5ff;\n  border-radius: 12px;\n  padding: 20px 24px;\n  margin-top: 24px;\n}\n\n.sp-how h3 {\n  margin: 0 0 12px 0;\n  font-size: 15px;\n  font-weight: 700;\n  color: #4c1d95;\n}\n\n.sp-how ol {\n  margin: 0;\n  padding-left: 20px;\n}\n\n.sp-how li {\n  font-size: 13px;\n  color: #374151;\n  line-height: 1.7;\n}\n\u003c/style\u003e\n\u003cdiv class=\"sp-hero\"\u003e\n  \u003ch2\u003eSVG to PNG Converter\u003c/h2\u003e\n  \u003cp\u003eUpload or paste SVG code, set your scale and background, and download a high-resolution PNG — entirely in your browser. No files are ever sent to a server.\u003c/p\u003e","title":"SVG to PNG Converter"},{"content":"Convert tabs to spaces (or spaces to tabs) instantly — choose your indent width, preview whitespace characters, and copy the result in one click.\nRelated Tool: Format HTML → HTML Beautifier Mode: Tabs → Spaces Spaces → Tabs Tab Width: 2 spaces 4 spaces 8 spaces Trim trailing whitespace Normalize line endings (CRLF → LF) Show whitespace in preview Convert Swap Input / Output Clear Upload File INPUT Paste OUTPUT (tabs → spaces) Copy Copied! Lines: 0 Input chars: 0 Output chars: 0 Tab stops (input): 0 Space indents (input): 0 Trailing WS lines: 0 How it works:\nTabs → Spaces: Each tab is expanded to the next tab-stop boundary using the chosen width (2, 4, or 8 spaces). Spaces → Tabs: Only leading whitespace is converted — groups of N spaces become one tab. Remaining partial spaces are kept. Whitespace preview: Spaces show as ·, tabs show as → so you can verify the conversion at a glance. Trim trailing whitespace: Strips any spaces or tabs at the end of each line. Normalize line endings: Converts Windows CRLF and old Mac CR to Unix LF. ","permalink":"https://productivity-works.com/tools/tab-converter/","summary":"\u003cp\u003eConvert tabs to spaces (or spaces to tabs) instantly — choose your indent width, preview whitespace characters, and copy the result in one click.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eRelated Tool: Format HTML → \u003ca href=\"https://productivity-works.com/tools/html-beautifier/\"\u003eHTML Beautifier\u003c/a\u003e\n\u003c/p\u003e\u003c/blockquote\u003e\n\u003cdiv id=\"tc-app\"\u003e\n\u003cstyle\u003e\n#tc-app *,#tc-app *::before,#tc-app *::after{box-sizing:border-box;margin:0;padding:0}\n#tc-app{font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;font-size:14px;color:#1e293b;background:#f8fafc;border:1px solid #e2e8f0;border-radius:12px;padding:20px;max-width:900px}\n#tc-app h2{display:none}\n#tc-app .tc-row{display:flex;gap:12px;flex-wrap:wrap;align-items:center;margin-bottom:14px}\n#tc-app .tc-label{font-weight:600;font-size:13px;color:#475569;white-space:nowrap}\n#tc-app .tc-btn-group{display:flex;gap:6px;flex-wrap:wrap}\n#tc-app .tc-btn{padding:7px 14px;border-radius:7px;border:1.5px solid #cbd5e1;background:#fff;color:#334155;font-size:13px;font-weight:500;cursor:pointer;transition:all .15s}\n#tc-app .tc-btn:hover{border-color:#6366f1;color:#6366f1}\n#tc-app .tc-btn.active{background:#6366f1;border-color:#6366f1;color:#fff}\n#tc-app .tc-btn.action{background:#6366f1;border-color:#6366f1;color:#fff;font-weight:600}\n#tc-app .tc-btn.action:hover{background:#4f46e5;border-color:#4f46e5}\n#tc-app .tc-btn.secondary{background:#f1f5f9;border-color:#cbd5e1;color:#475569}\n#tc-app .tc-btn.secondary:hover{border-color:#6366f1;color:#6366f1;background:#f1f5f9}\n#tc-app .tc-check-label{display:flex;align-items:center;gap:6px;font-size:13px;color:#475569;cursor:pointer;user-select:none}\n#tc-app .tc-check-label input{accent-color:#6366f1;width:15px;height:15px;cursor:pointer}\n#tc-app .tc-panels{display:grid;grid-template-columns:1fr 1fr;gap:14px}\n@media(max-width:640px){#tc-app .tc-panels{grid-template-columns:1fr}}\n#tc-app .tc-panel-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}\n#tc-app .tc-panel-title{font-weight:600;font-size:13px;color:#64748b}\n#tc-app .tc-panel-actions{display:flex;gap:6px}\n#tc-app textarea{width:100%;height:280px;padding:10px 12px;border:1.5px solid #e2e8f0;border-radius:8px;font-family:\"SFMono-Regular\",Consolas,\"Liberation Mono\",Menlo,monospace;font-size:13px;line-height:1.6;color:#1e293b;background:#fff;resize:vertical;outline:none;transition:border-color .15s}\n#tc-app textarea:focus{border-color:#6366f1}\n#tc-app .tc-preview{width:100%;height:280px;padding:10px 12px;border:1.5px solid #e2e8f0;border-radius:8px;font-family:\"SFMono-Regular\",Consolas,\"Liberation Mono\",Menlo,monospace;font-size:13px;line-height:1.6;background:#fff;overflow:auto;white-space:pre;word-break:normal}\n#tc-app .tc-preview .ws-space{color:#94a3b8}\n#tc-app .tc-preview .ws-tab{color:#a78bfa}\n#tc-app .tc-stats{display:flex;gap:14px;flex-wrap:wrap;padding:10px 14px;background:#f1f5f9;border-radius:8px;margin-top:12px}\n#tc-app .tc-stat{font-size:12px;color:#64748b}\n#tc-app .tc-stat strong{color:#1e293b;font-weight:600}\n#tc-app .tc-divider{border:none;border-top:1px solid #e2e8f0;margin:14px 0}\n#tc-app .tc-copy-note{font-size:12px;color:#10b981;font-weight:600;display:none}\n#tc-app .tc-upload-wrap{position:relative;display:inline-block}\n#tc-app .tc-upload-wrap input[type=file]{position:absolute;inset:0;opacity:0;cursor:pointer;width:100%}\n#tc-app .tc-mode-desc{font-size:12px;color:#94a3b8;margin-left:4px}\n\u003c/style\u003e\n\u003c!-- Controls --\u003e\n\u003cdiv class=\"tc-row\"\u003e\n  \u003cspan class=\"tc-label\"\u003eMode:\u003c/span\u003e\n  \u003cdiv class=\"tc-btn-group\"\u003e\n    \u003cbutton class=\"tc-btn active\" id=\"tc-mode-t2s\" onclick=\"tcSetMode('t2s')\"\u003eTabs → Spaces\u003c/button\u003e\n    \u003cbutton class=\"tc-btn\" id=\"tc-mode-s2t\" onclick=\"tcSetMode('s2t')\"\u003eSpaces → Tabs\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"tc-row\"\u003e\n  \u003cspan class=\"tc-label\"\u003eTab Width:\u003c/span\u003e\n  \u003cdiv class=\"tc-btn-group\"\u003e\n    \u003cbutton class=\"tc-btn active\" id=\"tc-w-2\" onclick=\"tcSetWidth(2)\"\u003e2 spaces\u003c/button\u003e\n    \u003cbutton class=\"tc-btn\" id=\"tc-w-4\" onclick=\"tcSetWidth(4)\"\u003e4 spaces\u003c/button\u003e\n    \u003cbutton class=\"tc-btn\" id=\"tc-w-8\" onclick=\"tcSetWidth(8)\"\u003e8 spaces\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"tc-row\"\u003e\n  \u003clabel class=\"tc-check-label\"\u003e\u003cinput type=\"checkbox\" id=\"tc-trim\" onchange=\"tcProcess()\"\u003e Trim trailing whitespace\u003c/label\u003e\n  \u003clabel class=\"tc-check-label\"\u003e\u003cinput type=\"checkbox\" id=\"tc-normalize\" onchange=\"tcProcess()\"\u003e Normalize line endings (CRLF → LF)\u003c/label\u003e\n  \u003clabel class=\"tc-check-label\"\u003e\u003cinput type=\"checkbox\" id=\"tc-preview-ws\" onchange=\"tcProcess()\" checked\u003e Show whitespace in preview\u003c/label\u003e\n\u003c/div\u003e\n\u003cdiv class=\"tc-row\"\u003e\n  \u003cdiv class=\"tc-btn-group\"\u003e\n    \u003cbutton class=\"tc-btn action\" onclick=\"tcProcess()\"\u003eConvert\u003c/button\u003e\n    \u003cbutton class=\"tc-btn secondary\" onclick=\"tcSwap()\"\u003eSwap Input / Output\u003c/button\u003e\n    \u003cbutton class=\"tc-btn secondary\" onclick=\"tcClear()\"\u003eClear\u003c/button\u003e\n    \u003cdiv class=\"tc-upload-wrap\"\u003e\n      \u003cbutton class=\"tc-btn secondary\"\u003eUpload File\u003c/button\u003e\n      \u003cinput type=\"file\" accept=\"text/*\" onchange=\"tcUpload(event)\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003chr class=\"tc-divider\"\u003e\n\u003c!-- Editor panels --\u003e\n\u003cdiv class=\"tc-panels\"\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"tc-panel-head\"\u003e\n      \u003cspan class=\"tc-panel-title\"\u003eINPUT\u003c/span\u003e\n      \u003cdiv class=\"tc-panel-actions\"\u003e\n        \u003cbutton class=\"tc-btn secondary\" style=\"padding:4px 10px;font-size:12px\" onclick=\"tcPaste()\"\u003ePaste\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003ctextarea id=\"tc-input\" placeholder=\"Paste your code here, or upload a file above...\" oninput=\"tcProcess()\" spellcheck=\"false\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"tc-panel-head\"\u003e\n      \u003cspan class=\"tc-panel-title\"\u003eOUTPUT \u003cspan class=\"tc-mode-desc\" id=\"tc-mode-label\"\u003e(tabs → spaces)\u003c/span\u003e\u003c/span\u003e\n      \u003cdiv class=\"tc-panel-actions\"\u003e\n        \u003cbutton class=\"tc-btn secondary\" style=\"padding:4px 10px;font-size:12px\" onclick=\"tcCopy()\"\u003eCopy\u003c/button\u003e\n        \u003cspan class=\"tc-copy-note\" id=\"tc-copy-note\"\u003eCopied!\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tc-preview\" id=\"tc-output-preview\" aria-live=\"polite\"\u003e\u003c/div\u003e\n    \u003ctextarea id=\"tc-output-raw\" style=\"display:none\" readonly spellcheck=\"false\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Stats --\u003e\n\u003cdiv class=\"tc-stats\" id=\"tc-stats\"\u003e\n  \u003cspan class=\"tc-stat\"\u003eLines: \u003cstrong id=\"tc-stat-lines\"\u003e0\u003c/strong\u003e\u003c/span\u003e\n  \u003cspan class=\"tc-stat\"\u003eInput chars: \u003cstrong id=\"tc-stat-in-chars\"\u003e0\u003c/strong\u003e\u003c/span\u003e\n  \u003cspan class=\"tc-stat\"\u003eOutput chars: \u003cstrong id=\"tc-stat-out-chars\"\u003e0\u003c/strong\u003e\u003c/span\u003e\n  \u003cspan class=\"tc-stat\"\u003eTab stops (input): \u003cstrong id=\"tc-stat-tabs\"\u003e0\u003c/strong\u003e\u003c/span\u003e\n  \u003cspan class=\"tc-stat\"\u003eSpace indents (input): \u003cstrong id=\"tc-stat-spaces\"\u003e0\u003c/strong\u003e\u003c/span\u003e\n  \u003cspan class=\"tc-stat\"\u003eTrailing WS lines: \u003cstrong id=\"tc-stat-trailing\"\u003e0\u003c/strong\u003e\u003c/span\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n  var mode = 't2s';\n  var width = 2;\n\n  function setMode(m){\n    mode = m;\n    document.getElementById('tc-mode-t2s').classList.toggle('active', m==='t2s');\n    document.getElementById('tc-mode-s2t').classList.toggle('active', m==='s2t');\n    document.getElementById('tc-mode-label').textContent = m==='t2s' ? '(tabs → spaces)' : '(spaces → tabs)';\n    process();\n  }\n  function setWidth(w){\n    width = w;\n    [2,4,8].forEach(function(n){\n      document.getElementById('tc-w-'+n).classList.toggle('active', n===w);\n    });\n    process();\n  }\n\n  function process(){\n    var input = document.getElementById('tc-input').value;\n    var trim = document.getElementById('tc-trim').checked;\n    var norm = document.getElementById('tc-normalize').checked;\n    var showWs = document.getElementById('tc-preview-ws').checked;\n\n    // Normalize line endings first if requested\n    var text = norm ? input.replace(/\\r\\n/g,'\\n').replace(/\\r/g,'\\n') : input;\n\n    // Convert\n    var lines = text.split('\\n');\n    var outLines = lines.map(function(line){\n      var converted;\n      if(mode === 't2s'){\n        // Replace tabs with spaces — handle mid-line tabs too with tab-stop logic\n        var result = '';\n        var col = 0;\n        for(var i=0;i\u003cline.length;i++){\n          var ch = line[i];\n          if(ch==='\\t'){\n            var spaces = width - (col % width);\n            result += ' '.repeat(spaces);\n            col += spaces;\n          } else {\n            result += ch;\n            col++;\n          }\n        }\n        converted = result;\n      } else {\n        // Spaces → tabs: only convert leading whitespace\n        var leadMatch = line.match(/^( +)/);\n        if(leadMatch){\n          var spCount = leadMatch[1].length;\n          var tabs = Math.floor(spCount / width);\n          var rem = spCount % width;\n          converted = '\\t'.repeat(tabs) + ' '.repeat(rem) + line.slice(spCount);\n        } else {\n          converted = line;\n        }\n      }\n      if(trim) converted = converted.replace(/[ \\t]+$/, '');\n      return converted;\n    });\n\n    var output = outLines.join('\\n');\n    document.getElementById('tc-output-raw').value = output;\n\n    // Stats\n    var tabCount = 0;\n    var spaceIndentCount = 0;\n    var trailingCount = 0;\n    lines.forEach(function(l){\n      if(/\\t/.test(l)) tabCount++;\n      if(/^ +/.test(l)) spaceIndentCount++;\n      if(/[ \\t]+$/.test(l)) trailingCount++;\n    });\n    document.getElementById('tc-stat-lines').textContent = lines.length;\n    document.getElementById('tc-stat-in-chars').textContent = input.length;\n    document.getElementById('tc-stat-out-chars').textContent = output.length;\n    document.getElementById('tc-stat-tabs').textContent = tabCount;\n    document.getElementById('tc-stat-spaces').textContent = spaceIndentCount;\n    document.getElementById('tc-stat-trailing').textContent = trailingCount;\n\n    // Render preview\n    renderPreview(output, showWs);\n  }\n\n  function renderPreview(text, showWs){\n    var el = document.getElementById('tc-output-preview');\n    if(!showWs){\n      el.textContent = text;\n      return;\n    }\n    // Escape HTML then replace whitespace markers\n    var escaped = text\n      .replace(/\u0026/g,'\u0026amp;')\n      .replace(/\u003c/g,'\u0026lt;')\n      .replace(/\u003e/g,'\u0026gt;');\n    var marked = escaped\n      .replace(/ /g,'\u003cspan class=\"ws-space\"\u003e\\u00b7\u003c/span\u003e')\n      .replace(/\\t/g,'\u003cspan class=\"ws-tab\"\u003e\\u2192   \u003c/span\u003e');\n    el.innerHTML = marked;\n  }\n\n  function swap(){\n    var raw = document.getElementById('tc-output-raw').value;\n    document.getElementById('tc-input').value = raw;\n    process();\n  }\n\n  function clear(){\n    document.getElementById('tc-input').value = '';\n    document.getElementById('tc-output-raw').value = '';\n    document.getElementById('tc-output-preview').textContent = '';\n    ['tc-stat-lines','tc-stat-in-chars','tc-stat-out-chars','tc-stat-tabs','tc-stat-spaces','tc-stat-trailing'].forEach(function(id){\n      document.getElementById(id).textContent = '0';\n    });\n  }\n\n  function copy(){\n    var val = document.getElementById('tc-output-raw').value;\n    if(!val) return;\n    if(navigator.clipboard \u0026\u0026 navigator.clipboard.writeText){\n      navigator.clipboard.writeText(val).then(flashCopy);\n    } else {\n      var ta = document.createElement('textarea');\n      ta.value = val;\n      ta.style.position='fixed';ta.style.opacity='0';\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      flashCopy();\n    }\n  }\n\n  function flashCopy(){\n    var note = document.getElementById('tc-copy-note');\n    note.style.display='inline';\n    setTimeout(function(){ note.style.display='none'; }, 1800);\n  }\n\n  function paste(){\n    if(navigator.clipboard \u0026\u0026 navigator.clipboard.readText){\n      navigator.clipboard.readText().then(function(t){\n        document.getElementById('tc-input').value = t;\n        process();\n      });\n    }\n  }\n\n  function upload(e){\n    var file = e.target.files[0];\n    if(!file) return;\n    var reader = new FileReader();\n    reader.onload = function(ev){\n      document.getElementById('tc-input').value = ev.target.result;\n      process();\n    };\n    reader.readAsText(file);\n    e.target.value = '';\n  }\n\n  // Expose to inline handlers\n  window.tcSetMode = setMode;\n  window.tcSetWidth = setWidth;\n  window.tcProcess = process;\n  window.tcSwap = swap;\n  window.tcClear = clear;\n  window.tcCopy = copy;\n  window.tcPaste = paste;\n  window.tcUpload = upload;\n\n  // Init\n  process();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cstrong\u003eHow it works:\u003c/strong\u003e\u003c/p\u003e","title":"Tab ↔ Spaces Converter"},{"content":"Paste your Tailwind CSS utility classes and get them sorted in the recommended order — layout first, effects last. Supports single class strings or multi-element HTML snippets.\nRelated tools: CSS Variables Generator · CSS Media Query Generator Input Mode\nSingle Class String HTML Elements Paste Tailwind classes\nPaste a space-separated list of Tailwind utility classes.\nRemove duplicate classes Sort Classes Load Sample Clear Results\n\u0026lt;div class=\u0026quot;tw-diff\u0026quot; id=\u0026quot;tw-diff-wrap\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;tw-diff-box before\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;tw-diff-label before\u0026quot;\u0026gt;Before\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;tw-diff-content\u0026quot; id=\u0026quot;tw-before\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;tw-diff-box after\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;tw-diff-label after\u0026quot;\u0026gt;After\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;tw-diff-content\u0026quot; id=\u0026quot;tw-after\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;label\u0026gt;Sorted Output\u0026lt;/label\u0026gt; \u0026lt;div class=\u0026quot;tw-output-wrap\u0026quot;\u0026gt; \u0026lt;textarea class=\u0026quot;tw-output\u0026quot; id=\u0026quot;tw-output\u0026quot; readonly rows=\u0026quot;5\u0026quot;\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;button class=\u0026quot;tw-copy-btn\u0026quot; id=\u0026quot;tw-copy-btn\u0026quot; onclick=\u0026quot;twCopy()\u0026quot;\u0026gt;Copy\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; How the Sort Order Works The sorter follows Tailwind\u0026rsquo;s recommended class ordering — the same order enforced by the official Prettier plugin for Tailwind CSS :\nGroup Examples Layout flex, grid, block, hidden, overflow-hidden Positioning relative, absolute, top-0, z-10 Sizing w-full, h-16, max-w-md Spacing p-4, mx-auto, space-x-2 Typography text-lg, font-bold, leading-tight, text-gray-900 Backgrounds bg-white, from-blue-500, bg-gradient-to-r Borders rounded-xl, border, ring-2 Effects shadow-lg, opacity-75 Transitions transition, duration-200, ease-in-out Transforms scale-95, rotate-3, translate-x-1 Interactivity cursor-pointer, select-none Responsive (sm:, md:, lg:) and state (hover:, focus:, dark:) variants are sorted after their base-class group.\nTips HTML mode is useful when you paste a full component — every class=\u0026quot;...\u0026quot; attribute is sorted independently. Remove duplicates is on by default. Turn it off to see the full original class list sorted. The sorter works entirely in your browser — no data is sent anywhere. Related tools: CSS Variables Generator · CSS Media Query Generator ","permalink":"https://productivity-works.com/tools/tailwind-class-sorter/","summary":"\u003cp\u003ePaste your Tailwind CSS utility classes and get them sorted in the recommended order — layout first, effects last. Supports single class strings or multi-element HTML snippets.\u003c/p\u003e\n\u003cp\u003eRelated tools: \u003ca href=\"https://productivity-works.com/tools/css-variables-generator/\"\u003eCSS Variables Generator\u003c/a\u003e\n · \u003ca href=\"https://productivity-works.com/tools/media-query-generator/\"\u003eCSS Media Query Generator\u003c/a\u003e\n\u003c/p\u003e\n\u003cdiv id=\"tw-app\"\u003e\n\u003cstyle\u003e\n  #tw-app {\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n    max-width: 860px;\n    margin: 0 auto;\n    color: #1e293b;\n  }\n  #tw-app * { box-sizing: border-box; }\n\u003cp\u003e#tw-app .tw-card {\nbackground: #fff;\nborder: 1px solid #e2e8f0;\nborder-radius: 12px;\npadding: 24px;\nmargin-bottom: 20px;\nbox-shadow: 0 1px 4px rgba(0,0,0,0.06);\n}\u003c/p\u003e","title":"Tailwind CSS Class Sorter"},{"content":" Estimate your paycheck deductions in seconds. Enter your gross pay and filing details below to see federal income tax, FICA (Social Security + Medicare), and estimated state tax withheld — plus a full pay-period breakdown. Your Pay Information Gross Pay Amount Enter the amount for the pay period selected below. Pay Period Annual Monthly Biweekly (every 2 weeks) Weekly Filing Status Single Married Filing Jointly Head of Household Dependents / Allowances Each dependent reduces withholding (~$500/yr credit equivalent). Additional Withholding ($/paycheck) Optional extra amount withheld each pay period. State No state income tax Illinois (~4.95%) Massachusetts (~5.25%) Michigan (~5.5%) Georgia / Virginia (~6%) Wisconsin (~6.5%) South Carolina (~7%) Maryland (~7.25%) Minnesota / New Jersey (~7.5%) Oregon / Vermont (~8%) New York (~8.5%) California (~9.3%) Custom rate... Custom State Tax Rate (%) Calculate My Withholding Results — Per Paycheck Per Paycheck Monthly Annual Net Pay (Take-Home) -- Federal Income Tax -- Social Security -- Medicare -- State Tax -- Total Withheld -- Full Pay Period Breakdown Component Weekly Biweekly Monthly Annual Disclaimer: This calculator provides estimates based on 2025 federal tax brackets and simplified FICA rates. Actual withholding may differ based on W-4 elections, pre-tax deductions (401k, health insurance), local taxes, and other factors. Consult a tax professional for precise advice. State tax figures are flat-rate approximations only. Related Free Tools Salary to Hourly Calculator Overtime Pay Calculator Monthly Budget Calculator Retirement Savings Calculator Related Tools Freelance Rate Calculator Hourly To Salary Calculator Pension Simulator ","permalink":"https://productivity-works.com/tools/tax-withholding-calculator/","summary":"\u003cdiv id=\"tw-app\"\u003e\n\u003cstyle\u003e\n#tw-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 780px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#tw-app h2 {\n  font-size: 1.4rem;\n  font-weight: 700;\n  margin: 0 0 1rem 0;\n  color: #1a1a2e;\n}\n#tw-app .tw-intro {\n  background: #f0f4ff;\n  border-left: 4px solid #4361ee;\n  padding: 0.85rem 1.1rem;\n  border-radius: 0 6px 6px 0;\n  margin-bottom: 1.5rem;\n  font-size: 0.92rem;\n  color: #444;\n}\n#tw-app .tw-card {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 1.5rem;\n  margin-bottom: 1.2rem;\n  box-shadow: 0 2px 8px rgba(0,0,0,0.04);\n}\n#tw-app .tw-row {\n  display: flex;\n  gap: 1rem;\n  flex-wrap: wrap;\n  margin-bottom: 1rem;\n}\n#tw-app .tw-field {\n  flex: 1;\n  min-width: 200px;\n}\n#tw-app .tw-field label {\n  display: block;\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #555;\n  margin-bottom: 0.35rem;\n  text-transform: uppercase;\n  letter-spacing: 0.03em;\n}\n#tw-app .tw-field input,\n#tw-app .tw-field select {\n  width: 100%;\n  padding: 0.6rem 0.85rem;\n  border: 1.5px solid #d1d5db;\n  border-radius: 6px;\n  font-size: 1rem;\n  color: #1a1a2e;\n  background: #fafafa;\n  box-sizing: border-box;\n  transition: border-color 0.2s;\n  -webkit-appearance: none;\n}\n#tw-app .tw-field input:focus,\n#tw-app .tw-field select:focus {\n  outline: none;\n  border-color: #4361ee;\n  background: #fff;\n}\n#tw-app .tw-field .tw-hint {\n  font-size: 0.76rem;\n  color: #888;\n  margin-top: 0.25rem;\n}\n#tw-app .tw-btn {\n  display: block;\n  width: 100%;\n  padding: 0.85rem;\n  background: #4361ee;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 1.05rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.2s, transform 0.1s;\n  letter-spacing: 0.02em;\n}\n#tw-app .tw-btn:hover {\n  background: #3451d1;\n  transform: translateY(-1px);\n}\n#tw-app .tw-btn:active {\n  transform: translateY(0);\n}\n#tw-app .tw-results {\n  display: none;\n}\n#tw-app .tw-results.visible {\n  display: block;\n}\n#tw-app .tw-summary-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n  gap: 0.9rem;\n  margin-bottom: 1.2rem;\n}\n#tw-app .tw-stat {\n  background: #f8faff;\n  border: 1px solid #dce5ff;\n  border-radius: 8px;\n  padding: 0.85rem 1rem;\n  text-align: center;\n}\n#tw-app .tw-stat .tw-stat-label {\n  font-size: 0.75rem;\n  color: #666;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  margin-bottom: 0.3rem;\n}\n#tw-app .tw-stat .tw-stat-value {\n  font-size: 1.35rem;\n  font-weight: 800;\n  color: #4361ee;\n}\n#tw-app .tw-stat.net .tw-stat-value {\n  color: #2d8a4e;\n}\n#tw-app .tw-stat.deduct .tw-stat-value {\n  color: #c0392b;\n}\n#tw-app .tw-breakdown table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.9rem;\n}\n#tw-app .tw-breakdown th {\n  background: #f0f4ff;\n  padding: 0.55rem 0.8rem;\n  text-align: left;\n  font-size: 0.78rem;\n  font-weight: 700;\n  color: #4361ee;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#tw-app .tw-breakdown td {\n  padding: 0.55rem 0.8rem;\n  border-bottom: 1px solid #f0f0f0;\n  color: #333;\n}\n#tw-app .tw-breakdown tr:last-child td {\n  border-bottom: none;\n}\n#tw-app .tw-breakdown tr.total-row td {\n  font-weight: 700;\n  background: #fffbeb;\n  border-top: 2px solid #f0d060;\n}\n#tw-app .tw-breakdown tr.net-row td {\n  font-weight: 800;\n  background: #edfff4;\n  color: #2d8a4e;\n  border-top: 2px solid #6fcf97;\n  font-size: 1rem;\n}\n#tw-app .tw-period-tabs {\n  display: flex;\n  gap: 0.4rem;\n  flex-wrap: wrap;\n  margin-bottom: 1rem;\n}\n#tw-app .tw-period-tab {\n  padding: 0.35rem 0.9rem;\n  border: 1.5px solid #4361ee;\n  border-radius: 20px;\n  font-size: 0.82rem;\n  font-weight: 600;\n  cursor: pointer;\n  background: #fff;\n  color: #4361ee;\n  transition: all 0.15s;\n}\n#tw-app .tw-period-tab.active,\n#tw-app .tw-period-tab:hover {\n  background: #4361ee;\n  color: #fff;\n}\n#tw-app .tw-disclaimer {\n  font-size: 0.77rem;\n  color: #888;\n  background: #fafafa;\n  border: 1px solid #eee;\n  border-radius: 6px;\n  padding: 0.75rem 1rem;\n  margin-top: 1rem;\n}\n#tw-app .tw-related {\n  margin-top: 1.5rem;\n  padding-top: 1rem;\n  border-top: 1px solid #eee;\n}\n#tw-app .tw-related h3 {\n  font-size: 0.95rem;\n  font-weight: 700;\n  margin-bottom: 0.6rem;\n  color: #333;\n}\n#tw-app .tw-related ul {\n  margin: 0;\n  padding-left: 1.2rem;\n}\n#tw-app .tw-related li {\n  margin-bottom: 0.3rem;\n  font-size: 0.88rem;\n}\n#tw-app .tw-related a {\n  color: #4361ee;\n  text-decoration: none;\n}\n#tw-app .tw-related a:hover {\n  text-decoration: underline;\n}\n@media (max-width: 480px) {\n  #tw-app .tw-card { padding: 1rem; }\n  #tw-app .tw-stat .tw-stat-value { font-size: 1.1rem; }\n}\n\u003c/style\u003e\n\u003cdiv class=\"tw-intro\"\u003e\nEstimate your paycheck deductions in seconds. Enter your gross pay and filing details below to see federal income tax, FICA (Social Security + Medicare), and estimated state tax withheld — plus a full pay-period breakdown.\n\u003c/div\u003e\n\u003cdiv class=\"tw-card\"\u003e\n  \u003ch2\u003eYour Pay Information\u003c/h2\u003e\n  \u003cdiv class=\"tw-row\"\u003e\n    \u003cdiv class=\"tw-field\"\u003e\n      \u003clabel for=\"tw-gross\"\u003eGross Pay Amount\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"tw-gross\" placeholder=\"e.g. 75000\" min=\"0\" step=\"0.01\" /\u003e\n      \u003cdiv class=\"tw-hint\"\u003eEnter the amount for the pay period selected below.\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-field\"\u003e\n      \u003clabel for=\"tw-period\"\u003ePay Period\u003c/label\u003e\n      \u003cselect id=\"tw-period\"\u003e\n        \u003coption value=\"annual\"\u003eAnnual\u003c/option\u003e\n        \u003coption value=\"monthly\"\u003eMonthly\u003c/option\u003e\n        \u003coption value=\"biweekly\" selected\u003eBiweekly (every 2 weeks)\u003c/option\u003e\n        \u003coption value=\"weekly\"\u003eWeekly\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"tw-row\"\u003e\n    \u003cdiv class=\"tw-field\"\u003e\n      \u003clabel for=\"tw-status\"\u003eFiling Status\u003c/label\u003e\n      \u003cselect id=\"tw-status\"\u003e\n        \u003coption value=\"single\"\u003eSingle\u003c/option\u003e\n        \u003coption value=\"married\"\u003eMarried Filing Jointly\u003c/option\u003e\n        \u003coption value=\"hoh\"\u003eHead of Household\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-field\"\u003e\n      \u003clabel for=\"tw-dependents\"\u003eDependents / Allowances\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"tw-dependents\" value=\"0\" min=\"0\" max=\"20\" step=\"1\" /\u003e\n      \u003cdiv class=\"tw-hint\"\u003eEach dependent reduces withholding (~$500/yr credit equivalent).\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"tw-row\"\u003e\n    \u003cdiv class=\"tw-field\"\u003e\n      \u003clabel for=\"tw-extra\"\u003eAdditional Withholding ($/paycheck)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"tw-extra\" value=\"0\" min=\"0\" step=\"0.01\" /\u003e\n      \u003cdiv class=\"tw-hint\"\u003eOptional extra amount withheld each pay period.\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-field\"\u003e\n      \u003clabel for=\"tw-state\"\u003eState\u003c/label\u003e\n      \u003cselect id=\"tw-state\"\u003e\n        \u003coption value=\"0\"\u003eNo state income tax\u003c/option\u003e\n        \u003coption value=\"0.0495\"\u003eIllinois (~4.95%)\u003c/option\u003e\n        \u003coption value=\"0.0525\"\u003eMassachusetts (~5.25%)\u003c/option\u003e\n        \u003coption value=\"0.055\"\u003eMichigan (~5.5%)\u003c/option\u003e\n        \u003coption value=\"0.06\"\u003eGeorgia / Virginia (~6%)\u003c/option\u003e\n        \u003coption value=\"0.065\"\u003eWisconsin (~6.5%)\u003c/option\u003e\n        \u003coption value=\"0.07\"\u003eSouth Carolina (~7%)\u003c/option\u003e\n        \u003coption value=\"0.0725\"\u003eMaryland (~7.25%)\u003c/option\u003e\n        \u003coption value=\"0.075\"\u003eMinnesota / New Jersey (~7.5%)\u003c/option\u003e\n        \u003coption value=\"0.08\"\u003eOregon / Vermont (~8%)\u003c/option\u003e\n        \u003coption value=\"0.085\"\u003eNew York (~8.5%)\u003c/option\u003e\n        \u003coption value=\"0.093\"\u003eCalifornia (~9.3%)\u003c/option\u003e\n        \u003coption value=\"custom\"\u003eCustom rate...\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"tw-row\" id=\"tw-custom-state-row\" style=\"display:none;\"\u003e\n    \u003cdiv class=\"tw-field\"\u003e\n      \u003clabel for=\"tw-custom-rate\"\u003eCustom State Tax Rate (%)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"tw-custom-rate\" value=\"5\" min=\"0\" max=\"20\" step=\"0.01\" /\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cbutton class=\"tw-btn\" onclick=\"twCalculate()\"\u003eCalculate My Withholding\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"tw-card tw-results\" id=\"tw-results\"\u003e\n  \u003ch2\u003eResults — \u003cspan id=\"tw-result-period-label\"\u003ePer Paycheck\u003c/span\u003e\u003c/h2\u003e\n  \u003cdiv class=\"tw-period-tabs\"\u003e\n    \u003cbutton class=\"tw-period-tab active\" onclick=\"twShowPeriod('paycheck', this)\"\u003ePer Paycheck\u003c/button\u003e\n    \u003cbutton class=\"tw-period-tab\" onclick=\"twShowPeriod('monthly', this)\"\u003eMonthly\u003c/button\u003e\n    \u003cbutton class=\"tw-period-tab\" onclick=\"twShowPeriod('annual', this)\"\u003eAnnual\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"tw-summary-grid\"\u003e\n    \u003cdiv class=\"tw-stat net\"\u003e\n      \u003cdiv class=\"tw-stat-label\"\u003eNet Pay (Take-Home)\u003c/div\u003e\n      \u003cdiv class=\"tw-stat-value\" id=\"tw-net-pay\"\u003e--\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-stat deduct\"\u003e\n      \u003cdiv class=\"tw-stat-label\"\u003eFederal Income Tax\u003c/div\u003e\n      \u003cdiv class=\"tw-stat-value\" id=\"tw-fed-tax\"\u003e--\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-stat deduct\"\u003e\n      \u003cdiv class=\"tw-stat-label\"\u003eSocial Security\u003c/div\u003e\n      \u003cdiv class=\"tw-stat-value\" id=\"tw-ss\"\u003e--\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-stat deduct\"\u003e\n      \u003cdiv class=\"tw-stat-label\"\u003eMedicare\u003c/div\u003e\n      \u003cdiv class=\"tw-stat-value\" id=\"tw-medicare\"\u003e--\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-stat deduct\"\u003e\n      \u003cdiv class=\"tw-stat-label\"\u003eState Tax\u003c/div\u003e\n      \u003cdiv class=\"tw-stat-value\" id=\"tw-state-tax\"\u003e--\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-stat deduct\"\u003e\n      \u003cdiv class=\"tw-stat-label\"\u003eTotal Withheld\u003c/div\u003e\n      \u003cdiv class=\"tw-stat-value\" id=\"tw-total-withheld\"\u003e--\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"tw-breakdown\"\u003e\n    \u003ch2\u003eFull Pay Period Breakdown\u003c/h2\u003e\n    \u003ctable\u003e\n      \u003cthead\u003e\n        \u003ctr\u003e\n          \u003cth\u003eComponent\u003c/th\u003e\n          \u003cth\u003eWeekly\u003c/th\u003e\n          \u003cth\u003eBiweekly\u003c/th\u003e\n          \u003cth\u003eMonthly\u003c/th\u003e\n          \u003cth\u003eAnnual\u003c/th\u003e\n        \u003c/tr\u003e\n      \u003c/thead\u003e\n      \u003ctbody id=\"tw-table-body\"\u003e\n      \u003c/tbody\u003e\n    \u003c/table\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"tw-disclaimer\"\u003e\n    \u003cstrong\u003eDisclaimer:\u003c/strong\u003e This calculator provides estimates based on 2025 federal tax brackets and simplified FICA rates. Actual withholding may differ based on W-4 elections, pre-tax deductions (401k, health insurance), local taxes, and other factors. Consult a tax professional for precise advice. State tax figures are flat-rate approximations only.\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"tw-related\"\u003e\n  \u003ch3\u003eRelated Free Tools\u003c/h3\u003e\n  \u003cul\u003e\n    \u003cli\u003e\u003ca href=\"/tools/hourly-to-salary-calculator/\"\u003eSalary to Hourly Calculator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/salary-calculator/\"\u003eOvertime Pay Calculator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/budget-planner/\"\u003eMonthly Budget Calculator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/retirement-calculator/\"\u003eRetirement Savings Calculator\u003c/a\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  // 2025 Federal income tax brackets (marginal rates)\n  var BRACKETS = {\n    single: [\n      [11925, 0.10],\n      [48475, 0.12],\n      [103350, 0.22],\n      [197300, 0.24],\n      [250525, 0.32],\n      [626350, 0.35],\n      [Infinity, 0.37]\n    ],\n    married: [\n      [23850, 0.10],\n      [96950, 0.12],\n      [206700, 0.22],\n      [394600, 0.24],\n      [501050, 0.32],\n      [751600, 0.35],\n      [Infinity, 0.37]\n    ],\n    hoh: [\n      [17000, 0.10],\n      [64850, 0.12],\n      [103350, 0.22],\n      [197300, 0.24],\n      [250500, 0.32],\n      [626350, 0.35],\n      [Infinity, 0.37]\n    ]\n  };\n\n  var STANDARD_DEDUCTION = {\n    single: 15000,\n    married: 30000,\n    hoh: 22500\n  };\n\n  // SS wage base 2025\n  var SS_WAGE_BASE = 176100;\n  var SS_RATE = 0.062;\n  var MEDICARE_RATE = 0.0145;\n  var ADDITIONAL_MEDICARE_RATE = 0.009;\n  var ADDITIONAL_MEDICARE_THRESHOLD = { single: 200000, married: 250000, hoh: 200000 };\n\n  var twData = null;\n  var twCurrentPeriodView = 'paycheck';\n\n  document.getElementById('tw-state').addEventListener('change', function() {\n    document.getElementById('tw-custom-state-row').style.display =\n      this.value === 'custom' ? 'flex' : 'none';\n  });\n\n  function calcFederalTax(annualGross, status, dependents) {\n    var depCredit = dependents * 500;\n    var taxable = Math.max(0, annualGross - STANDARD_DEDUCTION[status] - depCredit);\n    var brackets = BRACKETS[status];\n    var tax = 0;\n    var prev = 0;\n    for (var i = 0; i \u003c brackets.length; i++) {\n      var limit = brackets[i][0];\n      var rate = brackets[i][1];\n      if (taxable \u003c= prev) break;\n      var chunk = Math.min(taxable, limit) - prev;\n      tax += chunk * rate;\n      prev = limit;\n    }\n    return Math.max(0, tax);\n  }\n\n  function fmt(n) {\n    return '$' + n.toFixed(2).replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',');\n  }\n\n  function toAnnual(amount, period) {\n    if (period === 'annual') return amount;\n    if (period === 'monthly') return amount * 12;\n    if (period === 'biweekly') return amount * 26;\n    if (period === 'weekly') return amount * 52;\n    return amount;\n  }\n\n  function fromAnnual(annualAmt, period) {\n    if (period === 'annual') return annualAmt;\n    if (period === 'monthly') return annualAmt / 12;\n    if (period === 'biweekly') return annualAmt / 26;\n    if (period === 'weekly') return annualAmt / 52;\n    return annualAmt;\n  }\n\n  window.twCalculate = function() {\n    var grossInput = parseFloat(document.getElementById('tw-gross').value) || 0;\n    var period = document.getElementById('tw-period').value;\n    var status = document.getElementById('tw-status').value;\n    var dependents = parseInt(document.getElementById('tw-dependents').value) || 0;\n    var extraPerPaycheck = parseFloat(document.getElementById('tw-extra').value) || 0;\n    var stateEl = document.getElementById('tw-state');\n    var stateRate = stateEl.value === 'custom'\n      ? (parseFloat(document.getElementById('tw-custom-rate').value) || 0) / 100\n      : parseFloat(stateEl.value) || 0;\n\n    var annualGross = toAnnual(grossInput, period);\n\n    // Federal income tax (annual)\n    var annualFedTax = calcFederalTax(annualGross, status, dependents);\n\n    // SS (capped at wage base)\n    var ssTaxable = Math.min(annualGross, SS_WAGE_BASE);\n    var annualSS = ssTaxable * SS_RATE;\n\n    // Medicare (with additional Medicare tax)\n    var annualMedicare = annualGross * MEDICARE_RATE;\n    var threshold = ADDITIONAL_MEDICARE_THRESHOLD[status];\n    if (annualGross \u003e threshold) {\n      annualMedicare += (annualGross - threshold) * ADDITIONAL_MEDICARE_RATE;\n    }\n\n    // State\n    var annualStateTax = annualGross * stateRate;\n\n    // Extra withholding (annualize from paycheck amount)\n    var annualExtra = 0;\n    if (period === 'biweekly') annualExtra = extraPerPaycheck * 26;\n    else if (period === 'weekly') annualExtra = extraPerPaycheck * 52;\n    else if (period === 'monthly') annualExtra = extraPerPaycheck * 12;\n    else annualExtra = extraPerPaycheck; // if annual, treat as annual extra\n\n    var annualTotal = annualFedTax + annualSS + annualMedicare + annualStateTax + annualExtra;\n    var annualNet = annualGross - annualTotal;\n\n    twData = {\n      annualGross: annualGross,\n      annualFedTax: annualFedTax,\n      annualSS: annualSS,\n      annualMedicare: annualMedicare,\n      annualStateTax: annualStateTax,\n      annualExtra: annualExtra,\n      annualTotal: annualTotal,\n      annualNet: annualNet,\n      inputPeriod: period,\n      extraPerPaycheck: extraPerPaycheck\n    };\n\n    twCurrentPeriodView = 'paycheck';\n    document.querySelectorAll('#tw-app .tw-period-tab').forEach(function(t) { t.classList.remove('active'); });\n    document.querySelectorAll('#tw-app .tw-period-tab')[0].classList.add('active');\n\n    twRenderResults();\n\n    var resultsEl = document.getElementById('tw-results');\n    resultsEl.classList.add('visible');\n    resultsEl.scrollIntoView({ behavior: 'smooth', block: 'start' });\n  };\n\n  function getPaycheckDivisor(inputPeriod) {\n    if (inputPeriod === 'weekly') return 52;\n    if (inputPeriod === 'monthly') return 12;\n    if (inputPeriod === 'annual') return 1;\n    return 26; // biweekly default\n  }\n\n  window.twShowPeriod = function(viewPeriod, btn) {\n    twCurrentPeriodView = viewPeriod;\n    document.querySelectorAll('#tw-app .tw-period-tab').forEach(function(t) { t.classList.remove('active'); });\n    btn.classList.add('active');\n    twRenderResults();\n  };\n\n  function twRenderResults() {\n    if (!twData) return;\n    var d = twData;\n\n    var divisors = { paycheck: getPaycheckDivisor(d.inputPeriod), monthly: 12, annual: 1 };\n    var div = divisors[twCurrentPeriodView];\n    var labels = { paycheck: 'Per Paycheck', monthly: 'Monthly', annual: 'Annual' };\n\n    document.getElementById('tw-result-period-label').textContent = labels[twCurrentPeriodView];\n\n    document.getElementById('tw-net-pay').textContent = fmt(d.annualNet / div);\n    document.getElementById('tw-fed-tax').textContent = fmt(d.annualFedTax / div);\n    document.getElementById('tw-ss').textContent = fmt(d.annualSS / div);\n    document.getElementById('tw-medicare').textContent = fmt(d.annualMedicare / div);\n    document.getElementById('tw-state-tax').textContent = fmt(d.annualStateTax / div);\n    document.getElementById('tw-total-withheld').textContent = fmt(d.annualTotal / div);\n\n    // Build breakdown table\n    var rows = [\n      ['Gross Pay', d.annualGross],\n      ['Federal Income Tax', d.annualFedTax],\n      ['Social Security (6.2%)', d.annualSS],\n      ['Medicare (1.45%+)', d.annualMedicare],\n      ['State Income Tax', d.annualStateTax],\n    ];\n    if (d.annualExtra \u003e 0) rows.push(['Additional Withholding', d.annualExtra]);\n    rows.push(null); // separator → total\n    rows.push(['_total', d.annualTotal]);\n    rows.push(['_net', d.annualNet]);\n\n    var tbody = document.getElementById('tw-table-body');\n    tbody.innerHTML = '';\n    rows.forEach(function(row) {\n      if (!row) return;\n      var tr = document.createElement('tr');\n      var isTotal = row[0] === '_total';\n      var isNet = row[0] === '_net';\n      if (isTotal) tr.className = 'total-row';\n      if (isNet) tr.className = 'net-row';\n      var label = isTotal ? 'Total Withheld' : isNet ? 'Net Take-Home Pay' : row[0];\n      var annual = row[1];\n      tr.innerHTML =\n        '\u003ctd\u003e' + label + '\u003c/td\u003e' +\n        '\u003ctd\u003e' + fmt(annual / 52) + '\u003c/td\u003e' +\n        '\u003ctd\u003e' + fmt(annual / 26) + '\u003c/td\u003e' +\n        '\u003ctd\u003e' + fmt(annual / 12) + '\u003c/td\u003e' +\n        '\u003ctd\u003e' + fmt(annual) + '\u003c/td\u003e';\n      tbody.appendChild(tr);\n    });\n  }\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"https://productivity-works.com/tools/freelance-rate-calculator/\"\u003eFreelance Rate Calculator\u003c/a\u003e\n\n\u003ca href=\"https://productivity-works.com/tools/hourly-to-salary-calculator/\"\u003eHourly To Salary Calculator\u003c/a\u003e\n\n\u003ca href=\"https://productivity-works.com/tools/pension-simulator/\"\u003ePension Simulator\u003c/a\u003e\n\u003c/p\u003e","title":"Tax Withholding Calculator - Estimate Paycheck Deductions"},{"content":" Input Text Characters: 0 Words: 0 Lines: 0 UPPERCASE lowercase Title Case Sentence case camelCase PascalCase snake_case kebab-case CONSTANT_CASE dot.case aLtErNaTiNg cAsE Reverse text Output Copy \u0026gt; Count word frequency \u0026rarr; Word Frequency Counter\n\u0026gt; Check text differences \u0026rarr; Text Diff Checker\n\u0026gt; Encode/decode strings \u0026rarr; Universal Encoder/Decoder ","permalink":"https://productivity-works.com/tools/text-case-converter/","summary":"\u003cdiv id=\"tc-app\"\u003e\n\u003cstyle\u003e\n#tc-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #cbd5e1;\n}\n\u003cp\u003e#tc-app * {\nbox-sizing: border-box;\n}\u003c/p\u003e\n\u003cp\u003e#tc-app .tc-label {\ndisplay: block;\nfont-size: 0.8rem;\nfont-weight: 600;\nletter-spacing: 0.08em;\ntext-transform: uppercase;\ncolor: #94a3b8;\nmargin-bottom: 6px;\n}\u003c/p\u003e\n\u003cp\u003e#tc-app textarea {\nwidth: 100%;\npadding: 12px 14px;\nbackground: #1e293b;\nborder: 1px solid #334155;\nborder-radius: 8px;\ncolor: #e2e8f0;\nfont-size: 1rem;\nline-height: 1.6;\nresize: vertical;\noutline: none;\ntransition: border-color 0.2s;\nfont-family: inherit;\n}\u003c/p\u003e","title":"Text Case Converter"},{"content":" Original Text Modified Text Compare \u0026#8646; Swap Clear Diff Result Copy Added Removed Related tools:\nCompare JSON → JSON Diff Checker Edit Markdown → Markdown Editor ","permalink":"https://productivity-works.com/tools/diff-checker/","summary":"\u003cdiv id=\"dc-app\"\u003e\n\u003cstyle\u003e\n#dc-app *,#dc-app *::before,#dc-app *::after{box-sizing:border-box;margin:0;padding:0}\n#dc-app{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:#1e293b;max-width:900px;margin:0 auto;padding:16px}\n#dc-app h2{font-size:1.1rem;font-weight:600;color:#334155;margin-bottom:10px}\n#dc-app .dc-inputs{display:grid;grid-template-columns:1fr 1fr;gap:12px}\n@media(max-width:600px){#dc-app .dc-inputs{grid-template-columns:1fr}}\n#dc-app .dc-col label{display:block;font-size:13px;font-weight:600;color:#475569;margin-bottom:6px}\n#dc-app textarea{width:100%;height:200px;padding:10px 12px;font-size:13px;font-family:monospace;border:1.5px solid #cbd5e1;border-radius:8px;resize:vertical;outline:none;line-height:1.6;background:#f8fafc;transition:border-color .2s}\n#dc-app textarea:focus{border-color:#3b82f6;background:#fff}\n#dc-app .dc-actions{display:flex;align-items:center;gap:10px;margin:14px 0}\n#dc-app button{cursor:pointer;border:none;border-radius:7px;font-size:13px;font-weight:600;padding:9px 20px;transition:opacity .15s}\n#dc-app button:hover{opacity:.85}\n#dc-app .btn-primary{background:#3b82f6;color:#fff}\n#dc-app .btn-secondary{background:#e2e8f0;color:#334155}\n#dc-app .btn-copy{background:#10b981;color:#fff}\n#dc-app .dc-stats{font-size:12px;color:#64748b;margin-left:auto;display:flex;gap:12px;flex-wrap:wrap}\n#dc-app .dc-stats span{background:#f1f5f9;border-radius:5px;padding:4px 10px}\n#dc-app .dc-result-wrap{border:1.5px solid #e2e8f0;border-radius:10px;overflow:hidden}\n#dc-app .dc-result-header{background:#f8fafc;padding:10px 14px;font-size:12px;font-weight:600;color:#64748b;display:flex;gap:16px;border-bottom:1px solid #e2e8f0}\n#dc-app .dc-result{padding:12px 14px;font-size:13px;font-family:monospace;line-height:1.8;white-space:pre-wrap;word-break:break-all;min-height:80px;max-height:420px;overflow-y:auto;background:#fff}\n#dc-app .dc-added{background:#dcfce7;color:#166534;border-radius:3px;padding:0 2px}\n#dc-app .dc-removed{background:#fee2e2;color:#991b1b;border-radius:3px;padding:0 2px;text-decoration:line-through}\n#dc-app .dc-empty{color:#94a3b8;font-style:italic;font-family:sans-serif}\n#dc-app .dc-legend{display:flex;gap:14px;font-size:12px;margin-top:10px}\n#dc-app .dc-legend span{display:flex;align-items:center;gap:5px}\n#dc-app .dc-legend .dot{width:12px;height:12px;border-radius:3px;display:inline-block}\n#dc-app .dot-add{background:#dcfce7;border:1px solid #86efac}\n#dc-app .dot-rem{background:#fee2e2;border:1px solid #fca5a5}\n\u003c/style\u003e\n\u003cdiv class=\"dc-inputs\"\u003e\n  \u003cdiv class=\"dc-col\"\u003e\n    \u003clabel for=\"dc-text-a\"\u003eOriginal Text\u003c/label\u003e\n    \u003ctextarea id=\"dc-text-a\" placeholder=\"Paste original text here...\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"dc-col\"\u003e\n    \u003clabel for=\"dc-text-b\"\u003eModified Text\u003c/label\u003e\n    \u003ctextarea id=\"dc-text-b\" placeholder=\"Paste modified text here...\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"dc-actions\"\u003e\n  \u003cbutton class=\"btn-primary\" onclick=\"dcRun()\"\u003eCompare\u003c/button\u003e\n  \u003cbutton class=\"btn-secondary\" onclick=\"dcSwap()\"\u003e\u0026#8646; Swap\u003c/button\u003e\n  \u003cbutton class=\"btn-secondary\" onclick=\"dcClear()\"\u003eClear\u003c/button\u003e\n  \u003cdiv class=\"dc-stats\" id=\"dc-stats\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"dc-result-wrap\" id=\"dc-result-wrap\" style=\"display:none\"\u003e\n  \u003cdiv class=\"dc-result-header\"\u003e\n    \u003cspan\u003eDiff Result\u003c/span\u003e\n    \u003cspan id=\"dc-result-meta\"\u003e\u003c/span\u003e\n    \u003cbutton class=\"btn-copy\" style=\"margin-left:auto;padding:4px 14px;font-size:12px\" onclick=\"dcCopy()\"\u003eCopy\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"dc-result\" id=\"dc-result\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"dc-legend\"\u003e\n  \u003cspan\u003e\u003cspan class=\"dot dot-add\"\u003e\u003c/span\u003e Added\u003c/span\u003e\n  \u003cspan\u003e\u003cspan class=\"dot dot-rem\"\u003e\u003c/span\u003e Removed\u003c/span\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n  function lcsTokens(a, b) {\n    var m = a.length, n = b.length;\n    // Use flat array for DP to save memory\n    var dp = new Uint16Array((m+1)*(n+1));\n    for (var i = m-1; i \u003e= 0; i--) {\n      for (var j = n-1; j \u003e= 0; j--) {\n        if (a[i] === b[j]) {\n          dp[i*(n+1)+j] = 1 + dp[(i+1)*(n+1)+(j+1)];\n        } else {\n          var x = dp[(i+1)*(n+1)+j], y = dp[i*(n+1)+(j+1)];\n          dp[i*(n+1)+j] = x \u003e y ? x : y;\n        }\n      }\n    }\n    // Backtrack\n    var ops = [], i = 0, j = 0;\n    while (i \u003c m \u0026\u0026 j \u003c n) {\n      if (a[i] === b[j]) { ops.push({t:'=',v:a[i]}); i++; j++; }\n      else if (dp[(i+1)*(n+1)+j] \u003e= dp[i*(n+1)+(j+1)]) { ops.push({t:'-',v:a[i]}); i++; }\n      else { ops.push({t:'+',v:b[j]}); j++; }\n    }\n    while (i \u003c m) { ops.push({t:'-',v:a[i]}); i++; }\n    while (j \u003c n) { ops.push({t:'+',v:b[j]}); j++; }\n    return ops;\n  }\n\n  function tokenize(text) {\n    // Split into words + whitespace tokens\n    return text.match(/\\S+|\\s+/g) || [];\n  }\n\n  window.dcRun = function() {\n    var a = document.getElementById('dc-text-a').value;\n    var b = document.getElementById('dc-text-b').value;\n    var wrap = document.getElementById('dc-result-wrap');\n    var out = document.getElementById('dc-result');\n    var meta = document.getElementById('dc-result-meta');\n    var statsEl = document.getElementById('dc-stats');\n\n    if (!a \u0026\u0026 !b) {\n      wrap.style.display = 'none';\n      statsEl.innerHTML = '';\n      return;\n    }\n\n    var tokA = tokenize(a);\n    var tokB = tokenize(b);\n    var ops = lcsTokens(tokA, tokB);\n\n    var added = 0, removed = 0;\n    var html = '';\n    for (var i = 0; i \u003c ops.length; i++) {\n      var o = ops[i];\n      var esc = o.v.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n      if (o.t === '=') { html += esc; }\n      else if (o.t === '+') { html += '\u003cspan class=\"dc-added\"\u003e'+esc+'\u003c/span\u003e'; added++; }\n      else { html += '\u003cspan class=\"dc-removed\"\u003e'+esc+'\u003c/span\u003e'; removed++; }\n    }\n\n    var linesA = a ? a.split('\\n').length : 0;\n    var linesB = b ? b.split('\\n').length : 0;\n\n    out.innerHTML = html || '\u003cspan class=\"dc-empty\"\u003eNo differences found.\u003c/span\u003e';\n    wrap.style.display = '';\n    meta.textContent = added + ' added · ' + removed + ' removed';\n    statsEl.innerHTML =\n      '\u003cspan\u003eA: ' + linesA + ' lines\u003c/span\u003e' +\n      '\u003cspan\u003eB: ' + linesB + ' lines\u003c/span\u003e' +\n      '\u003cspan\u003e+' + added + ' / -' + removed + '\u003c/span\u003e';\n  };\n\n  window.dcSwap = function() {\n    var a = document.getElementById('dc-text-a');\n    var b = document.getElementById('dc-text-b');\n    var tmp = a.value; a.value = b.value; b.value = tmp;\n  };\n\n  window.dcClear = function() {\n    document.getElementById('dc-text-a').value = '';\n    document.getElementById('dc-text-b').value = '';\n    document.getElementById('dc-result-wrap').style.display = 'none';\n    document.getElementById('dc-stats').innerHTML = '';\n  };\n\n  window.dcCopy = function() {\n    var el = document.getElementById('dc-result');\n    var text = el.innerText || el.textContent;\n    navigator.clipboard.writeText(text).then(function(){\n      var btn = event.target;\n      var orig = btn.textContent;\n      btn.textContent = 'Copied!';\n      setTimeout(function(){ btn.textContent = orig; }, 1500);\n    });\n  };\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cstrong\u003eRelated tools:\u003c/strong\u003e\u003c/p\u003e","title":"Text Diff Checker"},{"content":"Compare two texts instantly and see exactly what changed — line by line, color-coded, with summary stats. No server, no sign-up, no tracking. Everything runs in your browser.\nCompare ⇄ Swap Clear Ignore whitespace Case-insensitive Side by Side Unified Copy Result Original Modified 0 Added 0 Removed 0 Changed 0 Unchanged Diff Result Enter text above and click Compare to see the diff. Copied to clipboard! How It Works The tool implements a Longest Common Subsequence (LCS) algorithm in pure JavaScript — the same foundation used by git diff. It compares your texts line by line, then applies character-level diffing on changed lines so you can see exactly which characters were inserted or deleted.\nAll processing happens in your browser. No text is ever sent to a server.\nWhen to Use a Text Diff Checker Scenario What to look for Code review Changed logic, added/removed lines Document revision Edited paragraphs, reworded sentences Config comparison Environment differences Translation QA Structural alignment between source and target Tips Ignore whitespace — useful when comparing code reformatted by a linter or prettier. Case-insensitive — handy for comparing user-entered data that may have inconsistent capitalisation. Swap — quickly reverse the comparison direction to check the diff from the other side. Unified view — a single-column view familiar to git users (+/− markers). Format your code before diffing → SQL Formatter Preview Markdown in your browser → Markdown Preview ","permalink":"https://productivity-works.com/tools/text-diff-checker/","summary":"\u003cp\u003eCompare two texts instantly and see exactly what changed — line by line, color-coded, with summary stats. No server, no sign-up, no tracking. Everything runs in your browser.\u003c/p\u003e\n\u003cdiv id=\"df-app\"\u003e\n\u003cstyle\u003e\n#df-app *,\n#df-app *::before,\n#df-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#df-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 14px;\n  color: #1e293b;\n  line-height: 1.5;\n}\n#df-app .df-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  align-items: center;\n  margin-bottom: 14px;\n}\n#df-app .df-toolbar-group {\n  display: flex;\n  gap: 6px;\n  align-items: center;\n}\n#df-app .df-toolbar-sep {\n  width: 1px;\n  height: 26px;\n  background: #cbd5e1;\n  margin: 0 2px;\n}\n#df-app label.df-check {\n  display: flex;\n  align-items: center;\n  gap: 5px;\n  font-size: 13px;\n  color: #475569;\n  cursor: pointer;\n  user-select: none;\n}\n#df-app label.df-check input[type=\"checkbox\"] {\n  width: 15px;\n  height: 15px;\n  accent-color: #6366f1;\n  cursor: pointer;\n}\n#df-app .df-btn {\n  padding: 6px 14px;\n  border-radius: 6px;\n  border: 1.5px solid #cbd5e1;\n  background: #f8fafc;\n  color: #334155;\n  font-size: 13px;\n  font-weight: 500;\n  cursor: pointer;\n  transition: background 0.15s, border-color 0.15s, color 0.15s;\n  white-space: nowrap;\n}\n#df-app .df-btn:hover {\n  background: #f1f5f9;\n  border-color: #94a3b8;\n}\n#df-app .df-btn-primary {\n  background: #6366f1;\n  border-color: #6366f1;\n  color: #fff;\n  font-weight: 600;\n}\n#df-app .df-btn-primary:hover {\n  background: #4f46e5;\n  border-color: #4f46e5;\n}\n#df-app .df-btn-danger {\n  background: #fff;\n  border-color: #fca5a5;\n  color: #dc2626;\n}\n#df-app .df-btn-danger:hover {\n  background: #fef2f2;\n  border-color: #f87171;\n}\n#df-app .df-view-toggle {\n  display: flex;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 6px;\n  overflow: hidden;\n}\n#df-app .df-view-toggle button {\n  padding: 5px 12px;\n  border: none;\n  background: #f8fafc;\n  color: #64748b;\n  font-size: 12px;\n  font-weight: 500;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s;\n  border-right: 1px solid #cbd5e1;\n}\n#df-app .df-view-toggle button:last-child {\n  border-right: none;\n}\n#df-app .df-view-toggle button.active {\n  background: #6366f1;\n  color: #fff;\n}\n#df-app .df-inputs {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 12px;\n  margin-bottom: 14px;\n}\n@media (max-width: 640px) {\n  #df-app .df-inputs {\n    grid-template-columns: 1fr;\n  }\n}\n#df-app .df-input-block {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n}\n#df-app .df-input-label {\n  font-size: 12px;\n  font-weight: 600;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n}\n#df-app textarea.df-ta {\n  width: 100%;\n  height: 200px;\n  padding: 10px 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 13px;\n  line-height: 1.6;\n  color: #1e293b;\n  background: #f8fafc;\n  resize: vertical;\n  outline: none;\n  transition: border-color 0.15s;\n}\n#df-app textarea.df-ta:focus {\n  border-color: #6366f1;\n  background: #fff;\n}\n#df-app .df-stats {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  margin-bottom: 14px;\n  padding: 12px 16px;\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 8px;\n}\n#df-app .df-stat {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 13px;\n  color: #475569;\n}\n#df-app .df-stat-badge {\n  display: inline-block;\n  padding: 2px 8px;\n  border-radius: 999px;\n  font-size: 12px;\n  font-weight: 700;\n  min-width: 28px;\n  text-align: center;\n}\n#df-app .df-stat-added .df-stat-badge  { background: #dcfce7; color: #15803d; }\n#df-app .df-stat-removed .df-stat-badge { background: #fee2e2; color: #b91c1c; }\n#df-app .df-stat-changed .df-stat-badge { background: #fef9c3; color: #a16207; }\n#df-app .df-stat-unchanged .df-stat-badge { background: #f1f5f9; color: #475569; }\n#df-app .df-result-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin-bottom: 8px;\n}\n#df-app .df-result-title {\n  font-size: 12px;\n  font-weight: 600;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n}\n#df-app .df-result-wrap {\n  border: 1.5px solid #e2e8f0;\n  border-radius: 8px;\n  overflow: hidden;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 13px;\n  line-height: 1.6;\n}\n/* Side-by-side */\n#df-app .df-side {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  min-height: 60px;\n}\n#df-app .df-side-col {\n  overflow-x: auto;\n}\n#df-app .df-side-col:first-child {\n  border-right: 1.5px solid #e2e8f0;\n}\n#df-app .df-side-col-header {\n  padding: 6px 10px;\n  font-size: 11px;\n  font-weight: 600;\n  color: #94a3b8;\n  text-transform: uppercase;\n  letter-spacing: 0.07em;\n  background: #f1f5f9;\n  border-bottom: 1px solid #e2e8f0;\n}\n#df-app .df-line {\n  display: flex;\n  align-items: stretch;\n  min-height: 22px;\n}\n#df-app .df-line-num {\n  min-width: 38px;\n  padding: 1px 8px;\n  text-align: right;\n  color: #94a3b8;\n  user-select: none;\n  border-right: 1px solid #e2e8f0;\n  background: #f8fafc;\n  font-size: 12px;\n  flex-shrink: 0;\n}\n#df-app .df-line-content {\n  padding: 1px 10px;\n  white-space: pre;\n  flex: 1;\n  min-width: 0;\n}\n#df-app .df-line.added    { background: #f0fdf4; }\n#df-app .df-line.removed  { background: #fef2f2; }\n#df-app .df-line.changed  { background: #fefce8; }\n#df-app .df-line.empty    { background: #fafafa; }\n#df-app .df-line-num.added   { background: #dcfce7; color: #15803d; }\n#df-app .df-line-num.removed { background: #fee2e2; color: #b91c1c; }\n#df-app .df-line-num.changed { background: #fef9c3; color: #a16207; }\n#df-app .df-line-mark {\n  min-width: 18px;\n  text-align: center;\n  flex-shrink: 0;\n  font-weight: 700;\n  padding: 1px 2px;\n  font-size: 13px;\n}\n#df-app .added  .df-line-mark { color: #16a34a; }\n#df-app .removed .df-line-mark { color: #dc2626; }\n#df-app .changed .df-line-mark { color: #ca8a04; }\n/* Unified */\n#df-app .df-unified {\n  min-height: 60px;\n}\n#df-app .df-unified .df-line-num {\n  min-width: 60px;\n}\n#df-app .df-empty-state {\n  padding: 40px 20px;\n  text-align: center;\n  color: #94a3b8;\n  font-size: 14px;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n}\n#df-app .df-copy-toast {\n  display: none;\n  position: fixed;\n  bottom: 24px;\n  right: 24px;\n  background: #1e293b;\n  color: #fff;\n  padding: 10px 18px;\n  border-radius: 8px;\n  font-size: 13px;\n  font-weight: 500;\n  z-index: 9999;\n  box-shadow: 0 4px 16px rgba(0,0,0,0.18);\n  pointer-events: none;\n}\n#df-app .df-copy-toast.show { display: block; }\n/* char-level highlight within a changed line */\n#df-app .df-char-add  { background: #bbf7d0; border-radius: 2px; }\n#df-app .df-char-del  { background: #fecaca; border-radius: 2px; text-decoration: line-through; }\n\u003c/style\u003e\n\u003c!-- Toolbar --\u003e\n\u003cdiv class=\"df-toolbar\"\u003e\n  \u003cdiv class=\"df-toolbar-group\"\u003e\n    \u003cbutton class=\"df-btn df-btn-primary\" id=\"df-run\"\u003eCompare\u003c/button\u003e\n    \u003cbutton class=\"df-btn\" id=\"df-swap\"\u003e⇄ Swap\u003c/button\u003e\n    \u003cbutton class=\"df-btn df-btn-danger\" id=\"df-clear\"\u003eClear\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"df-toolbar-sep\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"df-toolbar-group\"\u003e\n    \u003clabel class=\"df-check\"\u003e\u003cinput type=\"checkbox\" id=\"df-ignore-ws\"\u003e Ignore whitespace\u003c/label\u003e\n    \u003clabel class=\"df-check\"\u003e\u003cinput type=\"checkbox\" id=\"df-ignore-case\"\u003e Case-insensitive\u003c/label\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"df-toolbar-sep\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"df-view-toggle\"\u003e\n    \u003cbutton id=\"df-view-side\" class=\"active\"\u003eSide by Side\u003c/button\u003e\n    \u003cbutton id=\"df-view-unified\"\u003eUnified\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"df-toolbar-sep\"\u003e\u003c/div\u003e\n  \u003cbutton class=\"df-btn\" id=\"df-copy\"\u003eCopy Result\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Inputs --\u003e\n\u003cdiv class=\"df-inputs\"\u003e\n  \u003cdiv class=\"df-input-block\"\u003e\n    \u003cdiv class=\"df-input-label\"\u003eOriginal\u003c/div\u003e\n    \u003ctextarea class=\"df-ta\" id=\"df-orig\" placeholder=\"Paste original text here...\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"df-input-block\"\u003e\n    \u003cdiv class=\"df-input-label\"\u003eModified\u003c/div\u003e\n    \u003ctextarea class=\"df-ta\" id=\"df-mod\" placeholder=\"Paste modified text here...\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Stats --\u003e\n\u003cdiv class=\"df-stats\" id=\"df-stats\" style=\"display:none;\"\u003e\n  \u003cdiv class=\"df-stat df-stat-added\"\u003e\u003cspan class=\"df-stat-badge\" id=\"df-s-add\"\u003e0\u003c/span\u003e Added\u003c/div\u003e\n  \u003cdiv class=\"df-stat df-stat-removed\"\u003e\u003cspan class=\"df-stat-badge\" id=\"df-s-rem\"\u003e0\u003c/span\u003e Removed\u003c/div\u003e\n  \u003cdiv class=\"df-stat df-stat-changed\"\u003e\u003cspan class=\"df-stat-badge\" id=\"df-s-chg\"\u003e0\u003c/span\u003e Changed\u003c/div\u003e\n  \u003cdiv class=\"df-stat df-stat-unchanged\"\u003e\u003cspan class=\"df-stat-badge\" id=\"df-s-unc\"\u003e0\u003c/span\u003e Unchanged\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Result --\u003e\n\u003cdiv class=\"df-result-header\"\u003e\n  \u003cdiv class=\"df-result-title\"\u003eDiff Result\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"df-result-wrap\" id=\"df-result\"\u003e\n  \u003cdiv class=\"df-empty-state\"\u003eEnter text above and click \u003cstrong\u003eCompare\u003c/strong\u003e to see the diff.\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"df-copy-toast\" id=\"df-toast\"\u003eCopied to clipboard!\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  'use strict';\n\n  // ── LCS-based line diff ──────────────────────────────────────────\n  function lcs(a, b) {\n    const m = a.length, n = b.length;\n    // Use Hunt-Szymanski / Myers O(ND) approximation for large inputs\n    // For simplicity and correctness we use the standard DP with space optimization\n    if (m === 0 || n === 0) return [];\n    // Build DP table row by row (only keep two rows)\n    const dp = new Array(n + 1).fill(0);\n    const prev = new Array(n + 1).fill(0);\n    const bt = [];  // backtrack: store full table only up to 400x400, else greedy\n    if (m * n \u003c= 160000) {\n      const table = Array.from({length: m + 1}, () =\u003e new Uint16Array(n + 1));\n      for (let i = 1; i \u003c= m; i++) {\n        for (let j = 1; j \u003c= n; j++) {\n          if (a[i-1] === b[j-1]) table[i][j] = table[i-1][j-1] + 1;\n          else table[i][j] = Math.max(table[i-1][j], table[i][j-1]);\n        }\n      }\n      // Backtrack\n      let i = m, j = n;\n      const seq = [];\n      while (i \u003e 0 \u0026\u0026 j \u003e 0) {\n        if (a[i-1] === b[j-1]) { seq.push([i-1, j-1]); i--; j--; }\n        else if (table[i-1][j] \u003e= table[i][j-1]) i--;\n        else j--;\n      }\n      return seq.reverse();\n    } else {\n      // Greedy: just pair matching lines in order (fallback for huge inputs)\n      const result = [];\n      let ai = 0, bi = 0;\n      const bMap = new Map();\n      b.forEach((line, idx) =\u003e {\n        if (!bMap.has(line)) bMap.set(line, []);\n        bMap.get(line).push(idx);\n      });\n      while (ai \u003c m \u0026\u0026 bi \u003c n) {\n        if (a[ai] === b[bi]) { result.push([ai, bi]); ai++; bi++; }\n        else { ai++; }\n      }\n      return result;\n    }\n  }\n\n  // Returns array of {type, origLine, modLine} objects\n  // type: 'unchanged' | 'added' | 'removed' | 'changed'\n  function computeDiff(origLines, modLines) {\n    const matches = lcs(origLines, modLines);\n    const result = [];\n    let oi = 0, mi = 0, matchIdx = 0;\n\n    while (oi \u003c origLines.length || mi \u003c modLines.length) {\n      const nextMatch = matches[matchIdx];\n      if (nextMatch \u0026\u0026 oi === nextMatch[0] \u0026\u0026 mi === nextMatch[1]) {\n        result.push({ type: 'unchanged', orig: origLines[oi], mod: modLines[mi], oi, mi });\n        oi++; mi++; matchIdx++;\n      } else {\n        // Collect consecutive non-matching orig and mod lines\n        const remStart = oi, addStart = mi;\n        while (oi \u003c origLines.length \u0026\u0026 (!nextMatch || oi \u003c nextMatch[0])) oi++;\n        // recalc nextMatch after advancing oi\n        const curMatch = matches[matchIdx];\n        while (mi \u003c modLines.length \u0026\u0026 (!curMatch || mi \u003c curMatch[1])) mi++;\n\n        const remLines = origLines.slice(remStart, oi);\n        const addLines = modLines.slice(addStart, mi);\n        const pairLen = Math.min(remLines.length, addLines.length);\n        // Pair removed+added as changed\n        for (let k = 0; k \u003c pairLen; k++) {\n          result.push({ type: 'changed', orig: remLines[k], mod: addLines[k], oi: remStart+k, mi: addStart+k });\n        }\n        for (let k = pairLen; k \u003c remLines.length; k++) {\n          result.push({ type: 'removed', orig: remLines[k], mod: null, oi: remStart+k, mi: null });\n        }\n        for (let k = pairLen; k \u003c addLines.length; k++) {\n          result.push({ type: 'added', orig: null, mod: addLines[k], oi: null, mi: addStart+k });\n        }\n      }\n    }\n    return result;\n  }\n\n  // ── Character-level diff for changed lines ────────────────────────\n  function charDiff(a, b) {\n    // Returns [origHTML, modHTML] with inline highlights\n    const la = Array.from(a), lb = Array.from(b);\n    if (la.length * lb.length \u003e 4000) {\n      return [esc(a), esc(b)]; // skip for very long lines\n    }\n    const m = la.length, n = lb.length;\n    const table = Array.from({length: m+1}, () =\u003e new Uint16Array(n+1));\n    for (let i = 1; i \u003c= m; i++)\n      for (let j = 1; j \u003c= n; j++)\n        table[i][j] = la[i-1]===lb[j-1] ? table[i-1][j-1]+1 : Math.max(table[i-1][j],table[i][j-1]);\n    // backtrack\n    let i = m, j = n;\n    const ops = []; // [type, char]  type: 'eq'|'del'|'ins'\n    while (i \u003e 0 || j \u003e 0) {\n      if (i \u003e 0 \u0026\u0026 j \u003e 0 \u0026\u0026 la[i-1]===lb[j-1]) { ops.push(['eq',la[i-1]]); i--; j--; }\n      else if (j \u003e 0 \u0026\u0026 (i===0 || table[i][j-1] \u003e= table[i-1][j])) { ops.push(['ins',lb[j-1]]); j--; }\n      else { ops.push(['del',la[i-1]]); i--; }\n    }\n    ops.reverse();\n    let oh='', mh='';\n    for (const [t,c] of ops) {\n      const ec = esc(c);\n      if      (t==='eq')  { oh += ec; mh += ec; }\n      else if (t==='del') { oh += `\u003cspan class=\"df-char-del\"\u003e${ec}\u003c/span\u003e`; }\n      else                { mh += `\u003cspan class=\"df-char-add\"\u003e${ec}\u003c/span\u003e`; }\n    }\n    return [oh, mh];\n  }\n\n  function esc(s) {\n    return s.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n  }\n\n  // ── Prepare lines ─────────────────────────────────────────────────\n  function prepareLines(text, ignoreWs, ignoreCase) {\n    let lines = text.split('\\n');\n    // Remove trailing empty line if text ends with newline\n    if (lines.length \u003e 0 \u0026\u0026 lines[lines.length-1] === '') lines.pop();\n    return lines.map(l =\u003e {\n      let r = l;\n      if (ignoreWs) r = r.trim().replace(/\\s+/g, ' ');\n      if (ignoreCase) r = r.toLowerCase();\n      return r;\n    });\n  }\n\n  // ── Render side-by-side ───────────────────────────────────────────\n  function renderSide(diffs) {\n    let leftHTML = '', rightHTML = '';\n    let ln = 0, rn = 0;\n\n    for (const d of diffs) {\n      if (d.type === 'unchanged') {\n        ln++; rn++;\n        const content = esc(d.orig);\n        leftHTML  += `\u003cdiv class=\"df-line\"\u003e\u003cdiv class=\"df-line-num\"\u003e${ln}\u003c/div\u003e\u003cdiv class=\"df-line-mark\"\u003e \u003c/div\u003e\u003cdiv class=\"df-line-content\"\u003e${content}\u003c/div\u003e\u003c/div\u003e`;\n        rightHTML += `\u003cdiv class=\"df-line\"\u003e\u003cdiv class=\"df-line-num\"\u003e${rn}\u003c/div\u003e\u003cdiv class=\"df-line-mark\"\u003e \u003c/div\u003e\u003cdiv class=\"df-line-content\"\u003e${content}\u003c/div\u003e\u003c/div\u003e`;\n      } else if (d.type === 'removed') {\n        ln++;\n        leftHTML  += `\u003cdiv class=\"df-line removed\"\u003e\u003cdiv class=\"df-line-num removed\"\u003e${ln}\u003c/div\u003e\u003cdiv class=\"df-line-mark\"\u003e−\u003c/div\u003e\u003cdiv class=\"df-line-content\"\u003e${esc(d.orig)}\u003c/div\u003e\u003c/div\u003e`;\n        rightHTML += `\u003cdiv class=\"df-line empty\"\u003e\u003cdiv class=\"df-line-num\"\u003e \u003c/div\u003e\u003cdiv class=\"df-line-mark\"\u003e \u003c/div\u003e\u003cdiv class=\"df-line-content\"\u003e \u003c/div\u003e\u003c/div\u003e`;\n      } else if (d.type === 'added') {\n        rn++;\n        leftHTML  += `\u003cdiv class=\"df-line empty\"\u003e\u003cdiv class=\"df-line-num\"\u003e \u003c/div\u003e\u003cdiv class=\"df-line-mark\"\u003e \u003c/div\u003e\u003cdiv class=\"df-line-content\"\u003e \u003c/div\u003e\u003c/div\u003e`;\n        rightHTML += `\u003cdiv class=\"df-line added\"\u003e\u003cdiv class=\"df-line-num added\"\u003e${rn}\u003c/div\u003e\u003cdiv class=\"df-line-mark\"\u003e+\u003c/div\u003e\u003cdiv class=\"df-line-content\"\u003e${esc(d.mod)}\u003c/div\u003e\u003c/div\u003e`;\n      } else { // changed\n        ln++; rn++;\n        const [oh, mh] = charDiff(d.orig, d.mod);\n        leftHTML  += `\u003cdiv class=\"df-line changed\"\u003e\u003cdiv class=\"df-line-num changed\"\u003e${ln}\u003c/div\u003e\u003cdiv class=\"df-line-mark\"\u003e~\u003c/div\u003e\u003cdiv class=\"df-line-content\"\u003e${oh}\u003c/div\u003e\u003c/div\u003e`;\n        rightHTML += `\u003cdiv class=\"df-line changed\"\u003e\u003cdiv class=\"df-line-num changed\"\u003e${rn}\u003c/div\u003e\u003cdiv class=\"df-line-mark\"\u003e~\u003c/div\u003e\u003cdiv class=\"df-line-content\"\u003e${mh}\u003c/div\u003e\u003c/div\u003e`;\n      }\n    }\n\n    if (!leftHTML \u0026\u0026 !rightHTML) {\n      return '\u003cdiv class=\"df-empty-state\"\u003eNo differences found — the texts are identical.\u003c/div\u003e';\n    }\n\n    return `\u003cdiv class=\"df-side\"\u003e\n      \u003cdiv class=\"df-side-col\"\u003e\n        \u003cdiv class=\"df-side-col-header\"\u003eOriginal\u003c/div\u003e\n        ${leftHTML}\n      \u003c/div\u003e\n      \u003cdiv class=\"df-side-col\"\u003e\n        \u003cdiv class=\"df-side-col-header\"\u003eModified\u003c/div\u003e\n        ${rightHTML}\n      \u003c/div\u003e\n    \u003c/div\u003e`;\n  }\n\n  // ── Render unified ────────────────────────────────────────────────\n  function renderUnified(diffs) {\n    let html = '';\n    let ln = 0, rn = 0;\n\n    for (const d of diffs) {\n      if (d.type === 'unchanged') {\n        ln++; rn++;\n        html += `\u003cdiv class=\"df-line\"\u003e\u003cdiv class=\"df-line-num\"\u003e${ln} / ${rn}\u003c/div\u003e\u003cdiv class=\"df-line-mark\"\u003e \u003c/div\u003e\u003cdiv class=\"df-line-content\"\u003e${esc(d.orig)}\u003c/div\u003e\u003c/div\u003e`;\n      } else if (d.type === 'removed') {\n        ln++;\n        html += `\u003cdiv class=\"df-line removed\"\u003e\u003cdiv class=\"df-line-num removed\"\u003e${ln} /\u003c/div\u003e\u003cdiv class=\"df-line-mark\"\u003e−\u003c/div\u003e\u003cdiv class=\"df-line-content\"\u003e${esc(d.orig)}\u003c/div\u003e\u003c/div\u003e`;\n      } else if (d.type === 'added') {\n        rn++;\n        html += `\u003cdiv class=\"df-line added\"\u003e\u003cdiv class=\"df-line-num added\"\u003e/ ${rn}\u003c/div\u003e\u003cdiv class=\"df-line-mark\"\u003e+\u003c/div\u003e\u003cdiv class=\"df-line-content\"\u003e${esc(d.mod)}\u003c/div\u003e\u003c/div\u003e`;\n      } else {\n        ln++; rn++;\n        const [oh, mh] = charDiff(d.orig, d.mod);\n        html += `\u003cdiv class=\"df-line removed\"\u003e\u003cdiv class=\"df-line-num removed\"\u003e${ln} /\u003c/div\u003e\u003cdiv class=\"df-line-mark\"\u003e−\u003c/div\u003e\u003cdiv class=\"df-line-content\"\u003e${oh}\u003c/div\u003e\u003c/div\u003e`;\n        html += `\u003cdiv class=\"df-line added\"\u003e\u003cdiv class=\"df-line-num added\"\u003e/ ${rn}\u003c/div\u003e\u003cdiv class=\"df-line-mark\"\u003e+\u003c/div\u003e\u003cdiv class=\"df-line-content\"\u003e${mh}\u003c/div\u003e\u003c/div\u003e`;\n      }\n    }\n\n    if (!html) {\n      return '\u003cdiv class=\"df-empty-state\"\u003eNo differences found — the texts are identical.\u003c/div\u003e';\n    }\n    return `\u003cdiv class=\"df-unified\"\u003e${html}\u003c/div\u003e`;\n  }\n\n  // ── Plain text for copy ───────────────────────────────────────────\n  function buildPlainText(diffs) {\n    let out = '';\n    for (const d of diffs) {\n      if      (d.type === 'unchanged') out += `  ${d.orig}\\n`;\n      else if (d.type === 'removed')   out += `- ${d.orig}\\n`;\n      else if (d.type === 'added')     out += `+ ${d.mod}\\n`;\n      else { out += `- ${d.orig}\\n+ ${d.mod}\\n`; }\n    }\n    return out;\n  }\n\n  // ── State ─────────────────────────────────────────────────────────\n  let currentDiffs = null;\n  let currentView = 'side'; // 'side' | 'unified'\n\n  function runDiff() {\n    const origText = document.getElementById('df-orig').value;\n    const modText  = document.getElementById('df-mod').value;\n    const ignoreWs   = document.getElementById('df-ignore-ws').checked;\n    const ignoreCase = document.getElementById('df-ignore-case').checked;\n\n    const origLines = prepareLines(origText, ignoreWs, ignoreCase);\n    const modLines  = prepareLines(modText,  ignoreWs, ignoreCase);\n\n    // We still want to display original (un-normalized) lines, so recompute display lines\n    const rawOrig = origText.split('\\n');\n    const rawMod  = modText.split('\\n');\n    if (rawOrig.length \u003e 0 \u0026\u0026 rawOrig[rawOrig.length-1]==='') rawOrig.pop();\n    if (rawMod.length  \u003e 0 \u0026\u0026 rawMod[rawMod.length-1]==='')   rawMod.pop();\n\n    const diffs = computeDiff(origLines, modLines);\n\n    // Map diffs back to raw (display) lines\n    const displayDiffs = diffs.map(d =\u003e {\n      const nd = { type: d.type };\n      if (d.oi !== null \u0026\u0026 d.oi !== undefined) nd.orig = rawOrig[d.oi] ?? '';\n      else nd.orig = null;\n      if (d.mi !== null \u0026\u0026 d.mi !== undefined) nd.mod  = rawMod[d.mi]  ?? '';\n      else nd.mod  = null;\n      return nd;\n    });\n\n    currentDiffs = displayDiffs;\n\n    // Stats\n    let added=0, removed=0, changed=0, unchanged=0;\n    for (const d of displayDiffs) {\n      if      (d.type==='added')     added++;\n      else if (d.type==='removed')   removed++;\n      else if (d.type==='changed')   changed++;\n      else                           unchanged++;\n    }\n    document.getElementById('df-s-add').textContent = added;\n    document.getElementById('df-s-rem').textContent = removed;\n    document.getElementById('df-s-chg').textContent = changed;\n    document.getElementById('df-s-unc').textContent = unchanged;\n    document.getElementById('df-stats').style.display = 'flex';\n\n    renderResult();\n  }\n\n  function renderResult() {\n    if (!currentDiffs) return;\n    const el = document.getElementById('df-result');\n    el.innerHTML = currentView === 'side'\n      ? renderSide(currentDiffs)\n      : renderUnified(currentDiffs);\n  }\n\n  function showToast(msg) {\n    const t = document.getElementById('df-toast');\n    t.textContent = msg;\n    t.classList.add('show');\n    setTimeout(() =\u003e t.classList.remove('show'), 2000);\n  }\n\n  // ── Wire up events ────────────────────────────────────────────────\n  document.getElementById('df-run').addEventListener('click', runDiff);\n\n  document.getElementById('df-swap').addEventListener('click', function() {\n    const a = document.getElementById('df-orig');\n    const b = document.getElementById('df-mod');\n    const tmp = a.value;\n    a.value = b.value;\n    b.value = tmp;\n    if (currentDiffs) runDiff();\n  });\n\n  document.getElementById('df-clear').addEventListener('click', function() {\n    document.getElementById('df-orig').value = '';\n    document.getElementById('df-mod').value  = '';\n    currentDiffs = null;\n    document.getElementById('df-stats').style.display = 'none';\n    document.getElementById('df-result').innerHTML = '\u003cdiv class=\"df-empty-state\"\u003eEnter text above and click \u003cstrong\u003eCompare\u003c/strong\u003e to see the diff.\u003c/div\u003e';\n  });\n\n  document.getElementById('df-copy').addEventListener('click', function() {\n    if (!currentDiffs) { showToast('Nothing to copy yet.'); return; }\n    const text = buildPlainText(currentDiffs);\n    navigator.clipboard.writeText(text).then(() =\u003e showToast('Copied to clipboard!')).catch(() =\u003e {\n      const ta = document.createElement('textarea');\n      ta.value = text; ta.style.position='fixed'; ta.style.opacity='0';\n      document.body.appendChild(ta); ta.select();\n      document.execCommand('copy'); document.body.removeChild(ta);\n      showToast('Copied to clipboard!');\n    });\n  });\n\n  document.getElementById('df-view-side').addEventListener('click', function() {\n    currentView = 'side';\n    this.classList.add('active');\n    document.getElementById('df-view-unified').classList.remove('active');\n    renderResult();\n  });\n\n  document.getElementById('df-view-unified').addEventListener('click', function() {\n    currentView = 'unified';\n    this.classList.add('active');\n    document.getElementById('df-view-side').classList.remove('active');\n    renderResult();\n  });\n\n  // Auto-run when both textareas have content (on input, debounced)\n  let debTimer;\n  function maybeAutoRun() {\n    clearTimeout(debTimer);\n    debTimer = setTimeout(function() {\n      if (document.getElementById('df-orig').value \u0026\u0026 document.getElementById('df-mod').value) {\n        runDiff();\n      }\n    }, 600);\n  }\n  document.getElementById('df-orig').addEventListener('input', maybeAutoRun);\n  document.getElementById('df-mod').addEventListener('input',  maybeAutoRun);\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-it-works\"\u003eHow It Works\u003c/h2\u003e\n\u003cp\u003eThe tool implements a \u003cstrong\u003eLongest Common Subsequence (LCS)\u003c/strong\u003e algorithm in pure JavaScript — the same foundation used by \u003ccode\u003egit diff\u003c/code\u003e. It compares your texts line by line, then applies character-level diffing on changed lines so you can see exactly which characters were inserted or deleted.\u003c/p\u003e","title":"Text Diff Checker"},{"content":" Original Text A Modified Text B Word-level diff Ignore whitespace Ignore case \u0026#9654; Compare \u0026#8646; Swap \u0026#10005; Clear Both \u0026#128203; Copy Diff Copied! Added: 0 | Removed: 0 | Unchanged: 0 | Similarity: 0% Diff Result Enter text in both panels above and click Compare to see differences. How to Use the Text Diff Checker Paste your original text into the left panel (labeled A). Paste your modified text into the right panel (labeled B). Click Compare (or press Ctrl+Enter) to generate the diff. Review the results: Lines highlighted in green (with +) were added in the modified text. Lines highlighted in red (with -) were removed from the original. Lines with no highlight are unchanged. Use the statistics bar to see a quick summary: lines added, removed, unchanged, and an overall similarity percentage. Options explained:\nWord-level diff — instead of highlighting entire lines, individual changed words within paired lines are highlighted. Useful for spotting small edits. Ignore whitespace — treats lines that differ only in leading/trailing spaces or tab width as identical. Ignore case — Hello and hello are treated as the same for comparison purposes. Swap — flips original and modified panels so you can reverse the direction of comparison. Copy Diff — copies the plain-text diff output to your clipboard. Related Tools Format and validate JSON → JSON Formatter Test regular expressions → Regex Tester Count words and characters → Word Counter Need a markdown editor? → Markdown Preview — write and preview markdown live\n","permalink":"https://productivity-works.com/tools/text-diff/","summary":"\u003cdiv id=\"diff-app\"\u003e\n\u003cstyle\u003e\n#diff-app * {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\n#diff-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  color: #1e293b;\n  max-width: 100%;\n}\n\n#diff-app .da-section-title {\n  font-size: 1.05rem;\n  font-weight: 600;\n  color: #334155;\n  margin-bottom: 8px;\n}\n\n/* Input panels */\n#diff-app .da-input-row {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 16px;\n  margin-bottom: 16px;\n}\n\n@media (max-width: 640px) {\n  #diff-app .da-input-row {\n    grid-template-columns: 1fr;\n  }\n}\n\n#diff-app .da-panel {\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n}\n\n#diff-app .da-panel-label {\n  font-size: 0.875rem;\n  font-weight: 600;\n  color: #475569;\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n\n#diff-app .da-panel-label .da-badge {\n  font-size: 0.7rem;\n  font-weight: 600;\n  padding: 2px 7px;\n  border-radius: 9999px;\n  letter-spacing: 0.03em;\n}\n\n#diff-app .da-badge-original {\n  background: #fee2e2;\n  color: #b91c1c;\n}\n\n#diff-app .da-badge-modified {\n  background: #dcfce7;\n  color: #15803d;\n}\n\n#diff-app textarea {\n  width: 100%;\n  height: 220px;\n  padding: 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-family: \"Fira Mono\", \"Cascadia Code\", \"Consolas\", monospace;\n  font-size: 0.85rem;\n  line-height: 1.6;\n  color: #1e293b;\n  background: #f8fafc;\n  resize: vertical;\n  transition: border-color 0.15s;\n  outline: none;\n}\n\n#diff-app textarea:focus {\n  border-color: #334155;\n  background: #fff;\n}\n\n/* Options row */\n#diff-app .da-options-row {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  gap: 16px;\n  padding: 12px 16px;\n  background: #f1f5f9;\n  border-radius: 8px;\n  margin-bottom: 14px;\n}\n\n#diff-app .da-toggle-group {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  font-size: 0.875rem;\n  color: #475569;\n  cursor: pointer;\n  user-select: none;\n}\n\n#diff-app .da-toggle-group input[type=\"checkbox\"] {\n  width: 16px;\n  height: 16px;\n  accent-color: #334155;\n  cursor: pointer;\n}\n\n/* Action buttons */\n#diff-app .da-actions {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  margin-bottom: 20px;\n}\n\n#diff-app button {\n  cursor: pointer;\n  border: none;\n  border-radius: 7px;\n  font-size: 0.875rem;\n  font-weight: 600;\n  padding: 9px 18px;\n  transition: background 0.15s, transform 0.1s;\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n}\n\n#diff-app button:active {\n  transform: scale(0.97);\n}\n\n#diff-app .da-btn-primary {\n  background: #334155;\n  color: #fff;\n  font-size: 0.95rem;\n  padding: 10px 24px;\n}\n\n#diff-app .da-btn-primary:hover {\n  background: #1e293b;\n}\n\n#diff-app .da-btn-secondary {\n  background: #e2e8f0;\n  color: #334155;\n}\n\n#diff-app .da-btn-secondary:hover {\n  background: #cbd5e1;\n}\n\n#diff-app .da-btn-success {\n  background: #f0fdf4;\n  color: #15803d;\n  border: 1.5px solid #bbf7d0;\n}\n\n#diff-app .da-btn-success:hover {\n  background: #dcfce7;\n}\n\n#diff-app .da-btn-danger {\n  background: #fff1f2;\n  color: #b91c1c;\n  border: 1.5px solid #fecdd3;\n}\n\n#diff-app .da-btn-danger:hover {\n  background: #ffe4e6;\n}\n\n/* Stats bar */\n#diff-app .da-stats {\n  display: none;\n  flex-wrap: wrap;\n  gap: 12px;\n  padding: 12px 16px;\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 8px;\n  margin-bottom: 16px;\n}\n\n#diff-app .da-stats.da-visible {\n  display: flex;\n}\n\n#diff-app .da-stat-item {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 0.875rem;\n}\n\n#diff-app .da-stat-dot {\n  width: 10px;\n  height: 10px;\n  border-radius: 50%;\n  flex-shrink: 0;\n}\n\n#diff-app .da-stat-dot-added   { background: #22c55e; }\n#diff-app .da-stat-dot-removed { background: #ef4444; }\n#diff-app .da-stat-dot-same    { background: #94a3b8; }\n#diff-app .da-stat-dot-sim     { background: #334155; }\n\n#diff-app .da-stat-value {\n  font-weight: 700;\n  color: #1e293b;\n}\n\n#diff-app .da-stat-sep {\n  color: #cbd5e1;\n}\n\n/* Diff output */\n#diff-app .da-output-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 8px;\n}\n\n#diff-app .da-output-wrap {\n  display: none;\n}\n\n#diff-app .da-output-wrap.da-visible {\n  display: block;\n}\n\n#diff-app .da-diff-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-family: \"Fira Mono\", \"Cascadia Code\", \"Consolas\", monospace;\n  font-size: 0.8rem;\n  line-height: 1.6;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 8px;\n  overflow: hidden;\n}\n\n#diff-app .da-diff-table td {\n  padding: 2px 10px;\n  vertical-align: top;\n  white-space: pre-wrap;\n  word-break: break-all;\n}\n\n#diff-app .da-line-num {\n  width: 38px;\n  min-width: 38px;\n  text-align: right;\n  color: #94a3b8;\n  font-size: 0.75rem;\n  padding-right: 12px;\n  user-select: none;\n  border-right: 1.5px solid #e2e8f0;\n}\n\n#diff-app .da-line-sym {\n  width: 22px;\n  min-width: 22px;\n  text-align: center;\n  font-weight: 700;\n  user-select: none;\n  padding: 2px 4px;\n}\n\n#diff-app .da-line-added {\n  background: #f0fdf4;\n}\n\n#diff-app .da-line-added .da-line-num,\n#diff-app .da-line-added .da-line-sym {\n  background: #dcfce7;\n  color: #15803d;\n}\n\n#diff-app .da-line-removed {\n  background: #fff1f2;\n}\n\n#diff-app .da-line-removed .da-line-num,\n#diff-app .da-line-removed .da-line-sym {\n  background: #ffe4e6;\n  color: #b91c1c;\n}\n\n#diff-app .da-line-same {\n  background: #fff;\n}\n\n#diff-app .da-line-same .da-line-num {\n  background: #f8fafc;\n}\n\n#diff-app .da-line-same .da-line-sym {\n  background: #f8fafc;\n  color: #94a3b8;\n}\n\n/* Word-level diff highlights */\n#diff-app .da-word-added {\n  background: #86efac;\n  border-radius: 3px;\n  padding: 0 2px;\n}\n\n#diff-app .da-word-removed {\n  background: #fca5a5;\n  border-radius: 3px;\n  padding: 0 2px;\n  text-decoration: line-through;\n}\n\n/* Empty state */\n#diff-app .da-empty {\n  text-align: center;\n  padding: 40px 20px;\n  color: #94a3b8;\n  font-size: 0.9rem;\n  border: 1.5px dashed #e2e8f0;\n  border-radius: 8px;\n}\n\n/* Copy feedback */\n#diff-app .da-copy-feedback {\n  font-size: 0.75rem;\n  color: #15803d;\n  font-weight: 600;\n  opacity: 0;\n  transition: opacity 0.3s;\n  margin-left: 6px;\n}\n\n#diff-app .da-copy-feedback.da-show {\n  opacity: 1;\n}\n\u003c/style\u003e\n\u003c!-- Input row --\u003e\n\u003cdiv class=\"da-input-row\"\u003e\n  \u003cdiv class=\"da-panel\"\u003e\n    \u003cdiv class=\"da-panel-label\"\u003e\n      Original Text\n      \u003cspan class=\"da-badge da-badge-original\"\u003eA\u003c/span\u003e\n    \u003c/div\u003e\n    \u003ctextarea id=\"da-original\" placeholder=\"Paste your original text here...\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"da-panel\"\u003e\n    \u003cdiv class=\"da-panel-label\"\u003e\n      Modified Text\n      \u003cspan class=\"da-badge da-badge-modified\"\u003eB\u003c/span\u003e\n    \u003c/div\u003e\n    \u003ctextarea id=\"da-modified\" placeholder=\"Paste your modified text here...\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Options --\u003e\n\u003cdiv class=\"da-options-row\"\u003e\n  \u003clabel class=\"da-toggle-group\"\u003e\n    \u003cinput type=\"checkbox\" id=\"da-opt-word\"\u003e Word-level diff\n  \u003c/label\u003e\n  \u003clabel class=\"da-toggle-group\"\u003e\n    \u003cinput type=\"checkbox\" id=\"da-opt-whitespace\"\u003e Ignore whitespace\n  \u003c/label\u003e\n  \u003clabel class=\"da-toggle-group\"\u003e\n    \u003cinput type=\"checkbox\" id=\"da-opt-case\"\u003e Ignore case\n  \u003c/label\u003e\n\u003c/div\u003e\n\u003c!-- Actions --\u003e\n\u003cdiv class=\"da-actions\"\u003e\n  \u003cbutton class=\"da-btn-primary\" id=\"da-btn-compare\"\u003e\u0026#9654; Compare\u003c/button\u003e\n  \u003cbutton class=\"da-btn-secondary\" id=\"da-btn-swap\"\u003e\u0026#8646; Swap\u003c/button\u003e\n  \u003cbutton class=\"da-btn-danger\" id=\"da-btn-clear\"\u003e\u0026#10005; Clear Both\u003c/button\u003e\n  \u003cbutton class=\"da-btn-success\" id=\"da-btn-copy\"\u003e\u0026#128203; Copy Diff\u003c/button\u003e\n  \u003cspan class=\"da-copy-feedback\" id=\"da-copy-feedback\"\u003eCopied!\u003c/span\u003e\n\u003c/div\u003e\n\u003c!-- Stats --\u003e\n\u003cdiv class=\"da-stats\" id=\"da-stats\"\u003e\n  \u003cdiv class=\"da-stat-item\"\u003e\n    \u003cspan class=\"da-stat-dot da-stat-dot-added\"\u003e\u003c/span\u003e\n    \u003cspan\u003eAdded: \u003cspan class=\"da-stat-value\" id=\"da-stat-added\"\u003e0\u003c/span\u003e\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cspan class=\"da-stat-sep\"\u003e|\u003c/span\u003e\n  \u003cdiv class=\"da-stat-item\"\u003e\n    \u003cspan class=\"da-stat-dot da-stat-dot-removed\"\u003e\u003c/span\u003e\n    \u003cspan\u003eRemoved: \u003cspan class=\"da-stat-value\" id=\"da-stat-removed\"\u003e0\u003c/span\u003e\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cspan class=\"da-stat-sep\"\u003e|\u003c/span\u003e\n  \u003cdiv class=\"da-stat-item\"\u003e\n    \u003cspan class=\"da-stat-dot da-stat-dot-same\"\u003e\u003c/span\u003e\n    \u003cspan\u003eUnchanged: \u003cspan class=\"da-stat-value\" id=\"da-stat-same\"\u003e0\u003c/span\u003e\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cspan class=\"da-stat-sep\"\u003e|\u003c/span\u003e\n  \u003cdiv class=\"da-stat-item\"\u003e\n    \u003cspan class=\"da-stat-dot da-stat-dot-sim\"\u003e\u003c/span\u003e\n    \u003cspan\u003eSimilarity: \u003cspan class=\"da-stat-value\" id=\"da-stat-sim\"\u003e0%\u003c/span\u003e\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Output --\u003e\n\u003cdiv class=\"da-output-wrap\" id=\"da-output-wrap\"\u003e\n  \u003cdiv class=\"da-output-header\"\u003e\n    \u003cspan class=\"da-section-title\"\u003eDiff Result\u003c/span\u003e\n  \u003c/div\u003e\n  \u003ctable class=\"da-diff-table\" id=\"da-diff-table\"\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003cdiv class=\"da-empty\" id=\"da-empty\"\u003e\n  Enter text in both panels above and click \u003cstrong\u003eCompare\u003c/strong\u003e to see differences.\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  'use strict';\n\n  // ---- LCS-based line diff ----\n  function lcs(a, b) {\n    var m = a.length, n = b.length;\n    // Use Hunt-Szymanski or simple DP; for large inputs we cap at 500 lines each.\n    var maxLen = 500;\n    if (m \u003e maxLen) a = a.slice(0, maxLen), m = maxLen;\n    if (n \u003e maxLen) b = b.slice(0, maxLen), n = maxLen;\n\n    var dp = [];\n    for (var i = 0; i \u003c= m; i++) {\n      dp[i] = new Array(n + 1).fill(0);\n    }\n    for (var i = 1; i \u003c= m; i++) {\n      for (var j = 1; j \u003c= n; j++) {\n        if (a[i-1] === b[j-1]) {\n          dp[i][j] = dp[i-1][j-1] + 1;\n        } else {\n          dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);\n        }\n      }\n    }\n    // Backtrack\n    var result = [];\n    var i = m, j = n;\n    while (i \u003e 0 \u0026\u0026 j \u003e 0) {\n      if (a[i-1] === b[j-1]) {\n        result.unshift({ type: 'same', val: a[i-1] });\n        i--; j--;\n      } else if (dp[i-1][j] \u003e= dp[i][j-1]) {\n        result.unshift({ type: 'removed', val: a[i-1] });\n        i--;\n      } else {\n        result.unshift({ type: 'added', val: b[j-1] });\n        j--;\n      }\n    }\n    while (i \u003e 0) { result.unshift({ type: 'removed', val: a[i-1] }); i--; }\n    while (j \u003e 0) { result.unshift({ type: 'added', val: b[j-1] }); j--; }\n    return result;\n  }\n\n  // ---- Word-level diff ----\n  function wordDiff(oldLine, newLine) {\n    var oldWords = tokenize(oldLine);\n    var newWords = tokenize(newLine);\n    var diff = lcs(oldWords, newWords);\n    var oldHtml = '', newHtml = '';\n    diff.forEach(function(d) {\n      var esc = escHtml(d.val);\n      if (d.type === 'same') {\n        oldHtml += esc;\n        newHtml += esc;\n      } else if (d.type === 'removed') {\n        oldHtml += '\u003cspan class=\"da-word-removed\"\u003e' + esc + '\u003c/span\u003e';\n      } else {\n        newHtml += '\u003cspan class=\"da-word-added\"\u003e' + esc + '\u003c/span\u003e';\n      }\n    });\n    return { oldHtml: oldHtml, newHtml: newHtml };\n  }\n\n  function tokenize(str) {\n    // Split into words and whitespace tokens\n    return str.match(/\\S+|\\s+/g) || [];\n  }\n\n  function escHtml(s) {\n    return s.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n  }\n\n  function normalize(line, ignWs, ignCase) {\n    if (ignWs) line = line.replace(/\\s+/g, ' ').trim();\n    if (ignCase) line = line.toLowerCase();\n    return line;\n  }\n\n  function getLines(text) {\n    return text.split('\\n');\n  }\n\n  // ---- Render ----\n  function render() {\n    var origText = document.getElementById('da-original').value;\n    var modiText = document.getElementById('da-modified').value;\n    var wordMode = document.getElementById('da-opt-word').checked;\n    var ignWs   = document.getElementById('da-opt-whitespace').checked;\n    var ignCase = document.getElementById('da-opt-case').checked;\n\n    var origLines = getLines(origText);\n    var modiLines = getLines(modiText);\n\n    // Normalize for comparison keys only\n    var origKeys = origLines.map(function(l){ return normalize(l, ignWs, ignCase); });\n    var modiKeys = modiLines.map(function(l){ return normalize(l, ignWs, ignCase); });\n\n    var diff = lcs(origKeys, modiKeys);\n\n    // Re-map diff to original line content\n    var oIdx = 0, mIdx = 0;\n    var rows = diff.map(function(d) {\n      if (d.type === 'same') {\n        var row = { type: 'same', orig: origLines[oIdx], modi: modiLines[mIdx], oNum: oIdx+1, mNum: mIdx+1 };\n        oIdx++; mIdx++;\n        return row;\n      } else if (d.type === 'removed') {\n        var row = { type: 'removed', orig: origLines[oIdx], oNum: oIdx+1 };\n        oIdx++;\n        return row;\n      } else {\n        var row = { type: 'added', modi: modiLines[mIdx], mNum: mIdx+1 };\n        mIdx++;\n        return row;\n      }\n    });\n\n    // Stats\n    var added = 0, removed = 0, same = 0;\n    rows.forEach(function(r) {\n      if (r.type === 'added') added++;\n      else if (r.type === 'removed') removed++;\n      else same++;\n    });\n    var total = added + removed + same;\n    var sim = total === 0 ? 100 : Math.round((same / total) * 100);\n\n    document.getElementById('da-stat-added').textContent   = added;\n    document.getElementById('da-stat-removed').textContent = removed;\n    document.getElementById('da-stat-same').textContent    = same;\n    document.getElementById('da-stat-sim').textContent     = sim + '%';\n    document.getElementById('da-stats').classList.add('da-visible');\n\n    // Build table\n    var tableEl = document.getElementById('da-diff-table');\n    var html = '';\n\n    rows.forEach(function(r) {\n      if (r.type === 'removed') {\n        var content = wordMode\n          ? '\u003ctd class=\"da-line-content\"\u003e' + escHtml(r.orig) + '\u003c/td\u003e'\n          : '\u003ctd class=\"da-line-content\"\u003e' + escHtml(r.orig) + '\u003c/td\u003e';\n        html += '\u003ctr class=\"da-line-removed\"\u003e'\n          + '\u003ctd class=\"da-line-num\"\u003e' + r.oNum + '\u003c/td\u003e'\n          + '\u003ctd class=\"da-line-sym\"\u003e-\u003c/td\u003e'\n          + '\u003ctd class=\"da-line-content\"\u003e' + escHtml(r.orig) + '\u003c/td\u003e'\n          + '\u003c/tr\u003e';\n      } else if (r.type === 'added') {\n        html += '\u003ctr class=\"da-line-added\"\u003e'\n          + '\u003ctd class=\"da-line-num\"\u003e' + r.mNum + '\u003c/td\u003e'\n          + '\u003ctd class=\"da-line-sym\"\u003e+\u003c/td\u003e'\n          + '\u003ctd class=\"da-line-content\"\u003e' + escHtml(r.modi) + '\u003c/td\u003e'\n          + '\u003c/tr\u003e';\n      } else {\n        // same — if word mode, we only highlight words if adjacent remove+add pairs exist; for 'same' lines no highlight needed\n        html += '\u003ctr class=\"da-line-same\"\u003e'\n          + '\u003ctd class=\"da-line-num\"\u003e' + r.oNum + '\u003c/td\u003e'\n          + '\u003ctd class=\"da-line-sym\"\u003e \u003c/td\u003e'\n          + '\u003ctd class=\"da-line-content\"\u003e' + escHtml(r.orig) + '\u003c/td\u003e'\n          + '\u003c/tr\u003e';\n      }\n    });\n\n    // Word-level: pair up adjacent removed+added and re-render\n    if (wordMode) {\n      // Re-parse rows into paired structure\n      html = '';\n      var i = 0;\n      while (i \u003c rows.length) {\n        var r = rows[i];\n        if (r.type === 'removed' \u0026\u0026 i + 1 \u003c rows.length \u0026\u0026 rows[i+1].type === 'added') {\n          // Pair: show word diff\n          var wd = wordDiff(r.orig, rows[i+1].modi);\n          html += '\u003ctr class=\"da-line-removed\"\u003e'\n            + '\u003ctd class=\"da-line-num\"\u003e' + r.oNum + '\u003c/td\u003e'\n            + '\u003ctd class=\"da-line-sym\"\u003e-\u003c/td\u003e'\n            + '\u003ctd class=\"da-line-content\"\u003e' + wd.oldHtml + '\u003c/td\u003e'\n            + '\u003c/tr\u003e';\n          html += '\u003ctr class=\"da-line-added\"\u003e'\n            + '\u003ctd class=\"da-line-num\"\u003e' + rows[i+1].mNum + '\u003c/td\u003e'\n            + '\u003ctd class=\"da-line-sym\"\u003e+\u003c/td\u003e'\n            + '\u003ctd class=\"da-line-content\"\u003e' + wd.newHtml + '\u003c/td\u003e'\n            + '\u003c/tr\u003e';\n          i += 2;\n        } else if (r.type === 'removed') {\n          html += '\u003ctr class=\"da-line-removed\"\u003e'\n            + '\u003ctd class=\"da-line-num\"\u003e' + r.oNum + '\u003c/td\u003e'\n            + '\u003ctd class=\"da-line-sym\"\u003e-\u003c/td\u003e'\n            + '\u003ctd class=\"da-line-content\"\u003e' + escHtml(r.orig) + '\u003c/td\u003e'\n            + '\u003c/tr\u003e';\n          i++;\n        } else if (r.type === 'added') {\n          html += '\u003ctr class=\"da-line-added\"\u003e'\n            + '\u003ctd class=\"da-line-num\"\u003e' + r.mNum + '\u003c/td\u003e'\n            + '\u003ctd class=\"da-line-sym\"\u003e+\u003c/td\u003e'\n            + '\u003ctd class=\"da-line-content\"\u003e' + escHtml(r.modi) + '\u003c/td\u003e'\n            + '\u003c/tr\u003e';\n          i++;\n        } else {\n          html += '\u003ctr class=\"da-line-same\"\u003e'\n            + '\u003ctd class=\"da-line-num\"\u003e' + r.oNum + '\u003c/td\u003e'\n            + '\u003ctd class=\"da-line-sym\"\u003e \u003c/td\u003e'\n            + '\u003ctd class=\"da-line-content\"\u003e' + escHtml(r.orig) + '\u003c/td\u003e'\n            + '\u003c/tr\u003e';\n          i++;\n        }\n      }\n    }\n\n    tableEl.innerHTML = html || '\u003ctr\u003e\u003ctd colspan=\"3\" style=\"padding:16px;text-align:center;color:#94a3b8;\"\u003eNo differences found. Texts are identical.\u003c/td\u003e\u003c/tr\u003e';\n\n    document.getElementById('da-output-wrap').classList.add('da-visible');\n    document.getElementById('da-empty').style.display = 'none';\n  }\n\n  function copyDiff() {\n    var tableEl = document.getElementById('da-diff-table');\n    if (!tableEl) return;\n    var rows = tableEl.querySelectorAll('tr');\n    var lines = [];\n    rows.forEach(function(row) {\n      var cells = row.querySelectorAll('td');\n      if (cells.length \u003e= 3) {\n        var sym = cells[1].textContent.trim() || ' ';\n        var content = cells[2].textContent;\n        lines.push(sym + ' ' + content);\n      }\n    });\n    var text = lines.join('\\n');\n    if (!text) return;\n\n    var textarea = document.createElement('textarea');\n    textarea.value = text;\n    textarea.style.position = 'fixed';\n    textarea.style.opacity = '0';\n    document.body.appendChild(textarea);\n    textarea.select();\n    document.execCommand('copy');\n    document.body.removeChild(textarea);\n\n    var fb = document.getElementById('da-copy-feedback');\n    fb.classList.add('da-show');\n    setTimeout(function() { fb.classList.remove('da-show'); }, 2000);\n  }\n\n  // Wire up events\n  document.getElementById('da-btn-compare').addEventListener('click', render);\n\n  document.getElementById('da-btn-swap').addEventListener('click', function() {\n    var orig = document.getElementById('da-original');\n    var modi = document.getElementById('da-modified');\n    var tmp = orig.value;\n    orig.value = modi.value;\n    modi.value = tmp;\n  });\n\n  document.getElementById('da-btn-clear').addEventListener('click', function() {\n    document.getElementById('da-original').value = '';\n    document.getElementById('da-modified').value = '';\n    document.getElementById('da-output-wrap').classList.remove('da-visible');\n    document.getElementById('da-stats').classList.remove('da-visible');\n    document.getElementById('da-empty').style.display = '';\n  });\n\n  document.getElementById('da-btn-copy').addEventListener('click', copyDiff);\n\n  // Allow Ctrl+Enter to compare\n  [document.getElementById('da-original'), document.getElementById('da-modified')].forEach(function(el) {\n    el.addEventListener('keydown', function(e) {\n      if (e.ctrlKey \u0026\u0026 e.key === 'Enter') render();\n    });\n  });\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-to-use-the-text-diff-checker\"\u003eHow to Use the Text Diff Checker\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003ePaste your original text\u003c/strong\u003e into the left panel (labeled A).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ePaste your modified text\u003c/strong\u003e into the right panel (labeled B).\u003c/li\u003e\n\u003cli\u003eClick \u003cstrong\u003eCompare\u003c/strong\u003e (or press Ctrl+Enter) to generate the diff.\u003c/li\u003e\n\u003cli\u003eReview the results:\n\u003cul\u003e\n\u003cli\u003eLines highlighted in \u003cstrong\u003egreen\u003c/strong\u003e (with \u003ccode\u003e+\u003c/code\u003e) were added in the modified text.\u003c/li\u003e\n\u003cli\u003eLines highlighted in \u003cstrong\u003ered\u003c/strong\u003e (with \u003ccode\u003e-\u003c/code\u003e) were removed from the original.\u003c/li\u003e\n\u003cli\u003eLines with no highlight are unchanged.\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eUse the \u003cstrong\u003estatistics bar\u003c/strong\u003e to see a quick summary: lines added, removed, unchanged, and an overall similarity percentage.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\u003cstrong\u003eOptions explained:\u003c/strong\u003e\u003c/p\u003e","title":"Text Diff Checker - Free Online Text Comparison Tool"},{"content":" Caesar Vigenere XOR AES-256 Caesar cipher shifts each letter by a fixed number of positions in the alphabet. The same shift is used for both encryption and decryption (just use the inverse). Non-letter characters are preserved unchanged. Shift (1–25) 13 Mode Encrypt Decrypt Input Text \u0026#128274; Run Caesar Clear Result Encrypted \u0026#128203; Copy Vigenere cipher uses a keyword to apply a different Caesar shift to each character. The keyword repeats to match the input length. Only letters A–Z and a–z are shifted; everything else passes through unchanged. Keyword Mode Encrypt Decrypt Input Text \u0026#128274; Run Vigenere Clear Result Encrypted \u0026#128203; Copy XOR cipher applies a bitwise XOR between each character and the repeating key. Applying the same key twice restores the original — so Encrypt and Decrypt are identical operations. Output is Base64-encoded to stay readable. Key (any text) Mode Encrypt Decrypt Input Text \u0026#128274; Run XOR Clear Result Encrypted \u0026#128203; Copy AES-256-GCM via the browser's native Web Crypto API. A random 96-bit IV is generated per encryption; ciphertext is stored as iv:ciphertext in Base64. Decryption automatically extracts the IV. Your password never leaves the browser. Password Mode Encrypt Decrypt Input Text \u0026#128274; Run AES-256 Clear Processing… Result Encrypted \u0026#128203; Copy How Each Cipher Works Caesar Cipher The Caesar cipher is one of the oldest encryption techniques. It shifts every letter in the plaintext forward (encrypt) or backward (decrypt) by a fixed number of positions in the 26-letter alphabet. A shift of 13 is the special case known as ROT13 — applying it twice returns the original text. Digits, spaces, and punctuation are left unchanged.\nStrength: Very weak by modern standards — there are only 25 possible shifts and brute-forcing all of them takes seconds.\nVigenere Cipher The Vigenere cipher improves on Caesar by using a keyword instead of a single fixed shift. Each letter of the keyword specifies the shift for the corresponding input letter. When the key is shorter than the input, it wraps around. This makes simple frequency analysis harder — but repeated key patterns can still be exploited by the Kasiski examination.\nStrength: Historically considered strong; now easily broken with key-length analysis. Good for learning and puzzles, not for securing sensitive data.\nXOR Cipher XOR applies the bitwise exclusive-OR operation between each byte of the plaintext and the corresponding byte of a repeating key. Because XOR is its own inverse, the encrypt and decrypt operations are identical — just apply the key again. This tool outputs the result as Base64 for readability. A truly random key of the same length as the message (a one-time pad) is theoretically unbreakable, but a short repeating key is vulnerable to known-plaintext attacks.\nStrength: Depends entirely on key length and randomness. Only use with long, random, non-repeating keys for serious security.\nAES-256-GCM AES (Advanced Encryption Standard) with a 256-bit key in GCM (Galois/Counter Mode) is the current industry standard for symmetric encryption. This tool derives the encryption key from your password using PBKDF2 with SHA-256 and 100,000 iterations over a random 16-byte salt — making brute-force dictionary attacks significantly harder. A random 96-bit IV (nonce) is generated for every encryption operation, so encrypting the same plaintext twice produces different ciphertext. The output encodes salt, IV, and ciphertext as Base64 separated by colons.\nStrength: Very strong when used with a long, random password. The Web Crypto API runs in the browser\u0026rsquo;s native cryptographic layer — your password and plaintext never leave your device.\nPrivacy Notice All operations run entirely in your browser. No text, keys, or passwords are transmitted to any server. AES-256-GCM uses the browser\u0026rsquo;s built-in Web Crypto API (SubtleCrypto). Caesar, Vigenere, and XOR are implemented in pure JavaScript.\nGenerate secure random passwords → Password Generator Hash text with MD5, SHA-256, SHA-512 → Hash Generator Encode and rotate text with ROT13 → ROT13 Encoder ","permalink":"https://productivity-works.com/tools/text-encryption/","summary":"\u003cdiv id=\"te-app\"\u003e\n\u003cstyle\u003e\n#te-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 820px;\n  margin: 0 auto;\n  color: #1e293b;\n}\n#te-app *, #te-app *::before, #te-app *::after {\n  box-sizing: border-box;\n}\n/* ---- Tab bar ---- */\n#te-app .te-tab-bar {\n  display: flex;\n  gap: 0;\n  border-bottom: 2px solid #e2e8f0;\n  margin-bottom: 20px;\n  overflow-x: auto;\n}\n#te-app .te-tab {\n  padding: 9px 20px;\n  font-size: 13px;\n  font-weight: 600;\n  color: #64748b;\n  cursor: pointer;\n  border: none;\n  background: none;\n  border-bottom: 2.5px solid transparent;\n  margin-bottom: -2px;\n  transition: color 0.15s, border-color 0.15s;\n  white-space: nowrap;\n}\n#te-app .te-tab.active { color: #7c3aed; border-bottom-color: #7c3aed; }\n#te-app .te-tab:hover:not(.active) { color: #334155; }\n/* ---- Panel ---- */\n#te-app .te-panel { display: none; }\n#te-app .te-panel.active { display: block; }\n/* ---- Card ---- */\n#te-app .te-card {\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 18px 20px;\n  margin-bottom: 16px;\n}\n/* ---- Labels ---- */\n#te-app .te-label {\n  display: block;\n  font-size: 11px;\n  font-weight: 700;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 6px;\n}\n/* ---- Textareas ---- */\n#te-app .te-textarea {\n  width: 100%;\n  min-height: 110px;\n  padding: 11px 13px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 14px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", monospace;\n  resize: vertical;\n  background: #fff;\n  color: #1e293b;\n  transition: border-color 0.2s, box-shadow 0.2s;\n}\n#te-app .te-textarea:focus {\n  outline: none;\n  border-color: #7c3aed;\n  box-shadow: 0 0 0 3px rgba(124,58,237,0.12);\n}\n/* ---- Select ---- */\n#te-app .te-select {\n  padding: 8px 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  font-size: 13px;\n  background: #fff;\n  color: #1e293b;\n  cursor: pointer;\n  transition: border-color 0.2s;\n  min-width: 160px;\n}\n#te-app .te-select:focus { outline: none; border-color: #7c3aed; }\n/* ---- Input field ---- */\n#te-app .te-input {\n  padding: 8px 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  font-size: 13px;\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  background: #fff;\n  color: #1e293b;\n  transition: border-color 0.2s, box-shadow 0.2s;\n  width: 100%;\n}\n#te-app .te-input:focus {\n  outline: none;\n  border-color: #7c3aed;\n  box-shadow: 0 0 0 3px rgba(124,58,237,0.12);\n}\n/* ---- Controls row ---- */\n#te-app .te-row {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: flex-end;\n  gap: 10px;\n  margin-bottom: 12px;\n}\n#te-app .te-field {\n  display: flex;\n  flex-direction: column;\n  gap: 4px;\n  flex: 1;\n  min-width: 140px;\n}\n/* ---- Mode toggle ---- */\n#te-app .te-mode-group {\n  display: flex;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 7px;\n  overflow: hidden;\n  flex-shrink: 0;\n}\n#te-app .te-mode-btn {\n  padding: 8px 18px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  border: none;\n  background: #f1f5f9;\n  color: #64748b;\n  transition: background 0.15s, color 0.15s;\n  line-height: 1;\n}\n#te-app .te-mode-btn.active { background: #7c3aed; color: #fff; }\n#te-app .te-mode-btn:hover:not(.active) { background: #e2e8f0; }\n/* ---- Buttons ---- */\n#te-app .te-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 5px;\n  padding: 9px 18px;\n  border-radius: 7px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  border: none;\n  transition: background 0.18s, transform 0.1s;\n  user-select: none;\n  line-height: 1;\n  white-space: nowrap;\n}\n#te-app .te-btn:active { transform: scale(0.97); }\n#te-app .te-btn-primary   { background: #7c3aed; color: #fff; }\n#te-app .te-btn-primary:hover { background: #6d28d9; }\n#te-app .te-btn-secondary { background: #e2e8f0; color: #374151; }\n#te-app .te-btn-secondary:hover { background: #cbd5e1; }\n/* ---- Output area ---- */\n#te-app .te-output-wrap {\n  display: none;\n  margin-top: 4px;\n}\n#te-app .te-output-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin-bottom: 6px;\n}\n#te-app .te-output-label {\n  font-size: 11px;\n  font-weight: 700;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n#te-app .te-output-ta {\n  width: 100%;\n  min-height: 100px;\n  padding: 11px 13px;\n  border: 1.5px solid #c4b5fd;\n  border-radius: 8px;\n  font-size: 14px;\n  font-family: \"SFMono-Regular\", Consolas, monospace;\n  resize: vertical;\n  background: #faf5ff;\n  color: #1e293b;\n  word-break: break-all;\n}\n/* ---- Status ---- */\n#te-app .te-status {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 9px 14px;\n  border-radius: 8px;\n  font-size: 13px;\n  margin-bottom: 14px;\n}\n#te-app .te-status.ok   { background:#f0fdf4; border:1px solid #bbf7d0; color:#166534; }\n#te-app .te-status.err  { background:#fef2f2; border:1px solid #fecaca; color:#991b1b; }\n#te-app .te-status.hidden { display:none; }\n/* ---- Shift range ---- */\n#te-app .te-shift-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n#te-app .te-range {\n  flex: 1;\n  accent-color: #7c3aed;\n  cursor: pointer;\n}\n#te-app .te-shift-val {\n  min-width: 28px;\n  text-align: center;\n  font-weight: 700;\n  font-size: 15px;\n  color: #7c3aed;\n}\n/* ---- Badge ---- */\n#te-app .te-badge {\n  display: inline-block;\n  padding: 3px 9px;\n  border-radius: 5px;\n  font-size: 11px;\n  font-weight: 700;\n  white-space: nowrap;\n  letter-spacing: 0.04em;\n  background: #ede9fe;\n  color: #5b21b6;\n}\n/* ---- Copy button ---- */\n#te-app .te-copy {\n  display: inline-flex;\n  align-items: center;\n  gap: 4px;\n  padding: 5px 12px;\n  border-radius: 6px;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  border: 1.5px solid #e2e8f0;\n  background: #fff;\n  color: #475569;\n  transition: all 0.15s;\n  white-space: nowrap;\n}\n#te-app .te-copy:hover   { border-color:#7c3aed; color:#7c3aed; background:#f5f3ff; }\n#te-app .te-copy.copied  { border-color:#10b981; color:#10b981; background:#ecfdf5; }\n/* ---- Info box ---- */\n#te-app .te-info {\n  padding: 12px 16px;\n  background: #f5f3ff;\n  border: 1px solid #ddd6fe;\n  border-radius: 8px;\n  font-size: 12.5px;\n  color: #4c1d95;\n  line-height: 1.6;\n  margin-bottom: 14px;\n}\n/* ---- AES progress ---- */\n#te-app .te-progress {\n  display: none;\n  align-items: center;\n  gap: 10px;\n  margin-top: 8px;\n  font-size: 12px;\n  color: #7c3aed;\n}\n#te-app .te-progress.visible { display: flex; }\n#te-app .te-progress-track {\n  flex: 1; height: 4px;\n  background: #ede9fe;\n  border-radius: 3px;\n  overflow: hidden;\n}\n#te-app .te-progress-fill {\n  height: 100%;\n  background: #7c3aed;\n  border-radius: 3px;\n  width: 0%;\n  transition: width 0.2s;\n}\n/* ---- Toast ---- */\n.te-toast {\n  position: fixed;\n  bottom: 24px; right: 24px;\n  background: #7c3aed; color: #fff;\n  padding: 10px 18px;\n  border-radius: 8px;\n  font-size: 13px;\n  font-weight: 600;\n  z-index: 9999;\n  box-shadow: 0 4px 16px rgba(0,0,0,0.18);\n  transition: opacity 0.3s;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n}\n@media (max-width: 540px) {\n  #te-app .te-mode-btn { padding: 8px 12px; }\n  #te-app .te-tab { padding: 9px 14px; }\n}\n\u003c/style\u003e\n\u003c!-- TAB BAR --\u003e\n\u003cdiv class=\"te-tab-bar\"\u003e\n  \u003cbutton class=\"te-tab active\" onclick=\"teTab('caesar',this)\"\u003eCaesar\u003c/button\u003e\n  \u003cbutton class=\"te-tab\" onclick=\"teTab('vigenere',this)\"\u003eVigenere\u003c/button\u003e\n  \u003cbutton class=\"te-tab\" onclick=\"teTab('xor',this)\"\u003eXOR\u003c/button\u003e\n  \u003cbutton class=\"te-tab\" onclick=\"teTab('aes',this)\"\u003eAES-256\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- ===== CAESAR PANEL ===== --\u003e\n\u003cdiv id=\"te-panel-caesar\" class=\"te-panel active\"\u003e\n  \u003cdiv class=\"te-card\"\u003e\n    \u003cdiv class=\"te-info\"\u003e\n      Caesar cipher shifts each letter by a fixed number of positions in the alphabet. The same shift is used for both encryption and decryption (just use the inverse). Non-letter characters are preserved unchanged.\n    \u003c/div\u003e\n    \u003cdiv class=\"te-row\"\u003e\n      \u003cdiv class=\"te-field\" style=\"max-width:260px\"\u003e\n        \u003cspan class=\"te-label\"\u003eShift (1–25)\u003c/span\u003e\n        \u003cdiv class=\"te-shift-row\"\u003e\n          \u003cinput type=\"range\" class=\"te-range\" id=\"te-cs-range\" min=\"1\" max=\"25\" value=\"13\"\n            oninput=\"document.getElementById('te-cs-val').textContent=this.value; teCaesarRun()\"\u003e\n          \u003cspan class=\"te-shift-val\" id=\"te-cs-val\"\u003e13\u003c/span\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"te-field\"\u003e\n        \u003cspan class=\"te-label\"\u003eMode\u003c/span\u003e\n        \u003cdiv class=\"te-mode-group\"\u003e\n          \u003cbutton class=\"te-mode-btn active\" id=\"te-cs-enc-btn\" onclick=\"teSetMode('caesar','encrypt')\"\u003eEncrypt\u003c/button\u003e\n          \u003cbutton class=\"te-mode-btn\" id=\"te-cs-dec-btn\" onclick=\"teSetMode('caesar','decrypt')\"\u003eDecrypt\u003c/button\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cspan class=\"te-label\"\u003eInput Text\u003c/span\u003e\n    \u003ctextarea id=\"te-cs-input\" class=\"te-textarea\" placeholder=\"Type or paste text here…\" oninput=\"teCaesarRun()\"\u003e\u003c/textarea\u003e\n    \u003cdiv style=\"display:flex;gap:8px;margin-top:10px;flex-wrap:wrap;\"\u003e\n      \u003cbutton class=\"te-btn te-btn-primary\" onclick=\"teCaesarRun()\"\u003e\u0026#128274; Run Caesar\u003c/button\u003e\n      \u003cbutton class=\"te-btn te-btn-secondary\" onclick=\"teClearPanel('caesar')\"\u003eClear\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"te-status hidden\" id=\"te-cs-status\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"te-output-wrap\" id=\"te-cs-output-wrap\"\u003e\n    \u003cdiv class=\"te-output-header\"\u003e\n      \u003cspan class=\"te-output-label\"\u003eResult \u003cspan class=\"te-badge\" id=\"te-cs-badge\"\u003eEncrypted\u003c/span\u003e\u003c/span\u003e\n      \u003cbutton class=\"te-copy\" id=\"te-cs-copy\" onclick=\"teCopyOutput('te-cs-out',this)\"\u003e\u0026#128203; Copy\u003c/button\u003e\n    \u003c/div\u003e\n    \u003ctextarea id=\"te-cs-out\" class=\"te-output-ta\" readonly\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ===== VIGENERE PANEL ===== --\u003e\n\u003cdiv id=\"te-panel-vigenere\" class=\"te-panel\"\u003e\n  \u003cdiv class=\"te-card\"\u003e\n    \u003cdiv class=\"te-info\"\u003e\n      Vigenere cipher uses a keyword to apply a different Caesar shift to each character. The keyword repeats to match the input length. Only letters A–Z and a–z are shifted; everything else passes through unchanged.\n    \u003c/div\u003e\n    \u003cdiv class=\"te-row\"\u003e\n      \u003cdiv class=\"te-field\"\u003e\n        \u003cspan class=\"te-label\"\u003eKeyword\u003c/span\u003e\n        \u003cinput type=\"text\" id=\"te-vig-key\" class=\"te-input\" placeholder=\"e.g. SECRET\" oninput=\"teVigenereRun()\"\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"te-field\" style=\"flex:0;min-width:auto\"\u003e\n        \u003cspan class=\"te-label\"\u003eMode\u003c/span\u003e\n        \u003cdiv class=\"te-mode-group\"\u003e\n          \u003cbutton class=\"te-mode-btn active\" id=\"te-vig-enc-btn\" onclick=\"teSetMode('vigenere','encrypt')\"\u003eEncrypt\u003c/button\u003e\n          \u003cbutton class=\"te-mode-btn\" id=\"te-vig-dec-btn\" onclick=\"teSetMode('vigenere','decrypt')\"\u003eDecrypt\u003c/button\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cspan class=\"te-label\"\u003eInput Text\u003c/span\u003e\n    \u003ctextarea id=\"te-vig-input\" class=\"te-textarea\" placeholder=\"Type or paste text here…\" oninput=\"teVigenereRun()\"\u003e\u003c/textarea\u003e\n    \u003cdiv style=\"display:flex;gap:8px;margin-top:10px;flex-wrap:wrap;\"\u003e\n      \u003cbutton class=\"te-btn te-btn-primary\" onclick=\"teVigenereRun()\"\u003e\u0026#128274; Run Vigenere\u003c/button\u003e\n      \u003cbutton class=\"te-btn te-btn-secondary\" onclick=\"teClearPanel('vigenere')\"\u003eClear\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"te-status hidden\" id=\"te-vig-status\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"te-output-wrap\" id=\"te-vig-output-wrap\"\u003e\n    \u003cdiv class=\"te-output-header\"\u003e\n      \u003cspan class=\"te-output-label\"\u003eResult \u003cspan class=\"te-badge\" id=\"te-vig-badge\"\u003eEncrypted\u003c/span\u003e\u003c/span\u003e\n      \u003cbutton class=\"te-copy\" id=\"te-vig-copy\" onclick=\"teCopyOutput('te-vig-out',this)\"\u003e\u0026#128203; Copy\u003c/button\u003e\n    \u003c/div\u003e\n    \u003ctextarea id=\"te-vig-out\" class=\"te-output-ta\" readonly\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ===== XOR PANEL ===== --\u003e\n\u003cdiv id=\"te-panel-xor\" class=\"te-panel\"\u003e\n  \u003cdiv class=\"te-card\"\u003e\n    \u003cdiv class=\"te-info\"\u003e\n      XOR cipher applies a bitwise XOR between each character and the repeating key. Applying the same key twice restores the original — so Encrypt and Decrypt are identical operations. Output is Base64-encoded to stay readable.\n    \u003c/div\u003e\n    \u003cdiv class=\"te-row\"\u003e\n      \u003cdiv class=\"te-field\"\u003e\n        \u003cspan class=\"te-label\"\u003eKey (any text)\u003c/span\u003e\n        \u003cinput type=\"text\" id=\"te-xor-key\" class=\"te-input\" placeholder=\"e.g. myKey123\" oninput=\"teXorRun()\"\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"te-field\" style=\"flex:0;min-width:auto\"\u003e\n        \u003cspan class=\"te-label\"\u003eMode\u003c/span\u003e\n        \u003cdiv class=\"te-mode-group\"\u003e\n          \u003cbutton class=\"te-mode-btn active\" id=\"te-xor-enc-btn\" onclick=\"teSetMode('xor','encrypt')\"\u003eEncrypt\u003c/button\u003e\n          \u003cbutton class=\"te-mode-btn\" id=\"te-xor-dec-btn\" onclick=\"teSetMode('xor','decrypt')\"\u003eDecrypt\u003c/button\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cspan class=\"te-label\"\u003eInput Text\u003c/span\u003e\n    \u003ctextarea id=\"te-xor-input\" class=\"te-textarea\" placeholder=\"Plain text to encrypt, or Base64 XOR ciphertext to decrypt…\" oninput=\"teXorRun()\"\u003e\u003c/textarea\u003e\n    \u003cdiv style=\"display:flex;gap:8px;margin-top:10px;flex-wrap:wrap;\"\u003e\n      \u003cbutton class=\"te-btn te-btn-primary\" onclick=\"teXorRun()\"\u003e\u0026#128274; Run XOR\u003c/button\u003e\n      \u003cbutton class=\"te-btn te-btn-secondary\" onclick=\"teClearPanel('xor')\"\u003eClear\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"te-status hidden\" id=\"te-xor-status\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"te-output-wrap\" id=\"te-xor-output-wrap\"\u003e\n    \u003cdiv class=\"te-output-header\"\u003e\n      \u003cspan class=\"te-output-label\"\u003eResult \u003cspan class=\"te-badge\" id=\"te-xor-badge\"\u003eEncrypted\u003c/span\u003e\u003c/span\u003e\n      \u003cbutton class=\"te-copy\" id=\"te-xor-copy\" onclick=\"teCopyOutput('te-xor-out',this)\"\u003e\u0026#128203; Copy\u003c/button\u003e\n    \u003c/div\u003e\n    \u003ctextarea id=\"te-xor-out\" class=\"te-output-ta\" readonly\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ===== AES-256 PANEL ===== --\u003e\n\u003cdiv id=\"te-panel-aes\" class=\"te-panel\"\u003e\n  \u003cdiv class=\"te-card\"\u003e\n    \u003cdiv class=\"te-info\"\u003e\n      AES-256-GCM via the browser's native \u003cstrong\u003eWeb Crypto API\u003c/strong\u003e. A random 96-bit IV is generated per encryption; ciphertext is stored as \u003ccode\u003eiv:ciphertext\u003c/code\u003e in Base64. Decryption automatically extracts the IV. Your password never leaves the browser.\n    \u003c/div\u003e\n    \u003cdiv class=\"te-row\"\u003e\n      \u003cdiv class=\"te-field\"\u003e\n        \u003cspan class=\"te-label\"\u003ePassword\u003c/span\u003e\n        \u003cinput type=\"password\" id=\"te-aes-pw\" class=\"te-input\" placeholder=\"Enter a strong password…\" oninput=\"teAesRun()\"\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"te-field\" style=\"flex:0;min-width:auto\"\u003e\n        \u003cspan class=\"te-label\"\u003eMode\u003c/span\u003e\n        \u003cdiv class=\"te-mode-group\"\u003e\n          \u003cbutton class=\"te-mode-btn active\" id=\"te-aes-enc-btn\" onclick=\"teSetMode('aes','encrypt')\"\u003eEncrypt\u003c/button\u003e\n          \u003cbutton class=\"te-mode-btn\" id=\"te-aes-dec-btn\" onclick=\"teSetMode('aes','decrypt')\"\u003eDecrypt\u003c/button\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cspan class=\"te-label\"\u003eInput Text\u003c/span\u003e\n    \u003ctextarea id=\"te-aes-input\" class=\"te-textarea\" placeholder=\"Plain text to encrypt, or Base64 AES ciphertext to decrypt…\" oninput=\"teAesRun()\"\u003e\u003c/textarea\u003e\n    \u003cdiv style=\"display:flex;gap:8px;margin-top:10px;flex-wrap:wrap;\"\u003e\n      \u003cbutton class=\"te-btn te-btn-primary\" onclick=\"teAesRun()\"\u003e\u0026#128274; Run AES-256\u003c/button\u003e\n      \u003cbutton class=\"te-btn te-btn-secondary\" onclick=\"teClearPanel('aes')\"\u003eClear\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"te-progress\" id=\"te-aes-prog\"\u003e\n      \u003cspan id=\"te-aes-prog-lbl\"\u003eProcessing…\u003c/span\u003e\n      \u003cdiv class=\"te-progress-track\"\u003e\u003cdiv class=\"te-progress-fill\" id=\"te-aes-prog-fill\"\u003e\u003c/div\u003e\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"te-status hidden\" id=\"te-aes-status\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"te-output-wrap\" id=\"te-aes-output-wrap\"\u003e\n    \u003cdiv class=\"te-output-header\"\u003e\n      \u003cspan class=\"te-output-label\"\u003eResult \u003cspan class=\"te-badge\" id=\"te-aes-badge\"\u003eEncrypted\u003c/span\u003e\u003c/span\u003e\n      \u003cbutton class=\"te-copy\" id=\"te-aes-copy\" onclick=\"teCopyOutput('te-aes-out',this)\"\u003e\u0026#128203; Copy\u003c/button\u003e\n    \u003c/div\u003e\n    \u003ctextarea id=\"te-aes-out\" class=\"te-output-ta\" readonly\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n'use strict';\n\n/* ============================================================\n   State\n   ============================================================ */\nvar teMode = { caesar: 'encrypt', vigenere: 'encrypt', xor: 'encrypt', aes: 'encrypt' };\nvar teAesDebounce = null;\n\n/* ============================================================\n   Tab switching\n   ============================================================ */\nwindow.teTab = function(name, btn) {\n  document.querySelectorAll('#te-app .te-tab').forEach(function(t){ t.classList.remove('active'); });\n  document.querySelectorAll('#te-app .te-panel').forEach(function(p){ p.classList.remove('active'); });\n  btn.classList.add('active');\n  document.getElementById('te-panel-'+name).classList.add('active');\n};\n\n/* ============================================================\n   Mode toggle\n   ============================================================ */\nwindow.teSetMode = function(cipher, mode) {\n  teMode[cipher] = mode;\n  var encBtn = document.getElementById('te-'+cipher.slice(0,3)+'-enc-btn') ||\n               document.getElementById('te-vig-enc-btn') ||\n               document.getElementById('te-xor-enc-btn') ||\n               document.getElementById('te-aes-enc-btn');\n  var decBtn = document.getElementById('te-'+cipher.slice(0,3)+'-dec-btn') ||\n               document.getElementById('te-vig-dec-btn') ||\n               document.getElementById('te-xor-dec-btn') ||\n               document.getElementById('te-aes-dec-btn');\n  // Find buttons within the active panel\n  var prefix = cipher === 'caesar' ? 'cs' : cipher === 'vigenere' ? 'vig' : cipher === 'xor' ? 'xor' : 'aes';\n  document.getElementById('te-'+prefix+'-enc-btn').classList.toggle('active', mode==='encrypt');\n  document.getElementById('te-'+prefix+'-dec-btn').classList.toggle('active', mode==='decrypt');\n  var badge = document.getElementById('te-'+prefix+'-badge');\n  if (badge) badge.textContent = mode === 'encrypt' ? 'Encrypted' : 'Decrypted';\n  // Re-run\n  if (cipher === 'caesar')   teCaesarRun();\n  if (cipher === 'vigenere') teVigenereRun();\n  if (cipher === 'xor')      teXorRun();\n  if (cipher === 'aes')      teAesRun();\n};\n\n/* ============================================================\n   Status helpers\n   ============================================================ */\nfunction teStatus(id, html, cls) {\n  var el = document.getElementById(id);\n  el.innerHTML = html;\n  el.className = 'te-status ' + cls;\n}\nfunction teShowOutput(prefix, text, mode) {\n  document.getElementById('te-'+prefix+'-out').value = text;\n  document.getElementById('te-'+prefix+'-output-wrap').style.display = 'block';\n  if (document.getElementById('te-'+prefix+'-badge'))\n    document.getElementById('te-'+prefix+'-badge').textContent = mode === 'encrypt' ? 'Encrypted' : 'Decrypted';\n}\nfunction teHideOutput(prefix) {\n  document.getElementById('te-'+prefix+'-output-wrap').style.display = 'none';\n}\n\n/* ============================================================\n   Clear panels\n   ============================================================ */\nwindow.teClearPanel = function(cipher) {\n  var prefix = cipher === 'caesar' ? 'cs' : cipher === 'vigenere' ? 'vig' : cipher === 'xor' ? 'xor' : 'aes';\n  var inp = document.getElementById('te-'+prefix+'-input') || document.getElementById('te-cs-input');\n  if (cipher === 'caesar')   document.getElementById('te-cs-input').value = '';\n  if (cipher === 'vigenere') document.getElementById('te-vig-input').value = '';\n  if (cipher === 'xor')      document.getElementById('te-xor-input').value = '';\n  if (cipher === 'aes')      document.getElementById('te-aes-input').value = '';\n  teHideOutput(prefix);\n  teStatus('te-'+prefix+'-status','','hidden');\n};\n\n/* ============================================================\n   Copy helper\n   ============================================================ */\nwindow.teCopyOutput = function(taId, btn) {\n  var val = document.getElementById(taId).value;\n  if (!val) return;\n  navigator.clipboard.writeText(val).then(function() {\n    btn.innerHTML = '\u0026#10003; Copied';\n    btn.classList.add('copied');\n    setTimeout(function(){ btn.innerHTML='\u0026#128203; Copy'; btn.classList.remove('copied'); }, 1800);\n  });\n};\n\nfunction teToast(msg) {\n  var t = document.createElement('div');\n  t.className = 'te-toast';\n  t.textContent = msg;\n  document.body.appendChild(t);\n  setTimeout(function(){ t.style.opacity='0'; setTimeout(function(){ t.remove(); },300); }, 1700);\n}\n\n/* ============================================================\n   Caesar Cipher\n   ============================================================ */\nfunction caesarShift(text, shift, decrypt) {\n  if (decrypt) shift = (26 - shift) % 26;\n  var result = '';\n  for (var i = 0; i \u003c text.length; i++) {\n    var c = text.charCodeAt(i);\n    if (c \u003e= 65 \u0026\u0026 c \u003c= 90) {\n      result += String.fromCharCode(((c - 65 + shift) % 26) + 65);\n    } else if (c \u003e= 97 \u0026\u0026 c \u003c= 122) {\n      result += String.fromCharCode(((c - 97 + shift) % 26) + 97);\n    } else {\n      result += text[i];\n    }\n  }\n  return result;\n}\n\nwindow.teCaesarRun = function() {\n  var text  = document.getElementById('te-cs-input').value;\n  var shift = parseInt(document.getElementById('te-cs-range').value, 10);\n  var mode  = teMode.caesar;\n  if (!text) { teHideOutput('cs'); teStatus('te-cs-status','','hidden'); return; }\n  var out = caesarShift(text, shift, mode === 'decrypt');\n  teShowOutput('cs', out, mode);\n  teStatus('te-cs-status', '\u0026#10003; ' + (mode==='encrypt'?'Encrypted':'Decrypted') + ' with shift ' + shift, 'ok');\n};\n\n/* ============================================================\n   Vigenere Cipher\n   ============================================================ */\nfunction vigenereProcess(text, key, decrypt) {\n  if (!key) return text;\n  key = key.toUpperCase().replace(/[^A-Z]/g, '');\n  if (!key.length) return text;\n  var result = '';\n  var ki = 0;\n  for (var i = 0; i \u003c text.length; i++) {\n    var c = text.charCodeAt(i);\n    var k = key.charCodeAt(ki % key.length) - 65;\n    if (c \u003e= 65 \u0026\u0026 c \u003c= 90) {\n      var shifted = decrypt ? ((c - 65 - k + 26) % 26) : ((c - 65 + k) % 26);\n      result += String.fromCharCode(shifted + 65);\n      ki++;\n    } else if (c \u003e= 97 \u0026\u0026 c \u003c= 122) {\n      var shifted2 = decrypt ? ((c - 97 - k + 26) % 26) : ((c - 97 + k) % 26);\n      result += String.fromCharCode(shifted2 + 97);\n      ki++;\n    } else {\n      result += text[i];\n    }\n  }\n  return result;\n}\n\nwindow.teVigenereRun = function() {\n  var text = document.getElementById('te-vig-input').value;\n  var key  = document.getElementById('te-vig-key').value;\n  var mode = teMode.vigenere;\n  if (!text) { teHideOutput('vig'); teStatus('te-vig-status','','hidden'); return; }\n  if (!key || !key.replace(/[^A-Za-z]/g,'').length) {\n    teStatus('te-vig-status','\u0026#9888; Please enter a keyword (letters only).','err');\n    teHideOutput('vig');\n    return;\n  }\n  var out = vigenereProcess(text, key, mode === 'decrypt');\n  teShowOutput('vig', out, mode);\n  teStatus('te-vig-status', '\u0026#10003; ' + (mode==='encrypt'?'Encrypted':'Decrypted') + ' with key \"' + key.toUpperCase().replace(/[^A-Z]/g,'') + '\"', 'ok');\n};\n\n/* ============================================================\n   XOR Cipher\n   ============================================================ */\nfunction xorEncrypt(text, key) {\n  if (!key) return text;\n  var bytes = new TextEncoder().encode(text);\n  var keyBytes = new TextEncoder().encode(key);\n  var out = new Uint8Array(bytes.length);\n  for (var i = 0; i \u003c bytes.length; i++) {\n    out[i] = bytes[i] ^ keyBytes[i % keyBytes.length];\n  }\n  return btoa(String.fromCharCode.apply(null, out));\n}\n\nfunction xorDecrypt(b64, key) {\n  if (!key) return b64;\n  try {\n    var raw = atob(b64);\n    var bytes = new Uint8Array(raw.length);\n    for (var i = 0; i \u003c raw.length; i++) bytes[i] = raw.charCodeAt(i);\n    var keyBytes = new TextEncoder().encode(key);\n    var out = new Uint8Array(bytes.length);\n    for (var j = 0; j \u003c bytes.length; j++) {\n      out[j] = bytes[j] ^ keyBytes[j % keyBytes.length];\n    }\n    return new TextDecoder().decode(out);\n  } catch(e) {\n    throw new Error('Invalid Base64 ciphertext. Make sure you paste the XOR-encrypted output.');\n  }\n}\n\nwindow.teXorRun = function() {\n  var text = document.getElementById('te-xor-input').value;\n  var key  = document.getElementById('te-xor-key').value;\n  var mode = teMode.xor;\n  if (!text) { teHideOutput('xor'); teStatus('te-xor-status','','hidden'); return; }\n  if (!key) {\n    teStatus('te-xor-status','\u0026#9888; Please enter a key.','err');\n    teHideOutput('xor');\n    return;\n  }\n  try {\n    var out = mode === 'encrypt' ? xorEncrypt(text, key) : xorDecrypt(text.trim(), key);\n    teShowOutput('xor', out, mode);\n    teStatus('te-xor-status', '\u0026#10003; ' + (mode==='encrypt'?'Encrypted (Base64 output)':'Decrypted'), 'ok');\n  } catch(e) {\n    teStatus('te-xor-status', '\u0026#10007; ' + e.message, 'err');\n    teHideOutput('xor');\n  }\n};\n\n/* ============================================================\n   AES-256-GCM via Web Crypto API\n   ============================================================ */\nfunction teAesShowProg(pct, msg) {\n  var el = document.getElementById('te-aes-prog');\n  el.classList.add('visible');\n  document.getElementById('te-aes-prog-fill').style.width = pct + '%';\n  document.getElementById('te-aes-prog-lbl').textContent  = msg;\n}\nfunction teAesHideProg() {\n  document.getElementById('te-aes-prog').classList.remove('visible');\n}\n\nasync function aesKeyFromPassword(password, salt) {\n  var enc = new TextEncoder();\n  var keyMaterial = await crypto.subtle.importKey(\n    'raw', enc.encode(password), 'PBKDF2', false, ['deriveKey']\n  );\n  return crypto.subtle.deriveKey(\n    { name: 'PBKDF2', salt: salt, iterations: 100000, hash: 'SHA-256' },\n    keyMaterial,\n    { name: 'AES-GCM', length: 256 },\n    false,\n    ['encrypt', 'decrypt']\n  );\n}\n\nfunction buf2b64(buf) {\n  return btoa(String.fromCharCode.apply(null, new Uint8Array(buf)));\n}\nfunction b64toBuf(b64) {\n  var raw = atob(b64);\n  var buf = new Uint8Array(raw.length);\n  for (var i = 0; i \u003c raw.length; i++) buf[i] = raw.charCodeAt(i);\n  return buf;\n}\n\nasync function aesEncrypt(plaintext, password) {\n  var enc = new TextEncoder();\n  var salt = crypto.getRandomValues(new Uint8Array(16));\n  var iv   = crypto.getRandomValues(new Uint8Array(12));\n  var key  = await aesKeyFromPassword(password, salt);\n  var cipherBuf = await crypto.subtle.encrypt(\n    { name: 'AES-GCM', iv: iv },\n    key,\n    enc.encode(plaintext)\n  );\n  // Format: base64(salt):base64(iv):base64(ciphertext)\n  return buf2b64(salt) + ':' + buf2b64(iv) + ':' + buf2b64(cipherBuf);\n}\n\nasync function aesDecrypt(encoded, password) {\n  var parts = encoded.trim().split(':');\n  if (parts.length !== 3) throw new Error('Invalid ciphertext format. Expected salt:iv:ciphertext in Base64.');\n  var salt      = b64toBuf(parts[0]);\n  var iv        = b64toBuf(parts[1]);\n  var cipherBuf = b64toBuf(parts[2]);\n  var key = await aesKeyFromPassword(password, salt);\n  var plainBuf = await crypto.subtle.decrypt(\n    { name: 'AES-GCM', iv: iv },\n    key,\n    cipherBuf\n  );\n  return new TextDecoder().decode(plainBuf);\n}\n\nwindow.teAesRun = function() {\n  clearTimeout(teAesDebounce);\n  teAesDebounce = setTimeout(teAesRunNow, 300);\n};\n\nasync function teAesRunNow() {\n  var text = document.getElementById('te-aes-input').value;\n  var pw   = document.getElementById('te-aes-pw').value;\n  var mode = teMode.aes;\n  if (!text) { teHideOutput('aes'); teStatus('te-aes-status','','hidden'); teAesHideProg(); return; }\n  if (!pw) {\n    teStatus('te-aes-status','\u0026#9888; Please enter a password.','err');\n    teHideOutput('aes');\n    return;\n  }\n  try {\n    if (mode === 'encrypt') {\n      teAesShowProg(30, 'Deriving key (PBKDF2)…');\n      var cipher = await aesEncrypt(text, pw);\n      teAesShowProg(100, 'Done');\n      setTimeout(teAesHideProg, 400);\n      teShowOutput('aes', cipher, mode);\n      teStatus('te-aes-status', '\u0026#10003; Encrypted with AES-256-GCM (PBKDF2 key derivation)', 'ok');\n    } else {\n      teAesShowProg(30, 'Deriving key (PBKDF2)…');\n      var plain = await aesDecrypt(text, pw);\n      teAesShowProg(100, 'Done');\n      setTimeout(teAesHideProg, 400);\n      teShowOutput('aes', plain, mode);\n      teStatus('te-aes-status', '\u0026#10003; Decrypted successfully', 'ok');\n    }\n  } catch(e) {\n    teAesHideProg();\n    var msg = e.message || 'Unknown error';\n    if (msg.indexOf('operation-specific') !== -1 || msg.indexOf('OperationError') !== -1 || msg.toLowerCase().indexOf('decrypt') !== -1) {\n      msg = 'Decryption failed — wrong password or corrupted ciphertext.';\n    }\n    teStatus('te-aes-status', '\u0026#10007; ' + msg, 'err');\n    teHideOutput('aes');\n  }\n}\n\n})();\n\u003c/script\u003e\n\u003c/div\u003e\u003c!-- /#te-app --\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-each-cipher-works\"\u003eHow Each Cipher Works\u003c/h2\u003e\n\u003ch3 id=\"caesar-cipher\"\u003eCaesar Cipher\u003c/h3\u003e\n\u003cp\u003eThe Caesar cipher is one of the oldest encryption techniques. It shifts every letter in the plaintext forward (encrypt) or backward (decrypt) by a fixed number of positions in the 26-letter alphabet. A shift of 13 is the special case known as ROT13 — applying it twice returns the original text. Digits, spaces, and punctuation are left unchanged.\u003c/p\u003e","title":"Text Encryption \u0026 Decryption"},{"content":" Input Text Repeat Count (1–10000) Separator New Line Space Comma Custom… Prefix (per repetition) Suffix (per repetition) Transform Options Numbered mode (1. text, 2. text…) Reverse text Shuffle lines Sort A→Z Sort Z→A Remove duplicate lines Generate\nCopy to Clipboard Copied! Related: Count text → Word Counter ","permalink":"https://productivity-works.com/tools/text-repeater/","summary":"\u003cstyle\u003e\n#rep-app *,\n#rep-app *::before,\n#rep-app *::after {\n  box-sizing: border-box;\n}\n#rep-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  max-width: 780px;\n  margin: 0 auto;\n  color: #1e293b;\n}\n#rep-app h2 {\n  font-size: 1.1rem;\n  font-weight: 600;\n  margin: 1.4rem 0 0.5rem;\n  color: #0f766e;\n}\n#rep-app label {\n  display: block;\n  font-size: 0.85rem;\n  font-weight: 500;\n  margin-bottom: 0.3rem;\n  color: #374151;\n}\n#rep-app textarea,\n#rep-app input[type=\"text\"],\n#rep-app input[type=\"number\"],\n#rep-app select {\n  width: 100%;\n  padding: 0.55rem 0.75rem;\n  border: 1px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 0.95rem;\n  background: #fff;\n  color: #1e293b;\n  transition: border-color 0.15s;\n  outline: none;\n}\n#rep-app textarea:focus,\n#rep-app input[type=\"text\"]:focus,\n#rep-app input[type=\"number\"]:focus,\n#rep-app select:focus {\n  border-color: #0d9488;\n  box-shadow: 0 0 0 3px rgba(13,148,136,.15);\n}\n#rep-app textarea {\n  min-height: 110px;\n  resize: vertical;\n  font-family: inherit;\n}\n#rep-app .row {\n  display: flex;\n  gap: 1rem;\n  flex-wrap: wrap;\n}\n#rep-app .row \u003e .field {\n  flex: 1;\n  min-width: 160px;\n}\n#rep-app .field {\n  margin-bottom: 0.85rem;\n}\n#rep-app .check-group {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.6rem 1.2rem;\n  margin-bottom: 0.85rem;\n}\n#rep-app .check-group label {\n  display: flex;\n  align-items: center;\n  gap: 0.4rem;\n  font-size: 0.9rem;\n  font-weight: 400;\n  cursor: pointer;\n  color: #374151;\n  margin: 0;\n}\n#rep-app input[type=\"checkbox\"] {\n  width: 16px;\n  height: 16px;\n  accent-color: #0d9488;\n  cursor: pointer;\n}\n#rep-app .sep-custom {\n  display: none;\n  margin-top: 0.5rem;\n}\n#rep-app .sep-custom.visible {\n  display: block;\n}\n#rep-app .btn-run {\n  display: inline-block;\n  background: #0d9488;\n  color: #fff;\n  border: none;\n  border-radius: 7px;\n  padding: 0.65rem 1.8rem;\n  font-size: 1rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s;\n  margin-top: 0.3rem;\n}\n#rep-app .btn-run:hover {\n  background: #0f766e;\n}\n#rep-app .output-wrap {\n  margin-top: 1.2rem;\n}\n#rep-app .output-meta {\n  font-size: 0.82rem;\n  color: #64748b;\n  margin-bottom: 0.4rem;\n}\n#rep-app .output-box {\n  position: relative;\n}\n#rep-app #rep-output {\n  width: 100%;\n  min-height: 140px;\n  border: 1px solid #cbd5e1;\n  border-radius: 6px;\n  padding: 0.65rem 0.75rem;\n  font-size: 0.9rem;\n  background: #f8fafc;\n  color: #1e293b;\n  resize: vertical;\n  font-family: inherit;\n}\n#rep-app .btn-copy {\n  display: inline-block;\n  margin-top: 0.55rem;\n  background: #fff;\n  color: #0d9488;\n  border: 1.5px solid #0d9488;\n  border-radius: 6px;\n  padding: 0.45rem 1.2rem;\n  font-size: 0.88rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s;\n}\n#rep-app .btn-copy:hover {\n  background: #0d9488;\n  color: #fff;\n}\n#rep-app .copy-feedback {\n  display: inline-block;\n  margin-left: 0.7rem;\n  font-size: 0.82rem;\n  color: #0d9488;\n  opacity: 0;\n  transition: opacity 0.3s;\n}\n#rep-app .copy-feedback.show {\n  opacity: 1;\n}\n#rep-app .divider {\n  border: none;\n  border-top: 1px solid #e2e8f0;\n  margin: 1.1rem 0;\n}\n#rep-app .error {\n  color: #dc2626;\n  font-size: 0.85rem;\n  margin-top: 0.25rem;\n}\n\u003c/style\u003e\n\u003cdiv id=\"rep-app\"\u003e\n\u003cdiv class=\"field\"\u003e\n  \u003clabel for=\"rep-input\"\u003eInput Text\u003c/label\u003e\n  \u003ctextarea id=\"rep-input\" placeholder=\"Type or paste your text here…\"\u003e\u003c/textarea\u003e\n\u003c/div\u003e\n\u003cdiv class=\"row\"\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"rep-count\"\u003eRepeat Count (1–10000)\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"rep-count\" value=\"3\" min=\"1\" max=\"10000\"\u003e\n    \u003cdiv class=\"error\" id=\"rep-count-err\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"rep-sep\"\u003eSeparator\u003c/label\u003e\n    \u003cselect id=\"rep-sep\"\u003e\n      \u003coption value=\"newline\"\u003eNew Line\u003c/option\u003e\n      \u003coption value=\"space\"\u003eSpace\u003c/option\u003e\n      \u003coption value=\"comma\"\u003eComma\u003c/option\u003e\n      \u003coption value=\"custom\"\u003eCustom…\u003c/option\u003e\n    \u003c/select\u003e\n    \u003cdiv class=\"sep-custom\" id=\"rep-sep-custom-wrap\"\u003e\n      \u003cinput type=\"text\" id=\"rep-sep-custom\" placeholder=\"Enter custom separator\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"row\"\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"rep-prefix\"\u003ePrefix (per repetition)\u003c/label\u003e\n    \u003cinput type=\"text\" id=\"rep-prefix\" placeholder=\"e.g. - \"\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"rep-suffix\"\u003eSuffix (per repetition)\u003c/label\u003e\n    \u003cinput type=\"text\" id=\"rep-suffix\" placeholder=\"e.g. ;\"\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003ch2\u003eTransform Options\u003c/h2\u003e\n\u003cdiv class=\"check-group\"\u003e\n  \u003clabel\u003e\u003cinput type=\"checkbox\" id=\"rep-numbered\"\u003e Numbered mode (1. text, 2. text…)\u003c/label\u003e\n  \u003clabel\u003e\u003cinput type=\"checkbox\" id=\"rep-reverse\"\u003e Reverse text\u003c/label\u003e\n  \u003clabel\u003e\u003cinput type=\"checkbox\" id=\"rep-shuffle\"\u003e Shuffle lines\u003c/label\u003e\n  \u003clabel\u003e\u003cinput type=\"checkbox\" id=\"rep-sort-az\"\u003e Sort A→Z\u003c/label\u003e\n  \u003clabel\u003e\u003cinput type=\"checkbox\" id=\"rep-sort-za\"\u003e Sort Z→A\u003c/label\u003e\n  \u003clabel\u003e\u003cinput type=\"checkbox\" id=\"rep-dedup\"\u003e Remove duplicate lines\u003c/label\u003e\n\u003c/div\u003e\n\u003cp\u003e\u003cbutton class=\"btn-run\" id=\"rep-run\"\u003eGenerate\u003c/button\u003e\u003c/p\u003e","title":"Text Repeater — Repeat, Format \u0026 Transform Text Online"},{"content":" Paste or type your text below Copy Text Clear 0 Words 0 Characters\n(with spaces) 0 Characters\n(no spaces) 0 Sentences 0 Paragraphs 0.0 Avg Word\nLength 0% unique words \u0026nbsp;(0 of 0) \u0026#128214; 0 sec Reading time (225 WPM) \u0026#127897; 0 sec Speaking time (130 WPM) Readability Scores Flesch Reading Ease (0–100, higher = easier) — 0 Hard60 Std100 Easy Enter text to calculate Flesch-Kincaid Grade Level (US school grade) — K (0)8th16+ (College) Enter text to calculate Top Keywords Enter text to see keyword analysis Count words → Word Counter Analyze frequency → Word Frequency Related Articles AI Writing Tools Comparison 2026: Best Options Ranked and Reviewed Claude AI vs ChatGPT Comparison 2026 AI Prompt Engineering Tips for Beginners 2026 ","permalink":"https://productivity-works.com/tools/text-statistics/","summary":"\u003cdiv id=\"ts-app\"\u003e\n\u003cstyle\u003e\n#ts-app {\n  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;\n  background: #0f0f13;\n  color: #e2e8f0;\n  border-radius: 12px;\n  padding: 24px;\n  margin: 0 auto;\n  max-width: 980px;\n  box-sizing: border-box;\n}\n#ts-app * { box-sizing: border-box; }\n\n#ts-app h2 {\n  font-size: 1.05rem;\n  font-weight: 600;\n  color: #f1f5f9;\n  margin: 0 0 12px 0;\n  letter-spacing: 0.02em;\n}\n#ts-app h3 {\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #94a3b8;\n  margin: 0 0 10px 0;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n}\n\n#ts-textarea {\n  width: 100%;\n  min-height: 170px;\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  color: #e2e8f0;\n  font-size: 0.97rem;\n  padding: 12px 14px;\n  resize: vertical;\n  font-family: inherit;\n  transition: border-color 0.2s;\n  outline: none;\n  line-height: 1.65;\n}\n#ts-textarea:focus { border-color: #6366f1; }\n#ts-textarea::placeholder { color: #4a4a6a; }\n\n.ts-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-top: 10px;\n  margin-bottom: 22px;\n}\n.ts-btn {\n  padding: 7px 16px;\n  border-radius: 6px;\n  border: none;\n  font-size: 0.84rem;\n  font-weight: 600;\n  cursor: pointer;\n  font-family: inherit;\n  transition: background 0.15s, transform 0.1s;\n}\n.ts-btn:active { transform: scale(0.97); }\n.ts-btn-primary { background: #6366f1; color: #fff; }\n.ts-btn-primary:hover { background: #4f46e5; }\n.ts-btn-danger { background: #1a1a24; color: #f87171; border: 1px solid #3d2020; }\n.ts-btn-danger:hover { background: #2d1a1a; }\n.ts-btn-copy { background: #1a1a24; color: #a5b4fc; border: 1px solid #2d2d4d; }\n.ts-btn-copy:hover { background: #22224a; }\n\n/* ── Main Stats Grid ── */\n.ts-stats-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));\n  gap: 10px;\n  margin-bottom: 20px;\n}\n.ts-stat-card {\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  padding: 12px 14px;\n  text-align: center;\n}\n.ts-stat-value {\n  font-size: 1.45rem;\n  font-weight: 700;\n  color: #a5b4fc;\n  line-height: 1.2;\n}\n.ts-stat-label {\n  font-size: 0.73rem;\n  color: #64748b;\n  margin-top: 4px;\n  line-height: 1.35;\n}\n\n/* ── Time Cards ── */\n.ts-time-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\n  gap: 10px;\n  margin-bottom: 20px;\n}\n.ts-time-card {\n  background: #161b2e;\n  border: 1px solid #1e2a4a;\n  border-radius: 8px;\n  padding: 12px 16px;\n  display: flex;\n  align-items: center;\n  gap: 12px;\n}\n.ts-time-icon { font-size: 1.4rem; line-height: 1; }\n.ts-time-val {\n  font-size: 1.1rem;\n  font-weight: 700;\n  color: #7dd3fc;\n}\n.ts-time-desc { font-size: 0.73rem; color: #64748b; margin-top: 2px; }\n\n/* ── Readability Section ── */\n.ts-readability-wrap {\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  padding: 16px;\n  margin-bottom: 20px;\n}\n.ts-scores-row {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 16px;\n  margin-top: 10px;\n}\n.ts-score-block { }\n.ts-score-label {\n  font-size: 0.75rem;\n  color: #64748b;\n  margin-bottom: 6px;\n}\n.ts-score-num {\n  font-size: 1.6rem;\n  font-weight: 700;\n  color: #f1f5f9;\n  line-height: 1;\n}\n.ts-score-desc {\n  font-size: 0.78rem;\n  color: #94a3b8;\n  margin-top: 5px;\n}\n.ts-rbar-track {\n  background: #0f0f18;\n  border-radius: 99px;\n  height: 8px;\n  margin: 8px 0 4px;\n  overflow: hidden;\n}\n.ts-rbar-fill {\n  height: 100%;\n  border-radius: 99px;\n  transition: width 0.4s ease, background 0.4s;\n}\n.ts-rbar-labels {\n  display: flex;\n  justify-content: space-between;\n  font-size: 0.68rem;\n  color: #475569;\n}\n\n/* ── Keywords Section ── */\n.ts-section {\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  padding: 16px;\n  margin-bottom: 20px;\n}\n.ts-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.85rem;\n}\n.ts-table th {\n  text-align: left;\n  color: #64748b;\n  font-size: 0.73rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  padding: 6px 10px;\n  border-bottom: 1px solid #2d2d3d;\n}\n.ts-table td {\n  padding: 7px 10px;\n  color: #e2e8f0;\n  border-bottom: 1px solid #1e1e2c;\n}\n.ts-table tr:last-child td { border-bottom: none; }\n.ts-table tr:hover td { background: #20202e; }\n.ts-density-bar {\n  display: inline-block;\n  height: 6px;\n  border-radius: 3px;\n  background: #6366f1;\n  vertical-align: middle;\n  margin-right: 6px;\n  transition: width 0.3s;\n}\n\n/* ── Unique Words Badge ── */\n.ts-unique-badge {\n  display: inline-flex;\n  align-items: center;\n  gap: 8px;\n  background: #12192e;\n  border: 1px solid #1e2a4a;\n  border-radius: 8px;\n  padding: 10px 16px;\n  margin-bottom: 20px;\n  font-size: 0.88rem;\n  color: #94a3b8;\n}\n.ts-unique-pct {\n  font-size: 1.4rem;\n  font-weight: 700;\n  color: #34d399;\n}\n\n.ts-empty {\n  color: #3a3a5a;\n  font-size: 0.84rem;\n  text-align: center;\n  padding: 14px 0;\n}\n\n@media (max-width: 600px) {\n  #ts-app { padding: 16px 12px; }\n  .ts-stats-grid { grid-template-columns: repeat(2, 1fr); }\n  .ts-time-grid { grid-template-columns: 1fr 1fr; }\n  .ts-scores-row { grid-template-columns: 1fr; }\n}\n\u003c/style\u003e\n\u003ch2\u003ePaste or type your text below\u003c/h2\u003e\n\u003ctextarea id=\"ts-textarea\" placeholder=\"Paste or type your text here to get comprehensive statistics instantly…\"\u003e\u003c/textarea\u003e\n\u003cdiv class=\"ts-toolbar\"\u003e\n  \u003cbutton class=\"ts-btn ts-btn-copy\" onclick=\"tsCopyText()\"\u003eCopy Text\u003c/button\u003e\n  \u003cbutton class=\"ts-btn ts-btn-danger\" onclick=\"tsClear()\"\u003eClear\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- Core Stats --\u003e\n\u003cdiv class=\"ts-stats-grid\"\u003e\n  \u003cdiv class=\"ts-stat-card\"\u003e\n    \u003cdiv class=\"ts-stat-value\" id=\"ts-words\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"ts-stat-label\"\u003eWords\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ts-stat-card\"\u003e\n    \u003cdiv class=\"ts-stat-value\" id=\"ts-chars\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"ts-stat-label\"\u003eCharacters\u003cbr\u003e(with spaces)\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ts-stat-card\"\u003e\n    \u003cdiv class=\"ts-stat-value\" id=\"ts-chars-no\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"ts-stat-label\"\u003eCharacters\u003cbr\u003e(no spaces)\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ts-stat-card\"\u003e\n    \u003cdiv class=\"ts-stat-value\" id=\"ts-sentences\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"ts-stat-label\"\u003eSentences\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ts-stat-card\"\u003e\n    \u003cdiv class=\"ts-stat-value\" id=\"ts-paragraphs\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"ts-stat-label\"\u003eParagraphs\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ts-stat-card\"\u003e\n    \u003cdiv class=\"ts-stat-value\" id=\"ts-avg-word\"\u003e0.0\u003c/div\u003e\n    \u003cdiv class=\"ts-stat-label\"\u003eAvg Word\u003cbr\u003eLength\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Unique Words --\u003e\n\u003cdiv class=\"ts-unique-badge\"\u003e\n  \u003cspan class=\"ts-unique-pct\" id=\"ts-unique-pct\"\u003e0%\u003c/span\u003e\n  \u003cspan\u003eunique words \u0026nbsp;(\u003cspan id=\"ts-unique-count\"\u003e0\u003c/span\u003e of \u003cspan id=\"ts-unique-total\"\u003e0\u003c/span\u003e)\u003c/span\u003e\n\u003c/div\u003e\n\u003c!-- Reading / Speaking Time --\u003e\n\u003cdiv class=\"ts-time-grid\"\u003e\n  \u003cdiv class=\"ts-time-card\"\u003e\n    \u003cdiv class=\"ts-time-icon\"\u003e\u0026#128214;\u003c/div\u003e\n    \u003cdiv\u003e\n      \u003cdiv class=\"ts-time-val\" id=\"ts-read-time\"\u003e0 sec\u003c/div\u003e\n      \u003cdiv class=\"ts-time-desc\"\u003eReading time (225 WPM)\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ts-time-card\"\u003e\n    \u003cdiv class=\"ts-time-icon\"\u003e\u0026#127897;\u003c/div\u003e\n    \u003cdiv\u003e\n      \u003cdiv class=\"ts-time-val\" id=\"ts-speak-time\"\u003e0 sec\u003c/div\u003e\n      \u003cdiv class=\"ts-time-desc\"\u003eSpeaking time (130 WPM)\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Readability Scores --\u003e\n\u003cdiv class=\"ts-readability-wrap\"\u003e\n  \u003ch3\u003eReadability Scores\u003c/h3\u003e\n  \u003cdiv class=\"ts-scores-row\"\u003e\n    \u003c!-- Flesch Reading Ease --\u003e\n    \u003cdiv class=\"ts-score-block\"\u003e\n      \u003cdiv class=\"ts-score-label\"\u003eFlesch Reading Ease (0–100, higher = easier)\u003c/div\u003e\n      \u003cdiv class=\"ts-score-num\" id=\"ts-fre-score\"\u003e—\u003c/div\u003e\n      \u003cdiv class=\"ts-rbar-track\"\u003e\n        \u003cdiv class=\"ts-rbar-fill\" id=\"ts-fre-fill\" style=\"width:0%;background:#6366f1;\"\u003e\u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"ts-rbar-labels\"\u003e\u003cspan\u003e0 Hard\u003c/span\u003e\u003cspan\u003e60 Std\u003c/span\u003e\u003cspan\u003e100 Easy\u003c/span\u003e\u003c/div\u003e\n      \u003cdiv class=\"ts-score-desc\" id=\"ts-fre-desc\"\u003eEnter text to calculate\u003c/div\u003e\n    \u003c/div\u003e\n    \u003c!-- Flesch-Kincaid Grade Level --\u003e\n    \u003cdiv class=\"ts-score-block\"\u003e\n      \u003cdiv class=\"ts-score-label\"\u003eFlesch-Kincaid Grade Level (US school grade)\u003c/div\u003e\n      \u003cdiv class=\"ts-score-num\" id=\"ts-fkg-score\"\u003e—\u003c/div\u003e\n      \u003cdiv class=\"ts-rbar-track\"\u003e\n        \u003cdiv class=\"ts-rbar-fill\" id=\"ts-fkg-fill\" style=\"width:0%;background:#6366f1;\"\u003e\u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"ts-rbar-labels\"\u003e\u003cspan\u003eK (0)\u003c/span\u003e\u003cspan\u003e8th\u003c/span\u003e\u003cspan\u003e16+ (College)\u003c/span\u003e\u003c/div\u003e\n      \u003cdiv class=\"ts-score-desc\" id=\"ts-fkg-desc\"\u003eEnter text to calculate\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Top Keywords --\u003e\n\u003cdiv class=\"ts-section\"\u003e\n  \u003ch3\u003eTop Keywords\u003c/h3\u003e\n  \u003cdiv id=\"ts-keywords-wrap\"\u003e\u003cdiv class=\"ts-empty\"\u003eEnter text to see keyword analysis\u003c/div\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n  var STOP = new Set([\n    'a','an','the','and','but','or','nor','for','yet','so','in','on','at','to',\n    'of','by','up','as','is','it','its','be','was','are','were','been','being',\n    'have','has','had','do','does','did','will','would','could','should','may',\n    'might','shall','can','this','that','these','those','i','you','he','she','we',\n    'they','my','your','his','her','our','their','me','him','us','them','what',\n    'which','who','when','where','why','how','all','each','every','both','few',\n    'more','most','other','some','such','no','not','only','own','same','than',\n    'too','very','just','because','if','then','there','here','about','into',\n    'through','during','before','after','above','below','from','with','without',\n    'also','any','get','got','use','used','one','two','three','new','good','like'\n  ]);\n\n  function getWords(t) { return t.match(/\\b[a-zA-Z']+\\b/g) || []; }\n\n  function getSentences(t) {\n    var s = t.trim();\n    if (!s) return 0;\n    var m = s.match(/[^.!?]*[.!?]+/g);\n    return m ? m.length : (s.length \u003e 0 ? 1 : 0);\n  }\n\n  function getParagraphs(t) {\n    var s = t.trim();\n    if (!s) return 0;\n    return s.split(/\\n\\s*\\n+/).filter(function(p){ return p.trim().length \u003e 0; }).length || 1;\n  }\n\n  function formatTime(mins) {\n    if (mins === 0) return '0 sec';\n    if (mins \u003c 1) return Math.round(mins * 60) + ' sec';\n    var m = Math.floor(mins);\n    var s = Math.round((mins - m) * 60);\n    if (s === 0) return m + ' min';\n    return m + ' min ' + s + ' sec';\n  }\n\n  function countSyllables(word) {\n    word = word.toLowerCase().replace(/[^a-z]/g, '');\n    if (!word) return 0;\n    if (word.length \u003c= 3) return 1;\n    word = word.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, '');\n    word = word.replace(/^y/, '');\n    var m = word.match(/[aeiouy]{1,2}/g);\n    return m ? Math.max(1, m.length) : 1;\n  }\n\n  function computeReadability(words, sentences) {\n    if (!words.length || !sentences) return null;\n    var syl = words.reduce(function(a,w){ return a + countSyllables(w); }, 0);\n    var asl = words.length / sentences;   // avg sentence length\n    var asw = syl / words.length;         // avg syllables per word\n    // Flesch Reading Ease\n    var fre = 206.835 - 1.015 * asl - 84.6 * asw;\n    fre = Math.round(Math.max(0, Math.min(100, fre)));\n    // Flesch-Kincaid Grade Level\n    var fkg = 0.39 * asl + 11.8 * asw - 15.59;\n    fkg = Math.max(0, fkg);\n    return { fre: fre, fkg: parseFloat(fkg.toFixed(1)) };\n  }\n\n  function freLabel(s) {\n    if (s \u003e= 90) return 'Very Easy — 5th grade';\n    if (s \u003e= 80) return 'Easy — 6th grade';\n    if (s \u003e= 70) return 'Fairly Easy — 7th grade';\n    if (s \u003e= 60) return 'Standard — 8th–9th grade';\n    if (s \u003e= 50) return 'Fairly Difficult — 10th–12th grade';\n    if (s \u003e= 30) return 'Difficult — College level';\n    return 'Very Difficult — Professional';\n  }\n\n  function freColor(s) {\n    if (s \u003e= 70) return '#4ade80';\n    if (s \u003e= 50) return '#facc15';\n    if (s \u003e= 30) return '#fb923c';\n    return '#f87171';\n  }\n\n  function fkgLabel(g) {\n    if (g \u003c= 1) return 'Kindergarten';\n    if (g \u003c= 6) return 'Elementary school';\n    if (g \u003c= 8) return 'Middle school';\n    if (g \u003c= 12) return 'High school';\n    if (g \u003c= 16) return 'College level';\n    return 'Graduate / Professional';\n  }\n\n  function fkgColor(g) {\n    if (g \u003c= 6) return '#4ade80';\n    if (g \u003c= 9) return '#facc15';\n    if (g \u003c= 12) return '#fb923c';\n    return '#f87171';\n  }\n\n  function esc(s) {\n    return s.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n  }\n\n  function analyze() {\n    var text = document.getElementById('ts-textarea').value;\n    var words = getWords(text);\n    var wc = words.length;\n    var sentences = getSentences(text);\n    var paragraphs = getParagraphs(text);\n\n    var avgLen = wc \u003e 0\n      ? (words.reduce(function(a,w){ return a + w.replace(/'/g,'').length; }, 0) / wc).toFixed(1)\n      : '0.0';\n\n    // Unique words\n    var lower = words.map(function(w){ return w.toLowerCase(); });\n    var uniqueSet = new Set(lower);\n    var uniqueCount = uniqueSet.size;\n    var uniquePct = wc \u003e 0 ? Math.round((uniqueCount / wc) * 100) : 0;\n\n    // Core stats\n    document.getElementById('ts-words').textContent = wc.toLocaleString();\n    document.getElementById('ts-chars').textContent = text.length.toLocaleString();\n    document.getElementById('ts-chars-no').textContent = text.replace(/\\s/g,'').length.toLocaleString();\n    document.getElementById('ts-sentences').textContent = sentences.toLocaleString();\n    document.getElementById('ts-paragraphs').textContent = paragraphs.toLocaleString();\n    document.getElementById('ts-avg-word').textContent = avgLen;\n\n    // Unique words\n    document.getElementById('ts-unique-pct').textContent = uniquePct + '%';\n    document.getElementById('ts-unique-count').textContent = uniqueCount.toLocaleString();\n    document.getElementById('ts-unique-total').textContent = wc.toLocaleString();\n\n    // Time\n    document.getElementById('ts-read-time').textContent = formatTime(wc / 225);\n    document.getElementById('ts-speak-time').textContent = formatTime(wc / 130);\n\n    // Readability\n    var rd = computeReadability(words, sentences);\n    var freScoreEl = document.getElementById('ts-fre-score');\n    var freDescEl  = document.getElementById('ts-fre-desc');\n    var freFillEl  = document.getElementById('ts-fre-fill');\n    var fkgScoreEl = document.getElementById('ts-fkg-score');\n    var fkgDescEl  = document.getElementById('ts-fkg-desc');\n    var fkgFillEl  = document.getElementById('ts-fkg-fill');\n\n    if (!rd) {\n      freScoreEl.textContent = '\\u2014'; freDescEl.textContent = 'Enter text to calculate';\n      freFillEl.style.width = '0%'; freFillEl.style.background = '#6366f1';\n      fkgScoreEl.textContent = '\\u2014'; fkgDescEl.textContent = 'Enter text to calculate';\n      fkgFillEl.style.width = '0%'; fkgFillEl.style.background = '#6366f1';\n    } else {\n      freScoreEl.textContent = rd.fre;\n      freDescEl.textContent = freLabel(rd.fre);\n      freFillEl.style.width = rd.fre + '%';\n      freFillEl.style.background = freColor(rd.fre);\n\n      fkgScoreEl.textContent = rd.fkg;\n      fkgDescEl.textContent = fkgLabel(rd.fkg);\n      // Grade 0–16 mapped to 0–100%\n      var fkgPct = Math.min(100, Math.round((rd.fkg / 16) * 100));\n      fkgFillEl.style.width = fkgPct + '%';\n      fkgFillEl.style.background = fkgColor(rd.fkg);\n    }\n\n    // Keywords\n    var wrap = document.getElementById('ts-keywords-wrap');\n    if (!wc) {\n      wrap.innerHTML = '\u003cdiv class=\"ts-empty\"\u003eEnter text to see keyword analysis\u003c/div\u003e';\n      return;\n    }\n    var freq = {};\n    lower.forEach(function(lw) {\n      if (!STOP.has(lw) \u0026\u0026 lw.replace(/'/g,'').length \u003e 1) {\n        freq[lw] = (freq[lw] || 0) + 1;\n      }\n    });\n    var sorted = Object.keys(freq).sort(function(a,b){ return freq[b]-freq[a]; }).slice(0, 10);\n    if (!sorted.length) {\n      wrap.innerHTML = '\u003cdiv class=\"ts-empty\"\u003eNo significant keywords found\u003c/div\u003e';\n      return;\n    }\n    var maxC = freq[sorted[0]];\n    var html = '\u003ctable class=\"ts-table\"\u003e\u003cthead\u003e\u003ctr\u003e\u003cth\u003e#\u003c/th\u003e\u003cth\u003eKeyword\u003c/th\u003e\u003cth\u003eCount\u003c/th\u003e\u003cth\u003eDensity\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e\u003ctbody\u003e';\n    sorted.forEach(function(word, i) {\n      var c = freq[word];\n      var d = ((c / wc) * 100).toFixed(2);\n      var bw = Math.round((c / maxC) * 90);\n      html += '\u003ctr\u003e'\n        + '\u003ctd style=\"color:#475569;\"\u003e' + (i+1) + '\u003c/td\u003e'\n        + '\u003ctd style=\"color:#a5b4fc;font-weight:600;\"\u003e' + esc(word) + '\u003c/td\u003e'\n        + '\u003ctd\u003e' + c + '\u003c/td\u003e'\n        + '\u003ctd\u003e\u003cspan class=\"ts-density-bar\" style=\"width:' + bw + 'px\"\u003e\u003c/span\u003e' + d + '%\u003c/td\u003e'\n        + '\u003c/tr\u003e';\n    });\n    html += '\u003c/tbody\u003e\u003c/table\u003e';\n    wrap.innerHTML = html;\n  }\n\n  window.tsClear = function() {\n    document.getElementById('ts-textarea').value = '';\n    analyze();\n  };\n\n  window.tsCopyText = function() {\n    var ta = document.getElementById('ts-textarea');\n    ta.select();\n    try { document.execCommand('copy'); } catch(e) {}\n    ta.setSelectionRange(0, 0);\n  };\n\n  document.getElementById('ts-textarea').addEventListener('input', analyze);\n  analyze();\n})();\n\u003c/script\u003e\n\u003cblockquote\u003e\n\u003cp\u003eCount words → \u003ca href=\"https://productivity-works.com/tools/word-counter/\"\u003eWord Counter\u003c/a\u003e\n\nAnalyze frequency → \u003ca href=\"https://productivity-works.com/tools/word-frequency/\"\u003eWord Frequency\u003c/a\u003e\n\u003c/p\u003e","title":"Text Statistics Analyzer"},{"content":" Enter Your Text The Web Speech API lets browsers convert text to speech entirely on-device, with no data sent to any server. Try adjusting the voice, speed, pitch, and volume below to find the combination that works best for you. 0 characters ~0 min read Voice \u0026amp; Language Filter by Language All Languages Voice Loading voices\u0026hellip; Playback Settings Speed 1.00x Pitch 1.00 Volume 100% \u0026#9654; Play \u0026#9646;\u0026#9646; Pause \u0026#9632; Stop Ready Current Word Spoken words will appear highlighted here during playback. Your browser does not support the Web Speech API. Please use a modern browser such as Chrome, Edge, or Safari. How to Use Paste or type your text into the text area above. Choose a voice — use the language filter to narrow down voices available in your browser. Adjust speed, pitch, and volume using the sliders (speed: 0.5x–2x). Press Play and watch the current word highlight in real time. Pause to temporarily stop, or Stop to cancel playback entirely. About This Tool This tool uses your browser\u0026rsquo;s built-in Web Speech API (speechSynthesis). No audio is sent to any server — everything happens locally on your device. Available voices depend on your operating system and browser.\nBrowser support: Chrome, Edge, Safari, and most modern browsers. Firefox has limited voice selection on some platforms.\nCount characters → Character Counter Translate Morse → Morse Code Translator ","permalink":"https://productivity-works.com/tools/text-to-speech/","summary":"\u003cdiv id=\"ts-app\"\u003e\n\u003cstyle\u003e\n#ts-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 800px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#ts-app h2 {\n  font-size: 1.1rem;\n  font-weight: 700;\n  margin: 0 0 0.6rem 0;\n  color: #1e293b;\n}\n#ts-app .ts-card {\n  background: #ffffff;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 1.25rem 1.5rem;\n  margin-bottom: 1rem;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.06);\n}\n#ts-app textarea#ts-text {\n  width: 100%;\n  min-height: 160px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  padding: 0.75rem 1rem;\n  font-size: 1rem;\n  font-family: inherit;\n  resize: vertical;\n  box-sizing: border-box;\n  transition: border-color 0.2s;\n  line-height: 1.65;\n  color: #1a1a2e;\n  background: #f8fafc;\n}\n#ts-app textarea#ts-text:focus {\n  outline: none;\n  border-color: #6366f1;\n  background: #fff;\n}\n#ts-app .ts-meta-row {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin-top: 0.4rem;\n  font-size: 0.82rem;\n  color: #64748b;\n}\n#ts-app .ts-controls-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1rem;\n}\n@media (max-width: 560px) {\n  #ts-app .ts-controls-grid { grid-template-columns: 1fr; }\n}\n#ts-app label.ts-label {\n  display: block;\n  font-size: 0.78rem;\n  font-weight: 700;\n  color: #475569;\n  margin-bottom: 0.3rem;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n#ts-app select.ts-select {\n  width: 100%;\n  padding: 0.5rem 2rem 0.5rem 0.75rem;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 7px;\n  font-size: 0.92rem;\n  font-family: inherit;\n  background: #f8fafc;\n  color: #1a1a2e;\n  cursor: pointer;\n  appearance: none;\n  -webkit-appearance: none;\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2364748b' d='M6 8L1 3h10z'/%3E%3C/svg%3E\");\n  background-repeat: no-repeat;\n  background-position: right 0.75rem center;\n}\n#ts-app select.ts-select:focus {\n  outline: none;\n  border-color: #6366f1;\n}\n#ts-app .ts-slider-wrap {\n  display: flex;\n  flex-direction: column;\n  gap: 0.25rem;\n}\n#ts-app .ts-slider-row {\n  display: flex;\n  align-items: center;\n  gap: 0.6rem;\n}\n#ts-app input[type=\"range\"].ts-slider {\n  flex: 1;\n  -webkit-appearance: none;\n  appearance: none;\n  height: 4px;\n  border-radius: 2px;\n  background: #e2e8f0;\n  cursor: pointer;\n}\n#ts-app input[type=\"range\"].ts-slider::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  width: 17px;\n  height: 17px;\n  border-radius: 50%;\n  background: #6366f1;\n  cursor: pointer;\n  box-shadow: 0 1px 4px rgba(99,102,241,0.45);\n}\n#ts-app input[type=\"range\"].ts-slider::-moz-range-thumb {\n  width: 17px;\n  height: 17px;\n  border-radius: 50%;\n  background: #6366f1;\n  border: none;\n  cursor: pointer;\n}\n#ts-app .ts-slider-val {\n  font-size: 0.82rem;\n  font-weight: 700;\n  color: #6366f1;\n  min-width: 3rem;\n  text-align: right;\n}\n#ts-app .ts-btn-row {\n  display: flex;\n  gap: 0.75rem;\n  flex-wrap: wrap;\n  align-items: center;\n}\n#ts-app button.ts-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 0.4rem;\n  padding: 0.6rem 1.4rem;\n  border: none;\n  border-radius: 8px;\n  font-size: 0.95rem;\n  font-weight: 700;\n  font-family: inherit;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s, opacity 0.15s;\n  white-space: nowrap;\n}\n#ts-app button.ts-btn:active { transform: scale(0.96); }\n#ts-app button.ts-btn:disabled { opacity: 0.4; cursor: not-allowed; }\n#ts-app button#ts-play   { background: #6366f1; color: #fff; }\n#ts-app button#ts-play:hover:not(:disabled)   { background: #4f46e5; }\n#ts-app button#ts-pause  { background: #f59e0b; color: #fff; }\n#ts-app button#ts-pause:hover:not(:disabled)  { background: #d97706; }\n#ts-app button#ts-stop   { background: #ef4444; color: #fff; }\n#ts-app button#ts-stop:hover:not(:disabled)   { background: #dc2626; }\n#ts-app .ts-status-bar {\n  display: flex;\n  align-items: center;\n  gap: 0.6rem;\n  font-size: 0.88rem;\n  color: #64748b;\n  padding: 0.55rem 0 0;\n}\n#ts-app .ts-dot {\n  width: 9px;\n  height: 9px;\n  border-radius: 50%;\n  background: #cbd5e1;\n  flex-shrink: 0;\n  transition: background 0.2s;\n}\n#ts-app .ts-dot.playing { background: #22c55e; animation: ts-pulse 1s infinite; }\n#ts-app .ts-dot.paused  { background: #f59e0b; }\n#ts-app .ts-dot.stopped { background: #ef4444; }\n@keyframes ts-pulse {\n  0%, 100% { opacity: 1; }\n  50%       { opacity: 0.35; }\n}\n#ts-app .ts-highlight-box {\n  background: #f0f4ff;\n  border-left: 3px solid #6366f1;\n  border-radius: 0 8px 8px 0;\n  padding: 0.7rem 1rem;\n  font-size: 0.9rem;\n  color: #3730a3;\n  min-height: 2.6rem;\n  line-height: 1.65;\n  word-break: break-word;\n  overflow-y: auto;\n  max-height: 180px;\n}\n#ts-app .ts-highlight-box .ts-word-current {\n  background: #6366f1;\n  color: #fff;\n  border-radius: 3px;\n  padding: 1px 4px;\n}\n#ts-app .ts-no-support {\n  background: #fef9c3;\n  border: 1px solid #fde047;\n  border-radius: 8px;\n  padding: 1rem 1.25rem;\n  font-size: 0.9rem;\n  color: #713f12;\n  margin-bottom: 1rem;\n}\n\u003c/style\u003e\n\u003c!-- Text input --\u003e\n\u003cdiv class=\"ts-card\"\u003e\n  \u003ch2\u003eEnter Your Text\u003c/h2\u003e\n  \u003ctextarea id=\"ts-text\" placeholder=\"Paste or type any text here and press Play to hear it read aloud...\"\u003eThe Web Speech API lets browsers convert text to speech entirely on-device, with no data sent to any server. Try adjusting the voice, speed, pitch, and volume below to find the combination that works best for you.\u003c/textarea\u003e\n  \u003cdiv class=\"ts-meta-row\"\u003e\n    \u003cspan id=\"ts-char-count\"\u003e0 characters\u003c/span\u003e\n    \u003cspan id=\"ts-read-time\"\u003e~0 min read\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Voice selector --\u003e\n\u003cdiv class=\"ts-card\"\u003e\n  \u003ch2\u003eVoice \u0026amp; Language\u003c/h2\u003e\n  \u003cdiv class=\"ts-controls-grid\"\u003e\n    \u003cdiv\u003e\n      \u003clabel class=\"ts-label\" for=\"ts-lang-filter\"\u003eFilter by Language\u003c/label\u003e\n      \u003cselect class=\"ts-select\" id=\"ts-lang-filter\"\u003e\n        \u003coption value=\"\"\u003eAll Languages\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003clabel class=\"ts-label\" for=\"ts-voice\"\u003eVoice\u003c/label\u003e\n      \u003cselect class=\"ts-select\" id=\"ts-voice\"\u003e\n        \u003coption value=\"\"\u003eLoading voices\u0026hellip;\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Playback settings --\u003e\n\u003cdiv class=\"ts-card\"\u003e\n  \u003ch2\u003ePlayback Settings\u003c/h2\u003e\n  \u003cdiv class=\"ts-controls-grid\"\u003e\n    \u003cdiv class=\"ts-slider-wrap\"\u003e\n      \u003clabel class=\"ts-label\" for=\"ts-rate\"\u003eSpeed\u003c/label\u003e\n      \u003cdiv class=\"ts-slider-row\"\u003e\n        \u003cinput type=\"range\" class=\"ts-slider\" id=\"ts-rate\" min=\"0.5\" max=\"2\" step=\"0.05\" value=\"1\"\u003e\n        \u003cspan class=\"ts-slider-val\" id=\"ts-rate-val\"\u003e1.00x\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ts-slider-wrap\"\u003e\n      \u003clabel class=\"ts-label\" for=\"ts-pitch\"\u003ePitch\u003c/label\u003e\n      \u003cdiv class=\"ts-slider-row\"\u003e\n        \u003cinput type=\"range\" class=\"ts-slider\" id=\"ts-pitch\" min=\"0.5\" max=\"2\" step=\"0.05\" value=\"1\"\u003e\n        \u003cspan class=\"ts-slider-val\" id=\"ts-pitch-val\"\u003e1.00\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ts-slider-wrap\"\u003e\n      \u003clabel class=\"ts-label\" for=\"ts-volume\"\u003eVolume\u003c/label\u003e\n      \u003cdiv class=\"ts-slider-row\"\u003e\n        \u003cinput type=\"range\" class=\"ts-slider\" id=\"ts-volume\" min=\"0\" max=\"1\" step=\"0.01\" value=\"1\"\u003e\n        \u003cspan class=\"ts-slider-val\" id=\"ts-volume-val\"\u003e100%\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Controls --\u003e\n\u003cdiv class=\"ts-card\"\u003e\n  \u003cdiv class=\"ts-btn-row\"\u003e\n    \u003cbutton class=\"ts-btn\" id=\"ts-play\"\u003e\u0026#9654; Play\u003c/button\u003e\n    \u003cbutton class=\"ts-btn\" id=\"ts-pause\" disabled\u003e\u0026#9646;\u0026#9646; Pause\u003c/button\u003e\n    \u003cbutton class=\"ts-btn\" id=\"ts-stop\" disabled\u003e\u0026#9632; Stop\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ts-status-bar\"\u003e\n    \u003cdiv class=\"ts-dot\" id=\"ts-dot\"\u003e\u003c/div\u003e\n    \u003cspan id=\"ts-status-text\"\u003eReady\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Word highlight --\u003e\n\u003cdiv class=\"ts-card\"\u003e\n  \u003ch2\u003eCurrent Word\u003c/h2\u003e\n  \u003cdiv class=\"ts-highlight-box\" id=\"ts-highlight-box\"\u003e\n    \u003cspan style=\"color:#94a3b8;\"\u003eSpoken words will appear highlighted here during playback.\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- No support notice --\u003e\n\u003cdiv id=\"ts-no-support\" class=\"ts-no-support\" style=\"display:none;\"\u003e\n  Your browser does not support the Web Speech API. Please use a modern browser such as Chrome, Edge, or Safari.\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  'use strict';\n\n  if (!('speechSynthesis' in window)) {\n    document.getElementById('ts-no-support').style.display = 'block';\n    ['ts-play','ts-pause','ts-stop'].forEach(function(id){\n      var el = document.getElementById(id);\n      if (el) el.disabled = true;\n    });\n    return;\n  }\n\n  var synth   = window.speechSynthesis;\n  var allVoices = [];\n  var currentUtt = null;\n  var tsState = 'stopped'; // 'stopped' | 'playing' | 'paused'\n\n  var elText      = document.getElementById('ts-text');\n  var elLangFilter= document.getElementById('ts-lang-filter');\n  var elVoice     = document.getElementById('ts-voice');\n  var elRate      = document.getElementById('ts-rate');\n  var elPitch     = document.getElementById('ts-pitch');\n  var elVolume    = document.getElementById('ts-volume');\n  var elRateVal   = document.getElementById('ts-rate-val');\n  var elPitchVal  = document.getElementById('ts-pitch-val');\n  var elVolVal    = document.getElementById('ts-volume-val');\n  var elPlay      = document.getElementById('ts-play');\n  var elPause     = document.getElementById('ts-pause');\n  var elStop      = document.getElementById('ts-stop');\n  var elDot       = document.getElementById('ts-dot');\n  var elStatus    = document.getElementById('ts-status-text');\n  var elHighlight = document.getElementById('ts-highlight-box');\n  var elCharCount = document.getElementById('ts-char-count');\n  var elReadTime  = document.getElementById('ts-read-time');\n\n  // --- Character / read-time counters ---\n  function updateMeta() {\n    var txt   = elText.value;\n    var chars = txt.length;\n    elCharCount.textContent = chars.toLocaleString() + ' character' + (chars === 1 ? '' : 's');\n    var words = txt.trim() === '' ? 0 : txt.trim().split(/\\s+/).length;\n    var mins  = Math.max(1, Math.ceil(words / 200));\n    elReadTime.textContent = '~' + mins + ' min read';\n  }\n  elText.addEventListener('input', updateMeta);\n  updateMeta();\n\n  // --- Slider display ---\n  elRate.addEventListener('input', function () {\n    elRateVal.textContent = parseFloat(elRate.value).toFixed(2) + 'x';\n  });\n  elPitch.addEventListener('input', function () {\n    elPitchVal.textContent = parseFloat(elPitch.value).toFixed(2);\n  });\n  elVolume.addEventListener('input', function () {\n    elVolVal.textContent = Math.round(parseFloat(elVolume.value) * 100) + '%';\n  });\n\n  // --- Voice loading ---\n  function populateLangFilter(voices) {\n    var langs = [];\n    voices.forEach(function (v) {\n      if (langs.indexOf(v.lang) === -1) langs.push(v.lang);\n    });\n    langs.sort();\n    elLangFilter.innerHTML = '\u003coption value=\"\"\u003eAll Languages\u003c/option\u003e';\n    langs.forEach(function (l) {\n      var opt = document.createElement('option');\n      opt.value = l;\n      opt.textContent = l;\n      elLangFilter.appendChild(opt);\n    });\n  }\n\n  function populateVoices(filterLang) {\n    var voices = filterLang\n      ? allVoices.filter(function (v) { return v.lang === filterLang; })\n      : allVoices;\n    elVoice.innerHTML = '';\n    if (voices.length === 0) {\n      elVoice.innerHTML = '\u003coption value=\"\"\u003eNo voices for this language\u003c/option\u003e';\n      return;\n    }\n    voices.forEach(function (v) {\n      var opt = document.createElement('option');\n      opt.value = v.name;\n      opt.textContent = v.name + ' (' + v.lang + ')' + (v.default ? ' \\u2605' : '');\n      elVoice.appendChild(opt);\n    });\n  }\n\n  function loadVoices() {\n    var voices = synth.getVoices();\n    if (voices.length === 0) return;\n    allVoices = voices;\n    populateLangFilter(voices);\n    populateVoices(elLangFilter.value);\n  }\n\n  synth.onvoiceschanged = loadVoices;\n  loadVoices();\n  setTimeout(loadVoices, 600);\n\n  elLangFilter.addEventListener('change', function () {\n    populateVoices(elLangFilter.value);\n  });\n\n  // --- UI state ---\n  function setUIState(state) {\n    tsState = state;\n    elDot.className = 'ts-dot ' + state;\n    if (state === 'playing') {\n      elStatus.textContent    = 'Playing\\u2026';\n      elPlay.disabled         = true;\n      elPause.disabled        = false;\n      elStop.disabled         = false;\n      elPause.innerHTML       = '\\u23ae\\u23ae Pause';\n    } else if (state === 'paused') {\n      elStatus.textContent    = 'Paused';\n      elPlay.disabled         = true;\n      elPause.disabled        = false;\n      elStop.disabled         = false;\n      elPause.innerHTML       = '\\u25b6 Resume';\n    } else {\n      elStatus.textContent    = 'Ready';\n      elPlay.disabled         = false;\n      elPause.disabled        = true;\n      elStop.disabled         = true;\n      elPause.innerHTML       = '\\u23ae\\u23ae Pause';\n      elHighlight.innerHTML   = '\u003cspan style=\"color:#94a3b8;\"\u003eSpoken words will appear highlighted here during playback.\u003c/span\u003e';\n    }\n  }\n\n  // --- Word highlighting ---\n  function escHtml(str) {\n    return str\n      .replace(/\u0026/g, '\u0026amp;')\n      .replace(/\u003c/g, '\u0026lt;')\n      .replace(/\u003e/g, '\u0026gt;');\n  }\n\n  function highlightWord(charIndex, charLength) {\n    var full   = elText.value;\n    var before = full.substring(0, charIndex);\n    var word   = full.substring(charIndex, charIndex + charLength);\n    var after  = full.substring(charIndex + charLength);\n    elHighlight.innerHTML =\n      escHtml(before) +\n      '\u003cspan class=\"ts-word-current\"\u003e' + escHtml(word) + '\u003c/span\u003e' +\n      escHtml(after);\n    var span = elHighlight.querySelector('.ts-word-current');\n    if (span) span.scrollIntoView({ block: 'nearest', behavior: 'smooth' });\n  }\n\n  // --- Play ---\n  elPlay.addEventListener('click', function () {\n    var text = elText.value.trim();\n    if (!text) {\n      elStatus.textContent = 'Please enter some text first.';\n      return;\n    }\n\n    synth.cancel();\n    currentUtt = new SpeechSynthesisUtterance(text);\n\n    var selectedName = elVoice.value;\n    var voice = allVoices.find(function (v) { return v.name === selectedName; });\n    if (voice) currentUtt.voice = voice;\n\n    currentUtt.rate   = parseFloat(elRate.value);\n    currentUtt.pitch  = parseFloat(elPitch.value);\n    currentUtt.volume = parseFloat(elVolume.value);\n\n    currentUtt.onstart = function ()  { setUIState('playing'); };\n    currentUtt.onend   = function ()  { setUIState('stopped'); };\n    currentUtt.onerror = function (e) {\n      if (e.error !== 'interrupted') {\n        elStatus.textContent = 'Error: ' + e.error;\n        setUIState('stopped');\n      }\n    };\n    currentUtt.onboundary = function (e) {\n      if (e.name === 'word') {\n        highlightWord(e.charIndex, e.charLength || 1);\n      }\n    };\n\n    synth.speak(currentUtt);\n    setUIState('playing');\n  });\n\n  // --- Pause / Resume ---\n  elPause.addEventListener('click', function () {\n    if (tsState === 'playing') {\n      synth.pause();\n      setUIState('paused');\n    } else if (tsState === 'paused') {\n      synth.resume();\n      setUIState('playing');\n    }\n  });\n\n  // --- Stop ---\n  elStop.addEventListener('click', function () {\n    synth.cancel();\n    setUIState('stopped');\n  });\n\n  // Init\n  setUIState('stopped');\n}());\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-to-use\"\u003eHow to Use\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003ePaste or type\u003c/strong\u003e your text into the text area above.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eChoose a voice\u003c/strong\u003e — use the language filter to narrow down voices available in your browser.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAdjust speed, pitch, and volume\u003c/strong\u003e using the sliders (speed: 0.5x–2x).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ePress Play\u003c/strong\u003e and watch the current word highlight in real time.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ePause\u003c/strong\u003e to temporarily stop, or \u003cstrong\u003eStop\u003c/strong\u003e to cancel playback entirely.\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"about-this-tool\"\u003eAbout This Tool\u003c/h2\u003e\n\u003cp\u003eThis tool uses your browser\u0026rsquo;s built-in \u003cstrong\u003eWeb Speech API\u003c/strong\u003e (\u003ccode\u003espeechSynthesis\u003c/code\u003e). No audio is sent to any server — everything happens locally on your device. Available voices depend on your operating system and browser.\u003c/p\u003e","title":"Text to Speech"},{"content":" 24h 12h Your local time is shown with a star Popular cities: New York London Tokyo Sydney Dubai Paris Singapore Los Angeles -- Select a timezone -- + Add Clock Add clocks using the presets or selector above. Convert timezones → Timezone Converter World clock → World Clock ","permalink":"https://productivity-works.com/tools/timezone-clock/","summary":"\u003cdiv id=\"tzc-app\"\u003e\n\u003cstyle\u003e\n#tzc-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 720px;\n  margin: 0 auto;\n  color: #1e293b;\n  box-sizing: border-box;\n}\n#tzc-app *, #tzc-app *::before, #tzc-app *::after {\n  box-sizing: border-box;\n}\n#tzc-app h2 {\n  font-size: 1.1rem;\n  font-weight: 700;\n  margin: 0 0 12px 0;\n  color: #0f172a;\n}\n#tzc-app .tzc-topbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  align-items: center;\n  margin-bottom: 16px;\n}\n#tzc-app .tzc-toggle {\n  display: flex;\n  background: #f1f5f9;\n  border-radius: 8px;\n  padding: 3px;\n  gap: 2px;\n}\n#tzc-app .tzc-toggle button {\n  padding: 5px 14px;\n  border: none;\n  background: transparent;\n  border-radius: 6px;\n  font-size: 13px;\n  font-weight: 600;\n  color: #64748b;\n  cursor: pointer;\n  transition: background 0.15s, color 0.15s;\n}\n#tzc-app .tzc-toggle button.active {\n  background: #fff;\n  color: #1e293b;\n  box-shadow: 0 1px 3px rgba(0,0,0,0.1);\n}\n#tzc-app .tzc-presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 6px;\n  margin-bottom: 14px;\n}\n#tzc-app .tzc-preset-btn {\n  padding: 5px 12px;\n  background: #e0f2fe;\n  color: #0369a1;\n  border: 1.5px solid #bae6fd;\n  border-radius: 20px;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n#tzc-app .tzc-preset-btn:hover {\n  background: #bae6fd;\n}\n#tzc-app .tzc-add-row {\n  display: flex;\n  gap: 8px;\n  margin-bottom: 18px;\n  flex-wrap: wrap;\n}\n#tzc-app .tzc-add-row select {\n  flex: 1;\n  min-width: 200px;\n  padding: 9px 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 14px;\n  color: #1e293b;\n  background: #f8fafc;\n}\n#tzc-app .tzc-add-row button {\n  padding: 9px 18px;\n  background: #6366f1;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 14px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n#tzc-app .tzc-add-row button:hover {\n  background: #4f46e5;\n}\n#tzc-app .tzc-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));\n  gap: 14px;\n}\n#tzc-app .tzc-card {\n  background: #fff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 16px 16px 14px;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.05);\n  position: relative;\n  transition: border-color 0.2s;\n}\n#tzc-app .tzc-card.day-card {\n  border-color: #fde68a;\n  background: linear-gradient(135deg, #fffbeb 0%, #fef9c3 100%);\n}\n#tzc-app .tzc-card.night-card {\n  border-color: #818cf8;\n  background: linear-gradient(135deg, #eef2ff 0%, #e0e7ff 100%);\n}\n#tzc-app .tzc-card-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 8px;\n}\n#tzc-app .tzc-city-name {\n  font-size: 14px;\n  font-weight: 700;\n  color: #0f172a;\n}\n#tzc-app .tzc-dn-icon {\n  font-size: 18px;\n  line-height: 1;\n}\n#tzc-app .tzc-time {\n  font-size: 2rem;\n  font-weight: 800;\n  color: #1e293b;\n  letter-spacing: -1px;\n  line-height: 1.1;\n  margin-bottom: 2px;\n}\n#tzc-app .tzc-date {\n  font-size: 12px;\n  color: #64748b;\n  margin-bottom: 6px;\n}\n#tzc-app .tzc-diff {\n  display: inline-block;\n  font-size: 11px;\n  font-weight: 600;\n  padding: 2px 8px;\n  border-radius: 20px;\n  background: #f1f5f9;\n  color: #475569;\n}\n#tzc-app .tzc-remove {\n  position: absolute;\n  top: 8px;\n  right: 8px;\n  width: 22px;\n  height: 22px;\n  background: #fee2e2;\n  color: #dc2626;\n  border: none;\n  border-radius: 50%;\n  font-size: 14px;\n  line-height: 22px;\n  text-align: center;\n  cursor: pointer;\n  display: none;\n  font-weight: 700;\n  padding: 0;\n}\n#tzc-app .tzc-card:hover .tzc-remove {\n  display: block;\n}\n#tzc-app .tzc-local-note {\n  font-size: 11px;\n  color: #94a3b8;\n  margin-top: 2px;\n}\n#tzc-app .tzc-empty {\n  text-align: center;\n  color: #94a3b8;\n  padding: 30px 0;\n  font-size: 14px;\n}\n@media (max-width: 480px) {\n  #tzc-app .tzc-time { font-size: 1.6rem; }\n  #tzc-app .tzc-grid { grid-template-columns: 1fr 1fr; }\n}\n\u003c/style\u003e\n\u003cdiv class=\"tzc-topbar\"\u003e\n  \u003cdiv class=\"tzc-toggle\"\u003e\n    \u003cbutton id=\"tzc-btn-24\" class=\"active\" onclick=\"tzcSetFormat(24)\"\u003e24h\u003c/button\u003e\n    \u003cbutton id=\"tzc-btn-12\" onclick=\"tzcSetFormat(12)\"\u003e12h\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cspan style=\"font-size:13px;color:#64748b;\"\u003eYour local time is shown with a star\u003c/span\u003e\n\u003c/div\u003e\n\u003cdiv style=\"margin-bottom:8px;font-size:13px;font-weight:600;color:#475569;\"\u003ePopular cities:\u003c/div\u003e\n\u003cdiv class=\"tzc-presets\"\u003e\n  \u003cbutton class=\"tzc-preset-btn\" onclick=\"tzcAddPreset('New York','America/New_York')\"\u003eNew York\u003c/button\u003e\n  \u003cbutton class=\"tzc-preset-btn\" onclick=\"tzcAddPreset('London','Europe/London')\"\u003eLondon\u003c/button\u003e\n  \u003cbutton class=\"tzc-preset-btn\" onclick=\"tzcAddPreset('Tokyo','Asia/Tokyo')\"\u003eTokyo\u003c/button\u003e\n  \u003cbutton class=\"tzc-preset-btn\" onclick=\"tzcAddPreset('Sydney','Australia/Sydney')\"\u003eSydney\u003c/button\u003e\n  \u003cbutton class=\"tzc-preset-btn\" onclick=\"tzcAddPreset('Dubai','Asia/Dubai')\"\u003eDubai\u003c/button\u003e\n  \u003cbutton class=\"tzc-preset-btn\" onclick=\"tzcAddPreset('Paris','Europe/Paris')\"\u003eParis\u003c/button\u003e\n  \u003cbutton class=\"tzc-preset-btn\" onclick=\"tzcAddPreset('Singapore','Asia/Singapore')\"\u003eSingapore\u003c/button\u003e\n  \u003cbutton class=\"tzc-preset-btn\" onclick=\"tzcAddPreset('Los Angeles','America/Los_Angeles')\"\u003eLos Angeles\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"tzc-add-row\"\u003e\n  \u003cselect id=\"tzc-tz-select\"\u003e\n    \u003coption value=\"\"\u003e-- Select a timezone --\u003c/option\u003e\n  \u003c/select\u003e\n  \u003cbutton onclick=\"tzcAddFromSelect()\"\u003e+ Add Clock\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"tzc-grid\" id=\"tzc-grid\"\u003e\n  \u003cdiv class=\"tzc-empty\" id=\"tzc-empty\"\u003eAdd clocks using the presets or selector above.\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n  const TZC_ZONES = [\n    [\"Africa/Abidjan\",\"Africa/Abidjan\"],[\"Africa/Accra\",\"Africa/Accra\"],[\"Africa/Addis_Ababa\",\"Africa/Addis Ababa\"],\n    [\"Africa/Algiers\",\"Africa/Algiers\"],[\"Africa/Cairo\",\"Africa/Cairo\"],[\"Africa/Casablanca\",\"Africa/Casablanca\"],\n    [\"Africa/Johannesburg\",\"Africa/Johannesburg\"],[\"Africa/Lagos\",\"Africa/Lagos\"],[\"Africa/Nairobi\",\"Africa/Nairobi\"],\n    [\"America/Anchorage\",\"America/Anchorage\"],[\"America/Bogota\",\"America/Bogota\"],[\"America/Buenos_Aires\",\"America/Buenos Aires\"],\n    [\"America/Chicago\",\"America/Chicago\"],[\"America/Denver\",\"America/Denver\"],[\"America/Los_Angeles\",\"America/Los Angeles\"],\n    [\"America/Mexico_City\",\"America/Mexico City\"],[\"America/New_York\",\"America/New York\"],[\"America/Sao_Paulo\",\"America/Sao Paulo\"],\n    [\"America/Toronto\",\"America/Toronto\"],[\"America/Vancouver\",\"America/Vancouver\"],\n    [\"Asia/Bangkok\",\"Asia/Bangkok\"],[\"Asia/Colombo\",\"Asia/Colombo\"],[\"Asia/Dubai\",\"Asia/Dubai\"],\n    [\"Asia/Hong_Kong\",\"Asia/Hong Kong\"],[\"Asia/Jakarta\",\"Asia/Jakarta\"],[\"Asia/Karachi\",\"Asia/Karachi\"],\n    [\"Asia/Kolkata\",\"Asia/Kolkata\"],[\"Asia/Kuala_Lumpur\",\"Asia/Kuala Lumpur\"],[\"Asia/Manila\",\"Asia/Manila\"],\n    [\"Asia/Seoul\",\"Asia/Seoul\"],[\"Asia/Shanghai\",\"Asia/Shanghai\"],[\"Asia/Singapore\",\"Asia/Singapore\"],\n    [\"Asia/Taipei\",\"Asia/Taipei\"],[\"Asia/Tehran\",\"Asia/Tehran\"],[\"Asia/Tokyo\",\"Asia/Tokyo\"],\n    [\"Atlantic/Azores\",\"Atlantic/Azores\"],[\"Australia/Adelaide\",\"Australia/Adelaide\"],[\"Australia/Brisbane\",\"Australia/Brisbane\"],\n    [\"Australia/Melbourne\",\"Australia/Melbourne\"],[\"Australia/Perth\",\"Australia/Perth\"],[\"Australia/Sydney\",\"Australia/Sydney\"],\n    [\"Europe/Amsterdam\",\"Europe/Amsterdam\"],[\"Europe/Athens\",\"Europe/Athens\"],[\"Europe/Berlin\",\"Europe/Berlin\"],\n    [\"Europe/Brussels\",\"Europe/Brussels\"],[\"Europe/Budapest\",\"Europe/Budapest\"],[\"Europe/Copenhagen\",\"Europe/Copenhagen\"],\n    [\"Europe/Dublin\",\"Europe/Dublin\"],[\"Europe/Helsinki\",\"Europe/Helsinki\"],[\"Europe/Istanbul\",\"Europe/Istanbul\"],\n    [\"Europe/Lisbon\",\"Europe/Lisbon\"],[\"Europe/London\",\"Europe/London\"],[\"Europe/Madrid\",\"Europe/Madrid\"],\n    [\"Europe/Moscow\",\"Europe/Moscow\"],[\"Europe/Oslo\",\"Europe/Oslo\"],[\"Europe/Paris\",\"Europe/Paris\"],\n    [\"Europe/Prague\",\"Europe/Prague\"],[\"Europe/Rome\",\"Europe/Rome\"],[\"Europe/Stockholm\",\"Europe/Stockholm\"],\n    [\"Europe/Warsaw\",\"Europe/Warsaw\"],[\"Europe/Zurich\",\"Europe/Zurich\"],\n    [\"Pacific/Auckland\",\"Pacific/Auckland\"],[\"Pacific/Fiji\",\"Pacific/Fiji\"],[\"Pacific/Honolulu\",\"Pacific/Honolulu\"],\n    [\"Pacific/Midway\",\"Pacific/Midway\"],[\"UTC\",\"UTC\"]\n  ];\n\n  let tzcClocks = [];\n  let tzcFormat = 24;\n  let tzcTimer = null;\n  const localTZ = Intl.DateTimeFormat().resolvedOptions().timeZone;\n\n  // Populate select\n  const sel = document.getElementById('tzc-tz-select');\n  TZC_ZONES.forEach(([tz, label]) =\u003e {\n    const opt = document.createElement('option');\n    opt.value = tz;\n    opt.textContent = label;\n    sel.appendChild(opt);\n  });\n\n  window.tzcSetFormat = function(f) {\n    tzcFormat = f;\n    document.getElementById('tzc-btn-24').classList.toggle('active', f === 24);\n    document.getElementById('tzc-btn-12').classList.toggle('active', f === 12);\n    tzcRender();\n  };\n\n  window.tzcAddPreset = function(city, tz) {\n    if (tzcClocks.find(c =\u003e c.tz === tz)) return;\n    tzcClocks.push({city, tz});\n    tzcRender();\n  };\n\n  window.tzcAddFromSelect = function() {\n    const s = document.getElementById('tzc-tz-select');\n    if (!s.value) return;\n    const tz = s.value;\n    const city = s.options[s.selectedIndex].text;\n    if (tzcClocks.find(c =\u003e c.tz === tz)) return;\n    tzcClocks.push({city, tz});\n    s.value = '';\n    tzcRender();\n  };\n\n  window.tzcRemove = function(tz) {\n    tzcClocks = tzcClocks.filter(c =\u003e c.tz !== tz);\n    tzcRender();\n  };\n\n  function tzcGetLocalOffset() {\n    const now = new Date();\n    return -now.getTimezoneOffset(); // minutes\n  }\n\n  function tzcGetZoneOffset(tz, now) {\n    try {\n      // Use Intl to get UTC offset for the zone\n      const utcStr = new Intl.DateTimeFormat('en-US', {timeZone:'UTC', hour:'2-digit', minute:'2-digit', hour12:false}).format(now);\n      const tzStr = new Intl.DateTimeFormat('en-US', {timeZone:tz, hour:'2-digit', minute:'2-digit', hour12:false}).format(now);\n      const [uh,um] = utcStr.split(':').map(Number);\n      const [th,tm] = tzStr.split(':').map(Number);\n      return (th - uh) * 60 + (tm - um);\n    } catch(e) { return 0; }\n  }\n\n  function tzcFormatDiff(diffMin) {\n    if (diffMin === 0) return 'Same time as you';\n    const sign = diffMin \u003e 0 ? '+' : '-';\n    const abs = Math.abs(diffMin);\n    const h = Math.floor(abs / 60);\n    const m = abs % 60;\n    return sign + h + (m ? ':' + String(m).padStart(2,'0') : '') + 'h from you';\n  }\n\n  function tzcIsDay(hour) {\n    return hour \u003e= 6 \u0026\u0026 hour \u003c 20;\n  }\n\n  function tzcRender() {\n    const grid = document.getElementById('tzc-grid');\n    const empty = document.getElementById('tzc-empty');\n    if (tzcClocks.length === 0) {\n      grid.innerHTML = '\u003cdiv class=\"tzc-empty\" id=\"tzc-empty\"\u003eAdd clocks using the presets or selector above.\u003c/div\u003e';\n      return;\n    }\n    const now = new Date();\n    const localOffMin = tzcGetLocalOffset();\n\n    grid.innerHTML = tzcClocks.map(({city, tz}) =\u003e {\n      const isLocal = tz === localTZ;\n      let timeStr, dateStr, hourNum;\n      try {\n        const opts12 = {timeZone:tz, hour:'numeric', minute:'2-digit', second:'2-digit', hour12:true};\n        const opts24 = {timeZone:tz, hour:'2-digit', minute:'2-digit', second:'2-digit', hour12:false};\n        timeStr = new Intl.DateTimeFormat('en-US', tzcFormat===12 ? opts12 : opts24).format(now);\n        dateStr = new Intl.DateTimeFormat('en-US', {timeZone:tz, weekday:'short', month:'short', day:'numeric'}).format(now);\n        const h = parseInt(new Intl.DateTimeFormat('en-US', {timeZone:tz, hour:'2-digit', hour12:false}).format(now));\n        hourNum = h;\n      } catch(e) {\n        timeStr = '--:--';\n        dateStr = '';\n        hourNum = 12;\n      }\n      const day = tzcIsDay(hourNum);\n      const dnIcon = day ? '☀️' : '🌙';\n      const cardClass = day ? 'tzc-card day-card' : 'tzc-card night-card';\n      const tzOff = tzcGetZoneOffset(tz, now);\n      const diffMin = tzOff - localOffMin;\n      const diffStr = isLocal ? '⭐ Your local time' : tzcFormatDiff(diffMin);\n\n      return `\u003cdiv class=\"${cardClass}\"\u003e\n        \u003cbutton class=\"tzc-remove\" onclick=\"tzcRemove('${tz.replace(/'/g,\"\\\\'\")}')\"\u003e\u0026#x2715;\u003c/button\u003e\n        \u003cdiv class=\"tzc-card-header\"\u003e\n          \u003cdiv class=\"tzc-city-name\"\u003e${city}${isLocal ? ' ⭐' : ''}\u003c/div\u003e\n          \u003cdiv class=\"tzc-dn-icon\"\u003e${dnIcon}\u003c/div\u003e\n        \u003c/div\u003e\n        \u003cdiv class=\"tzc-time\"\u003e${timeStr}\u003c/div\u003e\n        \u003cdiv class=\"tzc-date\"\u003e${dateStr}\u003c/div\u003e\n        \u003cdiv class=\"tzc-diff\"\u003e${diffStr}\u003c/div\u003e\n      \u003c/div\u003e`;\n    }).join('');\n  }\n\n  function tzcTick() {\n    tzcRender();\n    tzcTimer = setTimeout(tzcTick, 1000 - (Date.now() % 1000));\n  }\n\n  // Start with local time + common presets\n  tzcAddPreset('Your Local Time', localTZ);\n  tzcAddPreset('New York', 'America/New_York');\n  tzcAddPreset('London', 'Europe/London');\n  tzcAddPreset('Tokyo', 'Asia/Tokyo');\n  tzcTick();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003cblockquote\u003e\n\u003cp\u003eConvert timezones → \u003ca href=\"https://productivity-works.com/tools/timezone-converter/\"\u003eTimezone Converter\u003c/a\u003e\n\u003c/p\u003e","title":"Timezone Clock Dashboard"},{"content":" Converter World Clock Meeting Planner Convert Date \u0026 Time Date \u0026 Time From Timezone ⇄ To Timezone Converted Time — Live World Clock — — — — Compare Multiple Timezones Add Timezone + Add Meeting Planner — Business Hours Overlap Shows when 9am–5pm business hours overlap across your selected timezones. Add timezones in the World Clock tab first.\nNight Day (off-hours) Business hours All overlap Related: Plan your productivity with our Pomodoro Timer ","permalink":"https://productivity-works.com/tools/timezone-converter/","summary":"\u003cdiv id=\"tz-app\"\u003e\n\u003cstyle\u003e\n  #tz-app {\n    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, sans-serif;\n    max-width: 780px;\n    margin: 0 auto;\n    padding: 16px;\n    color: #1e1b4b;\n  }\n\n  #tz-app * { box-sizing: border-box; }\n\n  #tz-app h2 {\n    font-size: 1.1rem;\n    font-weight: 700;\n    color: #4f46e5;\n    margin: 0 0 12px;\n    padding-bottom: 6px;\n    border-bottom: 2px solid #e0e7ff;\n  }\n\n  /* Mode tabs */\n  #tz-app .tz-tabs {\n    display: flex;\n    gap: 8px;\n    margin-bottom: 24px;\n    flex-wrap: wrap;\n  }\n  #tz-app .tz-tab {\n    padding: 8px 20px;\n    border: 2px solid #4f46e5;\n    border-radius: 24px;\n    background: transparent;\n    color: #4f46e5;\n    font-size: 14px;\n    font-weight: 600;\n    cursor: pointer;\n    transition: all 0.2s;\n  }\n  #tz-app .tz-tab.active {\n    background: #4f46e5;\n    color: #fff;\n  }\n  #tz-app .tz-tab:hover:not(.active) {\n    background: #eef2ff;\n  }\n\n  /* Cards */\n  #tz-app .tz-card {\n    background: #fff;\n    border: 1px solid #e0e7ff;\n    border-radius: 12px;\n    padding: 20px;\n    margin-bottom: 20px;\n    box-shadow: 0 1px 4px rgba(79,70,229,0.07);\n  }\n\n  /* Live clock section */\n  #tz-app .tz-live-grid {\n    display: grid;\n    grid-template-columns: 1fr 1fr;\n    gap: 16px;\n    margin-bottom: 12px;\n  }\n  @media (max-width: 500px) {\n    #tz-app .tz-live-grid { grid-template-columns: 1fr; }\n  }\n  #tz-app .tz-clock-box {\n    background: #f5f3ff;\n    border-radius: 10px;\n    padding: 16px;\n    text-align: center;\n  }\n  #tz-app .tz-clock-label {\n    font-size: 12px;\n    font-weight: 600;\n    color: #6d6fae;\n    margin-bottom: 4px;\n    text-transform: uppercase;\n    letter-spacing: 0.05em;\n  }\n  #tz-app .tz-clock-time {\n    font-size: 2rem;\n    font-weight: 700;\n    color: #4f46e5;\n    font-variant-numeric: tabular-nums;\n    letter-spacing: -0.02em;\n  }\n  #tz-app .tz-clock-date {\n    font-size: 13px;\n    color: #6b7280;\n    margin-top: 2px;\n  }\n\n  /* Select */\n  #tz-app select {\n    width: 100%;\n    padding: 9px 12px;\n    border: 1.5px solid #c7d2fe;\n    border-radius: 8px;\n    font-size: 14px;\n    color: #1e1b4b;\n    background: #fff;\n    cursor: pointer;\n    outline: none;\n    transition: border-color 0.2s;\n    appearance: none;\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%234f46e5' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\");\n    background-repeat: no-repeat;\n    background-position: right 10px center;\n    padding-right: 36px;\n  }\n  #tz-app select:focus { border-color: #4f46e5; }\n\n  #tz-app label {\n    display: block;\n    font-size: 12px;\n    font-weight: 600;\n    color: #6d6fae;\n    margin-bottom: 4px;\n    text-transform: uppercase;\n    letter-spacing: 0.04em;\n  }\n\n  /* Converter row */\n  #tz-app .tz-conv-row {\n    display: grid;\n    grid-template-columns: 1fr auto 1fr;\n    gap: 12px;\n    align-items: end;\n    margin-bottom: 16px;\n  }\n  @media (max-width: 540px) {\n    #tz-app .tz-conv-row {\n      grid-template-columns: 1fr;\n    }\n    #tz-app .tz-swap-col { text-align: center; }\n  }\n\n  #tz-app .tz-swap-btn {\n    background: #4f46e5;\n    color: #fff;\n    border: none;\n    border-radius: 50%;\n    width: 38px;\n    height: 38px;\n    font-size: 18px;\n    cursor: pointer;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    transition: background 0.2s, transform 0.2s;\n    flex-shrink: 0;\n    margin: 0 auto;\n  }\n  #tz-app .tz-swap-btn:hover { background: #4338ca; transform: rotate(180deg); }\n\n  #tz-app input[type=\"datetime-local\"] {\n    width: 100%;\n    padding: 9px 12px;\n    border: 1.5px solid #c7d2fe;\n    border-radius: 8px;\n    font-size: 14px;\n    color: #1e1b4b;\n    outline: none;\n    transition: border-color 0.2s;\n  }\n  #tz-app input[type=\"datetime-local\"]:focus { border-color: #4f46e5; }\n\n  #tz-app .tz-result-box {\n    background: #f5f3ff;\n    border-radius: 10px;\n    padding: 14px 16px;\n    margin-top: 12px;\n    display: flex;\n    align-items: center;\n    gap: 12px;\n  }\n  #tz-app .tz-result-label { font-size: 12px; color: #6d6fae; font-weight: 600; }\n  #tz-app .tz-result-value { font-size: 1.3rem; font-weight: 700; color: #4f46e5; }\n  #tz-app .tz-result-offset { font-size: 12px; color: #9ca3af; margin-top: 2px; }\n\n  /* Multi-timezone comparison */\n  #tz-app .tz-multi-add-row {\n    display: flex;\n    gap: 10px;\n    align-items: flex-end;\n    margin-bottom: 16px;\n    flex-wrap: wrap;\n  }\n  #tz-app .tz-multi-add-row \u003e div { flex: 1; min-width: 200px; }\n  #tz-app .tz-add-btn {\n    background: #4f46e5;\n    color: #fff;\n    border: none;\n    border-radius: 8px;\n    padding: 9px 18px;\n    font-size: 14px;\n    font-weight: 600;\n    cursor: pointer;\n    transition: background 0.2s;\n    white-space: nowrap;\n  }\n  #tz-app .tz-add-btn:hover { background: #4338ca; }\n  #tz-app .tz-add-btn:disabled { background: #c7d2fe; cursor: not-allowed; }\n\n  #tz-app .tz-multi-list {\n    display: flex;\n    flex-direction: column;\n    gap: 10px;\n  }\n  #tz-app .tz-multi-item {\n    background: #fff;\n    border: 1.5px solid #e0e7ff;\n    border-radius: 10px;\n    padding: 12px 14px;\n    display: flex;\n    align-items: center;\n    gap: 12px;\n    flex-wrap: wrap;\n  }\n  #tz-app .tz-multi-item.is-day { border-color: #fbbf24; background: #fffbeb; }\n  #tz-app .tz-multi-item.is-night { border-color: #818cf8; background: #f5f3ff; }\n  #tz-app .tz-multi-name { font-weight: 600; font-size: 14px; flex: 1; min-width: 120px; }\n  #tz-app .tz-multi-time { font-size: 1.4rem; font-weight: 700; color: #4f46e5; font-variant-numeric: tabular-nums; }\n  #tz-app .tz-multi-date { font-size: 12px; color: #9ca3af; }\n  #tz-app .tz-multi-badge {\n    font-size: 11px;\n    padding: 2px 8px;\n    border-radius: 12px;\n    font-weight: 600;\n  }\n  #tz-app .badge-day { background: #fef3c7; color: #92400e; }\n  #tz-app .badge-night { background: #e0e7ff; color: #3730a3; }\n  #tz-app .tz-remove-btn {\n    background: none;\n    border: none;\n    color: #9ca3af;\n    font-size: 18px;\n    cursor: pointer;\n    line-height: 1;\n    padding: 0 4px;\n    transition: color 0.2s;\n  }\n  #tz-app .tz-remove-btn:hover { color: #ef4444; }\n\n  /* Timeline bar */\n  #tz-app .tz-timeline {\n    margin-top: 16px;\n  }\n  #tz-app .tz-tl-label {\n    font-size: 11px;\n    color: #9ca3af;\n    margin-bottom: 6px;\n    font-weight: 600;\n    text-transform: uppercase;\n  }\n  #tz-app .tz-tl-row {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    margin-bottom: 6px;\n  }\n  #tz-app .tz-tl-name {\n    font-size: 12px;\n    width: 90px;\n    flex-shrink: 0;\n    color: #4b5563;\n    font-weight: 500;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n  }\n  #tz-app .tz-tl-bar-wrap {\n    flex: 1;\n    height: 20px;\n    background: #1e1b4b;\n    border-radius: 4px;\n    position: relative;\n    overflow: hidden;\n  }\n  #tz-app .tz-tl-day-seg {\n    position: absolute;\n    top: 0;\n    height: 100%;\n    background: #fbbf24;\n    border-radius: 2px;\n  }\n  #tz-app .tz-tl-now-line {\n    position: absolute;\n    top: 0;\n    height: 100%;\n    width: 2px;\n    background: #ef4444;\n    border-radius: 1px;\n  }\n  #tz-app .tz-tl-hour {\n    font-size: 11px;\n    color: #9ca3af;\n    width: 90px;\n    flex-shrink: 0;\n  }\n  #tz-app .tz-tl-labels {\n    display: flex;\n    flex: 1;\n    justify-content: space-between;\n    font-size: 10px;\n    color: #9ca3af;\n    padding: 0 2px;\n    margin-top: 2px;\n  }\n\n  /* Meeting planner */\n  #tz-app .tz-meeting-grid {\n    overflow-x: auto;\n  }\n  #tz-app .tz-meeting-table {\n    width: 100%;\n    border-collapse: collapse;\n    min-width: 500px;\n  }\n  #tz-app .tz-meeting-table th {\n    font-size: 11px;\n    font-weight: 600;\n    color: #6d6fae;\n    text-align: center;\n    padding: 4px 2px;\n    border-bottom: 1px solid #e0e7ff;\n  }\n  #tz-app .tz-meeting-table td {\n    width: 3.8%;\n    height: 32px;\n    text-align: center;\n    font-size: 10px;\n    border: 1px solid #f3f4f6;\n  }\n  #tz-app .tz-meeting-table .tz-row-label {\n    font-size: 11px;\n    font-weight: 600;\n    color: #4b5563;\n    text-align: left;\n    padding: 4px 8px;\n    white-space: nowrap;\n    width: auto;\n  }\n  #tz-app .cell-night { background: #1e1b4b; }\n  #tz-app .cell-day { background: #fef9c3; }\n  #tz-app .cell-work { background: #6ee7b7; }\n  #tz-app .cell-overlap { background: #4f46e5; }\n  #tz-app .tz-legend {\n    display: flex;\n    gap: 16px;\n    flex-wrap: wrap;\n    margin-top: 12px;\n    font-size: 12px;\n    color: #6b7280;\n  }\n  #tz-app .tz-legend-item { display: flex; align-items: center; gap: 6px; }\n  #tz-app .tz-legend-dot {\n    width: 14px;\n    height: 14px;\n    border-radius: 3px;\n  }\n\n  /* Sections */\n  #tz-app .tz-section { display: none; }\n  #tz-app .tz-section.active { display: block; }\n\n  #tz-app .tz-field { margin-bottom: 14px; }\n\u003c/style\u003e\n\u003c!-- Mode Tabs --\u003e\n\u003cdiv class=\"tz-tabs\"\u003e\n  \u003cbutton class=\"tz-tab active\" onclick=\"tzSetMode('converter')\"\u003eConverter\u003c/button\u003e\n  \u003cbutton class=\"tz-tab\" onclick=\"tzSetMode('world-clock')\"\u003eWorld Clock\u003c/button\u003e\n  \u003cbutton class=\"tz-tab\" onclick=\"tzSetMode('meeting')\"\u003eMeeting Planner\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- ===== CONVERTER MODE ===== --\u003e\n\u003cdiv id=\"tz-section-converter\" class=\"tz-section active\"\u003e\n  \u003cdiv class=\"tz-card\"\u003e\n    \u003ch2\u003eConvert Date \u0026 Time\u003c/h2\u003e\n    \u003cdiv class=\"tz-field\"\u003e\n      \u003clabel\u003eDate \u0026 Time\u003c/label\u003e\n      \u003cinput type=\"datetime-local\" id=\"tz-dt-input\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tz-conv-row\"\u003e\n      \u003cdiv\u003e\n        \u003clabel\u003eFrom Timezone\u003c/label\u003e\n        \u003cselect id=\"tz-from\"\u003e\u003c/select\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"tz-swap-col\"\u003e\n        \u003cbutton class=\"tz-swap-btn\" onclick=\"tzSwap()\" title=\"Swap timezones\"\u003e⇄\u003c/button\u003e\n      \u003c/div\u003e\n      \u003cdiv\u003e\n        \u003clabel\u003eTo Timezone\u003c/label\u003e\n        \u003cselect id=\"tz-to\"\u003e\u003c/select\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tz-result-box\" id=\"tz-conv-result\"\u003e\n      \u003cdiv\u003e\n        \u003cdiv class=\"tz-result-label\"\u003eConverted Time\u003c/div\u003e\n        \u003cdiv class=\"tz-result-value\" id=\"tz-conv-value\"\u003e—\u003c/div\u003e\n        \u003cdiv class=\"tz-result-offset\" id=\"tz-conv-offset\"\u003e\u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ===== WORLD CLOCK MODE ===== --\u003e\n\u003cdiv id=\"tz-section-world-clock\" class=\"tz-section\"\u003e\n  \u003cdiv class=\"tz-card\"\u003e\n    \u003ch2\u003eLive World Clock\u003c/h2\u003e\n    \u003cdiv class=\"tz-live-grid\" id=\"tz-live-primary\"\u003e\n      \u003cdiv class=\"tz-clock-box\"\u003e\n        \u003cdiv class=\"tz-clock-label\" id=\"tz-live-from-label\"\u003e—\u003c/div\u003e\n        \u003cdiv class=\"tz-clock-time\" id=\"tz-live-from-time\"\u003e—\u003c/div\u003e\n        \u003cdiv class=\"tz-clock-date\" id=\"tz-live-from-date\"\u003e\u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"tz-clock-box\"\u003e\n        \u003cdiv class=\"tz-clock-label\" id=\"tz-live-to-label\"\u003e—\u003c/div\u003e\n        \u003cdiv class=\"tz-clock-time\" id=\"tz-live-to-time\"\u003e—\u003c/div\u003e\n        \u003cdiv class=\"tz-clock-date\" id=\"tz-live-to-date\"\u003e\u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"tz-card\"\u003e\n    \u003ch2\u003eCompare Multiple Timezones\u003c/h2\u003e\n    \u003cdiv class=\"tz-multi-add-row\"\u003e\n      \u003cdiv\u003e\n        \u003clabel\u003eAdd Timezone\u003c/label\u003e\n        \u003cselect id=\"tz-add-select\"\u003e\u003c/select\u003e\n      \u003c/div\u003e\n      \u003cbutton class=\"tz-add-btn\" id=\"tz-add-btn\" onclick=\"tzAddZone()\"\u003e+ Add\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tz-multi-list\" id=\"tz-multi-list\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"tz-timeline\" id=\"tz-timeline\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ===== MEETING PLANNER MODE ===== --\u003e\n\u003cdiv id=\"tz-section-meeting\" class=\"tz-section\"\u003e\n  \u003cdiv class=\"tz-card\"\u003e\n    \u003ch2\u003eMeeting Planner — Business Hours Overlap\u003c/h2\u003e\n    \u003cp style=\"font-size:13px;color:#6b7280;margin:0 0 16px;\"\u003eShows when 9am–5pm business hours overlap across your selected timezones. Add timezones in the World Clock tab first.\u003c/p\u003e","title":"Timezone Converter - Free World Clock \u0026 Meeting Planner"},{"content":" Add Participants Name: Timezone: + Add Quick add city: Visual Timeline — click any cell to select a meeting slot No participants yet. Add someone above. Best overlap (all 9–17) Business hours (9–17) Early/Late (7–9 or 17–20) Night Selected slot Best Meeting Times Add participants to see suggestions. Copy Meeting Invite Select a slot above or choose a time, then copy the invite text for all participants. Copy to Clipboard Copied! Related Tools: World Clock | Timezone Converter ","permalink":"https://productivity-works.com/tools/meeting-planner/","summary":"\u003cdiv id=\"mp-app\"\u003e\n\u003cstyle\u003e\n  #mp-app {\n    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, sans-serif;\n    max-width: 860px;\n    margin: 0 auto;\n    padding: 16px;\n    color: #1e1b4b;\n  }\n\n  #mp-app * { box-sizing: border-box; }\n\n  #mp-app h2 {\n    font-size: 1.05rem;\n    font-weight: 700;\n    margin: 0 0 10px;\n    color: #1e1b4b;\n  }\n\n  #mp-app .mp-section {\n    background: #f8f7ff;\n    border: 1px solid #e0e7ff;\n    border-radius: 10px;\n    padding: 16px;\n    margin-bottom: 16px;\n  }\n\n  #mp-app .mp-row {\n    display: flex;\n    gap: 8px;\n    align-items: center;\n    flex-wrap: wrap;\n    margin-bottom: 8px;\n  }\n\n  #mp-app label {\n    font-size: 0.82rem;\n    font-weight: 600;\n    color: #4b5563;\n    white-space: nowrap;\n  }\n\n  #mp-app input[type=\"text\"],\n  #mp-app select {\n    border: 1px solid #c7d2fe;\n    border-radius: 6px;\n    padding: 6px 10px;\n    font-size: 0.85rem;\n    background: #fff;\n    color: #1e1b4b;\n    outline: none;\n  }\n\n  #mp-app input[type=\"text\"]:focus,\n  #mp-app select:focus {\n    border-color: #6366f1;\n    box-shadow: 0 0 0 2px #e0e7ff;\n  }\n\n  #mp-app input[type=\"text\"] { width: 140px; }\n  #mp-app select { width: 240px; }\n\n  #mp-app .mp-btn {\n    background: #6366f1;\n    color: #fff;\n    border: none;\n    border-radius: 6px;\n    padding: 7px 14px;\n    font-size: 0.82rem;\n    font-weight: 600;\n    cursor: pointer;\n    white-space: nowrap;\n  }\n\n  #mp-app .mp-btn:hover { background: #4f46e5; }\n\n  #mp-app .mp-btn-sm {\n    background: transparent;\n    color: #6366f1;\n    border: 1px solid #6366f1;\n    border-radius: 5px;\n    padding: 3px 9px;\n    font-size: 0.75rem;\n    cursor: pointer;\n    font-weight: 600;\n  }\n\n  #mp-app .mp-btn-sm:hover { background: #e0e7ff; }\n\n  #mp-app .mp-btn-danger {\n    background: transparent;\n    color: #ef4444;\n    border: 1px solid #fca5a5;\n    border-radius: 5px;\n    padding: 3px 9px;\n    font-size: 0.75rem;\n    cursor: pointer;\n  }\n\n  #mp-app .mp-btn-danger:hover { background: #fef2f2; }\n\n  #mp-app .mp-presets {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 6px;\n    margin-top: 8px;\n  }\n\n  #mp-app .mp-preset-btn {\n    background: #e0e7ff;\n    color: #3730a3;\n    border: none;\n    border-radius: 5px;\n    padding: 4px 10px;\n    font-size: 0.75rem;\n    cursor: pointer;\n    font-weight: 600;\n  }\n\n  #mp-app .mp-preset-btn:hover { background: #c7d2fe; }\n\n  #mp-app .mp-participant-list {\n    display: flex;\n    flex-direction: column;\n    gap: 6px;\n    margin-bottom: 10px;\n  }\n\n  #mp-app .mp-participant {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    background: #fff;\n    border: 1px solid #e0e7ff;\n    border-radius: 7px;\n    padding: 7px 10px;\n  }\n\n  #mp-app .mp-color-dot {\n    width: 12px;\n    height: 12px;\n    border-radius: 50%;\n    flex-shrink: 0;\n  }\n\n  #mp-app .mp-p-name {\n    font-size: 0.85rem;\n    font-weight: 600;\n    flex: 1;\n  }\n\n  #mp-app .mp-p-tz {\n    font-size: 0.75rem;\n    color: #6b7280;\n    flex: 1;\n  }\n\n  #mp-app .mp-p-time {\n    font-size: 0.78rem;\n    color: #374151;\n    font-family: monospace;\n    min-width: 60px;\n    text-align: right;\n  }\n\n  /* Timeline */\n  #mp-app .mp-timeline-wrap {\n    overflow-x: auto;\n  }\n\n  #mp-app .mp-timeline {\n    min-width: 600px;\n  }\n\n  #mp-app .mp-tl-header {\n    display: grid;\n    grid-template-columns: 110px repeat(24, 1fr);\n    margin-bottom: 2px;\n  }\n\n  #mp-app .mp-tl-hcell {\n    font-size: 0.62rem;\n    color: #9ca3af;\n    text-align: center;\n    padding: 1px 0;\n  }\n\n  #mp-app .mp-tl-row {\n    display: grid;\n    grid-template-columns: 110px repeat(24, 1fr);\n    margin-bottom: 3px;\n    align-items: center;\n  }\n\n  #mp-app .mp-tl-label {\n    font-size: 0.72rem;\n    font-weight: 600;\n    color: #374151;\n    padding-right: 6px;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n    line-height: 1.3;\n  }\n\n  #mp-app .mp-tl-label span {\n    display: block;\n    font-size: 0.62rem;\n    font-weight: 400;\n    color: #9ca3af;\n  }\n\n  #mp-app .mp-tl-cell {\n    height: 28px;\n    border-radius: 2px;\n    cursor: pointer;\n    position: relative;\n    transition: filter 0.1s;\n  }\n\n  #mp-app .mp-tl-cell:hover { filter: brightness(0.88); }\n\n  #mp-app .mp-tl-cell.cell-work   { background: #22c55e; }\n  #mp-app .mp-tl-cell.cell-early  { background: #facc15; }\n  #mp-app .mp-tl-cell.cell-night  { background: #6366f1; opacity: 0.25; }\n  #mp-app .mp-tl-cell.cell-overlap { background: #10b981; border: 2px solid #059669; }\n  #mp-app .mp-tl-cell.cell-selected { outline: 3px solid #f59e0b; outline-offset: -1px; }\n\n  /* Overlap row */\n  #mp-app .mp-tl-row.overlap-row .mp-tl-label {\n    font-size: 0.7rem;\n    color: #065f46;\n    font-weight: 700;\n  }\n\n  #mp-app .mp-tl-row.overlap-row .mp-tl-cell.cell-overlap {\n    height: 20px;\n    border-radius: 3px;\n  }\n\n  #mp-app .mp-tl-row.overlap-row .mp-tl-cell.cell-no-overlap {\n    background: #f3f4f6;\n    height: 20px;\n    border-radius: 3px;\n  }\n\n  /* Legend */\n  #mp-app .mp-legend {\n    display: flex;\n    gap: 14px;\n    flex-wrap: wrap;\n    font-size: 0.75rem;\n    color: #6b7280;\n    margin-top: 10px;\n  }\n\n  #mp-app .mp-legend-item {\n    display: flex;\n    align-items: center;\n    gap: 4px;\n  }\n\n  #mp-app .mp-legend-swatch {\n    width: 14px;\n    height: 14px;\n    border-radius: 3px;\n  }\n\n  /* Suggestions */\n  #mp-app .mp-suggestions {\n    margin-top: 14px;\n  }\n\n  #mp-app .mp-suggestion-title {\n    font-size: 0.85rem;\n    font-weight: 700;\n    color: #065f46;\n    margin-bottom: 8px;\n  }\n\n  #mp-app .mp-suggestion-none {\n    font-size: 0.82rem;\n    color: #9ca3af;\n    font-style: italic;\n  }\n\n  #mp-app .mp-slot {\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    flex-wrap: wrap;\n    background: #ecfdf5;\n    border: 1px solid #6ee7b7;\n    border-radius: 7px;\n    padding: 8px 12px;\n    margin-bottom: 6px;\n    font-size: 0.82rem;\n  }\n\n  #mp-app .mp-slot-utc {\n    font-weight: 700;\n    color: #065f46;\n    white-space: nowrap;\n  }\n\n  #mp-app .mp-slot-times {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 6px;\n    flex: 1;\n  }\n\n  #mp-app .mp-slot-ptime {\n    background: #fff;\n    border: 1px solid #a7f3d0;\n    border-radius: 5px;\n    padding: 2px 8px;\n    font-size: 0.78rem;\n    white-space: nowrap;\n  }\n\n  /* Copy invite */\n  #mp-app .mp-copy-wrap {\n    margin-top: 14px;\n  }\n\n  #mp-app .mp-copy-area {\n    width: 100%;\n    border: 1px solid #c7d2fe;\n    border-radius: 7px;\n    padding: 10px;\n    font-size: 0.8rem;\n    font-family: monospace;\n    background: #fff;\n    color: #374151;\n    resize: vertical;\n    min-height: 80px;\n  }\n\n  #mp-app .mp-copy-row {\n    display: flex;\n    gap: 8px;\n    align-items: center;\n    margin-top: 6px;\n    flex-wrap: wrap;\n  }\n\n  #mp-app .mp-copy-status {\n    font-size: 0.78rem;\n    color: #22c55e;\n    display: none;\n  }\n\n  #mp-app .mp-tip {\n    font-size: 0.78rem;\n    color: #6b7280;\n    margin-top: 6px;\n    line-height: 1.5;\n  }\n\n  #mp-app .mp-no-p {\n    font-size: 0.82rem;\n    color: #9ca3af;\n    font-style: italic;\n    padding: 8px 0;\n  }\n\n  @media (max-width: 540px) {\n    #mp-app input[type=\"text\"] { width: 110px; }\n    #mp-app select { width: 200px; }\n  }\n\u003c/style\u003e\n\u003c!-- Section 1: Add Participants --\u003e\n\u003cdiv class=\"mp-section\"\u003e\n  \u003ch2\u003eAdd Participants\u003c/h2\u003e\n  \u003cdiv class=\"mp-row\"\u003e\n    \u003clabel\u003eName:\u003c/label\u003e\n    \u003cinput type=\"text\" id=\"mp-name\" placeholder=\"e.g. Alice\" maxlength=\"24\" /\u003e\n    \u003clabel\u003eTimezone:\u003c/label\u003e\n    \u003cselect id=\"mp-tz\"\u003e\u003c/select\u003e\n    \u003cbutton class=\"mp-btn\" id=\"mp-add-btn\"\u003e+ Add\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv\u003e\n    \u003clabel style=\"font-size:0.78rem; color:#6b7280;\"\u003eQuick add city:\u003c/label\u003e\n    \u003cdiv class=\"mp-presets\" id=\"mp-presets\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Section 2: Participants \u0026 Timeline --\u003e\n\u003cdiv class=\"mp-section\"\u003e\n  \u003ch2\u003eVisual Timeline \u003cspan style=\"font-weight:400; font-size:0.8rem; color:#6b7280;\"\u003e— click any cell to select a meeting slot\u003c/span\u003e\u003c/h2\u003e\n  \u003cdiv id=\"mp-participant-list\" class=\"mp-participant-list\"\u003e\n    \u003cdiv class=\"mp-no-p\"\u003eNo participants yet. Add someone above.\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"mp-timeline-wrap\"\u003e\n    \u003cdiv class=\"mp-timeline\" id=\"mp-timeline\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"mp-legend\"\u003e\n    \u003cdiv class=\"mp-legend-item\"\u003e\n      \u003cdiv class=\"mp-legend-swatch\" style=\"background:#10b981; border:2px solid #059669;\"\u003e\u003c/div\u003e\n      \u003cspan\u003eBest overlap (all 9–17)\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"mp-legend-item\"\u003e\n      \u003cdiv class=\"mp-legend-swatch\" style=\"background:#22c55e;\"\u003e\u003c/div\u003e\n      \u003cspan\u003eBusiness hours (9–17)\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"mp-legend-item\"\u003e\n      \u003cdiv class=\"mp-legend-swatch\" style=\"background:#facc15;\"\u003e\u003c/div\u003e\n      \u003cspan\u003eEarly/Late (7–9 or 17–20)\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"mp-legend-item\"\u003e\n      \u003cdiv class=\"mp-legend-swatch\" style=\"background:#6366f1; opacity:0.4;\"\u003e\u003c/div\u003e\n      \u003cspan\u003eNight\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"mp-legend-item\"\u003e\n      \u003cdiv class=\"mp-legend-swatch\" style=\"background:#f59e0b;\"\u003e\u003c/div\u003e\n      \u003cspan\u003eSelected slot\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Section 3: Best Meeting Times --\u003e\n\u003cdiv class=\"mp-section\" id=\"mp-suggest-section\"\u003e\n  \u003ch2\u003eBest Meeting Times\u003c/h2\u003e\n  \u003cdiv class=\"mp-suggestions\" id=\"mp-suggestions\"\u003e\n    \u003cdiv class=\"mp-suggestion-none\"\u003eAdd participants to see suggestions.\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Section 4: Copy Invite --\u003e\n\u003cdiv class=\"mp-section\" id=\"mp-invite-section\"\u003e\n  \u003ch2\u003eCopy Meeting Invite\u003c/h2\u003e\n  \u003cdiv class=\"mp-tip\"\u003eSelect a slot above or choose a time, then copy the invite text for all participants.\u003c/div\u003e\n  \u003cdiv class=\"mp-copy-wrap\"\u003e\n    \u003ctextarea class=\"mp-copy-area\" id=\"mp-invite-text\" readonly placeholder=\"Select a meeting slot above to generate invite text...\"\u003e\u003c/textarea\u003e\n    \u003cdiv class=\"mp-copy-row\"\u003e\n      \u003cbutton class=\"mp-btn\" id=\"mp-copy-btn\"\u003eCopy to Clipboard\u003c/button\u003e\n      \u003cspan class=\"mp-copy-status\" id=\"mp-copy-status\"\u003eCopied!\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  'use strict';\n\n  const ZONES = [\n    { value: 'UTC',                  label: 'UTC — Coordinated Universal Time', city: null },\n    { value: 'America/Los_Angeles',  label: 'PST/PDT — Los Angeles / San Francisco', city: 'Los Angeles' },\n    { value: 'America/Denver',       label: 'MST/MDT — Denver / Salt Lake City', city: null },\n    { value: 'America/Chicago',      label: 'CST/CDT — Chicago / Dallas', city: 'Chicago' },\n    { value: 'America/New_York',     label: 'EST/EDT — New York / Toronto', city: 'New York' },\n    { value: 'America/Sao_Paulo',    label: 'BRT — São Paulo / Rio de Janeiro', city: 'São Paulo' },\n    { value: 'Europe/London',        label: 'GMT/BST — London', city: 'London' },\n    { value: 'Europe/Paris',         label: 'CET/CEST — Paris / Berlin / Rome', city: 'Paris' },\n    { value: 'Europe/Helsinki',      label: 'EET/EEST — Helsinki / Kyiv', city: null },\n    { value: 'Europe/Moscow',        label: 'MSK — Moscow', city: 'Moscow' },\n    { value: 'Asia/Dubai',           label: 'GST — Dubai / Abu Dhabi', city: 'Dubai' },\n    { value: 'Asia/Karachi',         label: 'PKT — Karachi / Islamabad', city: null },\n    { value: 'Asia/Kolkata',         label: 'IST — Mumbai / New Delhi', city: 'Mumbai' },\n    { value: 'Asia/Dhaka',           label: 'BST — Dhaka', city: null },\n    { value: 'Asia/Bangkok',         label: 'ICT — Bangkok / Jakarta', city: 'Bangkok' },\n    { value: 'Asia/Singapore',       label: 'SGT — Singapore / Kuala Lumpur', city: 'Singapore' },\n    { value: 'Asia/Shanghai',        label: 'CST — Beijing / Shanghai / Taipei', city: 'Beijing' },\n    { value: 'Asia/Tokyo',           label: 'JST — Tokyo / Osaka', city: 'Tokyo' },\n    { value: 'Asia/Seoul',           label: 'KST — Seoul', city: 'Seoul' },\n    { value: 'Australia/Sydney',     label: 'AEST/AEDT — Sydney / Melbourne', city: 'Sydney' },\n    { value: 'Pacific/Auckland',     label: 'NZST/NZDT — Auckland', city: 'Auckland' },\n    { value: 'Pacific/Honolulu',     label: 'HST — Honolulu', city: 'Honolulu' },\n  ];\n\n  const COLORS = [\n    '#6366f1','#f59e0b','#ef4444','#3b82f6','#8b5cf6',\n    '#06b6d4','#ec4899','#14b8a6','#f97316','#84cc16'\n  ];\n\n  let participants = [];\n  let selectedSlot = null;\n  let clockTick = null;\n\n  // --- Build timezone select ---\n  const tzSel = document.getElementById('mp-tz');\n  ZONES.forEach(z =\u003e {\n    const opt = document.createElement('option');\n    opt.value = z.value;\n    opt.textContent = z.label;\n    tzSel.appendChild(opt);\n  });\n  // Default to local timezone if possible\n  try {\n    const local = Intl.DateTimeFormat().resolvedOptions().timeZone;\n    if (ZONES.find(z =\u003e z.value === local)) tzSel.value = local;\n  } catch(e) {}\n\n  // --- Build city presets ---\n  const presetsEl = document.getElementById('mp-presets');\n  ZONES.filter(z =\u003e z.city).forEach(z =\u003e {\n    const btn = document.createElement('button');\n    btn.className = 'mp-preset-btn';\n    btn.textContent = z.city;\n    btn.addEventListener('click', () =\u003e {\n      const nameEl = document.getElementById('mp-name');\n      if (!nameEl.value.trim()) nameEl.value = z.city;\n      tzSel.value = z.value;\n    });\n    presetsEl.appendChild(btn);\n  });\n\n  // --- Add participant ---\n  document.getElementById('mp-add-btn').addEventListener('click', addParticipant);\n  document.getElementById('mp-name').addEventListener('keydown', e =\u003e {\n    if (e.key === 'Enter') addParticipant();\n  });\n\n  function addParticipant() {\n    const nameEl = document.getElementById('mp-name');\n    const name = nameEl.value.trim() || tzSel.options[tzSel.selectedIndex].text.split('—')[1]?.trim() || 'Participant';\n    const tz = tzSel.value;\n    if (participants.length \u003e= 10) {\n      alert('Maximum 10 participants.');\n      return;\n    }\n    participants.push({\n      id: Date.now(),\n      name,\n      tz,\n      color: COLORS[participants.length % COLORS.length]\n    });\n    nameEl.value = '';\n    render();\n  }\n\n  function removeParticipant(id) {\n    participants = participants.filter(p =\u003e p.id !== id);\n    selectedSlot = null;\n    render();\n  }\n\n  // --- Format local time ---\n  function fmtTime(tz, date) {\n    return date.toLocaleString('en-US', {\n      timeZone: tz,\n      hour: '2-digit',\n      minute: '2-digit',\n      hour12: false\n    });\n  }\n\n  function fmtDateTime(tz, date) {\n    return date.toLocaleString('en-US', {\n      timeZone: tz,\n      weekday: 'short',\n      month: 'short',\n      day: 'numeric',\n      hour: '2-digit',\n      minute: '2-digit',\n      hour12: true\n    });\n  }\n\n  function getLocalHour(tz, utcHour) {\n    // Get what hour it is in `tz` when UTC is `utcHour` today\n    const now = new Date();\n    now.setUTCHours(utcHour, 0, 0, 0);\n    const hStr = now.toLocaleString('en-US', { timeZone: tz, hour: 'numeric', hour12: false });\n    return parseInt(hStr);\n  }\n\n  function getShortLabel(tz) {\n    const z = ZONES.find(z =\u003e z.value === tz);\n    if (!z) return tz;\n    return z.label.split('—')[0].trim();\n  }\n\n  function getLocationLabel(tz) {\n    const z = ZONES.find(z =\u003e z.value === tz);\n    if (!z) return '';\n    const parts = z.label.split('—');\n    return parts.slice(1).join('—').trim();\n  }\n\n  function cellClass(localH) {\n    if (localH \u003e= 9 \u0026\u0026 localH \u003c 17) return 'cell-work';\n    if ((localH \u003e= 7 \u0026\u0026 localH \u003c 9) || (localH \u003e= 17 \u0026\u0026 localH \u003c 20)) return 'cell-early';\n    return 'cell-night';\n  }\n\n  // --- Render ---\n  function render() {\n    renderParticipantList();\n    renderTimeline();\n    renderSuggestions();\n    renderInvite();\n  }\n\n  function renderParticipantList() {\n    const el = document.getElementById('mp-participant-list');\n    if (participants.length === 0) {\n      el.innerHTML = '\u003cdiv class=\"mp-no-p\"\u003eNo participants yet. Add someone above.\u003c/div\u003e';\n      return;\n    }\n    const now = new Date();\n    el.innerHTML = participants.map(p =\u003e `\n      \u003cdiv class=\"mp-participant\"\u003e\n        \u003cdiv class=\"mp-color-dot\" style=\"background:${p.color};\"\u003e\u003c/div\u003e\n        \u003cdiv class=\"mp-p-name\"\u003e${escHtml(p.name)}\u003c/div\u003e\n        \u003cdiv class=\"mp-p-tz\"\u003e${escHtml(getShortLabel(p.tz))} — ${escHtml(getLocationLabel(p.tz))}\u003c/div\u003e\n        \u003cdiv class=\"mp-p-time\" id=\"mp-ptime-${p.id}\"\u003e${fmtTime(p.tz, now)}\u003c/div\u003e\n        \u003cbutton class=\"mp-btn-danger\" onclick=\"window._mpRemove(${p.id})\"\u003eRemove\u003c/button\u003e\n      \u003c/div\u003e\n    `).join('');\n  }\n\n  function renderTimeline() {\n    const el = document.getElementById('mp-timeline');\n    if (participants.length === 0) {\n      el.innerHTML = '';\n      return;\n    }\n\n    // Hour labels row\n    let html = '\u003cdiv class=\"mp-tl-header\"\u003e\u003cdiv class=\"mp-tl-hcell\"\u003e\u003c/div\u003e';\n    for (let h = 0; h \u003c 24; h++) {\n      html += `\u003cdiv class=\"mp-tl-hcell\"\u003e${String(h).padStart(2,'0')}\u003c/div\u003e`;\n    }\n    html += '\u003c/div\u003e';\n\n    // Compute overlap hours (all participants in business hours)\n    const overlapHours = [];\n    for (let h = 0; h \u003c 24; h++) {\n      const allWork = participants.every(p =\u003e {\n        const lh = getLocalHour(p.tz, h);\n        return lh \u003e= 9 \u0026\u0026 lh \u003c 17;\n      });\n      overlapHours.push(allWork);\n    }\n\n    // Per-participant rows\n    participants.forEach(p =\u003e {\n      html += `\u003cdiv class=\"mp-tl-row\"\u003e`;\n      html += `\u003cdiv class=\"mp-tl-label\"\u003e${escHtml(p.name)}\u003cspan\u003e${escHtml(getShortLabel(p.tz))}\u003c/span\u003e\u003c/div\u003e`;\n      for (let h = 0; h \u003c 24; h++) {\n        const lh = getLocalHour(p.tz, h);\n        let cls = overlapHours[h] ? 'cell-overlap' : cellClass(lh);\n        if (selectedSlot === h) cls += ' cell-selected';\n        const lhFmt = String(lh).padStart(2,'0') + ':00';\n        html += `\u003cdiv class=\"mp-tl-cell ${cls}\" title=\"${escHtml(p.name)}: ${lhFmt}\" data-hour=\"${h}\" onclick=\"window._mpSelectSlot(${h})\"\u003e\u003c/div\u003e`;\n      }\n      html += '\u003c/div\u003e';\n    });\n\n    // Overlap summary row\n    html += `\u003cdiv class=\"mp-tl-row overlap-row\"\u003e`;\n    html += `\u003cdiv class=\"mp-tl-label\" style=\"color:#065f46;\"\u003eAll overlap\u003cspan\u003ebest times\u003c/span\u003e\u003c/div\u003e`;\n    for (let h = 0; h \u003c 24; h++) {\n      let cls = overlapHours[h] ? 'cell-overlap' : 'cell-no-overlap';\n      if (selectedSlot === h) cls += ' cell-selected';\n      html += `\u003cdiv class=\"mp-tl-cell ${cls}\" title=\"UTC ${String(h).padStart(2,'0')}:00${overlapHours[h] ? ' ✓ All in business hours' : ''}\" data-hour=\"${h}\" onclick=\"window._mpSelectSlot(${h})\"\u003e\u003c/div\u003e`;\n    }\n    html += '\u003c/div\u003e';\n\n    el.innerHTML = html;\n  }\n\n  function renderSuggestions() {\n    const el = document.getElementById('mp-suggestions');\n    if (participants.length === 0) {\n      el.innerHTML = '\u003cdiv class=\"mp-suggestion-none\"\u003eAdd participants to see suggestions.\u003c/div\u003e';\n      return;\n    }\n\n    // Find overlap hours\n    const overlapHours = [];\n    for (let h = 0; h \u003c 24; h++) {\n      const allWork = participants.every(p =\u003e {\n        const lh = getLocalHour(p.tz, h);\n        return lh \u003e= 9 \u0026\u0026 lh \u003c 17;\n      });\n      if (allWork) overlapHours.push(h);\n    }\n\n    // Find relaxed overlap (all 7-20)\n    const relaxedHours = [];\n    if (overlapHours.length === 0) {\n      for (let h = 0; h \u003c 24; h++) {\n        const allOk = participants.every(p =\u003e {\n          const lh = getLocalHour(p.tz, h);\n          return lh \u003e= 7 \u0026\u0026 lh \u003c 20;\n        });\n        if (allOk) relaxedHours.push(h);\n      }\n    }\n\n    const hours = overlapHours.length \u003e 0 ? overlapHours : relaxedHours;\n\n    if (hours.length === 0) {\n      el.innerHTML = `\n        \u003cdiv class=\"mp-suggestion-none\"\u003e\n          No overlapping business hours found across all participants.\u003cbr\u003e\n          Consider scheduling across fewer time zones or using asynchronous communication.\n        \u003c/div\u003e`;\n      return;\n    }\n\n    // Group consecutive hours into slots\n    const slots = [];\n    let start = hours[0], end = hours[0];\n    for (let i = 1; i \u003c hours.length; i++) {\n      if (hours[i] === hours[i-1] + 1) {\n        end = hours[i];\n      } else {\n        slots.push({ start, end });\n        start = hours[i]; end = hours[i];\n      }\n    }\n    slots.push({ start, end });\n\n    const quality = overlapHours.length \u003e 0 ? 'Best overlap (all within 9–17)' : 'Extended overlap (7–20, early/late for some)';\n\n    let html = `\u003cdiv class=\"mp-suggestion-title\"\u003e${quality} — ${hours.length} hour${hours.length\u003e1?'s':''}\u003c/div\u003e`;\n\n    slots.forEach(slot =\u003e {\n      const now = new Date();\n      now.setUTCHours(slot.start, 0, 0, 0);\n      const endDate = new Date(now);\n      endDate.setUTCHours(slot.end + 1, 0, 0, 0);\n\n      html += `\u003cdiv class=\"mp-slot\" onclick=\"window._mpSelectSlot(${slot.start})\" style=\"cursor:pointer;\"\u003e`;\n      html += `\u003cdiv class=\"mp-slot-utc\"\u003eUTC ${String(slot.start).padStart(2,'0')}:00–${String(slot.end+1).padStart(2,'0')}:00\u003c/div\u003e`;\n      html += `\u003cdiv class=\"mp-slot-times\"\u003e`;\n      participants.forEach(p =\u003e {\n        html += `\u003cdiv class=\"mp-slot-ptime\" style=\"border-color:${p.color};\"\u003e\n          \u003cstrong\u003e${escHtml(p.name)}\u003c/strong\u003e: ${fmtTime(p.tz, now)}–${fmtTime(p.tz, endDate)}\n        \u003c/div\u003e`;\n      });\n      html += `\u003c/div\u003e\u003c/div\u003e`;\n    });\n\n    el.innerHTML = html;\n  }\n\n  function renderInvite() {\n    const el = document.getElementById('mp-invite-text');\n    if (participants.length === 0 || selectedSlot === null) {\n      el.value = '';\n      return;\n    }\n    const h = selectedSlot;\n    const now = new Date();\n    now.setUTCHours(h, 0, 0, 0);\n    const endDate = new Date(now);\n    endDate.setUTCHours(h + 1, 0, 0, 0);\n\n    let text = `Meeting Invitation\\n`;\n    text += `==================\\n`;\n    text += `UTC: ${String(h).padStart(2,'0')}:00 – ${String(h+1).padStart(2,'0')}:00\\n\\n`;\n    text += `Local times for participants:\\n`;\n    participants.forEach(p =\u003e {\n      text += `  ${p.name} (${getShortLabel(p.tz)}): ${fmtDateTime(p.tz, now)}\\n`;\n    });\n    text += `\\nGenerated by Timezone Meeting Planner\\nhttps://productivity.works/tools/meeting-planner/`;\n    el.value = text;\n  }\n\n  // --- Clock tick ---\n  function startClock() {\n    if (clockTick) clearInterval(clockTick);\n    clockTick = setInterval(() =\u003e {\n      if (participants.length === 0) return;\n      const now = new Date();\n      participants.forEach(p =\u003e {\n        const el = document.getElementById(`mp-ptime-${p.id}`);\n        if (el) el.textContent = fmtTime(p.tz, now);\n      });\n    }, 10000);\n  }\n\n  // --- Slot selection ---\n  window._mpSelectSlot = function(h) {\n    selectedSlot = (selectedSlot === h) ? null : h;\n    renderTimeline();\n    renderInvite();\n  };\n\n  window._mpRemove = function(id) {\n    removeParticipant(id);\n  };\n\n  // --- Copy ---\n  document.getElementById('mp-copy-btn').addEventListener('click', () =\u003e {\n    const ta = document.getElementById('mp-invite-text');\n    if (!ta.value) return;\n    navigator.clipboard.writeText(ta.value).then(() =\u003e {\n      const st = document.getElementById('mp-copy-status');\n      st.style.display = 'inline';\n      setTimeout(() =\u003e { st.style.display = 'none'; }, 2000);\n    }).catch(() =\u003e {\n      ta.select();\n      document.execCommand('copy');\n    });\n  });\n\n  function escHtml(s) {\n    return String(s).replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;').replace(/\"/g,'\u0026quot;');\n  }\n\n  // --- Init: add a default participant ---\n  (function init() {\n    try {\n      const local = Intl.DateTimeFormat().resolvedOptions().timeZone;\n      const z = ZONES.find(z =\u003e z.value === local);\n      if (z) {\n        participants.push({\n          id: Date.now(),\n          name: z.city || z.label.split('—')[1]?.trim().split(' / ')[0] || 'You',\n          tz: local,\n          color: COLORS[0]\n        });\n      }\n    } catch(e) {}\n    render();\n    startClock();\n  })();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cstrong\u003eRelated Tools:\u003c/strong\u003e \u003ca href=\"https://productivity-works.com/tools/world-clock/\"\u003eWorld Clock\u003c/a\u003e\n | \u003ca href=\"https://productivity-works.com/tools/timezone-converter/\"\u003eTimezone Converter\u003c/a\u003e\n\u003c/p\u003e","title":"Timezone Meeting Planner"},{"content":" Bill Details Bill Amount $ Tip Percentage 18% No Tip 15% 18% 20% 25% Number of People Options Round up per-person amount (to nearest dollar) Tip on pre-tax amount (toggle off = tip after tax) Tax Amount ($) $ Custom split (unequal amounts) Calculate Split\nResults Tip Amount $0.00 Total with Tip $0.00 Each Person Pays $0.00 Custom Split Assign a share weight to each person. Amounts will be proportionally divided from the total with tip. Total accounted: $0.00 Total does not match bill total — adjust shares. Related Free Tools Loan \u0026amp; Mortgage Calculator Compound Interest Calculator Currency Converter Monthly Budget Planner ","permalink":"https://productivity-works.com/tools/tip-splitter/","summary":"\u003cdiv id=\"ts-app\"\u003e\n\u003cstyle\u003e\n#ts-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 680px;\n  margin: 0 auto;\n  color: #1a1a2e;\n}\n#ts-app * {\n  box-sizing: border-box;\n}\n#ts-app h2 {\n  font-size: 1.4rem;\n  font-weight: 700;\n  margin: 0 0 1.2rem 0;\n  color: #1a1a2e;\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n}\n#ts-app .ts-card {\n  background: #fff;\n  border: 1.5px solid #e5e7eb;\n  border-radius: 14px;\n  padding: 1.5rem;\n  margin-bottom: 1.2rem;\n  box-shadow: 0 2px 8px rgba(0,0,0,0.05);\n}\n#ts-app label {\n  display: block;\n  font-size: 0.85rem;\n  font-weight: 600;\n  color: #374151;\n  margin-bottom: 0.4rem;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#ts-app input[type=\"number\"] {\n  width: 100%;\n  padding: 0.7rem 1rem;\n  font-size: 1.1rem;\n  border: 1.5px solid #d1d5db;\n  border-radius: 8px;\n  outline: none;\n  transition: border-color 0.2s;\n  color: #1a1a2e;\n  background: #f9fafb;\n}\n#ts-app input[type=\"number\"]:focus {\n  border-color: #6366f1;\n  background: #fff;\n}\n#ts-app .ts-input-prefix {\n  position: relative;\n}\n#ts-app .ts-input-prefix span {\n  position: absolute;\n  left: 0.9rem;\n  top: 50%;\n  transform: translateY(-50%);\n  font-size: 1.1rem;\n  color: #6b7280;\n  font-weight: 600;\n  pointer-events: none;\n}\n#ts-app .ts-input-prefix input {\n  padding-left: 2rem;\n}\n#ts-app .ts-tip-row {\n  display: flex;\n  align-items: center;\n  gap: 1rem;\n  margin-bottom: 0.8rem;\n}\n#ts-app .ts-tip-row input[type=\"range\"] {\n  flex: 1;\n  -webkit-appearance: none;\n  height: 6px;\n  background: #e5e7eb;\n  border-radius: 3px;\n  outline: none;\n  cursor: pointer;\n}\n#ts-app input[type=\"range\"]::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  width: 20px;\n  height: 20px;\n  border-radius: 50%;\n  background: #6366f1;\n  cursor: pointer;\n  box-shadow: 0 1px 4px rgba(99,102,241,0.4);\n}\n#ts-app .ts-tip-pct-display {\n  min-width: 3.5rem;\n  text-align: center;\n  font-size: 1.3rem;\n  font-weight: 700;\n  color: #6366f1;\n}\n#ts-app .ts-quick-btns {\n  display: flex;\n  gap: 0.5rem;\n  flex-wrap: wrap;\n}\n#ts-app .ts-quick-btn {\n  padding: 0.4rem 0.9rem;\n  border: 1.5px solid #6366f1;\n  border-radius: 20px;\n  background: transparent;\n  color: #6366f1;\n  font-size: 0.9rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n#ts-app .ts-quick-btn:hover,\n#ts-app .ts-quick-btn.active {\n  background: #6366f1;\n  color: #fff;\n}\n#ts-app .ts-toggle-row {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  padding: 0.6rem 0;\n  border-bottom: 1px solid #f3f4f6;\n  font-size: 0.95rem;\n  color: #374151;\n}\n#ts-app .ts-toggle-row:last-child {\n  border-bottom: none;\n}\n#ts-app .ts-toggle {\n  position: relative;\n  width: 44px;\n  height: 24px;\n}\n#ts-app .ts-toggle input {\n  opacity: 0;\n  width: 0;\n  height: 0;\n  position: absolute;\n}\n#ts-app .ts-toggle-slider {\n  position: absolute;\n  inset: 0;\n  background: #d1d5db;\n  border-radius: 24px;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n#ts-app .ts-toggle-slider::before {\n  content: \"\";\n  position: absolute;\n  width: 18px;\n  height: 18px;\n  left: 3px;\n  top: 3px;\n  background: #fff;\n  border-radius: 50%;\n  transition: transform 0.2s;\n  box-shadow: 0 1px 3px rgba(0,0,0,0.2);\n}\n#ts-app .ts-toggle input:checked + .ts-toggle-slider {\n  background: #6366f1;\n}\n#ts-app .ts-toggle input:checked + .ts-toggle-slider::before {\n  transform: translateX(20px);\n}\n#ts-app .ts-results {\n  background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);\n  border-radius: 14px;\n  padding: 1.5rem;\n  color: #fff;\n  margin-bottom: 1.2rem;\n}\n#ts-app .ts-results h2 {\n  color: #fff;\n  margin-bottom: 1rem;\n}\n#ts-app .ts-result-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 0.8rem;\n  margin-bottom: 1rem;\n}\n#ts-app .ts-result-item {\n  background: rgba(255,255,255,0.15);\n  border-radius: 10px;\n  padding: 0.9rem 1rem;\n}\n#ts-app .ts-result-item .label {\n  font-size: 0.78rem;\n  opacity: 0.85;\n  margin-bottom: 0.3rem;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  font-weight: 600;\n}\n#ts-app .ts-result-item .value {\n  font-size: 1.5rem;\n  font-weight: 800;\n}\n#ts-app .ts-result-highlight {\n  background: rgba(255,255,255,0.25);\n  border-radius: 10px;\n  padding: 1rem;\n  text-align: center;\n}\n#ts-app .ts-result-highlight .label {\n  font-size: 0.85rem;\n  opacity: 0.9;\n  margin-bottom: 0.3rem;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  font-weight: 600;\n}\n#ts-app .ts-result-highlight .value {\n  font-size: 2.2rem;\n  font-weight: 800;\n}\n#ts-app .ts-custom-split {\n  margin-top: 1rem;\n}\n#ts-app .ts-person-row {\n  display: flex;\n  align-items: center;\n  gap: 0.7rem;\n  margin-bottom: 0.6rem;\n}\n#ts-app .ts-person-row label {\n  margin: 0;\n  font-size: 0.9rem;\n  font-weight: 600;\n  min-width: 70px;\n  text-transform: none;\n  letter-spacing: 0;\n  color: #374151;\n}\n#ts-app .ts-person-row input[type=\"number\"] {\n  flex: 1;\n  font-size: 1rem;\n  padding: 0.55rem 0.8rem;\n}\n#ts-app .ts-person-share {\n  min-width: 90px;\n  text-align: right;\n  font-weight: 700;\n  color: #6366f1;\n  font-size: 1rem;\n}\n#ts-app .ts-custom-total-row {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  padding: 0.7rem 0.2rem 0;\n  border-top: 2px dashed #e5e7eb;\n  margin-top: 0.4rem;\n  font-weight: 700;\n  font-size: 0.95rem;\n  color: #374151;\n}\n#ts-app .ts-custom-total-row span:last-child {\n  color: #ef4444;\n}\n#ts-app .ts-calc-btn {\n  width: 100%;\n  padding: 0.95rem;\n  background: #6366f1;\n  color: #fff;\n  font-size: 1.1rem;\n  font-weight: 700;\n  border: none;\n  border-radius: 10px;\n  cursor: pointer;\n  transition: background 0.2s, transform 0.1s;\n  margin-bottom: 1rem;\n}\n#ts-app .ts-calc-btn:hover {\n  background: #4f46e5;\n}\n#ts-app .ts-calc-btn:active {\n  transform: scale(0.98);\n}\n#ts-app .ts-section-label {\n  font-size: 0.78rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #9ca3af;\n  margin-bottom: 0.8rem;\n}\n#ts-app .ts-related {\n  border-top: 1.5px solid #e5e7eb;\n  padding-top: 1.2rem;\n  margin-top: 0.5rem;\n}\n#ts-app .ts-related h3 {\n  font-size: 1rem;\n  font-weight: 700;\n  margin: 0 0 0.7rem 0;\n  color: #374151;\n}\n#ts-app .ts-related ul {\n  margin: 0;\n  padding: 0 0 0 1.2rem;\n  list-style: disc;\n  color: #6366f1;\n  font-size: 0.95rem;\n  line-height: 1.8;\n}\n#ts-app .ts-related a {\n  color: #6366f1;\n  text-decoration: none;\n  font-weight: 600;\n}\n#ts-app .ts-related a:hover {\n  text-decoration: underline;\n}\n#ts-app .ts-warning {\n  font-size: 0.82rem;\n  color: #ef4444;\n  font-weight: 600;\n  margin-top: 0.3rem;\n  display: none;\n}\n#ts-app .ts-warning.visible {\n  display: block;\n}\n@media (max-width: 480px) {\n  #ts-app .ts-result-grid {\n    grid-template-columns: 1fr;\n  }\n  #ts-app .ts-result-item .value {\n    font-size: 1.3rem;\n  }\n}\n\u003c/style\u003e\n\u003c!-- INPUTS --\u003e\n\u003cdiv class=\"ts-card\"\u003e\n  \u003ch2\u003eBill Details\u003c/h2\u003e\n  \u003cdiv style=\"margin-bottom:1.2rem;\"\u003e\n    \u003clabel for=\"ts-bill\"\u003eBill Amount\u003c/label\u003e\n    \u003cdiv class=\"ts-input-prefix\"\u003e\n      \u003cspan\u003e$\u003c/span\u003e\n      \u003cinput type=\"number\" id=\"ts-bill\" min=\"0\" step=\"0.01\" placeholder=\"0.00\" value=\"\" /\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv style=\"margin-bottom:1.2rem;\"\u003e\n    \u003clabel\u003eTip Percentage\u003c/label\u003e\n    \u003cdiv class=\"ts-tip-row\"\u003e\n      \u003cinput type=\"range\" id=\"ts-tip-slider\" min=\"0\" max=\"30\" step=\"1\" value=\"18\" /\u003e\n      \u003cdiv class=\"ts-tip-pct-display\"\u003e\u003cspan id=\"ts-tip-pct-val\"\u003e18\u003c/span\u003e%\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ts-quick-btns\"\u003e\n      \u003cbutton class=\"ts-quick-btn\" data-pct=\"0\"\u003eNo Tip\u003c/button\u003e\n      \u003cbutton class=\"ts-quick-btn\" data-pct=\"15\"\u003e15%\u003c/button\u003e\n      \u003cbutton class=\"ts-quick-btn active\" data-pct=\"18\"\u003e18%\u003c/button\u003e\n      \u003cbutton class=\"ts-quick-btn\" data-pct=\"20\"\u003e20%\u003c/button\u003e\n      \u003cbutton class=\"ts-quick-btn\" data-pct=\"25\"\u003e25%\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv\u003e\n    \u003clabel for=\"ts-people\"\u003eNumber of People\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"ts-people\" min=\"1\" step=\"1\" value=\"2\" placeholder=\"2\" /\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- OPTIONS --\u003e\n\u003cdiv class=\"ts-card\"\u003e\n  \u003ch2\u003eOptions\u003c/h2\u003e\n  \u003cdiv class=\"ts-toggle-row\"\u003e\n    \u003cspan\u003eRound up per-person amount (to nearest dollar)\u003c/span\u003e\n    \u003clabel class=\"ts-toggle\"\u003e\n      \u003cinput type=\"checkbox\" id=\"ts-roundup\" /\u003e\n      \u003cspan class=\"ts-toggle-slider\"\u003e\u003c/span\u003e\n    \u003c/label\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ts-toggle-row\"\u003e\n    \u003cspan\u003eTip on pre-tax amount (toggle off = tip after tax)\u003c/span\u003e\n    \u003clabel class=\"ts-toggle\"\u003e\n      \u003cinput type=\"checkbox\" id=\"ts-pretax\" /\u003e\n      \u003cspan class=\"ts-toggle-slider\"\u003e\u003c/span\u003e\n    \u003c/label\u003e\n  \u003c/div\u003e\n  \u003cdiv id=\"ts-pretax-row\" style=\"display:none; margin-top:0.9rem;\"\u003e\n    \u003clabel for=\"ts-tax\"\u003eTax Amount ($)\u003c/label\u003e\n    \u003cdiv class=\"ts-input-prefix\"\u003e\n      \u003cspan\u003e$\u003c/span\u003e\n      \u003cinput type=\"number\" id=\"ts-tax\" min=\"0\" step=\"0.01\" placeholder=\"0.00\" value=\"\" /\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ts-toggle-row\" style=\"margin-top:0.6rem;\"\u003e\n    \u003cspan\u003eCustom split (unequal amounts)\u003c/span\u003e\n    \u003clabel class=\"ts-toggle\"\u003e\n      \u003cinput type=\"checkbox\" id=\"ts-custom-toggle\" /\u003e\n      \u003cspan class=\"ts-toggle-slider\"\u003e\u003c/span\u003e\n    \u003c/label\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- CALCULATE BUTTON --\u003e\n\u003cp\u003e\u003cbutton class=\"ts-calc-btn\" id=\"ts-calc-btn\"\u003eCalculate Split\u003c/button\u003e\u003c/p\u003e","title":"Tip \u0026 Bill Splitter - Split Bills Fairly"},{"content":" Bill Amount $ € £ ¥ Tip Percentage 10% 15% 18% 20% 25% Custom: % Split Between − 1 + person Results Tip Amount — at 18% Total Bill — bill + tip Per Person — 1 person Tip Per Person — 1 person Bill: — Tip: — Round Up Total ↑ Round Down Total ↓ Reset How to use this tip calculator: Enter your bill amount, choose a tip percentage (or type a custom one), then set how many people are splitting the bill. Use the round up/down buttons to land on a clean total, and switch currencies with the dropdown if you\u0026rsquo;re traveling abroad.\nTip guide:\n10% — Minimal service or takeout 15% — Standard / Fair 18% — Good service (US restaurant default) 20% — Great service 25%+ — Excellent / exceptional experience Related Tools Calculate loan payments and interest → Loan Calculator Generate a QR code for restaurant menus or payment links → QR Code Generator Calculate percentages for any purpose → Percentage Calculator ","permalink":"https://productivity-works.com/tools/tip-calculator/","summary":"\u003cdiv id=\"tip-app\"\u003e\n\u003cstyle\u003e\n#tip-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 680px;\n  margin: 0 auto;\n  color: #1f2937;\n}\n\n#tip-app * {\n  box-sizing: border-box;\n}\n\n.tip-card {\n  background: #fff;\n  border: 1.5px solid #fde68a;\n  border-radius: 16px;\n  padding: 28px 28px 24px;\n  margin-bottom: 20px;\n  box-shadow: 0 2px 12px rgba(217,119,6,0.08);\n}\n\n.tip-section-title {\n  font-size: 13px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: .06em;\n  color: #92400e;\n  margin: 0 0 12px;\n}\n\n/* Input row */\n.tip-input-row {\n  display: flex;\n  gap: 12px;\n  align-items: center;\n  flex-wrap: wrap;\n}\n\n.tip-currency-select {\n  flex: 0 0 72px;\n  padding: 10px 8px;\n  border: 1.5px solid #fcd34d;\n  border-radius: 10px;\n  font-size: 18px;\n  font-weight: 700;\n  color: #92400e;\n  background: #fffbeb;\n  text-align: center;\n  cursor: pointer;\n  appearance: none;\n}\n\n.tip-amount-input {\n  flex: 1 1 160px;\n  padding: 10px 14px;\n  border: 1.5px solid #fcd34d;\n  border-radius: 10px;\n  font-size: 24px;\n  font-weight: 700;\n  color: #1f2937;\n  background: #fffbeb;\n  width: 100%;\n}\n\n.tip-amount-input:focus,\n.tip-currency-select:focus {\n  outline: none;\n  border-color: #d97706;\n  box-shadow: 0 0 0 3px rgba(217,119,6,0.15);\n}\n\n/* Tip quick buttons */\n.tip-quick-btns {\n  display: flex;\n  gap: 8px;\n  flex-wrap: wrap;\n  margin-bottom: 12px;\n}\n\n.tip-pct-btn {\n  flex: 1 1 60px;\n  padding: 10px 4px;\n  border: 1.5px solid #fcd34d;\n  border-radius: 10px;\n  background: #fffbeb;\n  color: #92400e;\n  font-size: 15px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background .15s, border-color .15s, color .15s;\n  text-align: center;\n}\n\n.tip-pct-btn:hover {\n  background: #fef3c7;\n  border-color: #d97706;\n}\n\n.tip-pct-btn.active {\n  background: #d97706;\n  border-color: #b45309;\n  color: #fff;\n}\n\n/* Custom tip */\n.tip-custom-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-top: 4px;\n}\n\n.tip-custom-label {\n  font-size: 14px;\n  color: #6b7280;\n  white-space: nowrap;\n}\n\n.tip-custom-input {\n  width: 90px;\n  padding: 8px 12px;\n  border: 1.5px solid #fcd34d;\n  border-radius: 8px;\n  font-size: 16px;\n  font-weight: 600;\n  color: #1f2937;\n  background: #fffbeb;\n}\n\n.tip-custom-input:focus {\n  outline: none;\n  border-color: #d97706;\n  box-shadow: 0 0 0 3px rgba(217,119,6,0.15);\n}\n\n/* Quality label */\n.tip-quality-badge {\n  display: inline-block;\n  padding: 3px 12px;\n  border-radius: 20px;\n  font-size: 12px;\n  font-weight: 700;\n  letter-spacing: .04em;\n  margin-left: 8px;\n  vertical-align: middle;\n  transition: background .2s, color .2s;\n}\n\n/* People selector */\n.tip-people-row {\n  display: flex;\n  align-items: center;\n  gap: 14px;\n}\n\n.tip-people-btn {\n  width: 38px;\n  height: 38px;\n  border-radius: 50%;\n  border: 1.5px solid #fcd34d;\n  background: #fffbeb;\n  color: #92400e;\n  font-size: 22px;\n  font-weight: 700;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  line-height: 1;\n  transition: background .15s;\n}\n\n.tip-people-btn:hover {\n  background: #fef3c7;\n  border-color: #d97706;\n}\n\n.tip-people-num {\n  font-size: 26px;\n  font-weight: 800;\n  color: #d97706;\n  min-width: 32px;\n  text-align: center;\n}\n\n.tip-people-label {\n  font-size: 14px;\n  color: #6b7280;\n}\n\n/* Results */\n.tip-results-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 14px;\n  margin-bottom: 18px;\n}\n\n@media (max-width: 480px) {\n  .tip-results-grid {\n    grid-template-columns: 1fr;\n  }\n  .tip-amount-input {\n    font-size: 20px;\n  }\n}\n\n.tip-result-cell {\n  background: #fffbeb;\n  border: 1.5px solid #fde68a;\n  border-radius: 12px;\n  padding: 16px 18px;\n  text-align: center;\n}\n\n.tip-result-cell.highlight {\n  background: #d97706;\n  border-color: #b45309;\n}\n\n.tip-result-cell .label {\n  font-size: 12px;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: .05em;\n  color: #92400e;\n  margin-bottom: 6px;\n}\n\n.tip-result-cell.highlight .label {\n  color: #fef3c7;\n}\n\n.tip-result-cell .amount {\n  font-size: 26px;\n  font-weight: 800;\n  color: #1f2937;\n  line-height: 1.1;\n}\n\n.tip-result-cell.highlight .amount {\n  color: #fff;\n}\n\n.tip-result-cell .sub {\n  font-size: 12px;\n  color: #9ca3af;\n  margin-top: 4px;\n}\n\n.tip-result-cell.highlight .sub {\n  color: #fde68a;\n}\n\n/* Visual bar */\n.tip-bar-wrap {\n  margin: 4px 0 16px;\n}\n\n.tip-bar-labels {\n  display: flex;\n  justify-content: space-between;\n  font-size: 12px;\n  color: #6b7280;\n  margin-bottom: 5px;\n}\n\n.tip-bar-track {\n  width: 100%;\n  height: 18px;\n  background: #e5e7eb;\n  border-radius: 9px;\n  overflow: hidden;\n  display: flex;\n}\n\n.tip-bar-bill {\n  background: #6b7280;\n  height: 100%;\n  border-radius: 9px 0 0 9px;\n  transition: width .3s;\n}\n\n.tip-bar-tip {\n  background: #d97706;\n  height: 100%;\n  border-radius: 0 9px 9px 0;\n  transition: width .3s;\n}\n\n/* Round buttons */\n.tip-round-row {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n}\n\n.tip-round-btn {\n  flex: 1 1 auto;\n  padding: 9px 14px;\n  border: 1.5px solid #fcd34d;\n  border-radius: 9px;\n  background: #fffbeb;\n  color: #92400e;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background .15s, border-color .15s;\n  text-align: center;\n}\n\n.tip-round-btn:hover {\n  background: #fef3c7;\n  border-color: #d97706;\n}\n\n.tip-reset-btn {\n  width: 100%;\n  padding: 12px;\n  border: none;\n  border-radius: 10px;\n  background: #f3f4f6;\n  color: #6b7280;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  margin-top: 4px;\n  transition: background .15s;\n}\n\n.tip-reset-btn:hover {\n  background: #e5e7eb;\n}\n\u003c/style\u003e\n\u003c!-- Bill Amount --\u003e\n\u003cdiv class=\"tip-card\"\u003e\n  \u003cdiv class=\"tip-section-title\"\u003eBill Amount\u003c/div\u003e\n  \u003cdiv class=\"tip-input-row\"\u003e\n    \u003cselect class=\"tip-currency-select\" id=\"tip-currency\"\u003e\n      \u003coption value=\"$\"\u003e$\u003c/option\u003e\n      \u003coption value=\"€\"\u003e€\u003c/option\u003e\n      \u003coption value=\"£\"\u003e£\u003c/option\u003e\n      \u003coption value=\"¥\"\u003e¥\u003c/option\u003e\n    \u003c/select\u003e\n    \u003cinput class=\"tip-amount-input\" type=\"number\" id=\"tip-bill\" placeholder=\"0.00\" min=\"0\" step=\"0.01\" inputmode=\"decimal\" /\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Tip Percentage --\u003e\n\u003cdiv class=\"tip-card\"\u003e\n  \u003cdiv class=\"tip-section-title\"\u003e\n    Tip Percentage\n    \u003cspan class=\"tip-quality-badge\" id=\"tip-quality-badge\"\u003e\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"tip-quick-btns\"\u003e\n    \u003cbutton class=\"tip-pct-btn\" data-pct=\"10\"\u003e10%\u003c/button\u003e\n    \u003cbutton class=\"tip-pct-btn\" data-pct=\"15\"\u003e15%\u003c/button\u003e\n    \u003cbutton class=\"tip-pct-btn active\" data-pct=\"18\"\u003e18%\u003c/button\u003e\n    \u003cbutton class=\"tip-pct-btn\" data-pct=\"20\"\u003e20%\u003c/button\u003e\n    \u003cbutton class=\"tip-pct-btn\" data-pct=\"25\"\u003e25%\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"tip-custom-row\"\u003e\n    \u003cspan class=\"tip-custom-label\"\u003eCustom:\u003c/span\u003e\n    \u003cinput class=\"tip-custom-input\" type=\"number\" id=\"tip-custom-pct\" placeholder=\"%\" min=\"0\" max=\"100\" step=\"1\" inputmode=\"numeric\" /\u003e\n    \u003cspan style=\"font-size:14px;color:#6b7280;\"\u003e%\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Split Bill --\u003e\n\u003cdiv class=\"tip-card\"\u003e\n  \u003cdiv class=\"tip-section-title\"\u003eSplit Between\u003c/div\u003e\n  \u003cdiv class=\"tip-people-row\"\u003e\n    \u003cbutton class=\"tip-people-btn\" id=\"tip-minus\"\u003e−\u003c/button\u003e\n    \u003cdiv class=\"tip-people-num\" id=\"tip-people-num\"\u003e1\u003c/div\u003e\n    \u003cbutton class=\"tip-people-btn\" id=\"tip-plus\"\u003e+\u003c/button\u003e\n    \u003cspan class=\"tip-people-label\" id=\"tip-people-label\"\u003eperson\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Results --\u003e\n\u003cdiv class=\"tip-card\"\u003e\n  \u003cdiv class=\"tip-section-title\"\u003eResults\u003c/div\u003e\n  \u003cdiv class=\"tip-results-grid\"\u003e\n    \u003cdiv class=\"tip-result-cell\"\u003e\n      \u003cdiv class=\"label\"\u003eTip Amount\u003c/div\u003e\n      \u003cdiv class=\"amount\" id=\"res-tip\"\u003e—\u003c/div\u003e\n      \u003cdiv class=\"sub\" id=\"res-tip-pct\"\u003eat 18%\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tip-result-cell\"\u003e\n      \u003cdiv class=\"label\"\u003eTotal Bill\u003c/div\u003e\n      \u003cdiv class=\"amount\" id=\"res-total\"\u003e—\u003c/div\u003e\n      \u003cdiv class=\"sub\"\u003ebill + tip\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tip-result-cell highlight\" id=\"res-person-cell\"\u003e\n      \u003cdiv class=\"label\"\u003ePer Person\u003c/div\u003e\n      \u003cdiv class=\"amount\" id=\"res-person\"\u003e—\u003c/div\u003e\n      \u003cdiv class=\"sub\" id=\"res-person-sub\"\u003e1 person\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tip-result-cell\"\u003e\n      \u003cdiv class=\"label\"\u003eTip Per Person\u003c/div\u003e\n      \u003cdiv class=\"amount\" id=\"res-tip-person\"\u003e—\u003c/div\u003e\n      \u003cdiv class=\"sub\" id=\"res-tip-person-sub\"\u003e1 person\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Visual bar --\u003e\n  \u003cdiv class=\"tip-bar-wrap\"\u003e\n    \u003cdiv class=\"tip-bar-labels\"\u003e\n      \u003cspan id=\"bar-label-bill\"\u003eBill: —\u003c/span\u003e\n      \u003cspan id=\"bar-label-tip\"\u003eTip: —\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tip-bar-track\"\u003e\n      \u003cdiv class=\"tip-bar-bill\" id=\"bar-bill\" style=\"width:100%\"\u003e\u003c/div\u003e\n      \u003cdiv class=\"tip-bar-tip\" id=\"bar-tip\" style=\"width:0%\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Round buttons --\u003e\n  \u003cdiv class=\"tip-round-row\"\u003e\n    \u003cbutton class=\"tip-round-btn\" id=\"btn-round-up\"\u003eRound Up Total ↑\u003c/button\u003e\n    \u003cbutton class=\"tip-round-btn\" id=\"btn-round-down\"\u003eRound Down Total ↓\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cbutton class=\"tip-reset-btn\" id=\"btn-reset\"\u003eReset\u003c/button\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  'use strict';\n\n  // State\n  let bill = 0;\n  let tipPct = 18;\n  let people = 1;\n  let currency = '$';\n\n  // Elements\n  const elBill    = document.getElementById('tip-bill');\n  const elCur     = document.getElementById('tip-currency');\n  const elCustom  = document.getElementById('tip-custom-pct');\n  const elPeople  = document.getElementById('tip-people-num');\n  const elPLabel  = document.getElementById('tip-people-label');\n  const elQuality = document.getElementById('tip-quality-badge');\n\n  const elResTip       = document.getElementById('res-tip');\n  const elResTotal     = document.getElementById('res-total');\n  const elResPerson    = document.getElementById('res-person');\n  const elResTipPct    = document.getElementById('res-tip-pct');\n  const elResPersonSub = document.getElementById('res-person-sub');\n  const elResTipPerson = document.getElementById('res-tip-person');\n  const elResTipPersonSub = document.getElementById('res-tip-person-sub');\n\n  const barBill  = document.getElementById('bar-bill');\n  const barTip   = document.getElementById('bar-tip');\n  const lblBill  = document.getElementById('bar-label-bill');\n  const lblTip   = document.getElementById('bar-label-tip');\n\n  const pctBtns  = document.querySelectorAll('#tip-app .tip-pct-btn');\n\n  function fmt(n) {\n    if (currency === '¥') return currency + Math.round(n).toLocaleString();\n    return currency + n.toFixed(2);\n  }\n\n  function qualityLabel(pct) {\n    if (pct \u003c 10)  return { text: '',          bg: 'transparent', color: 'transparent' };\n    if (pct \u003c 15)  return { text: 'Fair',      bg: '#fef9c3', color: '#854d0e' };\n    if (pct \u003c 20)  return { text: 'Good',      bg: '#d1fae5', color: '#065f46' };\n    if (pct \u003c 25)  return { text: 'Great',     bg: '#bfdbfe', color: '#1e3a8a' };\n    return           { text: 'Excellent!',    bg: '#ede9fe', color: '#4c1d95' };\n  }\n\n  function calc(overrideBill) {\n    const b = (overrideBill !== undefined) ? overrideBill : bill;\n    const tipAmt   = b * tipPct / 100;\n    const total    = b + tipAmt;\n    const perPerson    = total / people;\n    const tipPerPerson = tipAmt / people;\n\n    elResTip.textContent       = fmt(tipAmt);\n    elResTotal.textContent     = fmt(total);\n    elResPerson.textContent    = fmt(perPerson);\n    elResTipPerson.textContent = fmt(tipPerPerson);\n    elResTipPct.textContent    = 'at ' + tipPct + '%';\n    elResPersonSub.textContent = people === 1 ? '1 person' : people + ' people';\n    elResTipPersonSub.textContent = people === 1 ? '1 person' : people + ' people';\n\n    // Bar\n    const billPct = total \u003e 0 ? (b / total * 100) : 100;\n    const tipBarPct = total \u003e 0 ? (tipAmt / total * 100) : 0;\n    barBill.style.width = billPct + '%';\n    barTip.style.width  = tipBarPct + '%';\n    lblBill.textContent = 'Bill: ' + fmt(b);\n    lblTip.textContent  = 'Tip: ' + fmt(tipAmt);\n\n    // Quality\n    const q = qualityLabel(tipPct);\n    elQuality.textContent   = q.text;\n    elQuality.style.background = q.bg;\n    elQuality.style.color      = q.color;\n  }\n\n  function setActivePct(pct) {\n    tipPct = pct;\n    pctBtns.forEach(b =\u003e {\n      b.classList.toggle('active', parseInt(b.dataset.pct, 10) === pct);\n    });\n    elCustom.value = '';\n    calc();\n  }\n\n  // Events — bill\n  elBill.addEventListener('input', function() {\n    bill = parseFloat(this.value) || 0;\n    calc();\n  });\n\n  // Events — currency\n  elCur.addEventListener('change', function() {\n    currency = this.value;\n    calc();\n  });\n\n  // Events — quick pct buttons\n  pctBtns.forEach(btn =\u003e {\n    btn.addEventListener('click', function() {\n      setActivePct(parseInt(this.dataset.pct, 10));\n    });\n  });\n\n  // Events — custom pct\n  elCustom.addEventListener('input', function() {\n    const v = parseFloat(this.value);\n    if (!isNaN(v) \u0026\u0026 v \u003e= 0) {\n      tipPct = v;\n      pctBtns.forEach(b =\u003e b.classList.remove('active'));\n      calc();\n    }\n  });\n\n  // Events — people\n  document.getElementById('tip-minus').addEventListener('click', function() {\n    if (people \u003e 1) { people--; update(); }\n  });\n  document.getElementById('tip-plus').addEventListener('click', function() {\n    if (people \u003c 20) { people++; update(); }\n  });\n\n  function update() {\n    elPeople.textContent = people;\n    elPLabel.textContent = people === 1 ? 'person' : 'people';\n    calc();\n  }\n\n  // Round up / down\n  document.getElementById('btn-round-up').addEventListener('click', function() {\n    if (bill \u003c= 0) return;\n    const tipAmt = bill * tipPct / 100;\n    const total  = bill + tipAmt;\n    const rounded = Math.ceil(total);\n    const newTip  = rounded - bill;\n    const newPct  = bill \u003e 0 ? (newTip / bill * 100) : tipPct;\n    tipPct = Math.round(newPct * 10) / 10;\n    elCustom.value = tipPct;\n    pctBtns.forEach(b =\u003e b.classList.remove('active'));\n    calc(bill);\n    // recalc with rounded total override approach\n    elResTotal.textContent = fmt(rounded);\n    elResTip.textContent   = fmt(newTip);\n    elResPerson.textContent    = fmt(rounded / people);\n    elResTipPerson.textContent = fmt(newTip / people);\n    barBill.style.width = (bill / rounded * 100) + '%';\n    barTip.style.width  = (newTip / rounded * 100) + '%';\n    lblTip.textContent  = 'Tip: ' + fmt(newTip);\n  });\n\n  document.getElementById('btn-round-down').addEventListener('click', function() {\n    if (bill \u003c= 0) return;\n    const tipAmt = bill * tipPct / 100;\n    const total  = bill + tipAmt;\n    const rounded = Math.floor(total);\n    if (rounded \u003c= bill) return;\n    const newTip  = rounded - bill;\n    const newPct  = bill \u003e 0 ? (newTip / bill * 100) : tipPct;\n    tipPct = Math.round(newPct * 10) / 10;\n    elCustom.value = tipPct;\n    pctBtns.forEach(b =\u003e b.classList.remove('active'));\n    calc(bill);\n    elResTotal.textContent = fmt(rounded);\n    elResTip.textContent   = fmt(newTip);\n    elResPerson.textContent    = fmt(rounded / people);\n    elResTipPerson.textContent = fmt(newTip / people);\n    barBill.style.width = (bill / rounded * 100) + '%';\n    barTip.style.width  = (newTip / rounded * 100) + '%';\n    lblTip.textContent  = 'Tip: ' + fmt(newTip);\n  });\n\n  // Reset\n  document.getElementById('btn-reset').addEventListener('click', function() {\n    bill = 0;\n    tipPct = 18;\n    people = 1;\n    currency = '$';\n    elBill.value = '';\n    elCur.value = '$';\n    elCustom.value = '';\n    elPeople.textContent = '1';\n    elPLabel.textContent = 'person';\n    pctBtns.forEach(b =\u003e b.classList.toggle('active', b.dataset.pct === '18'));\n    elResTip.textContent = '—';\n    elResTotal.textContent = '—';\n    elResPerson.textContent = '—';\n    elResTipPerson.textContent = '—';\n    elResTipPct.textContent = 'at 18%';\n    elResPersonSub.textContent = '1 person';\n    elResTipPersonSub.textContent = '1 person';\n    barBill.style.width = '100%';\n    barTip.style.width  = '0%';\n    lblBill.textContent = 'Bill: —';\n    lblTip.textContent  = 'Tip: —';\n    elQuality.textContent = '';\n    elQuality.style.background = 'transparent';\n  });\n\n  // Init\n  calc();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cstrong\u003eHow to use this tip calculator:\u003c/strong\u003e Enter your bill amount, choose a tip percentage (or type a custom one), then set how many people are splitting the bill. Use the round up/down buttons to land on a clean total, and switch currencies with the dropdown if you\u0026rsquo;re traveling abroad.\u003c/p\u003e","title":"Tip Calculator - Free Bill \u0026 Gratuity Splitter Tool"},{"content":"Convert TOML and INI configuration files to JSON — or convert JSON back to TOML. Paste your config, click Convert, and get clean output instantly. No installation, no server upload, everything runs in your browser.\nPresets: Cargo.toml pyproject.toml config.ini JSON Sample Pretty-print JSON Minify JSON output TOML / INI Input Clear Copy JSON Output Clear Copy TOML/INI \u0026rarr; JSON \u0026#8644; JSON \u0026rarr; TOML How It Works TOML/INI to JSON — Paste your config file in the left panel and click TOML/INI → JSON. The tool auto-detects whether the input is TOML or INI format.\nJSON to TOML — Paste or generate JSON in the right panel and click JSON → TOML to produce a clean TOML file in the left panel.\nSupported TOML Features Feature Example Strings (basic \u0026amp; literal) name = \u0026quot;hello\u0026quot;, path = 'C:\\tmp' Integers \u0026amp; floats port = 8080, ratio = 3.14 Booleans debug = true Arrays features = [\u0026quot;a\u0026quot;, \u0026quot;b\u0026quot;] Standard tables [database] Array of tables [[dependencies]] Dotted keys server.host = \u0026quot;localhost\u0026quot; Inline tables opts = { a = 1, b = 2 } INI Format Support INI sections ([section]), key = value pairs, and ; or # comments are all parsed. Values are auto-coerced: true/yes/on become booleans, numeric strings become numbers.\nTips Use Cargo.toml or pyproject.toml presets to see real-world examples. Enable Minify JSON to get compact output for embedding in code. The Copy button on each panel copies the content to your clipboard. JSON → TOML works from the right (JSON) panel — paste JSON there, then click the button. Related Tools YAML ↔ JSON Converter — Convert between YAML and JSON JSON Formatter \u0026amp; Validator — Format, validate, and minify JSON JSON Schema Generator — Generate JSON Schema from a JSON sample ","permalink":"https://productivity-works.com/tools/toml-json-converter/","summary":"\u003cp\u003eConvert TOML and INI configuration files to JSON — or convert JSON back to TOML. Paste your config, click Convert, and get clean output instantly. No installation, no server upload, everything runs in your browser.\u003c/p\u003e\n\u003cdiv id=\"toml-app\"\u003e\n\u003cstyle\u003e\n  #toml-app *,\n  #toml-app *::before,\n  #toml-app *::after {\n    box-sizing: border-box;\n    margin: 0;\n    padding: 0;\n  }\n  #toml-app {\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n    font-size: 14px;\n    color: #1a1a2e;\n    line-height: 1.5;\n  }\n  #toml-app .ta-toolbar {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 8px;\n    margin-bottom: 14px;\n    align-items: center;\n  }\n  #toml-app .ta-btn {\n    display: inline-flex;\n    align-items: center;\n    gap: 5px;\n    padding: 7px 14px;\n    border: none;\n    border-radius: 6px;\n    font-size: 13px;\n    font-weight: 600;\n    cursor: pointer;\n    transition: background 0.15s, transform 0.1s;\n    white-space: nowrap;\n  }\n  #toml-app .ta-btn:active { transform: scale(0.97); }\n  #toml-app .ta-btn-primary {\n    background: #4f46e5;\n    color: #fff;\n  }\n  #toml-app .ta-btn-primary:hover { background: #4338ca; }\n  #toml-app .ta-btn-secondary {\n    background: #e0e7ff;\n    color: #3730a3;\n  }\n  #toml-app .ta-btn-secondary:hover { background: #c7d2fe; }\n  #toml-app .ta-btn-ghost {\n    background: #f3f4f6;\n    color: #374151;\n    border: 1px solid #d1d5db;\n  }\n  #toml-app .ta-btn-ghost:hover { background: #e5e7eb; }\n  #toml-app .ta-btn-success {\n    background: #059669;\n    color: #fff;\n  }\n  #toml-app .ta-btn-success:hover { background: #047857; }\n  #toml-app .ta-panels {\n    display: grid;\n    grid-template-columns: 1fr 1fr;\n    gap: 16px;\n  }\n  @media (max-width: 700px) {\n    #toml-app .ta-panels {\n      grid-template-columns: 1fr;\n    }\n  }\n  #toml-app .ta-panel {\n    display: flex;\n    flex-direction: column;\n    gap: 8px;\n  }\n  #toml-app .ta-panel-header {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    flex-wrap: wrap;\n    gap: 6px;\n  }\n  #toml-app .ta-panel-label {\n    font-size: 13px;\n    font-weight: 700;\n    color: #374151;\n    text-transform: uppercase;\n    letter-spacing: 0.05em;\n  }\n  #toml-app .ta-panel-actions {\n    display: flex;\n    gap: 5px;\n    flex-wrap: wrap;\n  }\n  #toml-app textarea {\n    width: 100%;\n    min-height: 340px;\n    padding: 12px;\n    font-family: \"SF Mono\", \"Fira Code\", \"Consolas\", monospace;\n    font-size: 12.5px;\n    line-height: 1.6;\n    border: 2px solid #d1d5db;\n    border-radius: 8px;\n    resize: vertical;\n    background: #f9fafb;\n    color: #111827;\n    transition: border-color 0.15s;\n    outline: none;\n  }\n  #toml-app textarea:focus { border-color: #4f46e5; background: #fff; }\n  #toml-app textarea.ta-error { border-color: #ef4444; background: #fff5f5; }\n  #toml-app .ta-status {\n    min-height: 22px;\n    font-size: 12px;\n    padding: 3px 6px;\n    border-radius: 4px;\n  }\n  #toml-app .ta-status.ok { color: #065f46; background: #d1fae5; }\n  #toml-app .ta-status.err { color: #991b1b; background: #fee2e2; }\n  #toml-app .ta-center-col {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    gap: 10px;\n    padding: 0 4px;\n  }\n  #toml-app .ta-convert-row {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 10px;\n    margin: 12px 0;\n    flex-wrap: wrap;\n  }\n  #toml-app .ta-arrow {\n    font-size: 20px;\n    color: #6b7280;\n    line-height: 1;\n  }\n  #toml-app .ta-options-row {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 16px;\n    margin-bottom: 14px;\n    align-items: center;\n  }\n  #toml-app .ta-check-label {\n    display: flex;\n    align-items: center;\n    gap: 6px;\n    font-size: 13px;\n    color: #374151;\n    cursor: pointer;\n    user-select: none;\n  }\n  #toml-app .ta-check-label input[type=\"checkbox\"] {\n    width: 15px;\n    height: 15px;\n    accent-color: #4f46e5;\n    cursor: pointer;\n  }\n  #toml-app .ta-presets-row {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 7px;\n    margin-bottom: 14px;\n    align-items: center;\n  }\n  #toml-app .ta-presets-label {\n    font-size: 12px;\n    font-weight: 600;\n    color: #6b7280;\n    text-transform: uppercase;\n    letter-spacing: 0.05em;\n  }\n  #toml-app .ta-preset-btn {\n    padding: 4px 10px;\n    border: 1px solid #c7d2fe;\n    border-radius: 5px;\n    background: #eef2ff;\n    color: #3730a3;\n    font-size: 12px;\n    font-weight: 500;\n    cursor: pointer;\n    transition: background 0.12s;\n  }\n  #toml-app .ta-preset-btn:hover { background: #c7d2fe; }\n  #toml-app .ta-divider {\n    height: 1px;\n    background: #e5e7eb;\n    margin: 16px 0;\n  }\n  #toml-app .ta-copy-flash {\n    animation: taCopyFlash 0.6s ease;\n  }\n  @keyframes taCopyFlash {\n    0% { background: #a7f3d0; }\n    100% { background: #f9fafb; }\n  }\n\u003c/style\u003e\n\u003cdiv class=\"ta-presets-row\"\u003e\n  \u003cspan class=\"ta-presets-label\"\u003ePresets:\u003c/span\u003e\n  \u003cbutton class=\"ta-preset-btn\" onclick=\"taLoadPreset('cargo')\"\u003eCargo.toml\u003c/button\u003e\n  \u003cbutton class=\"ta-preset-btn\" onclick=\"taLoadPreset('pyproject')\"\u003epyproject.toml\u003c/button\u003e\n  \u003cbutton class=\"ta-preset-btn\" onclick=\"taLoadPreset('ini')\"\u003econfig.ini\u003c/button\u003e\n  \u003cbutton class=\"ta-preset-btn\" onclick=\"taLoadPreset('json')\"\u003eJSON Sample\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ta-options-row\"\u003e\n  \u003clabel class=\"ta-check-label\"\u003e\n    \u003cinput type=\"checkbox\" id=\"ta-pretty\" checked\u003e\n    Pretty-print JSON\n  \u003c/label\u003e\n  \u003clabel class=\"ta-check-label\"\u003e\n    \u003cinput type=\"checkbox\" id=\"ta-minify\"\u003e\n    Minify JSON output\n  \u003c/label\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ta-panels\"\u003e\n  \u003cdiv class=\"ta-panel\"\u003e\n    \u003cdiv class=\"ta-panel-header\"\u003e\n      \u003cspan class=\"ta-panel-label\"\u003eTOML / INI Input\u003c/span\u003e\n      \u003cdiv class=\"ta-panel-actions\"\u003e\n        \u003cbutton class=\"ta-btn ta-btn-ghost\" onclick=\"taClear('ta-input')\" style=\"font-size:12px;padding:4px 9px;\"\u003eClear\u003c/button\u003e\n        \u003cbutton class=\"ta-btn ta-btn-ghost\" onclick=\"taCopyText('ta-input')\" style=\"font-size:12px;padding:4px 9px;\"\u003eCopy\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003ctextarea id=\"ta-input\" placeholder=\"Paste TOML or INI here...\" spellcheck=\"false\"\u003e\u003c/textarea\u003e\n    \u003cdiv id=\"ta-input-status\" class=\"ta-status\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ta-panel\"\u003e\n    \u003cdiv class=\"ta-panel-header\"\u003e\n      \u003cspan class=\"ta-panel-label\"\u003eJSON Output\u003c/span\u003e\n      \u003cdiv class=\"ta-panel-actions\"\u003e\n        \u003cbutton class=\"ta-btn ta-btn-ghost\" onclick=\"taClear('ta-output')\" style=\"font-size:12px;padding:4px 9px;\"\u003eClear\u003c/button\u003e\n        \u003cbutton class=\"ta-btn ta-btn-ghost\" onclick=\"taCopyText('ta-output')\" style=\"font-size:12px;padding:4px 9px;\"\u003eCopy\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003ctextarea id=\"ta-output\" placeholder=\"JSON output appears here...\" spellcheck=\"false\"\u003e\u003c/textarea\u003e\n    \u003cdiv id=\"ta-output-status\" class=\"ta-status\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ta-convert-row\"\u003e\n  \u003cbutton class=\"ta-btn ta-btn-primary\" onclick=\"taConvertToJson()\"\u003eTOML/INI \u0026rarr; JSON\u003c/button\u003e\n  \u003cspan class=\"ta-arrow\"\u003e\u0026#8644;\u003c/span\u003e\n  \u003cbutton class=\"ta-btn ta-btn-success\" onclick=\"taConvertToToml()\"\u003eJSON \u0026rarr; TOML\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ta-divider\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  // ─── Presets ─────────────────────────────────────────────────────────────\n  const PRESETS = {\n    cargo: `[package]\nname = \"my-app\"\nversion = \"0.1.0\"\nedition = \"2021\"\nauthors = [\"Alice \u003calice@example.com\u003e\"]\ndescription = \"A sample Rust project\"\nlicense = \"MIT\"\n\n[dependencies]\nserde = { version = \"1.0\", features = [\"derive\"] }\ntokio = { version = \"1\", features = [\"full\"] }\nreqwest = \"0.11\"\n\n[dev-dependencies]\ntokio-test = \"0.4\"\n\n[profile.release]\nopt-level = 3\nlto = true\n`,\n    pyproject: `[build-system]\nrequires = [\"setuptools\u003e=61.0\", \"wheel\"]\nbuild-backend = \"setuptools.backends.legacy:build\"\n\n[project]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"An example Python package\"\nlicense = { text = \"MIT\" }\nrequires-python = \"\u003e=3.9\"\ndependencies = [\n  \"requests\u003e=2.28\",\n  \"pydantic\u003e=2.0\",\n  \"click\u003e=8.0\"\n]\n\n[project.optional-dependencies]\ndev = [\"pytest\", \"black\", \"mypy\"]\n\n[tool.black]\nline-length = 88\ntarget-version = [\"py39\", \"py310\", \"py311\"]\n`,\n    ini: `[general]\napp_name = MyApplication\nversion = 3.2.1\ndebug = false\nmax_retries = 5\n\n[database]\nhost = localhost\nport = 5432\nname = production_db\nuser = admin\npool_size = 10\n\n[logging]\nlevel = INFO\nfile = /var/log/myapp.log\nrotate = true\nmax_size_mb = 100\n\n[cache]\nbackend = redis\nhost = 127.0.0.1\nport = 6379\nttl = 3600\n`,\n    json: `{\n  \"server\": {\n    \"host\": \"0.0.0.0\",\n    \"port\": 8080,\n    \"debug\": false\n  },\n  \"database\": {\n    \"url\": \"postgres://localhost/mydb\",\n    \"pool_size\": 10,\n    \"timeout\": 30\n  },\n  \"features\": [\"auth\", \"api\", \"websocket\"],\n  \"limits\": {\n    \"max_upload_mb\": 50,\n    \"rate_limit_rps\": 100\n  }\n}`\n  };\n\n  window.taLoadPreset = function (name) {\n    if (name === 'json') {\n      document.getElementById('ta-output').value = PRESETS.json;\n      setStatus('ta-output-status', '', '');\n    } else {\n      document.getElementById('ta-input').value = PRESETS[name] || '';\n      setStatus('ta-input-status', '', '');\n    }\n    clearStatus('ta-input-status');\n    clearStatus('ta-output-status');\n    document.getElementById('ta-input').classList.remove('ta-error');\n    document.getElementById('ta-output').classList.remove('ta-error');\n  };\n\n  window.taClear = function (id) {\n    document.getElementById(id).value = '';\n    document.getElementById(id).classList.remove('ta-error');\n    const statusId = id === 'ta-input' ? 'ta-input-status' : 'ta-output-status';\n    clearStatus(statusId);\n  };\n\n  window.taCopyText = function (id) {\n    const el = document.getElementById(id);\n    const val = el.value;\n    if (!val) return;\n    navigator.clipboard.writeText(val).then(() =\u003e {\n      el.classList.add('ta-copy-flash');\n      setTimeout(() =\u003e el.classList.remove('ta-copy-flash'), 700);\n    }).catch(() =\u003e {\n      el.select();\n      document.execCommand('copy');\n    });\n  };\n\n  function setStatus(id, type, msg) {\n    const el = document.getElementById(id);\n    el.className = 'ta-status' + (type ? ' ' + type : '');\n    el.textContent = msg;\n  }\n  function clearStatus(id) { setStatus(id, '', ''); }\n\n  // ─── TOML Parser ─────────────────────────────────────────────────────────\n  function parseTOML(src) {\n    const lines = src.split('\\n');\n    const root = {};\n    let current = root;\n    let currentPath = [];\n    let i = 0;\n\n    function getOrCreate(obj, keys) {\n      let ref = obj;\n      for (const k of keys) {\n        if (!(k in ref)) ref[k] = {};\n        else if (Array.isArray(ref[k])) ref = ref[k][ref[k].length - 1];\n        else if (typeof ref[k] !== 'object') throw new Error('Key conflict: ' + k);\n        if (!Array.isArray(ref[k])) ref = ref[k];\n      }\n      return ref;\n    }\n\n    function deepGet(obj, keys) {\n      let ref = obj;\n      for (let ki = 0; ki \u003c keys.length; ki++) {\n        const k = keys[ki];\n        if (!(k in ref)) return undefined;\n        if (Array.isArray(ref[k]) \u0026\u0026 ki \u003c keys.length - 1) ref = ref[k][ref[k].length - 1];\n        else ref = ref[k];\n      }\n      return ref;\n    }\n\n    function parseValue(s) {\n      s = s.trim();\n      // Multi-line strings not fully supported — basic only\n      if (s.startsWith('\"\"\"') || s.startsWith(\"'''\")) {\n        const delim = s.slice(0, 3);\n        const end = s.indexOf(delim, 3);\n        if (end === -1) throw new Error('Unterminated multi-line string');\n        return s.slice(3, end);\n      }\n      if (s.startsWith('\"')) {\n        const m = s.match(/^\"((?:[^\"\\\\]|\\\\.)*)\"$/);\n        if (!m) throw new Error('Invalid string: ' + s);\n        return m[1].replace(/\\\\n/g, '\\n').replace(/\\\\t/g, '\\t').replace(/\\\\r/g, '\\r').replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, '\\\\');\n      }\n      if (s.startsWith(\"'\")) {\n        const m = s.match(/^'([^']*)'$/);\n        if (!m) throw new Error('Invalid literal string: ' + s);\n        return m[1];\n      }\n      if (s.startsWith('[')) return parseArray(s);\n      if (s.startsWith('{')) return parseInlineTable(s);\n      if (s === 'true') return true;\n      if (s === 'false') return false;\n      if (/^-?\\d{4}-\\d{2}-\\d{2}(T[\\d:+Z.-]+)?$/.test(s)) return s; // datetime as string\n      if (/^[+-]?(?:0x[0-9a-fA-F_]+|0o[0-7_]+|0b[01_]+)$/.test(s)) {\n        const clean = s.replace(/_/g, '');\n        return parseInt(clean, clean.startsWith('0x') ? 16 : clean.startsWith('0o') ? 8 : 2);\n      }\n      if (/^[+-]?(inf|nan)$/.test(s)) return s === 'inf' || s === '+inf' ? Infinity : s === '-inf' ? -Infinity : NaN;\n      if (/^[+-]?\\d[\\d_]*$/.test(s)) return parseInt(s.replace(/_/g, ''), 10);\n      if (/^[+-]?\\d[\\d_]*\\.[\\d_]*(e[+-]?\\d+)?$/i.test(s)) return parseFloat(s.replace(/_/g, ''));\n      if (/^[+-]?\\d[\\d_]*e[+-]?\\d+$/i.test(s)) return parseFloat(s.replace(/_/g, ''));\n      throw new Error('Cannot parse value: ' + s);\n    }\n\n    function parseArray(s) {\n      s = s.trim();\n      if (!s.startsWith('[')) throw new Error('Expected [');\n      let depth = 0, inStr = false, strChar = '', result = [], buf = '';\n      for (let ci = 0; ci \u003c s.length; ci++) {\n        const ch = s[ci];\n        if (inStr) {\n          if (ch === strChar \u0026\u0026 s[ci-1] !== '\\\\') inStr = false;\n          buf += ch;\n        } else if (ch === '\"' || ch === \"'\") {\n          inStr = true; strChar = ch; buf += ch;\n        } else if (ch === '[' || ch === '{') {\n          depth++; buf += ch;\n        } else if (ch === ']' || ch === '}') {\n          depth--;\n          if (depth === 0) {\n            const trimmed = buf.trim();\n            if (trimmed) result.push(parseValue(trimmed));\n            break;\n          }\n          buf += ch;\n        } else if (ch === ',' \u0026\u0026 depth === 1) {\n          const trimmed = buf.trim();\n          if (trimmed) result.push(parseValue(trimmed));\n          buf = '';\n        } else if (ch === '#' \u0026\u0026 depth === 1) {\n          break;\n        } else {\n          if (depth \u003e= 1) buf += ch;\n        }\n      }\n      return result;\n    }\n\n    function parseInlineTable(s) {\n      s = s.trim();\n      if (!s.startsWith('{')) throw new Error('Expected {');\n      const inner = s.slice(1, s.lastIndexOf('}'));\n      const obj = {};\n      let depth = 0, inStr = false, strChar = '', buf = '', parts = [];\n      for (let ci = 0; ci \u003c inner.length; ci++) {\n        const ch = inner[ci];\n        if (inStr) {\n          if (ch === strChar \u0026\u0026 inner[ci-1] !== '\\\\') inStr = false;\n          buf += ch;\n        } else if (ch === '\"' || ch === \"'\") {\n          inStr = true; strChar = ch; buf += ch;\n        } else if (ch === '{' || ch === '[') { depth++; buf += ch; }\n        else if (ch === '}' || ch === ']') { depth--; buf += ch; }\n        else if (ch === ',' \u0026\u0026 depth === 0) { parts.push(buf.trim()); buf = ''; }\n        else buf += ch;\n      }\n      if (buf.trim()) parts.push(buf.trim());\n      for (const part of parts) {\n        const eq = part.indexOf('=');\n        if (eq === -1) throw new Error('Invalid inline table entry: ' + part);\n        const k = part.slice(0, eq).trim();\n        const v = part.slice(eq + 1).trim();\n        obj[unquoteKey(k)] = parseValue(v);\n      }\n      return obj;\n    }\n\n    function unquoteKey(k) {\n      if ((k.startsWith('\"') \u0026\u0026 k.endsWith('\"')) || (k.startsWith(\"'\") \u0026\u0026 k.endsWith(\"'\")))\n        return k.slice(1, -1);\n      return k;\n    }\n\n    function parseKey(raw) {\n      const parts = [];\n      let buf = '', inStr = false, strChar = '';\n      for (let ci = 0; ci \u003c raw.length; ci++) {\n        const ch = raw[ci];\n        if (inStr) {\n          if (ch === strChar) { inStr = false; parts.push(buf); buf = ''; }\n          else buf += ch;\n        } else if (ch === '\"' || ch === \"'\") {\n          inStr = true; strChar = ch;\n        } else if (ch === '.') {\n          if (buf.trim()) parts.push(buf.trim());\n          buf = '';\n        } else buf += ch;\n      }\n      if (buf.trim()) parts.push(buf.trim());\n      return parts.map(unquoteKey);\n    }\n\n    while (i \u003c lines.length) {\n      let line = lines[i].replace(/\\s*#[^\"']*$/, '').trim();\n      i++;\n      if (!line) continue;\n\n      // Array of tables [[key]]\n      if (line.startsWith('[[')) {\n        const key = line.slice(2, line.indexOf(']]')).trim();\n        const keyParts = parseKey(key);\n        let ref = root;\n        for (let ki = 0; ki \u003c keyParts.length - 1; ki++) {\n          const k = keyParts[ki];\n          if (!(k in ref)) ref[k] = {};\n          if (Array.isArray(ref[k])) ref = ref[k][ref[k].length - 1];\n          else ref = ref[k];\n        }\n        const last = keyParts[keyParts.length - 1];\n        if (!Array.isArray(ref[last])) ref[last] = [];\n        const newTable = {};\n        ref[last].push(newTable);\n        current = newTable;\n        currentPath = keyParts;\n        continue;\n      }\n\n      // Standard table [key]\n      if (line.startsWith('[')) {\n        const key = line.slice(1, line.indexOf(']')).trim();\n        currentPath = parseKey(key);\n        current = root;\n        for (let ki = 0; ki \u003c currentPath.length; ki++) {\n          const k = currentPath[ki];\n          if (!(k in current)) current[k] = {};\n          if (Array.isArray(current[k])) current = current[k][current[k].length - 1];\n          else current = current[k];\n        }\n        continue;\n      }\n\n      // Key-value\n      const eqIdx = line.indexOf('=');\n      if (eqIdx === -1) throw new Error('Expected = in line: ' + line);\n      const rawKey = line.slice(0, eqIdx).trim();\n      let rawVal = line.slice(eqIdx + 1).trim();\n\n      // Handle multi-line arrays that continue on next lines\n      if (rawVal.startsWith('[') \u0026\u0026 !rawVal.includes(']')) {\n        while (i \u003c lines.length \u0026\u0026 !rawVal.includes(']')) {\n          rawVal += ' ' + lines[i].replace(/\\s*#.*$/, '').trim();\n          i++;\n        }\n      }\n\n      const keyParts = parseKey(rawKey);\n      let ref = current;\n      for (let ki = 0; ki \u003c keyParts.length - 1; ki++) {\n        const k = keyParts[ki];\n        if (!(k in ref)) ref[k] = {};\n        ref = ref[k];\n      }\n      ref[keyParts[keyParts.length - 1]] = parseValue(rawVal);\n    }\n    return root;\n  }\n\n  // ─── INI Parser ──────────────────────────────────────────────────────────\n  function isINI(src) {\n    const lines = src.trim().split('\\n');\n    for (const l of lines) {\n      const t = l.trim();\n      if (!t || t.startsWith(';') || t.startsWith('#') || t.startsWith('[') || t.includes('=')) continue;\n      return false;\n    }\n    // Has at least one [section] or key=value at root\n    return true;\n  }\n\n  function parseINI(src) {\n    const lines = src.split('\\n');\n    const result = {};\n    let section = null;\n    for (const raw of lines) {\n      const line = raw.trim();\n      if (!line || line.startsWith(';') || line.startsWith('#')) continue;\n      if (line.startsWith('[') \u0026\u0026 line.endsWith(']')) {\n        section = line.slice(1, -1).trim();\n        if (!(section in result)) result[section] = {};\n        continue;\n      }\n      const eq = line.indexOf('=');\n      if (eq === -1) continue;\n      const key = line.slice(0, eq).trim();\n      const val = line.slice(eq + 1).trim();\n      const target = section ? result[section] : result;\n      target[key] = coerceINI(val);\n    }\n    return result;\n  }\n\n  function coerceINI(v) {\n    if (v === 'true' || v === 'yes' || v === 'on') return true;\n    if (v === 'false' || v === 'no' || v === 'off') return false;\n    if (/^-?\\d+$/.test(v)) return parseInt(v, 10);\n    if (/^-?\\d+\\.\\d+$/.test(v)) return parseFloat(v);\n    // Strip surrounding quotes if any\n    if ((v.startsWith('\"') \u0026\u0026 v.endsWith('\"')) || (v.startsWith(\"'\") \u0026\u0026 v.endsWith(\"'\")))\n      return v.slice(1, -1);\n    return v;\n  }\n\n  // ─── JSON → TOML ─────────────────────────────────────────────────────────\n  function tomlStringify(obj, indent) {\n    return buildTOML(obj, [], indent || '');\n  }\n\n  function buildTOML(obj, path, indent) {\n    let scalars = '', tables = '';\n    for (const [k, v] of Object.entries(obj)) {\n      const key = /^[A-Za-z0-9_-]+$/.test(k) ? k : '\"' + k + '\"';\n      if (v === null || v === undefined) {\n        scalars += key + ' = \"\"\\n';\n      } else if (typeof v === 'boolean') {\n        scalars += key + ' = ' + v + '\\n';\n      } else if (typeof v === 'number') {\n        scalars += key + ' = ' + v + '\\n';\n      } else if (typeof v === 'string') {\n        scalars += key + ' = ' + JSON.stringify(v) + '\\n';\n      } else if (Array.isArray(v)) {\n        if (v.length === 0 || typeof v[0] !== 'object' || v[0] === null) {\n          scalars += key + ' = ' + tomlArray(v) + '\\n';\n        } else {\n          // Array of tables\n          for (const item of v) {\n            tables += '\\n[[' + [...path, key].join('.') + ']]\\n';\n            tables += buildTOMLScalars(item, [...path, key]);\n          }\n        }\n      } else if (typeof v === 'object') {\n        tables += '\\n[' + [...path, key].join('.') + ']\\n';\n        tables += buildTOML(v, [...path, key], indent);\n      }\n    }\n    return scalars + tables;\n  }\n\n  function buildTOMLScalars(obj, path) {\n    let out = '';\n    for (const [k, v] of Object.entries(obj)) {\n      const key = /^[A-Za-z0-9_-]+$/.test(k) ? k : '\"' + k + '\"';\n      if (typeof v === 'object' \u0026\u0026 v !== null \u0026\u0026 !Array.isArray(v)) {\n        out += '\\n[' + [...path, key].join('.') + ']\\n';\n        out += buildTOMLScalars(v, [...path, key]);\n      } else if (Array.isArray(v) \u0026\u0026 v.length \u003e 0 \u0026\u0026 typeof v[0] === 'object') {\n        for (const item of v) {\n          out += '\\n[[' + [...path, key].join('.') + ']]\\n';\n          out += buildTOMLScalars(item, [...path, key]);\n        }\n      } else {\n        if (v === null) out += key + ' = \"\"\\n';\n        else if (typeof v === 'boolean') out += key + ' = ' + v + '\\n';\n        else if (typeof v === 'number') out += key + ' = ' + v + '\\n';\n        else if (typeof v === 'string') out += key + ' = ' + JSON.stringify(v) + '\\n';\n        else out += key + ' = ' + tomlArray(v) + '\\n';\n      }\n    }\n    return out;\n  }\n\n  function tomlArray(arr) {\n    return '[' + arr.map(v =\u003e {\n      if (typeof v === 'string') return JSON.stringify(v);\n      if (typeof v === 'object' \u0026\u0026 v !== null) return '{' + Object.entries(v).map(([k2, v2]) =\u003e k2 + ' = ' + (typeof v2 === 'string' ? JSON.stringify(v2) : v2)).join(', ') + '}';\n      return String(v);\n    }).join(', ') + ']';\n  }\n\n  // ─── Convert functions ───────────────────────────────────────────────────\n  window.taConvertToJson = function () {\n    const input = document.getElementById('ta-input');\n    const output = document.getElementById('ta-output');\n    const pretty = document.getElementById('ta-pretty').checked;\n    const minify = document.getElementById('ta-minify').checked;\n    const src = input.value.trim();\n    if (!src) {\n      setStatus('ta-input-status', 'err', 'Input is empty.');\n      input.classList.add('ta-error');\n      return;\n    }\n    input.classList.remove('ta-error');\n    try {\n      let parsed;\n      // Detect INI by looking for ; comments or = without TOML-style quoting needs\n      const looksLikeINI = /^\\s*\\[/.test(src) \u0026\u0026 /^\\s*(;|#)/.test(src.split('\\n').find(l =\u003e l.trim()) || '');\n      // Better heuristic: if has ; comments or no quotes around string values -\u003e likely INI\n      const hasINIComments = src.split('\\n').some(l =\u003e l.trim().startsWith(';'));\n      parsed = (hasINIComments || (isINI(src) \u0026\u0026 !src.includes('\"\"\"') \u0026\u0026 !src.match(/\\[.+\\..+\\]/)))\n        ? parseINI(src)\n        : parseTOML(src);\n      const indent = (!minify \u0026\u0026 pretty) ? 2 : undefined;\n      output.value = JSON.stringify(parsed, null, indent);\n      output.classList.remove('ta-error');\n      setStatus('ta-input-status', 'ok', 'Parsed successfully.');\n      setStatus('ta-output-status', 'ok', 'JSON ready.');\n    } catch (e) {\n      input.classList.add('ta-error');\n      output.value = '';\n      setStatus('ta-input-status', 'err', 'Error: ' + e.message);\n      setStatus('ta-output-status', '', '');\n    }\n  };\n\n  window.taConvertToToml = function () {\n    const input = document.getElementById('ta-input');\n    const output = document.getElementById('ta-output');\n    const src = output.value.trim() || document.getElementById('ta-output').value.trim();\n    const jsonSrc = output.value.trim();\n    if (!jsonSrc) {\n      setStatus('ta-output-status', 'err', 'JSON output area is empty. Paste JSON there first.');\n      output.classList.add('ta-error');\n      return;\n    }\n    output.classList.remove('ta-error');\n    try {\n      const parsed = JSON.parse(jsonSrc);\n      if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n        throw new Error('Top-level JSON must be an object (not array or primitive).');\n      }\n      const toml = tomlStringify(parsed).trimStart();\n      input.value = toml;\n      input.classList.remove('ta-error');\n      setStatus('ta-output-status', 'ok', 'Converted to TOML.');\n      setStatus('ta-input-status', 'ok', 'TOML ready.');\n    } catch (e) {\n      output.classList.add('ta-error');\n      setStatus('ta-output-status', 'err', 'Error: ' + e.message);\n    }\n  };\n\n  // Sync pretty/minify options\n  document.getElementById('ta-pretty').addEventListener('change', function () {\n    if (this.checked) document.getElementById('ta-minify').checked = false;\n  });\n  document.getElementById('ta-minify').addEventListener('change', function () {\n    if (this.checked) document.getElementById('ta-pretty').checked = false;\n  });\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-it-works\"\u003eHow It Works\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eTOML/INI to JSON\u003c/strong\u003e — Paste your config file in the left panel and click \u003cstrong\u003eTOML/INI → JSON\u003c/strong\u003e. The tool auto-detects whether the input is TOML or INI format.\u003c/p\u003e","title":"TOML/INI ↔ JSON Converter"},{"content":"Find out how fast you really type — just start typing to begin, and get your WPM, accuracy, and error count updated live. No account needed, works entirely in your browser.\nDifficulty Easy Medium Hard Duration 30 seconds 60 seconds 120 seconds \u0026nbsp; Reset 60s Press any key to start Click the text area above and start typing — the timer starts on your first keystroke.\n0WPM 100%Accuracy 0Characters 0Errors 0sElapsed Time's Up!\n0WPM 0%Accuracy 0Characters Try Again Count lines of code → Line Counter Format your code → Code Minifier \u0026amp; Beautifier Check text differences → Text Diff Checker Related Articles Best Language Learning Apps 2026: Actually Become Conversational ","permalink":"https://productivity-works.com/tools/typing-speed-test/","summary":"\u003cp\u003eFind out how fast you really type — just start typing to begin, and get your WPM, accuracy, and error count updated live. No account needed, works entirely in your browser.\u003c/p\u003e\n\u003cdiv id=\"tt-app\"\u003e\n\u003cstyle\u003e\n#tt-app {\n  font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n  max-width: 780px;\n  margin: 0 auto;\n  color: #1e293b;\n}\n#tt-app * { box-sizing: border-box; }\n\u003cp\u003e#tt-app .tt-controls {\ndisplay: flex;\nflex-wrap: wrap;\ngap: 12px;\nalign-items: flex-end;\nmargin-bottom: 18px;\n}\n#tt-app .tt-control-group {\ndisplay: flex;\nflex-direction: column;\ngap: 4px;\n}\n#tt-app .tt-control-group label {\nfont-size: 11px;\nfont-weight: 600;\ntext-transform: uppercase;\nletter-spacing: 0.06em;\ncolor: #64748b;\n}\n#tt-app select {\nbackground: #f1f5f9;\nborder: 1px solid #cbd5e1;\nborder-radius: 6px;\npadding: 7px 30px 7px 12px;\nfont-size: 14px;\ncolor: #1e293b;\ncursor: pointer;\nfont-family: inherit;\nappearance: none;\nbackground-image: url(\u0026ldquo;data:image/svg+xml,%3Csvg xmlns=\u0026lsquo;\u003ca href=\"http://www.w3.org/2000/svg%27\" target=\"_blank\" rel=\"noopener noreferrer\"\u003ehttp://www.w3.org/2000/svg'\u003c/a\u003e\n width=\u0026lsquo;12\u0026rsquo; height=\u0026lsquo;8\u0026rsquo; viewBox=\u0026lsquo;0 0 12 8\u0026rsquo;%3E%3Cpath d=\u0026lsquo;M1 1l5 5 5-5\u0026rsquo; stroke=\u0026rsquo;%2364748b\u0026rsquo; stroke-width=\u0026lsquo;1.5\u0026rsquo; fill=\u0026lsquo;none\u0026rsquo; stroke-linecap=\u0026lsquo;round\u0026rsquo;/%3E%3C/svg%3E\u0026rdquo;);\nbackground-repeat: no-repeat;\nbackground-position: right 10px center;\n}\n#tt-app select:focus { outline: 2px solid #3b82f6; outline-offset: 1px; }\u003c/p\u003e","title":"Typing Speed Test"},{"content":" Typography Scale Generator Build modular type scales for harmonious, consistent web typography\nPresets Blog App UI Documentation Landing Page Settings Base Font Size (px) Scale Ratio Minor Second — 1.067 Major Second — 1.125 Minor Third — 1.200 Major Third — 1.250 Perfect Fourth — 1.333 Augmented Fourth — 1.414 Perfect Fifth — 1.500 Golden Ratio — 1.618 Output Unit rem px em Type Scale Preview Level Size Line Height Preview CSS Output Copy CSS Related Tools CSS Variables Generator Font Pair Suggester Reading Time Calculator ","permalink":"https://productivity-works.com/tools/typography-scale/","summary":"\u003cdiv id=\"ts-app2\"\u003e\n\u003cstyle\u003e\n#ts-app2 *,\n#ts-app2 *::before,\n#ts-app2 *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\n#ts-app2 {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  color: #1a1a2e;\n  line-height: 1.6;\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 0 16px;\n}\n\n#ts-app2 .ts-card {\n  background: #ffffff;\n  border: 1px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 24px;\n  margin-bottom: 20px;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.05);\n}\n\n#ts-app2 .ts-hero {\n  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n  color: #fff;\n  border-radius: 14px;\n  padding: 36px 28px 28px;\n  margin-bottom: 24px;\n  text-align: center;\n}\n\n#ts-app2 .ts-hero h2 {\n  font-size: 1.75rem;\n  font-weight: 800;\n  margin-bottom: 8px;\n  letter-spacing: -0.02em;\n}\n\n#ts-app2 .ts-hero p {\n  font-size: 1rem;\n  opacity: 0.88;\n}\n\n#ts-app2 .ts-section-title {\n  font-size: 0.8rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #7c3aed;\n  margin-bottom: 14px;\n}\n\n#ts-app2 .ts-controls-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n  gap: 16px;\n  margin-bottom: 16px;\n}\n\n#ts-app2 .ts-field label {\n  display: block;\n  font-size: 0.8rem;\n  font-weight: 600;\n  color: #374151;\n  margin-bottom: 6px;\n}\n\n#ts-app2 .ts-field input[type=\"number\"],\n#ts-app2 .ts-field select {\n  width: 100%;\n  padding: 10px 14px;\n  border: 1.5px solid #d1d5db;\n  border-radius: 8px;\n  font-size: 0.95rem;\n  background: #f9fafb;\n  color: #1a1a2e;\n  transition: border-color 0.2s;\n  appearance: none;\n  -webkit-appearance: none;\n}\n\n#ts-app2 .ts-field input[type=\"number\"]:focus,\n#ts-app2 .ts-field select:focus {\n  outline: none;\n  border-color: #7c3aed;\n  background: #fff;\n}\n\n#ts-app2 .ts-presets {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 6px;\n}\n\n#ts-app2 .ts-preset-btn {\n  padding: 6px 14px;\n  border-radius: 20px;\n  border: 1.5px solid #d1d5db;\n  background: #f9fafb;\n  font-size: 0.8rem;\n  font-weight: 600;\n  color: #374151;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n\n#ts-app2 .ts-preset-btn:hover,\n#ts-app2 .ts-preset-btn.active {\n  background: #7c3aed;\n  border-color: #7c3aed;\n  color: #fff;\n}\n\n#ts-app2 .ts-unit-toggle {\n  display: flex;\n  gap: 6px;\n  flex-wrap: wrap;\n}\n\n#ts-app2 .ts-unit-btn {\n  padding: 6px 16px;\n  border-radius: 8px;\n  border: 1.5px solid #d1d5db;\n  background: #f9fafb;\n  font-size: 0.82rem;\n  font-weight: 700;\n  color: #374151;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n\n#ts-app2 .ts-unit-btn.active {\n  background: #7c3aed;\n  border-color: #7c3aed;\n  color: #fff;\n}\n\n#ts-app2 .ts-scale-table {\n  width: 100%;\n  border-collapse: collapse;\n  margin-bottom: 8px;\n}\n\n#ts-app2 .ts-scale-table th {\n  font-size: 0.75rem;\n  font-weight: 700;\n  color: #6b7280;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  padding: 8px 12px;\n  text-align: left;\n  border-bottom: 2px solid #e5e7eb;\n  background: #f9fafb;\n}\n\n#ts-app2 .ts-scale-table td {\n  padding: 10px 12px;\n  border-bottom: 1px solid #f0f0f0;\n  vertical-align: middle;\n}\n\n#ts-app2 .ts-scale-table tr:last-child td {\n  border-bottom: none;\n}\n\n#ts-app2 .ts-scale-table tr:hover td {\n  background: #faf5ff;\n}\n\n#ts-app2 .ts-tag-label {\n  display: inline-block;\n  background: #ede9fe;\n  color: #7c3aed;\n  font-size: 0.75rem;\n  font-weight: 700;\n  padding: 2px 8px;\n  border-radius: 5px;\n  letter-spacing: 0.04em;\n}\n\n#ts-app2 .ts-size-value {\n  font-family: \"SF Mono\", \"Fira Code\", \"Fira Mono\", monospace;\n  font-size: 0.82rem;\n  color: #374151;\n}\n\n#ts-app2 .ts-lh-value {\n  font-size: 0.78rem;\n  color: #6b7280;\n}\n\n#ts-app2 .ts-preview-col {\n  max-width: 260px;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n}\n\n#ts-app2 .ts-preview-text {\n  color: #1a1a2e;\n  font-weight: 600;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  display: block;\n}\n\n#ts-app2 .ts-css-output {\n  position: relative;\n  background: #1e1e2e;\n  border-radius: 10px;\n  padding: 20px;\n  overflow: auto;\n  max-height: 400px;\n}\n\n#ts-app2 .ts-css-output pre {\n  font-family: \"SF Mono\", \"Fira Code\", \"Fira Mono\", monospace;\n  font-size: 0.82rem;\n  line-height: 1.7;\n  color: #cdd6f4;\n  white-space: pre;\n  margin: 0;\n}\n\n#ts-app2 .ts-css-output .tok-prop   { color: #89b4fa; }\n#ts-app2 .ts-css-output .tok-val    { color: #a6e3a1; }\n#ts-app2 .tok-selector              { color: #cba6f7; }\n#ts-app2 .tok-comment               { color: #6c7086; font-style: italic; }\n#ts-app2 .tok-brace                 { color: #f38ba8; }\n\n#ts-app2 .ts-copy-btn {\n  position: absolute;\n  top: 14px;\n  right: 14px;\n  padding: 6px 14px;\n  background: #7c3aed;\n  color: #fff;\n  border: none;\n  border-radius: 7px;\n  font-size: 0.78rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.15s;\n  letter-spacing: 0.03em;\n}\n\n#ts-app2 .ts-copy-btn:hover { background: #6d28d9; }\n#ts-app2 .ts-copy-btn.copied { background: #059669; }\n\n#ts-app2 .ts-crosslinks {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  margin-top: 4px;\n}\n\n#ts-app2 .ts-crosslinks a {\n  color: #7c3aed;\n  text-decoration: none;\n  font-size: 0.85rem;\n  font-weight: 600;\n  padding: 5px 12px;\n  border: 1.5px solid #ddd6fe;\n  border-radius: 8px;\n  background: #faf5ff;\n  transition: all 0.15s;\n}\n\n#ts-app2 .ts-crosslinks a:hover {\n  background: #7c3aed;\n  color: #fff;\n  border-color: #7c3aed;\n}\n\n#ts-app2 .ts-info-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  margin-top: 10px;\n}\n\n#ts-app2 .ts-info-chip {\n  background: #f0fdf4;\n  border: 1px solid #bbf7d0;\n  color: #166534;\n  font-size: 0.78rem;\n  font-weight: 600;\n  padding: 4px 12px;\n  border-radius: 20px;\n}\n\n@media (max-width: 600px) {\n  #ts-app2 .ts-hero h2 { font-size: 1.3rem; }\n  #ts-app2 .ts-controls-grid { grid-template-columns: 1fr; }\n  #ts-app2 .ts-scale-table th:nth-child(4),\n  #ts-app2 .ts-scale-table td:nth-child(4) { display: none; }\n  #ts-app2 .ts-preview-col { max-width: 120px; }\n}\n\u003c/style\u003e\n\u003cdiv class=\"ts-hero\"\u003e\n  \u003ch2\u003eTypography Scale Generator\u003c/h2\u003e\n  \u003cp\u003eBuild modular type scales for harmonious, consistent web typography\u003c/p\u003e","title":"Typography Scale Generator — Modular Type System"},{"content":"Browse Unicode characters by category or search by name. Click any character to copy it — along with its code point, HTML entity, CSS content value, and UTF-8 hex bytes.\nRecently Copied Related Tools Search emoji → Emoji Search Encode HTML entities → HTML Entity Encoder ","permalink":"https://productivity-works.com/tools/character-map/","summary":"\u003cp\u003eBrowse Unicode characters by category or search by name. Click any character to copy it — along with its code point, HTML entity, CSS \u003ccode\u003econtent\u003c/code\u003e value, and UTF-8 hex bytes.\u003c/p\u003e\n\u003cdiv id=\"cm-app\"\u003e\n\u003cstyle\u003e\n#cm-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  max-width: 980px;\n  margin: 0 auto;\n  color: #1a1a1a;\n}\n#cm-app *, #cm-app *::before, #cm-app *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n/* ── Controls ── */\n#cm-app .cm-controls {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  margin-bottom: 18px;\n  align-items: center;\n}\n#cm-app .cm-search {\n  flex: 1 1 220px;\n  padding: 9px 14px;\n  border: 1.5px solid #d1d5db;\n  border-radius: 8px;\n  font-size: 14px;\n  outline: none;\n  transition: border-color .2s;\n}\n#cm-app .cm-search:focus { border-color: #6366f1; }\n#cm-app .cm-cat-select {\n  flex: 1 1 180px;\n  padding: 9px 12px;\n  border: 1.5px solid #d1d5db;\n  border-radius: 8px;\n  font-size: 14px;\n  background: #fff;\n  cursor: pointer;\n  outline: none;\n}\n#cm-app .cm-cat-select:focus { border-color: #6366f1; }\n\n/* ── Category tabs ── */\n#cm-app .cm-tabs {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 6px;\n  margin-bottom: 16px;\n}\n#cm-app .cm-tab {\n  padding: 6px 13px;\n  border: 1.5px solid #e5e7eb;\n  border-radius: 20px;\n  font-size: 13px;\n  background: #f9fafb;\n  cursor: pointer;\n  transition: background .15s, border-color .15s, color .15s;\n  user-select: none;\n}\n#cm-app .cm-tab:hover { background: #ede9fe; border-color: #a5b4fc; }\n#cm-app .cm-tab.active { background: #6366f1; border-color: #6366f1; color: #fff; font-weight: 600; }\n\n/* ── Grid ── */\n#cm-app .cm-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(52px, 1fr));\n  gap: 6px;\n  margin-bottom: 20px;\n  min-height: 80px;\n}\n#cm-app .cm-cell {\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  height: 52px;\n  border: 1.5px solid #e5e7eb;\n  border-radius: 8px;\n  font-size: 22px;\n  cursor: pointer;\n  background: #fff;\n  transition: background .15s, border-color .15s, transform .1s;\n  user-select: none;\n}\n#cm-app .cm-cell:hover {\n  background: #ede9fe;\n  border-color: #6366f1;\n  transform: scale(1.12);\n  z-index: 2;\n}\n#cm-app .cm-cell:active { transform: scale(1.04); }\n#cm-app .cm-cell .cm-tip {\n  display: none;\n  position: absolute;\n  bottom: calc(100% + 6px);\n  left: 50%;\n  transform: translateX(-50%);\n  background: #1e1b4b;\n  color: #fff;\n  font-size: 11px;\n  padding: 5px 8px;\n  border-radius: 6px;\n  white-space: nowrap;\n  pointer-events: none;\n  z-index: 10;\n  line-height: 1.5;\n  text-align: center;\n}\n#cm-app .cm-cell:hover .cm-tip { display: block; }\n\n/* ── Detail panel ── */\n#cm-app .cm-detail {\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 16px 20px;\n  margin-bottom: 20px;\n  display: none;\n}\n#cm-app .cm-detail.visible { display: block; }\n#cm-app .cm-detail-char {\n  font-size: 48px;\n  line-height: 1;\n  margin-bottom: 10px;\n}\n#cm-app .cm-detail-name {\n  font-size: 15px;\n  font-weight: 700;\n  color: #4f46e5;\n  margin-bottom: 10px;\n}\n#cm-app .cm-detail-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));\n  gap: 8px;\n}\n#cm-app .cm-detail-row {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 7px;\n  padding: 8px 12px;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 8px;\n}\n#cm-app .cm-detail-label {\n  font-size: 11px;\n  color: #64748b;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: .04em;\n  white-space: nowrap;\n}\n#cm-app .cm-detail-val {\n  font-size: 13px;\n  font-family: monospace;\n  color: #1e293b;\n  word-break: break-all;\n}\n#cm-app .cm-copy-btn {\n  padding: 3px 10px;\n  background: #6366f1;\n  color: #fff;\n  border: none;\n  border-radius: 5px;\n  font-size: 12px;\n  cursor: pointer;\n  white-space: nowrap;\n  transition: background .15s;\n}\n#cm-app .cm-copy-btn:hover { background: #4f46e5; }\n\n/* ── Recently copied ── */\n#cm-app .cm-recent-section { margin-bottom: 22px; }\n#cm-app .cm-section-title {\n  font-size: 13px;\n  font-weight: 700;\n  color: #6b7280;\n  text-transform: uppercase;\n  letter-spacing: .06em;\n  margin-bottom: 8px;\n}\n#cm-app .cm-recent-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 6px;\n}\n#cm-app .cm-recent-chip {\n  display: flex;\n  align-items: center;\n  gap: 5px;\n  padding: 5px 10px;\n  background: #f1f5f9;\n  border: 1px solid #e2e8f0;\n  border-radius: 20px;\n  font-size: 18px;\n  cursor: pointer;\n  transition: background .15s;\n  user-select: none;\n}\n#cm-app .cm-recent-chip:hover { background: #ede9fe; border-color: #a5b4fc; }\n#cm-app .cm-recent-chip .chip-label { font-size: 11px; color: #64748b; }\n\n/* ── Toast ── */\n#cm-app .cm-toast {\n  position: fixed;\n  bottom: 24px;\n  right: 24px;\n  background: #1e1b4b;\n  color: #fff;\n  padding: 10px 18px;\n  border-radius: 8px;\n  font-size: 14px;\n  z-index: 9999;\n  opacity: 0;\n  transform: translateY(10px);\n  transition: opacity .25s, transform .25s;\n  pointer-events: none;\n}\n#cm-app .cm-toast.show { opacity: 1; transform: translateY(0); }\n\n/* ── No results ── */\n#cm-app .cm-empty {\n  grid-column: 1/-1;\n  text-align: center;\n  color: #9ca3af;\n  font-size: 14px;\n  padding: 30px 0;\n}\n\n/* ── Responsive ── */\n@media (max-width: 480px) {\n  #cm-app .cm-grid { grid-template-columns: repeat(auto-fill, minmax(44px, 1fr)); gap: 4px; }\n  #cm-app .cm-cell { height: 44px; font-size: 18px; }\n  #cm-app .cm-detail-char { font-size: 36px; }\n}\n\u003c/style\u003e\n\u003c!-- Controls --\u003e\n\u003cdiv class=\"cm-controls\"\u003e\n  \u003cinput class=\"cm-search\" id=\"cm-search\" type=\"search\" placeholder=\"Search by name (e.g. arrow, heart, star)...\" autocomplete=\"off\" /\u003e\n\u003c/div\u003e\n\u003c!-- Category tabs --\u003e\n\u003cdiv class=\"cm-tabs\" id=\"cm-tabs\"\u003e\u003c/div\u003e\n\u003c!-- Recently copied --\u003e\n\u003cdiv class=\"cm-recent-section\" id=\"cm-recent-section\" style=\"display:none;\"\u003e\n  \u003cdiv class=\"cm-section-title\"\u003eRecently Copied\u003c/div\u003e\n  \u003cdiv class=\"cm-recent-row\" id=\"cm-recent-row\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Detail panel --\u003e\n\u003cdiv class=\"cm-detail\" id=\"cm-detail\"\u003e\n  \u003cdiv class=\"cm-detail-char\" id=\"cm-detail-char\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"cm-detail-name\" id=\"cm-detail-name\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"cm-detail-grid\" id=\"cm-detail-grid\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Character grid --\u003e\n\u003cdiv class=\"cm-grid\" id=\"cm-grid\"\u003e\u003c/div\u003e\n\u003c!-- Toast --\u003e\n\u003cdiv class=\"cm-toast\" id=\"cm-toast\"\u003e\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n'use strict';\n\n/* ── Character database ── */\nconst CATS = [\n  { id:'latin',    label:'Basic Latin' },\n  { id:'latin-ext',label:'Latin Extended' },\n  { id:'greek',    label:'Greek' },\n  { id:'cyrillic', label:'Cyrillic' },\n  { id:'arrows',   label:'Arrows' },\n  { id:'math',     label:'Math Symbols' },\n  { id:'box',      label:'Box Drawing' },\n  { id:'currency', label:'Currency' },\n  { id:'dingbats', label:'Dingbats' },\n];\n\nfunction makeRange(start, end, names) {\n  const out = [];\n  for (let cp = start; cp \u003c= end; cp++) {\n    out.push({ cp, name: names[cp] || '' });\n  }\n  return out;\n}\n\n/* Named character data (curated subset; full names for common chars) */\nconst NAMED = {};\nconst define = (cp, name) =\u003e { NAMED[cp] = name; };\n\n// Basic Latin printable (0x20–0x7E)\nconst latinNames = {\n  0x20:'SPACE',0x21:'EXCLAMATION MARK',0x22:'QUOTATION MARK',0x23:'NUMBER SIGN',\n  0x24:'DOLLAR SIGN',0x25:'PERCENT SIGN',0x26:'AMPERSAND',0x27:'APOSTROPHE',\n  0x28:'LEFT PARENTHESIS',0x29:'RIGHT PARENTHESIS',0x2A:'ASTERISK',0x2B:'PLUS SIGN',\n  0x2C:'COMMA',0x2D:'HYPHEN-MINUS',0x2E:'FULL STOP',0x2F:'SOLIDUS',\n  0x30:'DIGIT ZERO',0x31:'DIGIT ONE',0x32:'DIGIT TWO',0x33:'DIGIT THREE',\n  0x34:'DIGIT FOUR',0x35:'DIGIT FIVE',0x36:'DIGIT SIX',0x37:'DIGIT SEVEN',\n  0x38:'DIGIT EIGHT',0x39:'DIGIT NINE',0x3A:'COLON',0x3B:'SEMICOLON',\n  0x3C:'LESS-THAN SIGN',0x3D:'EQUALS SIGN',0x3E:'GREATER-THAN SIGN',0x3F:'QUESTION MARK',\n  0x40:'COMMERCIAL AT',0x41:'LATIN CAPITAL LETTER A',0x42:'LATIN CAPITAL LETTER B',\n  0x43:'LATIN CAPITAL LETTER C',0x44:'LATIN CAPITAL LETTER D',0x45:'LATIN CAPITAL LETTER E',\n  0x46:'LATIN CAPITAL LETTER F',0x47:'LATIN CAPITAL LETTER G',0x48:'LATIN CAPITAL LETTER H',\n  0x49:'LATIN CAPITAL LETTER I',0x4A:'LATIN CAPITAL LETTER J',0x4B:'LATIN CAPITAL LETTER K',\n  0x4C:'LATIN CAPITAL LETTER L',0x4D:'LATIN CAPITAL LETTER M',0x4E:'LATIN CAPITAL LETTER N',\n  0x4F:'LATIN CAPITAL LETTER O',0x50:'LATIN CAPITAL LETTER P',0x51:'LATIN CAPITAL LETTER Q',\n  0x52:'LATIN CAPITAL LETTER R',0x53:'LATIN CAPITAL LETTER S',0x54:'LATIN CAPITAL LETTER T',\n  0x55:'LATIN CAPITAL LETTER U',0x56:'LATIN CAPITAL LETTER V',0x57:'LATIN CAPITAL LETTER W',\n  0x58:'LATIN CAPITAL LETTER X',0x59:'LATIN CAPITAL LETTER Y',0x5A:'LATIN CAPITAL LETTER Z',\n  0x5B:'LEFT SQUARE BRACKET',0x5C:'REVERSE SOLIDUS',0x5D:'RIGHT SQUARE BRACKET',\n  0x5E:'CIRCUMFLEX ACCENT',0x5F:'LOW LINE',0x60:'GRAVE ACCENT',\n  0x61:'LATIN SMALL LETTER A',0x62:'LATIN SMALL LETTER B',0x63:'LATIN SMALL LETTER C',\n  0x64:'LATIN SMALL LETTER D',0x65:'LATIN SMALL LETTER E',0x66:'LATIN SMALL LETTER F',\n  0x67:'LATIN SMALL LETTER G',0x68:'LATIN SMALL LETTER H',0x69:'LATIN SMALL LETTER I',\n  0x6A:'LATIN SMALL LETTER J',0x6B:'LATIN SMALL LETTER K',0x6C:'LATIN SMALL LETTER L',\n  0x6D:'LATIN SMALL LETTER M',0x6E:'LATIN SMALL LETTER N',0x6F:'LATIN SMALL LETTER O',\n  0x70:'LATIN SMALL LETTER P',0x71:'LATIN SMALL LETTER Q',0x72:'LATIN SMALL LETTER R',\n  0x73:'LATIN SMALL LETTER S',0x74:'LATIN SMALL LETTER T',0x75:'LATIN SMALL LETTER U',\n  0x76:'LATIN SMALL LETTER V',0x77:'LATIN SMALL LETTER W',0x78:'LATIN SMALL LETTER X',\n  0x79:'LATIN SMALL LETTER Y',0x7A:'LATIN SMALL LETTER Z',0x7B:'LEFT CURLY BRACKET',\n  0x7C:'VERTICAL LINE',0x7D:'RIGHT CURLY BRACKET',0x7E:'TILDE',\n};\n\n// Latin Extended (selected)\nconst latinExtNames = {\n  0xC0:'LATIN CAPITAL LETTER A WITH GRAVE',0xC1:'LATIN CAPITAL LETTER A WITH ACUTE',\n  0xC2:'LATIN CAPITAL LETTER A WITH CIRCUMFLEX',0xC3:'LATIN CAPITAL LETTER A WITH TILDE',\n  0xC4:'LATIN CAPITAL LETTER A WITH DIAERESIS',0xC5:'LATIN CAPITAL LETTER A WITH RING ABOVE',\n  0xC6:'LATIN CAPITAL LETTER AE',0xC7:'LATIN CAPITAL LETTER C WITH CEDILLA',\n  0xC8:'LATIN CAPITAL LETTER E WITH GRAVE',0xC9:'LATIN CAPITAL LETTER E WITH ACUTE',\n  0xCA:'LATIN CAPITAL LETTER E WITH CIRCUMFLEX',0xCB:'LATIN CAPITAL LETTER E WITH DIAERESIS',\n  0xCC:'LATIN CAPITAL LETTER I WITH GRAVE',0xCD:'LATIN CAPITAL LETTER I WITH ACUTE',\n  0xCE:'LATIN CAPITAL LETTER I WITH CIRCUMFLEX',0xCF:'LATIN CAPITAL LETTER I WITH DIAERESIS',\n  0xD0:'LATIN CAPITAL LETTER ETH',0xD1:'LATIN CAPITAL LETTER N WITH TILDE',\n  0xD2:'LATIN CAPITAL LETTER O WITH GRAVE',0xD3:'LATIN CAPITAL LETTER O WITH ACUTE',\n  0xD4:'LATIN CAPITAL LETTER O WITH CIRCUMFLEX',0xD5:'LATIN CAPITAL LETTER O WITH TILDE',\n  0xD6:'LATIN CAPITAL LETTER O WITH DIAERESIS',0xD7:'MULTIPLICATION SIGN',\n  0xD8:'LATIN CAPITAL LETTER O WITH STROKE',0xD9:'LATIN CAPITAL LETTER U WITH GRAVE',\n  0xDA:'LATIN CAPITAL LETTER U WITH ACUTE',0xDB:'LATIN CAPITAL LETTER U WITH CIRCUMFLEX',\n  0xDC:'LATIN CAPITAL LETTER U WITH DIAERESIS',0xDD:'LATIN CAPITAL LETTER Y WITH ACUTE',\n  0xDE:'LATIN CAPITAL LETTER THORN',0xDF:'LATIN SMALL LETTER SHARP S',\n  0xE0:'LATIN SMALL LETTER A WITH GRAVE',0xE1:'LATIN SMALL LETTER A WITH ACUTE',\n  0xE2:'LATIN SMALL LETTER A WITH CIRCUMFLEX',0xE3:'LATIN SMALL LETTER A WITH TILDE',\n  0xE4:'LATIN SMALL LETTER A WITH DIAERESIS',0xE5:'LATIN SMALL LETTER A WITH RING ABOVE',\n  0xE6:'LATIN SMALL LETTER AE',0xE7:'LATIN SMALL LETTER C WITH CEDILLA',\n  0xE8:'LATIN SMALL LETTER E WITH GRAVE',0xE9:'LATIN SMALL LETTER E WITH ACUTE',\n  0xEA:'LATIN SMALL LETTER E WITH CIRCUMFLEX',0xEB:'LATIN SMALL LETTER E WITH DIAERESIS',\n  0xEC:'LATIN SMALL LETTER I WITH GRAVE',0xED:'LATIN SMALL LETTER I WITH ACUTE',\n  0xEE:'LATIN SMALL LETTER I WITH CIRCUMFLEX',0xEF:'LATIN SMALL LETTER I WITH DIAERESIS',\n  0xF0:'LATIN SMALL LETTER ETH',0xF1:'LATIN SMALL LETTER N WITH TILDE',\n  0xF2:'LATIN SMALL LETTER O WITH GRAVE',0xF3:'LATIN SMALL LETTER O WITH ACUTE',\n  0xF4:'LATIN SMALL LETTER O WITH CIRCUMFLEX',0xF5:'LATIN SMALL LETTER O WITH TILDE',\n  0xF6:'LATIN SMALL LETTER O WITH DIAERESIS',0xF7:'DIVISION SIGN',\n  0xF8:'LATIN SMALL LETTER O WITH STROKE',0xF9:'LATIN SMALL LETTER U WITH GRAVE',\n  0xFA:'LATIN SMALL LETTER U WITH ACUTE',0xFB:'LATIN SMALL LETTER U WITH CIRCUMFLEX',\n  0xFC:'LATIN SMALL LETTER U WITH DIAERESIS',0xFD:'LATIN SMALL LETTER Y WITH ACUTE',\n  0xFE:'LATIN SMALL LETTER THORN',0xFF:'LATIN SMALL LETTER Y WITH DIAERESIS',\n  // Latin Extended-A\n  0x100:'LATIN CAPITAL LETTER A WITH MACRON',0x101:'LATIN SMALL LETTER A WITH MACRON',\n  0x102:'LATIN CAPITAL LETTER A WITH BREVE',0x103:'LATIN SMALL LETTER A WITH BREVE',\n  0x110:'LATIN CAPITAL LETTER D WITH STROKE',0x111:'LATIN SMALL LETTER D WITH STROKE',\n  0x126:'LATIN CAPITAL LETTER H WITH STROKE',0x127:'LATIN SMALL LETTER H WITH STROKE',\n  0x131:'LATIN SMALL LETTER DOTLESS I',0x141:'LATIN CAPITAL LETTER L WITH STROKE',\n  0x142:'LATIN SMALL LETTER L WITH STROKE',0x152:'LATIN CAPITAL LIGATURE OE',\n  0x153:'LATIN SMALL LIGATURE OE',0x160:'LATIN CAPITAL LETTER S WITH CARON',\n  0x161:'LATIN SMALL LETTER S WITH CARON',0x178:'LATIN CAPITAL LETTER Y WITH DIAERESIS',\n  0x17D:'LATIN CAPITAL LETTER Z WITH CARON',0x17E:'LATIN SMALL LETTER Z WITH CARON',\n};\n\n// Greek (0x370–0x3FF core)\nconst greekNames = {\n  0x391:'GREEK CAPITAL LETTER ALPHA',0x392:'GREEK CAPITAL LETTER BETA',\n  0x393:'GREEK CAPITAL LETTER GAMMA',0x394:'GREEK CAPITAL LETTER DELTA',\n  0x395:'GREEK CAPITAL LETTER EPSILON',0x396:'GREEK CAPITAL LETTER ZETA',\n  0x397:'GREEK CAPITAL LETTER ETA',0x398:'GREEK CAPITAL LETTER THETA',\n  0x399:'GREEK CAPITAL LETTER IOTA',0x39A:'GREEK CAPITAL LETTER KAPPA',\n  0x39B:'GREEK CAPITAL LETTER LAMDA',0x39C:'GREEK CAPITAL LETTER MU',\n  0x39D:'GREEK CAPITAL LETTER NU',0x39E:'GREEK CAPITAL LETTER XI',\n  0x39F:'GREEK CAPITAL LETTER OMICRON',0x3A0:'GREEK CAPITAL LETTER PI',\n  0x3A1:'GREEK CAPITAL LETTER RHO',0x3A3:'GREEK CAPITAL LETTER SIGMA',\n  0x3A4:'GREEK CAPITAL LETTER TAU',0x3A5:'GREEK CAPITAL LETTER UPSILON',\n  0x3A6:'GREEK CAPITAL LETTER PHI',0x3A7:'GREEK CAPITAL LETTER CHI',\n  0x3A8:'GREEK CAPITAL LETTER PSI',0x3A9:'GREEK CAPITAL LETTER OMEGA',\n  0x3B1:'GREEK SMALL LETTER ALPHA',0x3B2:'GREEK SMALL LETTER BETA',\n  0x3B3:'GREEK SMALL LETTER GAMMA',0x3B4:'GREEK SMALL LETTER DELTA',\n  0x3B5:'GREEK SMALL LETTER EPSILON',0x3B6:'GREEK SMALL LETTER ZETA',\n  0x3B7:'GREEK SMALL LETTER ETA',0x3B8:'GREEK SMALL LETTER THETA',\n  0x3B9:'GREEK SMALL LETTER IOTA',0x3BA:'GREEK SMALL LETTER KAPPA',\n  0x3BB:'GREEK SMALL LETTER LAMDA',0x3BC:'GREEK SMALL LETTER MU',\n  0x3BD:'GREEK SMALL LETTER NU',0x3BE:'GREEK SMALL LETTER XI',\n  0x3BF:'GREEK SMALL LETTER OMICRON',0x3C0:'GREEK SMALL LETTER PI',\n  0x3C1:'GREEK SMALL LETTER RHO',0x3C2:'GREEK SMALL LETTER FINAL SIGMA',\n  0x3C3:'GREEK SMALL LETTER SIGMA',0x3C4:'GREEK SMALL LETTER TAU',\n  0x3C5:'GREEK SMALL LETTER UPSILON',0x3C6:'GREEK SMALL LETTER PHI',\n  0x3C7:'GREEK SMALL LETTER CHI',0x3C8:'GREEK SMALL LETTER PSI',\n  0x3C9:'GREEK SMALL LETTER OMEGA',\n};\n\n// Cyrillic (0x410–0x44F)\nconst cyrillicNames = {\n  0x410:'CYRILLIC CAPITAL LETTER A',0x411:'CYRILLIC CAPITAL LETTER BE',\n  0x412:'CYRILLIC CAPITAL LETTER VE',0x413:'CYRILLIC CAPITAL LETTER GHE',\n  0x414:'CYRILLIC CAPITAL LETTER DE',0x415:'CYRILLIC CAPITAL LETTER IE',\n  0x416:'CYRILLIC CAPITAL LETTER ZHE',0x417:'CYRILLIC CAPITAL LETTER ZE',\n  0x418:'CYRILLIC CAPITAL LETTER I',0x419:'CYRILLIC CAPITAL LETTER SHORT I',\n  0x41A:'CYRILLIC CAPITAL LETTER KA',0x41B:'CYRILLIC CAPITAL LETTER EL',\n  0x41C:'CYRILLIC CAPITAL LETTER EM',0x41D:'CYRILLIC CAPITAL LETTER EN',\n  0x41E:'CYRILLIC CAPITAL LETTER O',0x41F:'CYRILLIC CAPITAL LETTER PE',\n  0x420:'CYRILLIC CAPITAL LETTER ER',0x421:'CYRILLIC CAPITAL LETTER ES',\n  0x422:'CYRILLIC CAPITAL LETTER TE',0x423:'CYRILLIC CAPITAL LETTER U',\n  0x424:'CYRILLIC CAPITAL LETTER EF',0x425:'CYRILLIC CAPITAL LETTER HA',\n  0x426:'CYRILLIC CAPITAL LETTER TSE',0x427:'CYRILLIC CAPITAL LETTER CHE',\n  0x428:'CYRILLIC CAPITAL LETTER SHA',0x429:'CYRILLIC CAPITAL LETTER SHCHA',\n  0x42A:'CYRILLIC CAPITAL LETTER HARD SIGN',0x42B:'CYRILLIC CAPITAL LETTER YERU',\n  0x42C:'CYRILLIC CAPITAL LETTER SOFT SIGN',0x42D:'CYRILLIC CAPITAL LETTER E',\n  0x42E:'CYRILLIC CAPITAL LETTER YU',0x42F:'CYRILLIC CAPITAL LETTER YA',\n  0x430:'CYRILLIC SMALL LETTER A',0x431:'CYRILLIC SMALL LETTER BE',\n  0x432:'CYRILLIC SMALL LETTER VE',0x433:'CYRILLIC SMALL LETTER GHE',\n  0x434:'CYRILLIC SMALL LETTER DE',0x435:'CYRILLIC SMALL LETTER IE',\n  0x436:'CYRILLIC SMALL LETTER ZHE',0x437:'CYRILLIC SMALL LETTER ZE',\n  0x438:'CYRILLIC SMALL LETTER I',0x439:'CYRILLIC SMALL LETTER SHORT I',\n  0x43A:'CYRILLIC SMALL LETTER KA',0x43B:'CYRILLIC SMALL LETTER EL',\n  0x43C:'CYRILLIC SMALL LETTER EM',0x43D:'CYRILLIC SMALL LETTER EN',\n  0x43E:'CYRILLIC SMALL LETTER O',0x43F:'CYRILLIC SMALL LETTER PE',\n  0x440:'CYRILLIC SMALL LETTER ER',0x441:'CYRILLIC SMALL LETTER ES',\n  0x442:'CYRILLIC SMALL LETTER TE',0x443:'CYRILLIC SMALL LETTER U',\n  0x444:'CYRILLIC SMALL LETTER EF',0x445:'CYRILLIC SMALL LETTER HA',\n  0x446:'CYRILLIC SMALL LETTER TSE',0x447:'CYRILLIC SMALL LETTER CHE',\n  0x448:'CYRILLIC SMALL LETTER SHA',0x449:'CYRILLIC SMALL LETTER SHCHA',\n  0x44A:'CYRILLIC SMALL LETTER HARD SIGN',0x44B:'CYRILLIC SMALL LETTER YERU',\n  0x44C:'CYRILLIC SMALL LETTER SOFT SIGN',0x44D:'CYRILLIC SMALL LETTER E',\n  0x44E:'CYRILLIC SMALL LETTER YU',0x44F:'CYRILLIC SMALL LETTER YA',\n};\n\n// Arrows (selected)\nconst arrowNames = {\n  0x2190:'LEFTWARDS ARROW',0x2191:'UPWARDS ARROW',0x2192:'RIGHTWARDS ARROW',\n  0x2193:'DOWNWARDS ARROW',0x2194:'LEFT RIGHT ARROW',0x2195:'UP DOWN ARROW',\n  0x2196:'NORTH WEST ARROW',0x2197:'NORTH EAST ARROW',0x2198:'SOUTH EAST ARROW',\n  0x2199:'SOUTH WEST ARROW',0x219A:'LEFTWARDS ARROW WITH STROKE',\n  0x219B:'RIGHTWARDS ARROW WITH STROKE',0x219C:'LEFTWARDS WAVE ARROW',\n  0x219D:'RIGHTWARDS WAVE ARROW',0x219E:'LEFTWARDS TWO HEADED ARROW',\n  0x219F:'UPWARDS TWO HEADED ARROW',0x21A0:'RIGHTWARDS TWO HEADED ARROW',\n  0x21A1:'DOWNWARDS TWO HEADED ARROW',0x21A2:'LEFTWARDS ARROW WITH TAIL',\n  0x21A3:'RIGHTWARDS ARROW WITH TAIL',0x21A4:'LEFTWARDS ARROW FROM BAR',\n  0x21A6:'RIGHTWARDS ARROW FROM BAR',0x21A9:'LEFTWARDS ARROW WITH HOOK',\n  0x21AA:'RIGHTWARDS ARROW WITH HOOK',0x21AB:'LEFTWARDS ARROW WITH LOOP',\n  0x21AC:'RIGHTWARDS ARROW WITH LOOP',0x21AD:'LEFT RIGHT WAVE ARROW',\n  0x21AE:'LEFT RIGHT ARROW WITH STROKE',0x21B0:'UPWARDS ARROW WITH TIP LEFTWARDS',\n  0x21B1:'UPWARDS ARROW WITH TIP RIGHTWARDS',0x21B2:'DOWNWARDS ARROW WITH TIP LEFTWARDS',\n  0x21B3:'DOWNWARDS ARROW WITH TIP RIGHTWARDS',0x21B4:'RIGHTWARDS ARROW WITH CORNER DOWNWARDS',\n  0x21B5:'DOWNWARDS ARROW WITH CORNER LEFTWARDS',0x21B6:'ANTICLOCKWISE TOP SEMICIRCLE ARROW',\n  0x21B7:'CLOCKWISE TOP SEMICIRCLE ARROW',0x21BA:'ANTICLOCKWISE OPEN CIRCLE ARROW',\n  0x21BB:'CLOCKWISE OPEN CIRCLE ARROW',0x21BC:'LEFTWARDS HARPOON WITH BARB UPWARDS',\n  0x21BD:'LEFTWARDS HARPOON WITH BARB DOWNWARDS',0x21BE:'UPWARDS HARPOON WITH BARB RIGHTWARDS',\n  0x21BF:'UPWARDS HARPOON WITH BARB LEFTWARDS',0x21C0:'RIGHTWARDS HARPOON WITH BARB UPWARDS',\n  0x21C4:'RIGHTWARDS ARROW OVER LEFTWARDS ARROW',0x21C6:'LEFTWARDS ARROW OVER RIGHTWARDS ARROW',\n  0x21C7:'LEFTWARDS PAIRED ARROWS',0x21C8:'UPWARDS PAIRED ARROWS',\n  0x21C9:'RIGHTWARDS PAIRED ARROWS',0x21CA:'DOWNWARDS PAIRED ARROWS',\n  0x21CC:'RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON',\n  0x21D0:'LEFTWARDS DOUBLE ARROW',0x21D1:'UPWARDS DOUBLE ARROW',\n  0x21D2:'RIGHTWARDS DOUBLE ARROW',0x21D3:'DOWNWARDS DOUBLE ARROW',\n  0x21D4:'LEFT RIGHT DOUBLE ARROW',0x21D5:'UP DOWN DOUBLE ARROW',\n  0x21E6:'LEFTWARDS WHITE ARROW',0x21E7:'UPWARDS WHITE ARROW',\n  0x21E8:'RIGHTWARDS WHITE ARROW',0x21E9:'DOWNWARDS WHITE ARROW',\n  0x27A1:'BLACK RIGHTWARDS ARROW',0x2B05:'LEFTWARDS BLACK ARROW',\n  0x2B06:'UPWARDS BLACK ARROW',0x2B07:'DOWNWARDS BLACK ARROW',\n  0x2B0C:'LEFT RIGHT BLACK ARROW',0x2B0D:'UP DOWN BLACK ARROW',\n};\n\n// Math symbols (selected)\nconst mathNames = {\n  0x2200:'FOR ALL',0x2201:'COMPLEMENT',0x2202:'PARTIAL DIFFERENTIAL',\n  0x2203:'THERE EXISTS',0x2204:'THERE DOES NOT EXIST',0x2205:'EMPTY SET',\n  0x2206:'INCREMENT',0x2207:'NABLA',0x2208:'ELEMENT OF',0x2209:'NOT AN ELEMENT OF',\n  0x220A:'SMALL ELEMENT OF',0x220B:'CONTAINS AS MEMBER',0x220C:'DOES NOT CONTAIN AS MEMBER',\n  0x220F:'N-ARY PRODUCT',0x2210:'N-ARY COPRODUCT',0x2211:'N-ARY SUMMATION',\n  0x2212:'MINUS SIGN',0x2213:'MINUS-OR-PLUS SIGN',0x2214:'DOT PLUS',\n  0x2215:'DIVISION SLASH',0x2216:'SET MINUS',0x2217:'ASTERISK OPERATOR',\n  0x2218:'RING OPERATOR',0x2219:'BULLET OPERATOR',0x221A:'SQUARE ROOT',\n  0x221B:'CUBE ROOT',0x221C:'FOURTH ROOT',0x221D:'PROPORTIONAL TO',\n  0x221E:'INFINITY',0x221F:'RIGHT ANGLE',0x2220:'ANGLE',\n  0x2221:'MEASURED ANGLE',0x2222:'SPHERICAL ANGLE',0x2223:'DIVIDES',\n  0x2224:'DOES NOT DIVIDE',0x2225:'PARALLEL TO',0x2226:'NOT PARALLEL TO',\n  0x2227:'LOGICAL AND',0x2228:'LOGICAL OR',0x2229:'INTERSECTION',\n  0x222A:'UNION',0x222B:'INTEGRAL',0x222C:'DOUBLE INTEGRAL',\n  0x222D:'TRIPLE INTEGRAL',0x222E:'CONTOUR INTEGRAL',0x2234:'THEREFORE',\n  0x2235:'BECAUSE',0x2236:'RATIO',0x2237:'PROPORTION',\n  0x223C:'TILDE OPERATOR',0x2248:'ALMOST EQUAL TO',0x2260:'NOT EQUAL TO',\n  0x2261:'IDENTICAL TO',0x2264:'LESS-THAN OR EQUAL TO',0x2265:'GREATER-THAN OR EQUAL TO',\n  0x2295:'CIRCLED PLUS',0x2296:'CIRCLED MINUS',0x2297:'CIRCLED TIMES',\n  0x2299:'CIRCLED DOT OPERATOR',0x22A2:'RIGHT TACK',0x22A3:'LEFT TACK',\n  0x22A4:'DOWN TACK',0x22A5:'UP TACK',0x22BB:'XOR',0x22BC:'NAND',\n  0x25B3:'WHITE UP-POINTING TRIANGLE',0x25BD:'WHITE DOWN-POINTING TRIANGLE',\n  0x2026:'HORIZONTAL ELLIPSIS',0x2032:'PRIME',0x2033:'DOUBLE PRIME',\n  0x2034:'TRIPLE PRIME',0x2044:'FRACTION SLASH',0x2118:'SCRIPT CAPITAL P',\n  0x2135:'ALEF SYMBOL',0x2153:'VULGAR FRACTION ONE THIRD',\n  0x2154:'VULGAR FRACTION TWO THIRDS',0x2155:'VULGAR FRACTION ONE FIFTH',\n  0x2189:'VULGAR FRACTION ZERO THIRDS',0x00B1:'PLUS-MINUS SIGN',\n  0x00B2:'SUPERSCRIPT TWO',0x00B3:'SUPERSCRIPT THREE',0x00B9:'SUPERSCRIPT ONE',\n  0x00BC:'VULGAR FRACTION ONE QUARTER',0x00BD:'VULGAR FRACTION ONE HALF',\n  0x00BE:'VULGAR FRACTION THREE QUARTERS',0x00D7:'MULTIPLICATION SIGN',0x00F7:'DIVISION SIGN',\n};\n\n// Box Drawing (0x2500–0x257F)\nconst boxNames = {};\nconst boxLabels = [\n  'LIGHT HORIZONTAL','HEAVY HORIZONTAL','LIGHT VERTICAL','HEAVY VERTICAL',\n  'LIGHT TRIPLE DASH HORIZONTAL','HEAVY TRIPLE DASH HORIZONTAL',\n  'LIGHT TRIPLE DASH VERTICAL','HEAVY TRIPLE DASH VERTICAL',\n  'LIGHT QUADRUPLE DASH HORIZONTAL','HEAVY QUADRUPLE DASH HORIZONTAL',\n  'LIGHT QUADRUPLE DASH VERTICAL','HEAVY QUADRUPLE DASH VERTICAL',\n  'LIGHT DOWN AND RIGHT','HEAVY DOWN AND RIGHT',\n  'DOWN LIGHT AND RIGHT HEAVY','DOWN HEAVY AND RIGHT LIGHT',\n  'HEAVY DOWN AND RIGHT','LIGHT DOWN AND LEFT','HEAVY DOWN AND LEFT',\n  'DOWN LIGHT AND LEFT HEAVY','DOWN HEAVY AND LEFT LIGHT',\n  'HEAVY DOWN AND LEFT','LIGHT UP AND RIGHT','HEAVY UP AND RIGHT',\n  'UP LIGHT AND RIGHT HEAVY','UP HEAVY AND RIGHT LIGHT',\n  'HEAVY UP AND RIGHT','LIGHT UP AND LEFT','HEAVY UP AND LEFT',\n  'UP LIGHT AND LEFT HEAVY','UP HEAVY AND LEFT LIGHT','HEAVY UP AND LEFT',\n  'LIGHT VERTICAL AND RIGHT','HEAVY VERTICAL AND RIGHT',\n  'VERTICAL LIGHT AND RIGHT HEAVY','UP HEAVY AND RIGHT DOWN LIGHT',\n  'DOWN HEAVY AND RIGHT UP LIGHT','HEAVY VERTICAL AND RIGHT',\n  'LIGHT VERTICAL AND LEFT','HEAVY VERTICAL AND LEFT',\n  'VERTICAL LIGHT AND LEFT HEAVY','UP HEAVY AND LEFT DOWN LIGHT',\n  'DOWN HEAVY AND LEFT UP LIGHT','HEAVY VERTICAL AND LEFT',\n  'LIGHT DOWN AND HORIZONTAL','HEAVY DOWN AND HORIZONTAL',\n  'DOWN LIGHT AND HORIZONTAL HEAVY','DOWN HEAVY AND HORIZONTAL LIGHT',\n  'HEAVY DOWN AND HORIZONTAL','LIGHT UP AND HORIZONTAL',\n  'HEAVY UP AND HORIZONTAL','UP LIGHT AND HORIZONTAL HEAVY',\n  'UP HEAVY AND HORIZONTAL LIGHT','HEAVY UP AND HORIZONTAL',\n  'LIGHT VERTICAL AND HORIZONTAL','HEAVY VERTICAL AND HORIZONTAL',\n  'VERTICAL LIGHT AND HORIZONTAL HEAVY','VERTICAL HEAVY AND HORIZONTAL LIGHT',\n  'HEAVY VERTICAL AND HORIZONTAL','LIGHT DOUBLE DASH HORIZONTAL',\n  'HEAVY DOUBLE DASH HORIZONTAL','LIGHT DOUBLE DASH VERTICAL',\n  'HEAVY DOUBLE DASH VERTICAL','DOUBLE HORIZONTAL','DOUBLE VERTICAL',\n  'DOWN SINGLE AND RIGHT DOUBLE','DOWN DOUBLE AND RIGHT SINGLE',\n  'DOUBLE DOWN AND RIGHT','DOWN SINGLE AND LEFT DOUBLE',\n  'DOWN DOUBLE AND LEFT SINGLE','DOUBLE DOWN AND LEFT',\n  'UP SINGLE AND RIGHT DOUBLE','UP DOUBLE AND RIGHT SINGLE',\n  'DOUBLE UP AND RIGHT','UP SINGLE AND LEFT DOUBLE',\n  'UP DOUBLE AND LEFT SINGLE','DOUBLE UP AND LEFT',\n  'VERTICAL SINGLE AND RIGHT DOUBLE','VERTICAL DOUBLE AND RIGHT SINGLE',\n  'DOUBLE VERTICAL AND RIGHT','VERTICAL SINGLE AND LEFT DOUBLE',\n  'VERTICAL DOUBLE AND LEFT SINGLE','DOUBLE VERTICAL AND LEFT',\n  'DOWN SINGLE AND HORIZONTAL DOUBLE','DOWN DOUBLE AND HORIZONTAL SINGLE',\n  'DOUBLE DOWN AND HORIZONTAL','UP SINGLE AND HORIZONTAL DOUBLE',\n  'UP DOUBLE AND HORIZONTAL SINGLE','DOUBLE UP AND HORIZONTAL',\n  'VERTICAL SINGLE AND HORIZONTAL DOUBLE','VERTICAL DOUBLE AND HORIZONTAL SINGLE',\n  'DOUBLE VERTICAL AND HORIZONTAL',\n  'LIGHT ARC DOWN AND RIGHT','LIGHT ARC DOWN AND LEFT',\n  'LIGHT ARC UP AND LEFT','LIGHT ARC UP AND RIGHT',\n  'LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT',\n  'LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT',\n  'LIGHT DIAGONAL CROSS','LIGHT LEFT','LIGHT UP','LIGHT RIGHT','LIGHT DOWN',\n  'HEAVY LEFT','HEAVY UP','HEAVY RIGHT','HEAVY DOWN',\n  'LIGHT LEFT AND HEAVY RIGHT','LIGHT UP AND HEAVY DOWN',\n  'HEAVY LEFT AND LIGHT RIGHT','HEAVY UP AND LIGHT DOWN',\n];\nfor (let i = 0; i \u003c 0x80 \u0026\u0026 i \u003c boxLabels.length; i++) {\n  boxNames[0x2500 + i] = 'BOX DRAWINGS ' + boxLabels[i];\n}\n\n// Currency\nconst currencyNames = {\n  0x24:'DOLLAR SIGN',0xA2:'CENT SIGN',0xA3:'POUND SIGN',0xA4:'CURRENCY SIGN',\n  0xA5:'YEN SIGN',0x20A0:'EURO-CURRENCY SIGN',0x20A1:'COLON SIGN',\n  0x20A2:'CRUZEIRO SIGN',0x20A3:'FRENCH FRANC SIGN',0x20A4:'LIRA SIGN',\n  0x20A5:'MILL SIGN',0x20A6:'NAIRA SIGN',0x20A7:'PESETA SIGN',\n  0x20A8:'RUPEE SIGN',0x20A9:'WON SIGN',0x20AA:'NEW SHEQEL SIGN',\n  0x20AB:'DONG SIGN',0x20AC:'EURO SIGN',0x20AD:'KIP SIGN',\n  0x20AE:'TUGRIK SIGN',0x20AF:'DRACHMA SIGN',0x20B0:'GERMAN PENNY SIGN',\n  0x20B1:'PESO SIGN',0x20B2:'GUARANI SIGN',0x20B3:'AUSTRAL SIGN',\n  0x20B4:'HRYVNIA SIGN',0x20B5:'CEDI SIGN',0x20B6:'LIVRE TOURNOIS SIGN',\n  0x20B7:'SPESMILO SIGN',0x20B8:'TENGE SIGN',0x20B9:'INDIAN RUPEE SIGN',\n  0x20BA:'TURKISH LIRA SIGN',0x20BB:'NORDIC MARK SIGN',0x20BC:'MANAT SIGN',\n  0x20BD:'RUBLE SIGN',0x20BE:'LARI SIGN',0x20BF:'BITCOIN SIGN',\n  0x20C0:'SOM SIGN',\n};\n\n// Dingbats (selected from U+2700–U+27BF and common symbols)\nconst dingbatNames = {\n  0x2600:'BLACK SUN WITH RAYS',0x2601:'CLOUD',0x2602:'UMBRELLA',\n  0x2603:'SNOWMAN',0x2604:'COMET',0x2605:'BLACK STAR',0x2606:'WHITE STAR',\n  0x2607:'LIGHTNING',0x2608:'THUNDERSTORM',0x2609:'SUN',\n  0x260A:'ASCENDING NODE',0x260B:'DESCENDING NODE',0x260C:'CONJUNCTION',\n  0x260D:'OPPOSITION',0x260E:'BLACK TELEPHONE',0x260F:'WHITE TELEPHONE',\n  0x2610:'BALLOT BOX',0x2611:'BALLOT BOX WITH CHECK',0x2612:'BALLOT BOX WITH X',\n  0x2613:'SALTIRE',0x2614:'UMBRELLA WITH RAIN DROPS',\n  0x2615:'HOT BEVERAGE',0x2616:'WHITE SHOGI PIECE',0x2617:'BLACK SHOGI PIECE',\n  0x2618:'SHAMROCK',0x2619:'REVERSED ROTATED FLORAL HEART BULLET',\n  0x261A:'BLACK LEFT POINTING INDEX',0x261B:'BLACK RIGHT POINTING INDEX',\n  0x261C:'WHITE LEFT POINTING INDEX',0x261D:'WHITE UP POINTING INDEX',\n  0x261E:'WHITE RIGHT POINTING INDEX',0x261F:'WHITE DOWN POINTING INDEX',\n  0x2620:'SKULL AND CROSSBONES',0x2621:'CAUTION SIGN',0x2622:'RADIOACTIVE SIGN',\n  0x2623:'BIOHAZARD SIGN',0x2624:'CADUCEUS',0x2625:'ANKH',\n  0x2626:'ORTHODOX CROSS',0x2627:'CHI RHO',0x2628:'CROSS OF LORRAINE',\n  0x2629:'CROSS OF JERUSALEM',0x262A:'STAR AND CRESCENT',\n  0x262B:'FARSI SYMBOL',0x262C:'ADI SHAKTI',0x262D:'HAMMER AND SICKLE',\n  0x262E:'PEACE SYMBOL',0x262F:'YIN YANG',\n  0x2630:'TRIGRAM FOR HEAVEN',0x2631:'TRIGRAM FOR LAKE',\n  0x2632:'TRIGRAM FOR FIRE',0x2633:'TRIGRAM FOR THUNDER',\n  0x2634:'TRIGRAM FOR WIND',0x2635:'TRIGRAM FOR WATER',\n  0x2636:'TRIGRAM FOR MOUNTAIN',0x2637:'TRIGRAM FOR EARTH',\n  0x2638:'WHEEL OF DHARMA',0x2639:'WHITE FROWNING FACE',\n  0x263A:'WHITE SMILING FACE',0x263B:'BLACK SMILING FACE',\n  0x263C:'WHITE SUN WITH RAYS',0x263D:'FIRST QUARTER MOON',\n  0x263E:'LAST QUARTER MOON',0x263F:'MERCURY',\n  0x2640:'FEMALE SIGN',0x2641:'EARTH',0x2642:'MALE SIGN',\n  0x2643:'JUPITER',0x2644:'SATURN',0x2645:'URANUS',0x2646:'NEPTUNE',\n  0x2647:'PLUTO',0x2648:'ARIES',0x2649:'TAURUS',0x264A:'GEMINI',\n  0x264B:'CANCER',0x264C:'LEO',0x264D:'VIRGO',0x264E:'LIBRA',\n  0x264F:'SCORPIUS',0x2650:'SAGITTARIUS',0x2651:'CAPRICORN',\n  0x2652:'AQUARIUS',0x2653:'PISCES',0x2654:'WHITE CHESS KING',\n  0x2655:'WHITE CHESS QUEEN',0x2656:'WHITE CHESS ROOK',\n  0x2657:'WHITE CHESS BISHOP',0x2658:'WHITE CHESS KNIGHT',\n  0x2659:'WHITE CHESS PAWN',0x265A:'BLACK CHESS KING',\n  0x265B:'BLACK CHESS QUEEN',0x265C:'BLACK CHESS ROOK',\n  0x265D:'BLACK CHESS BISHOP',0x265E:'BLACK CHESS KNIGHT',\n  0x265F:'BLACK CHESS PAWN',0x2660:'BLACK SPADE SUIT',\n  0x2661:'WHITE HEART SUIT',0x2662:'WHITE DIAMOND SUIT',\n  0x2663:'BLACK CLUB SUIT',0x2664:'WHITE SPADE SUIT',\n  0x2665:'BLACK HEART SUIT',0x2666:'BLACK DIAMOND SUIT',\n  0x2667:'WHITE CLUB SUIT',0x2668:'HOT SPRINGS',0x2669:'QUARTER NOTE',\n  0x266A:'EIGHTH NOTE',0x266B:'BEAMED EIGHTH NOTES',\n  0x266C:'BEAMED SIXTEENTH NOTES',0x266D:'MUSIC FLAT SIGN',\n  0x266E:'MUSIC NATURAL SIGN',0x266F:'MUSIC SHARP SIGN',\n  0x2670:'WEST SYRIAC CROSS',0x2671:'EAST SYRIAC CROSS',\n  0x2672:'UNIVERSAL RECYCLING SYMBOL',0x2673:'RECYCLING SYMBOL FOR TYPE-1 PLASTICS',\n  0x2674:'RECYCLING SYMBOL FOR TYPE-2 PLASTICS',0x2675:'RECYCLING SYMBOL FOR TYPE-3 PLASTICS',\n  0x267B:'BLACK UNIVERSAL RECYCLING SYMBOL',0x267C:'RECYCLED PAPER SYMBOL',\n  0x267E:'PERMANENT PAPER SIGN',0x267F:'WHEELCHAIR SYMBOL',\n  0x2680:'DIE FACE-1',0x2681:'DIE FACE-2',0x2682:'DIE FACE-3',\n  0x2683:'DIE FACE-4',0x2684:'DIE FACE-5',0x2685:'DIE FACE-6',\n  0x2686:'WHITE CIRCLE WITH DOT RIGHT',0x2687:'WHITE CIRCLE WITH TWO DOTS',\n  0x2688:'BLACK CIRCLE WITH WHITE DOT RIGHT',0x2689:'BLACK CIRCLE WITH TWO WHITE DOTS',\n  0x2702:'BLACK SCISSORS',0x2703:'WHITE SCISSORS',0x2704:'SCISSORS',\n  0x2706:'TELEPHONE LOCATION SIGN',0x2707:'TAPE DRIVE',\n  0x2708:'AIRPLANE',0x2709:'ENVELOPE',0x270A:'RAISED FIST',\n  0x270B:'RAISED HAND',0x270C:'VICTORY HAND',0x270D:'WRITING HAND',\n  0x270E:'LOWER RIGHT PENCIL',0x270F:'PENCIL',0x2710:'UPPER RIGHT PENCIL',\n  0x2711:'WHITE NIB',0x2712:'BLACK NIB',0x2713:'CHECK MARK',\n  0x2714:'HEAVY CHECK MARK',0x2715:'MULTIPLICATION X',0x2716:'HEAVY MULTIPLICATION X',\n  0x2717:'BALLOT X',0x2718:'HEAVY BALLOT X',0x2719:'OUTLINED GREEK CROSS',\n  0x271A:'HEAVY GREEK CROSS',0x271B:'OPEN CENTRE CROSS',0x271C:'HEAVY OPEN CENTRE CROSS',\n  0x271D:'LATIN CROSS',0x271E:'SHADOWED WHITE LATIN CROSS',0x271F:'OUTLINED LATIN CROSS',\n  0x2720:'MALTESE CROSS',0x2721:'STAR OF DAVID',0x2722:'FOUR TEARDROP-SPOKED ASTERISK',\n  0x2723:'FOUR BALLOON-SPOKED ASTERISK',0x2724:'HEAVY FOUR BALLOON-SPOKED ASTERISK',\n  0x2725:'FOUR CLUB-SPOKED ASTERISK',0x2726:'BLACK FOUR POINTED STAR',\n  0x2727:'WHITE FOUR POINTED STAR',0x2728:'SPARKLES',0x2729:'STRESS OUTLINED WHITE STAR',\n  0x272A:'CIRCLED WHITE STAR',0x272B:'OPEN CENTRE BLACK STAR',\n  0x272C:'BLACK CENTRE WHITE STAR',0x272D:'OUTLINED BLACK STAR',\n  0x272E:'HEAVY OUTLINED BLACK STAR',0x272F:'PINWHEEL STAR',\n  0x2730:'SHADOWED WHITE STAR',0x2731:'HEAVY ASTERISK',0x2732:'OPEN CENTRE ASTERISK',\n  0x2733:'EIGHT SPOKED ASTERISK',0x2734:'EIGHT POINTED BLACK STAR',\n  0x2735:'EIGHT POINTED PINWHEEL STAR',0x2736:'SIX POINTED BLACK STAR',\n  0x2737:'EIGHT POINTED RECTILINEAR BLACK STAR',0x2738:'HEAVY EIGHT POINTED RECTILINEAR BLACK STAR',\n  0x2739:'TWELVE POINTED BLACK STAR',0x273A:'SIXTEEN POINTED ASTERISK',\n  0x273B:'TEARDROP-SPOKED ASTERISK',0x273C:'OPEN CENTRE TEARDROP-SPOKED ASTERISK',\n  0x273D:'HEAVY TEARDROP-SPOKED ASTERISK',0x273E:'SIX PETALLED BLACK AND WHITE FLORETTE',\n  0x273F:'BLACK FLORETTE',0x2740:'WHITE FLORETTE',0x2741:'EIGHT PETALLED OUTLINED BLACK FLORETTE',\n  0x2742:'CIRCLED OPEN CENTRE EIGHT POINTED STAR',0x2743:'HEAVY TEARDROP-SPOKED PINWHEEL ASTERISK',\n  0x2744:'SNOWFLAKE',0x2745:'TIGHT TRIFOLIATE SNOWFLAKE',0x2746:'HEAVY CHEVRON SNOWFLAKE',\n  0x2747:'SPARKLE',0x2748:'HEAVY SPARKLE',0x2749:'BALLOON-SPOKED ASTERISK',\n  0x274A:'EIGHT TEARDROP-SPOKED PROPELLER ASTERISK',\n  0x274B:'HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK',\n  0x274D:'SHADOWED WHITE CIRCLE',0x274F:'LOWER RIGHT DROP-SHADOWED WHITE SQUARE',\n  0x2750:'UPPER RIGHT DROP-SHADOWED WHITE SQUARE',0x2751:'LOWER RIGHT SHADOWED WHITE SQUARE',\n  0x2752:'UPPER RIGHT SHADOWED WHITE SQUARE',0x2756:'BLACK DIAMOND MINUS WHITE X',\n  0x2757:'HEAVY EXCLAMATION MARK ORNAMENT',0x2763:'HEAVY HEART EXCLAMATION MARK ORNAMENT',\n  0x2764:'HEAVY BLACK HEART',0x2765:'ROTATED HEAVY BLACK HEART BULLET',\n  0x2766:'FLORAL HEART',0x2767:'ROTATED FLORAL HEART BULLET',\n  0x2794:'HEAVY WIDE-HEADED RIGHTWARDS ARROW',\n  0x2798:'HEAVY SOUTH EAST ARROW',0x2799:'HEAVY RIGHTWARDS ARROW',\n  0x279A:'HEAVY NORTH EAST ARROW',0x279B:'DRAFTING POINT RIGHTWARDS ARROW',\n  0x279C:'HEAVY ROUND-TIPPED RIGHTWARDS ARROW',0x279D:'TRIANGLE-HEADED RIGHTWARDS ARROW',\n  0x279E:'HEAVY TRIANGLE-HEADED RIGHTWARDS ARROW',0x279F:'DASHED TRIANGLE-HEADED RIGHTWARDS ARROW',\n  0x27A0:'HEAVY DASHED TRIANGLE-HEADED RIGHTWARDS ARROW',0x27A1:'BLACK RIGHTWARDS ARROW',\n  0x27B0:'CURLY LOOP',0x27B2:'CAUTION SIGN',0x27B3:'WHITE FEATHERED RIGHT ARROW HEAD',\n};\n\n/* ── Build category lists ── */\nconst CAT_DATA = {\n  latin:     Object.keys(latinNames).map(k=\u003e({cp:+k, name:latinNames[k]})),\n  'latin-ext': Object.keys(latinExtNames).map(k=\u003e({cp:+k, name:latinExtNames[k]})),\n  greek:     Object.keys(greekNames).map(k=\u003e({cp:+k, name:greekNames[k]})),\n  cyrillic:  Object.keys(cyrillicNames).map(k=\u003e({cp:+k, name:cyrillicNames[k]})),\n  arrows:    Object.keys(arrowNames).map(k=\u003e({cp:+k, name:arrowNames[k]})),\n  math:      Object.keys(mathNames).map(k=\u003e({cp:+k, name:mathNames[k]})),\n  box:       Object.keys(boxNames).map(k=\u003e({cp:+k, name:boxNames[k]})),\n  currency:  Object.keys(currencyNames).map(k=\u003e({cp:+k, name:currencyNames[k]})),\n  dingbats:  Object.keys(dingbatNames).map(k=\u003e({cp:+k, name:dingbatNames[k]})),\n};\n\n/* ── UTF-8 encoder ── */\nfunction toUtf8Hex(cp) {\n  const bytes = [];\n  if (cp \u003c 0x80) {\n    bytes.push(cp);\n  } else if (cp \u003c 0x800) {\n    bytes.push(0xC0 | (cp \u003e\u003e 6), 0x80 | (cp \u0026 0x3F));\n  } else if (cp \u003c 0x10000) {\n    bytes.push(0xE0 | (cp \u003e\u003e 12), 0x80 | ((cp \u003e\u003e 6) \u0026 0x3F), 0x80 | (cp \u0026 0x3F));\n  } else {\n    bytes.push(\n      0xF0 | (cp \u003e\u003e 18),\n      0x80 | ((cp \u003e\u003e 12) \u0026 0x3F),\n      0x80 | ((cp \u003e\u003e 6) \u0026 0x3F),\n      0x80 | (cp \u0026 0x3F)\n    );\n  }\n  return bytes.map(b =\u003e b.toString(16).toUpperCase().padStart(2,'0')).join(' ');\n}\n\n/* ── HTML entity ── */\nconst NAMED_ENTITIES = {\n  0x22:'\u0026quot;',0x26:'\u0026amp;',0x27:'\u0026apos;',0x3C:'\u0026lt;',0x3E:'\u0026gt;',\n  0xA0:'\u0026nbsp;',0xA9:'\u0026copy;',0xAE:'\u0026reg;',0x2122:'\u0026trade;',\n  0xA3:'\u0026pound;',0xA5:'\u0026yen;',0xA2:'\u0026cent;',0x20AC:'\u0026euro;',\n  0xB1:'\u0026plusmn;',0xD7:'\u0026times;',0xF7:'\u0026divide;',\n  0x221E:'\u0026infin;',0x2248:'\u0026asymp;',0x2260:'\u0026ne;',\n  0x2264:'\u0026le;',0x2265:'\u0026ge;',0x2190:'\u0026larr;',0x2191:'\u0026uarr;',\n  0x2192:'\u0026rarr;',0x2193:'\u0026darr;',0x2194:'\u0026harr;',0x21D4:'\u0026hArr;',\n  0x2665:'\u0026hearts;',0x2666:'\u0026diams;',0x2663:'\u0026clubs;',0x2660:'\u0026spades;',\n};\n\nfunction htmlEntity(cp) {\n  if (NAMED_ENTITIES[cp]) return NAMED_ENTITIES[cp];\n  return '\u0026#' + cp + ';';\n}\n\nfunction cpToChar(cp) {\n  return cp \u003e 0xFFFF\n    ? String.fromCodePoint(cp)\n    : String.fromCharCode(cp);\n}\n\nfunction cpHex(cp) {\n  return 'U+' + cp.toString(16).toUpperCase().padStart(4,'0');\n}\n\nfunction cssContent(cp) {\n  return '\"\\\\' + cp.toString(16).toUpperCase() + '\"';\n}\n\n/* ── State ── */\nlet currentCat = 'arrows';\nlet searchQuery = '';\nlet selectedChar = null;\nconst RECENT_KEY = 'cm_recent_v1';\n\nfunction getRecent() {\n  try { return JSON.parse(localStorage.getItem(RECENT_KEY) || '[]'); } catch(e) { return []; }\n}\nfunction addRecent(cp, name) {\n  let arr = getRecent().filter(x =\u003e x.cp !== cp);\n  arr.unshift({cp, name});\n  if (arr.length \u003e 20) arr = arr.slice(0, 20);\n  try { localStorage.setItem(RECENT_KEY, JSON.stringify(arr)); } catch(e) {}\n}\n\n/* ── Copy ── */\nlet toastTimer;\nfunction copyText(text, label) {\n  const fallback = () =\u003e {\n    const ta = document.createElement('textarea');\n    ta.value = text;\n    ta.style.cssText = 'position:fixed;opacity:0;top:0;left:0';\n    document.body.appendChild(ta);\n    ta.select();\n    document.execCommand('copy');\n    ta.remove();\n  };\n  if (navigator.clipboard \u0026\u0026 window.isSecureContext) {\n    navigator.clipboard.writeText(text).catch(fallback);\n  } else { fallback(); }\n  showToast('Copied: ' + label);\n}\n\nfunction showToast(msg) {\n  const t = document.getElementById('cm-toast');\n  t.textContent = msg;\n  t.classList.add('show');\n  clearTimeout(toastTimer);\n  toastTimer = setTimeout(() =\u003e t.classList.remove('show'), 2000);\n}\n\n/* ── Render ── */\nfunction getChars() {\n  if (searchQuery.length \u003e= 2) {\n    const q = searchQuery.toLowerCase();\n    const all = [];\n    for (const cat of CATS) {\n      for (const ch of CAT_DATA[cat.id]) {\n        if (ch.name.toLowerCase().includes(q)) {\n          all.push(ch);\n        }\n      }\n    }\n    return all;\n  }\n  return CAT_DATA[currentCat] || [];\n}\n\nfunction renderGrid() {\n  const grid = document.getElementById('cm-grid');\n  const chars = getChars();\n  if (!chars.length) {\n    grid.innerHTML = '\u003cdiv class=\"cm-empty\"\u003eNo characters found. Try a different search term.\u003c/div\u003e';\n    return;\n  }\n  grid.innerHTML = chars.map(ch =\u003e {\n    const char = cpToChar(ch.cp);\n    const hex = cpHex(ch.cp);\n    const tipName = ch.name || hex;\n    return `\u003cdiv class=\"cm-cell\" data-cp=\"${ch.cp}\" title=\"${tipName}\" role=\"button\" tabindex=\"0\" aria-label=\"${tipName}\"\u003e\n      ${char}\n      \u003cspan class=\"cm-tip\"\u003e${tipName}\u003cbr\u003e\u003csmall\u003e${hex}\u003c/small\u003e\u003c/span\u003e\n    \u003c/div\u003e`;\n  }).join('');\n\n  grid.querySelectorAll('.cm-cell').forEach(el =\u003e {\n    el.addEventListener('click', () =\u003e selectChar(+el.dataset.cp, chars.find(c=\u003ec.cp===+el.dataset.cp)?.name||''));\n    el.addEventListener('keydown', e =\u003e { if (e.key==='Enter'||e.key===' ') selectChar(+el.dataset.cp, chars.find(c=\u003ec.cp===+el.dataset.cp)?.name||''); });\n  });\n}\n\nfunction selectChar(cp, name) {\n  selectedChar = {cp, name};\n  addRecent(cp, name);\n  renderRecent();\n\n  const char = cpToChar(cp);\n  copyText(char, char + ' ' + cpHex(cp));\n\n  // Detail panel\n  document.getElementById('cm-detail-char').textContent = char;\n  document.getElementById('cm-detail-name').textContent = name || cpHex(cp);\n\n  const fields = [\n    { label:'Code Point', val: cpHex(cp) },\n    { label:'HTML Entity', val: htmlEntity(cp) },\n    { label:'CSS content', val: cssContent(cp) },\n    { label:'UTF-8 Hex', val: toUtf8Hex(cp) },\n  ];\n\n  document.getElementById('cm-detail-grid').innerHTML = fields.map(f =\u003e `\n    \u003cdiv class=\"cm-detail-row\"\u003e\n      \u003cspan class=\"cm-detail-label\"\u003e${f.label}\u003c/span\u003e\n      \u003cspan class=\"cm-detail-val\"\u003e${f.val}\u003c/span\u003e\n      \u003cbutton class=\"cm-copy-btn\" data-copy=\"${f.val}\"\u003eCopy\u003c/button\u003e\n    \u003c/div\u003e\n  `).join('');\n\n  document.getElementById('cm-detail-grid').querySelectorAll('.cm-copy-btn').forEach(btn =\u003e {\n    btn.addEventListener('click', e =\u003e {\n      e.stopPropagation();\n      copyText(btn.dataset.copy, btn.dataset.copy);\n    });\n  });\n\n  document.getElementById('cm-detail').classList.add('visible');\n}\n\nfunction renderTabs() {\n  const tabs = document.getElementById('cm-tabs');\n  tabs.innerHTML = CATS.map(c =\u003e\n    `\u003cbutton class=\"cm-tab${c.id===currentCat?' active':''}\" data-cat=\"${c.id}\"\u003e${c.label}\u003c/button\u003e`\n  ).join('');\n  tabs.querySelectorAll('.cm-tab').forEach(btn =\u003e {\n    btn.addEventListener('click', () =\u003e {\n      currentCat = btn.dataset.cat;\n      searchQuery = '';\n      document.getElementById('cm-search').value = '';\n      document.getElementById('cm-detail').classList.remove('visible');\n      renderTabs();\n      renderGrid();\n    });\n  });\n}\n\nfunction renderRecent() {\n  const recent = getRecent();\n  const sec = document.getElementById('cm-recent-section');\n  const row = document.getElementById('cm-recent-row');\n  if (!recent.length) { sec.style.display='none'; return; }\n  sec.style.display = 'block';\n  row.innerHTML = recent.map(r =\u003e {\n    const char = cpToChar(r.cp);\n    const hex = cpHex(r.cp);\n    return `\u003cdiv class=\"cm-recent-chip\" data-cp=\"${r.cp}\" title=\"${r.name||hex}\" role=\"button\" tabindex=\"0\"\u003e\n      ${char}\u003cspan class=\"chip-label\"\u003e${hex}\u003c/span\u003e\n    \u003c/div\u003e`;\n  }).join('');\n  row.querySelectorAll('.cm-recent-chip').forEach(el =\u003e {\n    el.addEventListener('click', () =\u003e {\n      const cp = +el.dataset.cp;\n      const name = getRecent().find(x=\u003ex.cp===cp)?.name||'';\n      selectChar(cp, name);\n    });\n  });\n}\n\n/* ── Search ── */\ndocument.getElementById('cm-search').addEventListener('input', function() {\n  searchQuery = this.value.trim();\n  document.getElementById('cm-detail').classList.remove('visible');\n  // Deactivate tabs when searching\n  document.querySelectorAll('#cm-tabs .cm-tab').forEach(t =\u003e t.classList.remove('active'));\n  if (!searchQuery) {\n    document.querySelectorAll('#cm-tabs .cm-tab').forEach(t =\u003e {\n      if (t.dataset.cat === currentCat) t.classList.add('active');\n    });\n  }\n  renderGrid();\n});\n\n/* ── Init ── */\nrenderTabs();\nrenderRecent();\nrenderGrid();\n\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch3 id=\"related-tools\"\u003eRelated Tools\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003eSearch emoji → \u003ca href=\"https://productivity-works.com/tools/emoji-search/\"\u003eEmoji Search\u003c/a\u003e\n\nEncode HTML entities → \u003ca href=\"https://productivity-works.com/tools/html-entity-encoder/\"\u003eHTML Entity Encoder\u003c/a\u003e\n\u003c/p\u003e","title":"Unicode Character Map"},{"content":"Browse and search the Unicode character set. Pick a category or type a character name — click any result to copy it, along with its code point, HTML entity, CSS value, and UTF-8 bytes.\n\u0026#x2715; Related tools: HTML Entity Encoder Emoji Picker Morse Code Translator How to Use Browse by category — click a category button (Arrows, Math, Currency, etc.) to see all characters in that group. Search — type a character name like arrow left or a code point like U+2192 to filter across all categories. Click any character — the detail panel shows the character, Unicode code point, HTML entities (decimal and hex), CSS content value, and UTF-8 byte sequence. Each field has its own Copy button. What Each Field Means Field Example Use it when\u0026hellip; Code Point U+2192 Referencing a character in documentation HTML (decimal) \u0026amp;#8594; Embedding in HTML without encoding issues HTML (hex) \u0026amp;#x2192; Same as above, hex form CSS content '\\2192' Using characters in ::before / ::after pseudo-elements UTF-8 bytes 0xE2 0x86 0x92 Working with raw byte streams or file encoding Related Tools HTML Entity Encoder — encode and decode HTML entities in bulk Emoji Picker — browse and copy emoji with skin-tone and keyword search Morse Code Translator — convert text to Morse code and back ","permalink":"https://productivity-works.com/tools/unicode-character-map/","summary":"\u003cp\u003eBrowse and search the Unicode character set. Pick a category or type a character name — click any result to copy it, along with its code point, HTML entity, CSS value, and UTF-8 bytes.\u003c/p\u003e\n\u003cdiv id=\"umap-app\"\u003e\n\u003cstyle\u003e\n/* ── Scoped styles: all rules prefixed with #umap-app ── */\n#umap-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  max-width: 960px;\n  margin: 0 auto;\n  color: #1a1a1a;\n}\n#umap-app *,\n#umap-app *::before,\n#umap-app *::after {\n  box-sizing: border-box;\n}\n\n/* Controls */\n#umap-app .umap-controls {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  margin-bottom: 18px;\n}\n#umap-app .umap-search {\n  flex: 1 1 220px;\n  padding: 10px 14px;\n  font-size: 1rem;\n  border: 2px solid #d1d5db;\n  border-radius: 8px;\n  outline: none;\n  transition: border-color 0.2s;\n}\n#umap-app .umap-search:focus {\n  border-color: #6366f1;\n}\n#umap-app .umap-category-wrap {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  align-items: center;\n}\n#umap-app .umap-cat-btn {\n  padding: 7px 14px;\n  font-size: 0.85rem;\n  border: 2px solid #d1d5db;\n  border-radius: 20px;\n  background: #fff;\n  cursor: pointer;\n  transition: background 0.15s, border-color 0.15s, color 0.15s;\n  white-space: nowrap;\n}\n#umap-app .umap-cat-btn:hover {\n  border-color: #6366f1;\n  color: #6366f1;\n}\n#umap-app .umap-cat-btn.active {\n  background: #6366f1;\n  border-color: #6366f1;\n  color: #fff;\n}\n\n/* Status bar */\n#umap-app .umap-status {\n  font-size: 0.82rem;\n  color: #6b7280;\n  margin-bottom: 12px;\n  min-height: 1.2em;\n}\n\n/* Grid */\n#umap-app .umap-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));\n  gap: 8px;\n  margin-bottom: 24px;\n}\n#umap-app .umap-cell {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  padding: 10px 4px 6px;\n  border: 1.5px solid #e5e7eb;\n  border-radius: 8px;\n  cursor: pointer;\n  background: #fafafa;\n  transition: border-color 0.15s, background 0.15s, transform 0.1s;\n  min-height: 72px;\n  position: relative;\n  user-select: none;\n}\n#umap-app .umap-cell:hover {\n  border-color: #6366f1;\n  background: #eef2ff;\n  transform: translateY(-2px);\n}\n#umap-app .umap-cell:active {\n  transform: translateY(0);\n}\n#umap-app .umap-cell.copied {\n  border-color: #10b981;\n  background: #d1fae5;\n}\n#umap-app .umap-glyph {\n  font-size: 1.6rem;\n  line-height: 1;\n  margin-bottom: 4px;\n}\n#umap-app .umap-cp {\n  font-size: 0.62rem;\n  color: #9ca3af;\n  font-family: monospace;\n}\n\n/* Detail panel */\n#umap-app .umap-detail {\n  display: none;\n  border: 2px solid #6366f1;\n  border-radius: 12px;\n  padding: 20px 24px;\n  background: #f5f3ff;\n  margin-bottom: 24px;\n  position: relative;\n}\n#umap-app .umap-detail.visible {\n  display: block;\n}\n#umap-app .umap-detail-close {\n  position: absolute;\n  top: 12px;\n  right: 14px;\n  background: none;\n  border: none;\n  font-size: 1.2rem;\n  cursor: pointer;\n  color: #6b7280;\n  line-height: 1;\n}\n#umap-app .umap-detail-glyph {\n  font-size: 3.5rem;\n  line-height: 1;\n  margin-bottom: 12px;\n}\n#umap-app .umap-detail-name {\n  font-weight: 700;\n  font-size: 1rem;\n  margin-bottom: 14px;\n  color: #374151;\n}\n#umap-app .umap-detail-rows {\n  display: grid;\n  grid-template-columns: auto 1fr auto;\n  gap: 10px 16px;\n  align-items: center;\n}\n#umap-app .umap-detail-label {\n  font-size: 0.8rem;\n  color: #6b7280;\n  white-space: nowrap;\n}\n#umap-app .umap-detail-value {\n  font-family: monospace;\n  font-size: 0.92rem;\n  color: #1a1a1a;\n  word-break: break-all;\n}\n#umap-app .umap-copy-btn {\n  padding: 4px 12px;\n  font-size: 0.78rem;\n  background: #6366f1;\n  color: #fff;\n  border: none;\n  border-radius: 6px;\n  cursor: pointer;\n  transition: background 0.15s;\n  white-space: nowrap;\n}\n#umap-app .umap-copy-btn:hover {\n  background: #4f46e5;\n}\n#umap-app .umap-copy-btn.ok {\n  background: #10b981;\n}\n\n/* Toast */\n#umap-app .umap-toast {\n  position: fixed;\n  bottom: 28px;\n  left: 50%;\n  transform: translateX(-50%) translateY(20px);\n  background: #1f2937;\n  color: #fff;\n  padding: 10px 22px;\n  border-radius: 24px;\n  font-size: 0.9rem;\n  opacity: 0;\n  pointer-events: none;\n  transition: opacity 0.25s, transform 0.25s;\n  z-index: 9999;\n  white-space: nowrap;\n}\n#umap-app .umap-toast.show {\n  opacity: 1;\n  transform: translateX(-50%) translateY(0);\n}\n\n/* No results */\n#umap-app .umap-empty {\n  text-align: center;\n  color: #9ca3af;\n  padding: 48px 0;\n  font-size: 1rem;\n}\n\n/* Related links */\n#umap-app .umap-related {\n  border-top: 1px solid #e5e7eb;\n  padding-top: 18px;\n  margin-top: 8px;\n  font-size: 0.88rem;\n  color: #6b7280;\n}\n#umap-app .umap-related a {\n  color: #6366f1;\n  text-decoration: none;\n  margin-right: 14px;\n}\n#umap-app .umap-related a:hover {\n  text-decoration: underline;\n}\n\n/* Mobile */\n@media (max-width: 600px) {\n  #umap-app .umap-grid {\n    grid-template-columns: repeat(auto-fill, minmax(60px, 1fr));\n    gap: 6px;\n  }\n  #umap-app .umap-glyph {\n    font-size: 1.3rem;\n  }\n  #umap-app .umap-detail-rows {\n    grid-template-columns: auto 1fr;\n  }\n  #umap-app .umap-copy-btn {\n    grid-column: 1 / -1;\n  }\n  #umap-app .umap-detail-glyph {\n    font-size: 2.5rem;\n  }\n}\n\u003c/style\u003e\n\u003cdiv class=\"umap-controls\"\u003e\n  \u003cinput\n    class=\"umap-search\"\n    id=\"umap-search\"\n    type=\"search\"\n    placeholder=\"Search by name or code point, e.g. \u0026quot;arrow\u0026quot; or \u0026quot;U+2192\u0026quot;\"\n    autocomplete=\"off\"\n  /\u003e\n\u003c/div\u003e\n\u003cdiv class=\"umap-controls\" style=\"margin-top:-4px\"\u003e\n  \u003cdiv class=\"umap-category-wrap\" id=\"umap-cats\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"umap-status\" id=\"umap-status\"\u003e\u003c/div\u003e\n\u003cdiv class=\"umap-detail\" id=\"umap-detail\"\u003e\n  \u003cbutton class=\"umap-detail-close\" id=\"umap-detail-close\" title=\"Close\"\u003e\u0026#x2715;\u003c/button\u003e\n  \u003cdiv class=\"umap-detail-glyph\" id=\"umap-dg\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"umap-detail-name\" id=\"umap-dn\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"umap-detail-rows\" id=\"umap-dr\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"umap-grid\" id=\"umap-grid\"\u003e\u003c/div\u003e\n\u003cdiv class=\"umap-toast\" id=\"umap-toast\"\u003e\u003c/div\u003e\n\u003cdiv class=\"umap-related\"\u003e\n  Related tools:\n  \u003ca href=\"/tools/html-entity-encoder/\"\u003eHTML Entity Encoder\u003c/a\u003e\n  \u003ca href=\"/tools/emoji-picker/\"\u003eEmoji Picker\u003c/a\u003e\n  \u003ca href=\"/tools/morse-code-translator/\"\u003eMorse Code Translator\u003c/a\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  \"use strict\";\n\n  /* ── Character data ── */\n  var CATS = [\n    {\n      id: \"arrows\",\n      label: \"Arrows\",\n      chars: [\n        [0x2190, \"LEFTWARDS ARROW\"],\n        [0x2191, \"UPWARDS ARROW\"],\n        [0x2192, \"RIGHTWARDS ARROW\"],\n        [0x2193, \"DOWNWARDS ARROW\"],\n        [0x2194, \"LEFT RIGHT ARROW\"],\n        [0x2195, \"UP DOWN ARROW\"],\n        [0x2196, \"NORTH WEST ARROW\"],\n        [0x2197, \"NORTH EAST ARROW\"],\n        [0x2198, \"SOUTH EAST ARROW\"],\n        [0x2199, \"SOUTH WEST ARROW\"],\n        [0x219a, \"LEFTWARDS ARROW WITH STROKE\"],\n        [0x219b, \"RIGHTWARDS ARROW WITH STROKE\"],\n        [0x219c, \"LEFTWARDS WAVE ARROW\"],\n        [0x219d, \"RIGHTWARDS WAVE ARROW\"],\n        [0x219e, \"LEFTWARDS TWO HEADED ARROW\"],\n        [0x21a0, \"RIGHTWARDS TWO HEADED ARROW\"],\n        [0x21a2, \"LEFTWARDS ARROW WITH TAIL\"],\n        [0x21a3, \"RIGHTWARDS ARROW WITH TAIL\"],\n        [0x21a6, \"RIGHTWARDS ARROW FROM BAR\"],\n        [0x21a9, \"LEFTWARDS ARROW WITH HOOK\"],\n        [0x21aa, \"RIGHTWARDS ARROW WITH HOOK\"],\n        [0x21b0, \"UPWARDS ARROW WITH TIP LEFTWARDS\"],\n        [0x21b1, \"UPWARDS ARROW WITH TIP RIGHTWARDS\"],\n        [0x21b2, \"DOWNWARDS ARROW WITH TIP LEFTWARDS\"],\n        [0x21b3, \"DOWNWARDS ARROW WITH TIP RIGHTWARDS\"],\n        [0x21b4, \"RIGHTWARDS ARROW WITH CORNER DOWNWARDS\"],\n        [0x21b5, \"DOWNWARDS ARROW WITH CORNER LEFTWARDS\"],\n        [0x21b6, \"ANTICLOCKWISE TOP SEMICIRCLE ARROW\"],\n        [0x21b7, \"CLOCKWISE TOP SEMICIRCLE ARROW\"],\n        [0x21ba, \"ANTICLOCKWISE OPEN CIRCLE ARROW\"],\n        [0x21bb, \"CLOCKWISE OPEN CIRCLE ARROW\"],\n        [0x21bc, \"LEFTWARDS HARPOON WITH BARB UPWARDS\"],\n        [0x21c0, \"RIGHTWARDS HARPOON WITH BARB UPWARDS\"],\n        [0x21c4, \"RIGHTWARDS ARROW OVER LEFTWARDS ARROW\"],\n        [0x21c6, \"LEFTWARDS ARROW OVER RIGHTWARDS ARROW\"],\n        [0x21c7, \"LEFTWARDS PAIRED ARROWS\"],\n        [0x21c8, \"UPWARDS PAIRED ARROWS\"],\n        [0x21c9, \"RIGHTWARDS PAIRED ARROWS\"],\n        [0x21ca, \"DOWNWARDS PAIRED ARROWS\"],\n        [0x21cb, \"LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON\"],\n        [0x21cc, \"RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON\"],\n        [0x21cd, \"LEFTWARDS DOUBLE ARROW WITH STROKE\"],\n        [0x21ce, \"LEFT RIGHT DOUBLE ARROW WITH STROKE\"],\n        [0x21cf, \"RIGHTWARDS DOUBLE ARROW WITH STROKE\"],\n        [0x21d0, \"LEFTWARDS DOUBLE ARROW\"],\n        [0x21d1, \"UPWARDS DOUBLE ARROW\"],\n        [0x21d2, \"RIGHTWARDS DOUBLE ARROW\"],\n        [0x21d3, \"DOWNWARDS DOUBLE ARROW\"],\n        [0x21d4, \"LEFT RIGHT DOUBLE ARROW\"],\n        [0x21d5, \"UP DOWN DOUBLE ARROW\"],\n        [0x21e6, \"LEFTWARDS WHITE ARROW\"],\n        [0x21e7, \"UPWARDS WHITE ARROW\"],\n        [0x21e8, \"RIGHTWARDS WHITE ARROW\"],\n        [0x21e9, \"DOWNWARDS WHITE ARROW\"],\n        [0x27a1, \"BLACK RIGHTWARDS ARROW\"],\n        [0x2b05, \"LEFTWARDS BLACK ARROW\"],\n        [0x2b06, \"UPWARDS BLACK ARROW\"],\n        [0x2b07, \"DOWNWARDS BLACK ARROW\"],\n        [0x2b95, \"RIGHTWARDS BLACK ARROW\"],\n      ],\n    },\n    {\n      id: \"math\",\n      label: \"Math\",\n      chars: [\n        [0x00b1, \"PLUS-MINUS SIGN\"],\n        [0x00d7, \"MULTIPLICATION SIGN\"],\n        [0x00f7, \"DIVISION SIGN\"],\n        [0x2200, \"FOR ALL\"],\n        [0x2202, \"PARTIAL DIFFERENTIAL\"],\n        [0x2203, \"THERE EXISTS\"],\n        [0x2204, \"THERE DOES NOT EXIST\"],\n        [0x2205, \"EMPTY SET\"],\n        [0x2207, \"NABLA\"],\n        [0x2208, \"ELEMENT OF\"],\n        [0x2209, \"NOT AN ELEMENT OF\"],\n        [0x220b, \"CONTAINS AS MEMBER\"],\n        [0x220f, \"N-ARY PRODUCT\"],\n        [0x2211, \"N-ARY SUMMATION\"],\n        [0x2212, \"MINUS SIGN\"],\n        [0x2213, \"MINUS-OR-PLUS SIGN\"],\n        [0x2217, \"ASTERISK OPERATOR\"],\n        [0x221a, \"SQUARE ROOT\"],\n        [0x221b, \"CUBE ROOT\"],\n        [0x221c, \"FOURTH ROOT\"],\n        [0x221e, \"INFINITY\"],\n        [0x2220, \"ANGLE\"],\n        [0x2221, \"MEASURED ANGLE\"],\n        [0x2222, \"SPHERICAL ANGLE\"],\n        [0x2223, \"DIVIDES\"],\n        [0x2225, \"PARALLEL TO\"],\n        [0x2227, \"LOGICAL AND\"],\n        [0x2228, \"LOGICAL OR\"],\n        [0x2229, \"INTERSECTION\"],\n        [0x222a, \"UNION\"],\n        [0x222b, \"INTEGRAL\"],\n        [0x222c, \"DOUBLE INTEGRAL\"],\n        [0x222d, \"TRIPLE INTEGRAL\"],\n        [0x2236, \"RATIO\"],\n        [0x2237, \"PROPORTION\"],\n        [0x223c, \"TILDE OPERATOR\"],\n        [0x2243, \"ASYMPTOTICALLY EQUAL TO\"],\n        [0x2245, \"APPROXIMATELY EQUAL TO\"],\n        [0x2248, \"ALMOST EQUAL TO\"],\n        [0x2249, \"NOT ALMOST EQUAL TO\"],\n        [0x2260, \"NOT EQUAL TO\"],\n        [0x2261, \"IDENTICAL TO\"],\n        [0x2262, \"NOT IDENTICAL TO\"],\n        [0x2264, \"LESS-THAN OR EQUAL TO\"],\n        [0x2265, \"GREATER-THAN OR EQUAL TO\"],\n        [0x226a, \"MUCH LESS-THAN\"],\n        [0x226b, \"MUCH GREATER-THAN\"],\n        [0x2282, \"SUBSET OF\"],\n        [0x2283, \"SUPERSET OF\"],\n        [0x2284, \"NOT A SUBSET OF\"],\n        [0x2286, \"SUBSET OF OR EQUAL TO\"],\n        [0x2287, \"SUPERSET OF OR EQUAL TO\"],\n        [0x2295, \"CIRCLED PLUS\"],\n        [0x2297, \"CIRCLED TIMES\"],\n        [0x22c5, \"DOT OPERATOR\"],\n        [0x25b3, \"WHITE UP-POINTING TRIANGLE\"],\n        [0x03c0, \"GREEK SMALL LETTER PI\"],\n        [0x03b1, \"GREEK SMALL LETTER ALPHA\"],\n        [0x03b2, \"GREEK SMALL LETTER BETA\"],\n        [0x03b3, \"GREEK SMALL LETTER GAMMA\"],\n      ],\n    },\n    {\n      id: \"currency\",\n      label: \"Currency\",\n      chars: [\n        [0x0024, \"DOLLAR SIGN\"],\n        [0x00a2, \"CENT SIGN\"],\n        [0x00a3, \"POUND SIGN\"],\n        [0x00a4, \"CURRENCY SIGN\"],\n        [0x00a5, \"YEN SIGN\"],\n        [0x20a0, \"EURO-CURRENCY SIGN\"],\n        [0x20a1, \"COLON SIGN\"],\n        [0x20a2, \"CRUZEIRO SIGN\"],\n        [0x20a3, \"FRENCH FRANC SIGN\"],\n        [0x20a4, \"LIRA SIGN\"],\n        [0x20a6, \"NAIRA SIGN\"],\n        [0x20a7, \"PESETA SIGN\"],\n        [0x20a8, \"RUPEE SIGN\"],\n        [0x20a9, \"WON SIGN\"],\n        [0x20aa, \"NEW SHEQEL SIGN\"],\n        [0x20ab, \"DONG SIGN\"],\n        [0x20ac, \"EURO SIGN\"],\n        [0x20ad, \"KIP SIGN\"],\n        [0x20ae, \"TUGRIK SIGN\"],\n        [0x20af, \"DRACHMA SIGN\"],\n        [0x20b0, \"GERMAN PENNY SIGN\"],\n        [0x20b1, \"PESO SIGN\"],\n        [0x20b2, \"GUARANI SIGN\"],\n        [0x20b3, \"AUSTRAL SIGN\"],\n        [0x20b4, \"HRYVNIA SIGN\"],\n        [0x20b5, \"CEDI SIGN\"],\n        [0x20b6, \"LIVRE TOURNOIS SIGN\"],\n        [0x20b7, \"SPESMILO SIGN\"],\n        [0x20b8, \"TENGE SIGN\"],\n        [0x20b9, \"INDIAN RUPEE SIGN\"],\n        [0x20ba, \"TURKISH LIRA SIGN\"],\n        [0x20bb, \"NORDIC MARK SIGN\"],\n        [0x20bc, \"MANAT SIGN\"],\n        [0x20bd, \"RUBLE SIGN\"],\n        [0x20be, \"LARI SIGN\"],\n        [0x20bf, \"BITCOIN SIGN\"],\n        [0x fdfc, \"RIAL SIGN\"],\n        [0x09f3, \"BENGALI RUPEE SIGN\"],\n        [0x0af1, \"GUJARATI RUPEE SIGN\"],\n        [0x0bf9, \"TAMIL RUPEE SIGN\"],\n      ],\n    },\n    {\n      id: \"symbols\",\n      label: \"Symbols\",\n      chars: [\n        [0x00a9, \"COPYRIGHT SIGN\"],\n        [0x00ae, \"REGISTERED SIGN\"],\n        [0x2122, \"TRADE MARK SIGN\"],\n        [0x00b0, \"DEGREE SIGN\"],\n        [0x00b5, \"MICRO SIGN\"],\n        [0x00b6, \"PILCROW SIGN\"],\n        [0x00a7, \"SECTION SIGN\"],\n        [0x2020, \"DAGGER\"],\n        [0x2021, \"DOUBLE DAGGER\"],\n        [0x2022, \"BULLET\"],\n        [0x2023, \"TRIANGULAR BULLET\"],\n        [0x2026, \"HORIZONTAL ELLIPSIS\"],\n        [0x2030, \"PER MILLE SIGN\"],\n        [0x2031, \"PER TEN THOUSAND SIGN\"],\n        [0x2032, \"PRIME\"],\n        [0x2033, \"DOUBLE PRIME\"],\n        [0x2034, \"TRIPLE PRIME\"],\n        [0x2035, \"REVERSED PRIME\"],\n        [0x203b, \"REFERENCE MARK\"],\n        [0x203c, \"DOUBLE EXCLAMATION MARK\"],\n        [0x203d, \"INTERROBANG\"],\n        [0x2047, \"DOUBLE QUESTION MARK\"],\n        [0x2048, \"QUESTION EXCLAMATION MARK\"],\n        [0x2049, \"EXCLAMATION QUESTION MARK\"],\n        [0x204a, \"TIRONIAN SIGN ET\"],\n        [0x2116, \"NUMERO SIGN\"],\n        [0x211e, \"PRESCRIPTION TAKE\"],\n        [0x2120, \"SERVICE MARK\"],\n        [0x2126, \"OHM SIGN\"],\n        [0x212b, \"ANGSTROM SIGN\"],\n        [0x2139, \"INFORMATION SOURCE\"],\n        [0x2190, \"LEFTWARDS ARROW\"],\n        [0x231a, \"WATCH\"],\n        [0x231b, \"HOURGLASS\"],\n        [0x2328, \"KEYBOARD\"],\n        [0x23ce, \"RETURN SYMBOL\"],\n        [0x23f0, \"ALARM CLOCK\"],\n        [0x23f3, \"HOURGLASS WITH FLOWING SAND\"],\n        [0x2602, \"UMBRELLA\"],\n        [0x2603, \"SNOWMAN\"],\n        [0x2604, \"COMET\"],\n        [0x260e, \"BLACK TELEPHONE\"],\n        [0x2615, \"HOT BEVERAGE\"],\n        [0x2620, \"SKULL AND CROSSBONES\"],\n        [0x2622, \"RADIOACTIVE SIGN\"],\n        [0x2623, \"BIOHAZARD SIGN\"],\n        [0x262e, \"PEACE SYMBOL\"],\n        [0x262f, \"YIN YANG\"],\n        [0x2639, \"WHITE FROWNING FACE\"],\n        [0x263a, \"WHITE SMILING FACE\"],\n        [0x2640, \"FEMALE SIGN\"],\n        [0x2642, \"MALE SIGN\"],\n        [0x2660, \"BLACK SPADE SUIT\"],\n        [0x2663, \"BLACK CLUB SUIT\"],\n        [0x2665, \"BLACK HEART SUIT\"],\n        [0x2666, \"BLACK DIAMOND SUIT\"],\n        [0x2713, \"CHECK MARK\"],\n        [0x2714, \"HEAVY CHECK MARK\"],\n        [0x2715, \"MULTIPLICATION X\"],\n        [0x2716, \"HEAVY MULTIPLICATION X\"],\n      ],\n    },\n    {\n      id: \"dingbats\",\n      label: \"Dingbats\",\n      chars: [\n        [0x2701, \"UPPER BLADE SCISSORS\"],\n        [0x2702, \"BLACK SCISSORS\"],\n        [0x2703, \"LOWER BLADE SCISSORS\"],\n        [0x2704, \"WHITE SCISSORS\"],\n        [0x2706, \"TELEPHONE LOCATION SIGN\"],\n        [0x2707, \"TAPE DRIVE\"],\n        [0x2708, \"AIRPLANE\"],\n        [0x2709, \"ENVELOPE\"],\n        [0x270a, \"RAISED FIST\"],\n        [0x270b, \"RAISED HAND\"],\n        [0x270c, \"VICTORY HAND\"],\n        [0x270d, \"WRITING HAND\"],\n        [0x270e, \"LOWER RIGHT PENCIL\"],\n        [0x270f, \"PENCIL\"],\n        [0x2710, \"UPPER RIGHT PENCIL\"],\n        [0x2711, \"WHITE NIB\"],\n        [0x2712, \"BLACK NIB\"],\n        [0x2713, \"CHECK MARK\"],\n        [0x2714, \"HEAVY CHECK MARK\"],\n        [0x2715, \"MULTIPLICATION X\"],\n        [0x2716, \"HEAVY MULTIPLICATION X\"],\n        [0x2717, \"BALLOT X\"],\n        [0x2718, \"HEAVY BALLOT X\"],\n        [0x2719, \"OUTLINED GREEK CROSS\"],\n        [0x271a, \"HEAVY GREEK CROSS\"],\n        [0x271b, \"OPEN CENTRE CROSS\"],\n        [0x271c, \"HEAVY OPEN CENTRE CROSS\"],\n        [0x2720, \"MALTESE CROSS\"],\n        [0x2721, \"STAR OF DAVID\"],\n        [0x2722, \"FOUR TEARDROP-SPOKED ASTERISK\"],\n        [0x2723, \"FOUR BALLOON-SPOKED ASTERISK\"],\n        [0x2724, \"HEAVY FOUR BALLOON-SPOKED ASTERISK\"],\n        [0x2725, \"FOUR CLUB-SPOKED ASTERISK\"],\n        [0x2726, \"BLACK FOUR POINTED STAR\"],\n        [0x2727, \"WHITE FOUR POINTED STAR\"],\n        [0x2728, \"SPARKLES\"],\n        [0x2729, \"STRESS OUTLINED WHITE STAR\"],\n        [0x272a, \"CIRCLED WHITE STAR\"],\n        [0x272b, \"OPEN CENTRE BLACK STAR\"],\n        [0x272c, \"BLACK CENTRE WHITE STAR\"],\n        [0x272d, \"OUTLINED BLACK STAR\"],\n        [0x272e, \"HEAVY OUTLINED BLACK STAR\"],\n        [0x272f, \"PINWHEEL STAR\"],\n        [0x2730, \"SHADOWED WHITE STAR\"],\n        [0x2731, \"HEAVY ASTERISK\"],\n        [0x2732, \"OPEN CENTRE ASTERISK\"],\n        [0x2733, \"EIGHT SPOKED ASTERISK\"],\n        [0x2734, \"EIGHT POINTED BLACK STAR\"],\n        [0x2735, \"EIGHT POINTED PINWHEEL STAR\"],\n        [0x2736, \"SIX POINTED BLACK STAR\"],\n        [0x2737, \"EIGHT POINTED RECTILINEAR BLACK STAR\"],\n        [0x2738, \"HEAVY EIGHT POINTED RECTILINEAR BLACK STAR\"],\n        [0x2739, \"TWELVE POINTED BLACK STAR\"],\n        [0x273a, \"SIXTEEN POINTED ASTERISK\"],\n        [0x273f, \"BLACK FLORETTE\"],\n        [0x2740, \"WHITE FLORETTE\"],\n        [0x2741, \"EIGHT PETALLED OUTLINED BLACK FLORETTE\"],\n        [0x2744, \"SNOWFLAKE\"],\n        [0x2745, \"TIGHT TRIFOLIATE SNOWFLAKE\"],\n        [0x2746, \"HEAVY CHEVRON SNOWFLAKE\"],\n        [0x2762, \"HEAVY EXCLAMATION MARK ORNAMENT\"],\n        [0x2763, \"HEAVY HEART EXCLAMATION MARK ORNAMENT\"],\n        [0x2764, \"HEAVY BLACK HEART\"],\n        [0x2765, \"ROTATED HEAVY BLACK HEART BULLET\"],\n        [0x2767, \"ROTATED FLORAL HEART BULLET\"],\n      ],\n    },\n    {\n      id: \"box\",\n      label: \"Box Drawing\",\n      chars: [\n        [0x2500, \"BOX DRAWINGS LIGHT HORIZONTAL\"],\n        [0x2501, \"BOX DRAWINGS HEAVY HORIZONTAL\"],\n        [0x2502, \"BOX DRAWINGS LIGHT VERTICAL\"],\n        [0x2503, \"BOX DRAWINGS HEAVY VERTICAL\"],\n        [0x2504, \"BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL\"],\n        [0x2505, \"BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL\"],\n        [0x2506, \"BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL\"],\n        [0x2507, \"BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL\"],\n        [0x2508, \"BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL\"],\n        [0x2509, \"BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL\"],\n        [0x250c, \"BOX DRAWINGS LIGHT DOWN AND RIGHT\"],\n        [0x250d, \"BOX DRAWINGS DOWN LIGHT AND RIGHT HEAVY\"],\n        [0x250e, \"BOX DRAWINGS DOWN HEAVY AND RIGHT LIGHT\"],\n        [0x250f, \"BOX DRAWINGS HEAVY DOWN AND RIGHT\"],\n        [0x2510, \"BOX DRAWINGS LIGHT DOWN AND LEFT\"],\n        [0x2511, \"BOX DRAWINGS DOWN LIGHT AND LEFT HEAVY\"],\n        [0x2512, \"BOX DRAWINGS DOWN HEAVY AND LEFT LIGHT\"],\n        [0x2513, \"BOX DRAWINGS HEAVY DOWN AND LEFT\"],\n        [0x2514, \"BOX DRAWINGS LIGHT UP AND RIGHT\"],\n        [0x2515, \"BOX DRAWINGS UP LIGHT AND RIGHT HEAVY\"],\n        [0x2516, \"BOX DRAWINGS UP HEAVY AND RIGHT LIGHT\"],\n        [0x2517, \"BOX DRAWINGS HEAVY UP AND RIGHT\"],\n        [0x2518, \"BOX DRAWINGS LIGHT UP AND LEFT\"],\n        [0x2519, \"BOX DRAWINGS UP LIGHT AND LEFT HEAVY\"],\n        [0x251a, \"BOX DRAWINGS UP HEAVY AND LEFT LIGHT\"],\n        [0x251b, \"BOX DRAWINGS HEAVY UP AND LEFT\"],\n        [0x251c, \"BOX DRAWINGS LIGHT VERTICAL AND RIGHT\"],\n        [0x251d, \"BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY\"],\n        [0x2520, \"BOX DRAWINGS VERTICAL HEAVY AND RIGHT LIGHT\"],\n        [0x2523, \"BOX DRAWINGS HEAVY VERTICAL AND RIGHT\"],\n        [0x2524, \"BOX DRAWINGS LIGHT VERTICAL AND LEFT\"],\n        [0x252c, \"BOX DRAWINGS LIGHT DOWN AND HORIZONTAL\"],\n        [0x2534, \"BOX DRAWINGS LIGHT UP AND HORIZONTAL\"],\n        [0x253c, \"BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL\"],\n        [0x2550, \"BOX DRAWINGS DOUBLE HORIZONTAL\"],\n        [0x2551, \"BOX DRAWINGS DOUBLE VERTICAL\"],\n        [0x2554, \"BOX DRAWINGS DOUBLE DOWN AND RIGHT\"],\n        [0x2557, \"BOX DRAWINGS DOUBLE DOWN AND LEFT\"],\n        [0x255a, \"BOX DRAWINGS DOUBLE UP AND RIGHT\"],\n        [0x255d, \"BOX DRAWINGS DOUBLE UP AND LEFT\"],\n        [0x256c, \"BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL\"],\n        [0x2580, \"UPPER HALF BLOCK\"],\n        [0x2584, \"LOWER HALF BLOCK\"],\n        [0x2588, \"FULL BLOCK\"],\n        [0x258c, \"LEFT HALF BLOCK\"],\n        [0x2590, \"RIGHT HALF BLOCK\"],\n        [0x2591, \"LIGHT SHADE\"],\n        [0x2592, \"MEDIUM SHADE\"],\n        [0x2593, \"DARK SHADE\"],\n        [0x25a0, \"BLACK SQUARE\"],\n        [0x25a1, \"WHITE SQUARE\"],\n        [0x25aa, \"BLACK SMALL SQUARE\"],\n        [0x25ab, \"WHITE SMALL SQUARE\"],\n        [0x25b2, \"BLACK UP-POINTING TRIANGLE\"],\n        [0x25b6, \"BLACK RIGHT-POINTING TRIANGLE\"],\n        [0x25bc, \"BLACK DOWN-POINTING TRIANGLE\"],\n        [0x25c0, \"BLACK LEFT-POINTING TRIANGLE\"],\n        [0x25c6, \"BLACK DIAMOND\"],\n        [0x25c7, \"WHITE DIAMOND\"],\n        [0x25cf, \"BLACK CIRCLE\"],\n        [0x25d8, \"INVERSE BULLET\"],\n        [0x25e6, \"WHITE BULLET\"],\n      ],\n    },\n    {\n      id: \"greek\",\n      label: \"Greek\",\n      chars: [\n        [0x0391, \"GREEK CAPITAL LETTER ALPHA\"],\n        [0x0392, \"GREEK CAPITAL LETTER BETA\"],\n        [0x0393, \"GREEK CAPITAL LETTER GAMMA\"],\n        [0x0394, \"GREEK CAPITAL LETTER DELTA\"],\n        [0x0395, \"GREEK CAPITAL LETTER EPSILON\"],\n        [0x0396, \"GREEK CAPITAL LETTER ZETA\"],\n        [0x0397, \"GREEK CAPITAL LETTER ETA\"],\n        [0x0398, \"GREEK CAPITAL LETTER THETA\"],\n        [0x0399, \"GREEK CAPITAL LETTER IOTA\"],\n        [0x039a, \"GREEK CAPITAL LETTER KAPPA\"],\n        [0x039b, \"GREEK CAPITAL LETTER LAMDA\"],\n        [0x039c, \"GREEK CAPITAL LETTER MU\"],\n        [0x039d, \"GREEK CAPITAL LETTER NU\"],\n        [0x039e, \"GREEK CAPITAL LETTER XI\"],\n        [0x039f, \"GREEK CAPITAL LETTER OMICRON\"],\n        [0x03a0, \"GREEK CAPITAL LETTER PI\"],\n        [0x03a1, \"GREEK CAPITAL LETTER RHO\"],\n        [0x03a3, \"GREEK CAPITAL LETTER SIGMA\"],\n        [0x03a4, \"GREEK CAPITAL LETTER TAU\"],\n        [0x03a5, \"GREEK CAPITAL LETTER UPSILON\"],\n        [0x03a6, \"GREEK CAPITAL LETTER PHI\"],\n        [0x03a7, \"GREEK CAPITAL LETTER CHI\"],\n        [0x03a8, \"GREEK CAPITAL LETTER PSI\"],\n        [0x03a9, \"GREEK CAPITAL LETTER OMEGA\"],\n        [0x03b1, \"GREEK SMALL LETTER ALPHA\"],\n        [0x03b2, \"GREEK SMALL LETTER BETA\"],\n        [0x03b3, \"GREEK SMALL LETTER GAMMA\"],\n        [0x03b4, \"GREEK SMALL LETTER DELTA\"],\n        [0x03b5, \"GREEK SMALL LETTER EPSILON\"],\n        [0x03b6, \"GREEK SMALL LETTER ZETA\"],\n        [0x03b7, \"GREEK SMALL LETTER ETA\"],\n        [0x03b8, \"GREEK SMALL LETTER THETA\"],\n        [0x03b9, \"GREEK SMALL LETTER IOTA\"],\n        [0x03ba, \"GREEK SMALL LETTER KAPPA\"],\n        [0x03bb, \"GREEK SMALL LETTER LAMDA\"],\n        [0x03bc, \"GREEK SMALL LETTER MU\"],\n        [0x03bd, \"GREEK SMALL LETTER NU\"],\n        [0x03be, \"GREEK SMALL LETTER XI\"],\n        [0x03bf, \"GREEK SMALL LETTER OMICRON\"],\n        [0x03c0, \"GREEK SMALL LETTER PI\"],\n        [0x03c1, \"GREEK SMALL LETTER RHO\"],\n        [0x03c3, \"GREEK SMALL LETTER SIGMA\"],\n        [0x03c4, \"GREEK SMALL LETTER TAU\"],\n        [0x03c5, \"GREEK SMALL LETTER UPSILON\"],\n        [0x03c6, \"GREEK SMALL LETTER PHI\"],\n        [0x03c7, \"GREEK SMALL LETTER CHI\"],\n        [0x03c8, \"GREEK SMALL LETTER PSI\"],\n        [0x03c9, \"GREEK SMALL LETTER OMEGA\"],\n      ],\n    },\n    {\n      id: \"cyrillic\",\n      label: \"Cyrillic\",\n      chars: [\n        [0x0410, \"CYRILLIC CAPITAL LETTER A\"],\n        [0x0411, \"CYRILLIC CAPITAL LETTER BE\"],\n        [0x0412, \"CYRILLIC CAPITAL LETTER VE\"],\n        [0x0413, \"CYRILLIC CAPITAL LETTER GHE\"],\n        [0x0414, \"CYRILLIC CAPITAL LETTER DE\"],\n        [0x0415, \"CYRILLIC CAPITAL LETTER IE\"],\n        [0x0416, \"CYRILLIC CAPITAL LETTER ZHE\"],\n        [0x0417, \"CYRILLIC CAPITAL LETTER ZE\"],\n        [0x0418, \"CYRILLIC CAPITAL LETTER I\"],\n        [0x0419, \"CYRILLIC CAPITAL LETTER SHORT I\"],\n        [0x041a, \"CYRILLIC CAPITAL LETTER KA\"],\n        [0x041b, \"CYRILLIC CAPITAL LETTER EL\"],\n        [0x041c, \"CYRILLIC CAPITAL LETTER EM\"],\n        [0x041d, \"CYRILLIC CAPITAL LETTER EN\"],\n        [0x041e, \"CYRILLIC CAPITAL LETTER O\"],\n        [0x041f, \"CYRILLIC CAPITAL LETTER PE\"],\n        [0x0420, \"CYRILLIC CAPITAL LETTER ER\"],\n        [0x0421, \"CYRILLIC CAPITAL LETTER ES\"],\n        [0x0422, \"CYRILLIC CAPITAL LETTER TE\"],\n        [0x0423, \"CYRILLIC CAPITAL LETTER U\"],\n        [0x0424, \"CYRILLIC CAPITAL LETTER EF\"],\n        [0x0425, \"CYRILLIC CAPITAL LETTER HA\"],\n        [0x0426, \"CYRILLIC CAPITAL LETTER TSE\"],\n        [0x0427, \"CYRILLIC CAPITAL LETTER CHE\"],\n        [0x0428, \"CYRILLIC CAPITAL LETTER SHA\"],\n        [0x0429, \"CYRILLIC CAPITAL LETTER SHCHA\"],\n        [0x042a, \"CYRILLIC CAPITAL LETTER HARD SIGN\"],\n        [0x042b, \"CYRILLIC CAPITAL LETTER YERU\"],\n        [0x042c, \"CYRILLIC CAPITAL LETTER SOFT SIGN\"],\n        [0x042d, \"CYRILLIC CAPITAL LETTER E\"],\n        [0x042e, \"CYRILLIC CAPITAL LETTER YU\"],\n        [0x042f, \"CYRILLIC CAPITAL LETTER YA\"],\n        [0x0430, \"CYRILLIC SMALL LETTER A\"],\n        [0x0431, \"CYRILLIC SMALL LETTER BE\"],\n        [0x0432, \"CYRILLIC SMALL LETTER VE\"],\n        [0x0433, \"CYRILLIC SMALL LETTER GHE\"],\n        [0x0434, \"CYRILLIC SMALL LETTER DE\"],\n        [0x0435, \"CYRILLIC SMALL LETTER IE\"],\n        [0x0436, \"CYRILLIC SMALL LETTER ZHE\"],\n        [0x0437, \"CYRILLIC SMALL LETTER ZE\"],\n        [0x0438, \"CYRILLIC SMALL LETTER I\"],\n        [0x0439, \"CYRILLIC SMALL LETTER SHORT I\"],\n        [0x043a, \"CYRILLIC SMALL LETTER KA\"],\n        [0x043b, \"CYRILLIC SMALL LETTER EL\"],\n        [0x043c, \"CYRILLIC SMALL LETTER EM\"],\n        [0x043d, \"CYRILLIC SMALL LETTER EN\"],\n        [0x043e, \"CYRILLIC SMALL LETTER O\"],\n        [0x043f, \"CYRILLIC SMALL LETTER PE\"],\n        [0x0440, \"CYRILLIC SMALL LETTER ER\"],\n        [0x0441, \"CYRILLIC SMALL LETTER ES\"],\n        [0x0442, \"CYRILLIC SMALL LETTER TE\"],\n        [0x0443, \"CYRILLIC SMALL LETTER U\"],\n        [0x0444, \"CYRILLIC SMALL LETTER EF\"],\n        [0x0445, \"CYRILLIC SMALL LETTER HA\"],\n        [0x0446, \"CYRILLIC SMALL LETTER TSE\"],\n        [0x0447, \"CYRILLIC SMALL LETTER CHE\"],\n        [0x0448, \"CYRILLIC SMALL LETTER SHA\"],\n        [0x0449, \"CYRILLIC SMALL LETTER SHCHA\"],\n        [0x044a, \"CYRILLIC SMALL LETTER HARD SIGN\"],\n        [0x044b, \"CYRILLIC SMALL LETTER YERU\"],\n        [0x044c, \"CYRILLIC SMALL LETTER SOFT SIGN\"],\n        [0x044d, \"CYRILLIC SMALL LETTER E\"],\n        [0x044e, \"CYRILLIC SMALL LETTER YU\"],\n        [0x044f, \"CYRILLIC SMALL LETTER YA\"],\n      ],\n    },\n    {\n      id: \"emoji\",\n      label: \"Emoji\",\n      chars: [\n        [0x1f600, \"GRINNING FACE\"],\n        [0x1f601, \"GRINNING FACE WITH SMILING EYES\"],\n        [0x1f602, \"FACE WITH TEARS OF JOY\"],\n        [0x1f603, \"SMILING FACE WITH OPEN MOUTH\"],\n        [0x1f604, \"SMILING FACE WITH OPEN MOUTH AND SMILING EYES\"],\n        [0x1f605, \"SMILING FACE WITH OPEN MOUTH AND COLD SWEAT\"],\n        [0x1f606, \"SMILING FACE WITH OPEN MOUTH AND TIGHTLY-CLOSED EYES\"],\n        [0x1f607, \"SMILING FACE WITH HALO\"],\n        [0x1f608, \"SMILING FACE WITH HORNS\"],\n        [0x1f609, \"WINKING FACE\"],\n        [0x1f60a, \"SMILING FACE WITH SMILING EYES\"],\n        [0x1f60b, \"FACE SAVOURING DELICIOUS FOOD\"],\n        [0x1f60d, \"SMILING FACE WITH HEART-SHAPED EYES\"],\n        [0x1f60e, \"SMILING FACE WITH SUNGLASSES\"],\n        [0x1f610, \"NEUTRAL FACE\"],\n        [0x1f611, \"EXPRESSIONLESS FACE\"],\n        [0x1f614, \"PENSIVE FACE\"],\n        [0x1f615, \"CONFUSED FACE\"],\n        [0x1f618, \"FACE THROWING A KISS\"],\n        [0x1f61a, \"KISSING FACE WITH CLOSED EYES\"],\n        [0x1f61c, \"FACE WITH STUCK-OUT TONGUE AND WINKING EYE\"],\n        [0x1f620, \"ANGRY FACE\"],\n        [0x1f621, \"POUTING FACE\"],\n        [0x1f622, \"CRYING FACE\"],\n        [0x1f625, \"DISAPPOINTED BUT RELIEVED FACE\"],\n        [0x1f628, \"FEARFUL FACE\"],\n        [0x1f62d, \"LOUDLY CRYING FACE\"],\n        [0x1f631, \"FACE SCREAMING IN FEAR\"],\n        [0x1f634, \"SLEEPING FACE\"],\n        [0x1f637, \"FACE WITH MEDICAL MASK\"],\n        [0x1f638, \"GRINNING CAT FACE WITH SMILING EYES\"],\n        [0x1f44d, \"THUMBS UP SIGN\"],\n        [0x1f44e, \"THUMBS DOWN SIGN\"],\n        [0x1f44f, \"CLAPPING HANDS SIGN\"],\n        [0x1f450, \"OPEN HANDS SIGN\"],\n        [0x1f451, \"CROWN\"],\n        [0x1f452, \"WOMANS HAT\"],\n        [0x1f453, \"EYEGLASSES\"],\n        [0x1f454, \"NECKTIE\"],\n        [0x1f455, \"T-SHIRT\"],\n        [0x1f456, \"JEANS\"],\n        [0x1f457, \"DRESS\"],\n        [0x1f458, \"KIMONO\"],\n        [0x1f459, \"BIKINI\"],\n        [0x1f45a, \"WOMANS CLOTHES\"],\n        [0x1f4a9, \"PILE OF POO\"],\n        [0x1f4af, \"HUNDRED POINTS SYMBOL\"],\n        [0x1f525, \"FIRE\"],\n        [0x1f389, \"PARTY POPPER\"],\n        [0x1f38a, \"CONFETTI BALL\"],\n        [0x1f3b5, \"MUSICAL NOTE\"],\n        [0x1f3b6, \"MULTIPLE MUSICAL NOTES\"],\n        [0x1f31f, \"GLOWING STAR\"],\n        [0x2764, \"HEAVY BLACK HEART\"],\n        [0x1f49a, \"GREEN HEART\"],\n        [0x1f49b, \"YELLOW HEART\"],\n        [0x1f49c, \"PURPLE HEART\"],\n        [0x1f499, \"BLUE HEART\"],\n        [0x1f498, \"HEART WITH ARROW\"],\n      ],\n    },\n  ];\n\n  /* ── Flatten all chars for cross-category search ── */\n  var ALL_CHARS = [];\n  CATS.forEach(function (cat) {\n    cat.chars.forEach(function (ch) {\n      ALL_CHARS.push({ cp: ch[0], name: ch[1], cat: cat.id });\n    });\n  });\n\n  /* ── State ── */\n  var activeCat = \"arrows\";\n  var activeSearch = \"\";\n  var selectedChar = null;\n\n  /* ── DOM refs ── */\n  var grid = document.getElementById(\"umap-grid\");\n  var statusEl = document.getElementById(\"umap-status\");\n  var searchEl = document.getElementById(\"umap-search\");\n  var catsEl = document.getElementById(\"umap-cats\");\n  var detail = document.getElementById(\"umap-detail\");\n  var detailGlyph = document.getElementById(\"umap-dg\");\n  var detailName = document.getElementById(\"umap-dn\");\n  var detailRows = document.getElementById(\"umap-dr\");\n  var toastEl = document.getElementById(\"umap-toast\");\n  var toastTimer = null;\n\n  /* ── Utilities ── */\n  function cpToStr(cp) {\n    return String.fromCodePoint(cp);\n  }\n\n  function cpToHex(cp) {\n    return cp.toString(16).toUpperCase().padStart(4, \"0\");\n  }\n\n  function cpToCodePoint(cp) {\n    return \"U+\" + cpToHex(cp);\n  }\n\n  function cpToHtmlDec(cp) {\n    return \"\u0026#\" + cp + \";\";\n  }\n\n  function cpToHtmlHex(cp) {\n    return \"\u0026#x\" + cpToHex(cp) + \";\";\n  }\n\n  function cpToCss(cp) {\n    return '\\\\' + cp.toString(16).toUpperCase();\n  }\n\n  function cpToUtf8(cp) {\n    var bytes = [];\n    var s = cpToStr(cp);\n    for (var i = 0; i \u003c s.length; i++) {\n      var code = s.charCodeAt(i);\n      bytes.push(\"0x\" + code.toString(16).toUpperCase().padStart(2, \"0\"));\n    }\n    // Proper UTF-8 bytes\n    var utf8 = [];\n    if (cp \u003c 0x80) {\n      utf8.push(cp);\n    } else if (cp \u003c 0x800) {\n      utf8.push(0xc0 | (cp \u003e\u003e 6));\n      utf8.push(0x80 | (cp \u0026 0x3f));\n    } else if (cp \u003c 0x10000) {\n      utf8.push(0xe0 | (cp \u003e\u003e 12));\n      utf8.push(0x80 | ((cp \u003e\u003e 6) \u0026 0x3f));\n      utf8.push(0x80 | (cp \u0026 0x3f));\n    } else {\n      utf8.push(0xf0 | (cp \u003e\u003e 18));\n      utf8.push(0x80 | ((cp \u003e\u003e 12) \u0026 0x3f));\n      utf8.push(0x80 | ((cp \u003e\u003e 6) \u0026 0x3f));\n      utf8.push(0x80 | (cp \u0026 0x3f));\n    }\n    return utf8.map(function (b) {\n      return \"0x\" + b.toString(16).toUpperCase().padStart(2, \"0\");\n    }).join(\" \");\n  }\n\n  function showToast(msg) {\n    toastEl.textContent = msg;\n    toastEl.classList.add(\"show\");\n    clearTimeout(toastTimer);\n    toastTimer = setTimeout(function () {\n      toastEl.classList.remove(\"show\");\n    }, 1800);\n  }\n\n  function copyText(text) {\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(text).catch(function () {\n        fallbackCopy(text);\n      });\n    } else {\n      fallbackCopy(text);\n    }\n  }\n\n  function fallbackCopy(text) {\n    var ta = document.createElement(\"textarea\");\n    ta.value = text;\n    ta.style.position = \"fixed\";\n    ta.style.opacity = \"0\";\n    document.body.appendChild(ta);\n    ta.select();\n    try { document.execCommand(\"copy\"); } catch (e) {}\n    document.body.removeChild(ta);\n  }\n\n  /* ── Render ── */\n  function getFilteredChars() {\n    var q = activeSearch.trim().toLowerCase();\n    if (q === \"\") {\n      var cat = CATS.find(function (c) { return c.id === activeCat; });\n      return cat ? cat.chars.map(function (ch) {\n        return { cp: ch[0], name: ch[1] };\n      }) : [];\n    }\n    // Code point search: U+XXXX or plain hex like \"2192\"\n    var cpMatch = q.match(/^u\\+?([0-9a-f]{4,6})$/i) || q.match(/^0x([0-9a-f]{4,6})$/i);\n    if (!cpMatch) cpMatch = q.match(/^([0-9a-f]{4,6})$/i);\n    if (cpMatch) {\n      var target = parseInt(cpMatch[1], 16);\n      return ALL_CHARS.filter(function (c) { return c.cp === target; });\n    }\n    return ALL_CHARS.filter(function (c) {\n      return c.name.toLowerCase().indexOf(q) !== -1;\n    });\n  }\n\n  function renderGrid() {\n    var chars = getFilteredChars();\n    statusEl.textContent = chars.length + \" character\" + (chars.length !== 1 ? \"s\" : \"\") + \" shown\";\n    grid.innerHTML = \"\";\n    if (chars.length === 0) {\n      var empty = document.createElement(\"div\");\n      empty.className = \"umap-empty\";\n      empty.style.gridColumn = \"1 / -1\";\n      empty.textContent = \"No characters found.\";\n      grid.appendChild(empty);\n      return;\n    }\n    chars.forEach(function (ch) {\n      var cell = document.createElement(\"div\");\n      cell.className = \"umap-cell\";\n      cell.title = ch.name + \"\\n\" + cpToCodePoint(ch.cp);\n      var glyph = document.createElement(\"div\");\n      glyph.className = \"umap-glyph\";\n      glyph.textContent = cpToStr(ch.cp);\n      var cpLabel = document.createElement(\"div\");\n      cpLabel.className = \"umap-cp\";\n      cpLabel.textContent = cpToCodePoint(ch.cp);\n      cell.appendChild(glyph);\n      cell.appendChild(cpLabel);\n      cell.addEventListener(\"click\", function () {\n        openDetail(ch.cp, ch.name, cell);\n        cell.classList.add(\"copied\");\n        setTimeout(function () { cell.classList.remove(\"copied\"); }, 700);\n      });\n      grid.appendChild(cell);\n    });\n  }\n\n  /* ── Detail panel ── */\n  function openDetail(cp, name, _cell) {\n    selectedChar = { cp: cp, name: name };\n    detailGlyph.textContent = cpToStr(cp);\n    detailName.textContent = name + \" — \" + cpToCodePoint(cp);\n\n    var rows = [\n      { label: \"Character\", value: cpToStr(cp) },\n      { label: \"Code Point\", value: cpToCodePoint(cp) },\n      { label: \"HTML (decimal)\", value: cpToHtmlDec(cp) },\n      { label: \"HTML (hex)\", value: cpToHtmlHex(cp) },\n      { label: \"CSS content\", value: \"'\" + cpToCss(cp) + \"'\" },\n      { label: \"UTF-8 bytes\", value: cpToUtf8(cp) },\n    ];\n\n    detailRows.innerHTML = \"\";\n    rows.forEach(function (row) {\n      var lbl = document.createElement(\"div\");\n      lbl.className = \"umap-detail-label\";\n      lbl.textContent = row.label;\n\n      var val = document.createElement(\"div\");\n      val.className = \"umap-detail-value\";\n      val.textContent = row.value;\n\n      var btn = document.createElement(\"button\");\n      btn.className = \"umap-copy-btn\";\n      btn.textContent = \"Copy\";\n      (function (v, b) {\n        b.addEventListener(\"click\", function () {\n          copyText(v);\n          b.textContent = \"Copied!\";\n          b.classList.add(\"ok\");\n          showToast(\"Copied: \" + v);\n          setTimeout(function () {\n            b.textContent = \"Copy\";\n            b.classList.remove(\"ok\");\n          }, 1500);\n        });\n      })(row.value, btn);\n\n      detailRows.appendChild(lbl);\n      detailRows.appendChild(val);\n      detailRows.appendChild(btn);\n    });\n\n    detail.classList.add(\"visible\");\n    detail.scrollIntoView({ behavior: \"smooth\", block: \"nearest\" });\n  }\n\n  document.getElementById(\"umap-detail-close\").addEventListener(\"click\", function () {\n    detail.classList.remove(\"visible\");\n    selectedChar = null;\n  });\n\n  /* ── Category buttons ── */\n  CATS.forEach(function (cat) {\n    var btn = document.createElement(\"button\");\n    btn.className = \"umap-cat-btn\" + (cat.id === activeCat ? \" active\" : \"\");\n    btn.textContent = cat.label;\n    btn.addEventListener(\"click\", function () {\n      activeCat = cat.id;\n      activeSearch = \"\";\n      searchEl.value = \"\";\n      document.querySelectorAll(\"#umap-app .umap-cat-btn\").forEach(function (b) {\n        b.classList.remove(\"active\");\n      });\n      btn.classList.add(\"active\");\n      detail.classList.remove(\"visible\");\n      renderGrid();\n    });\n    catsEl.appendChild(btn);\n  });\n\n  /* ── Search ── */\n  var searchDebounce;\n  searchEl.addEventListener(\"input\", function () {\n    clearTimeout(searchDebounce);\n    searchDebounce = setTimeout(function () {\n      activeSearch = searchEl.value;\n      if (activeSearch.trim() !== \"\") {\n        document.querySelectorAll(\"#umap-app .umap-cat-btn\").forEach(function (b) {\n          b.classList.remove(\"active\");\n        });\n      } else {\n        document.querySelectorAll(\"#umap-app .umap-cat-btn\")[\n          CATS.findIndex(function (c) { return c.id === activeCat; })\n        ].classList.add(\"active\");\n      }\n      detail.classList.remove(\"visible\");\n      renderGrid();\n    }, 200);\n  });\n\n  /* ── Init ── */\n  renderGrid();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch3 id=\"how-to-use\"\u003eHow to Use\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eBrowse by category\u003c/strong\u003e — click a category button (Arrows, Math, Currency, etc.) to see all characters in that group.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSearch\u003c/strong\u003e — type a character name like \u003ccode\u003earrow left\u003c/code\u003e or a code point like \u003ccode\u003eU+2192\u003c/code\u003e to filter across all categories.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eClick any character\u003c/strong\u003e — the detail panel shows the character, Unicode code point, HTML entities (decimal and hex), CSS \u003ccode\u003econtent\u003c/code\u003e value, and UTF-8 byte sequence. Each field has its own \u003cstrong\u003eCopy\u003c/strong\u003e button.\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch3 id=\"what-each-field-means\"\u003eWhat Each Field Means\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eField\u003c/th\u003e\n          \u003cth\u003eExample\u003c/th\u003e\n          \u003cth\u003eUse it when\u0026hellip;\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eCode Point\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eU+2192\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eReferencing a character in documentation\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eHTML (decimal)\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e\u0026amp;#8594;\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eEmbedding in HTML without encoding issues\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eHTML (hex)\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e\u0026amp;#x2192;\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eSame as above, hex form\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eCSS content\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e'\\2192'\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eUsing characters in \u003ccode\u003e::before\u003c/code\u003e / \u003ccode\u003e::after\u003c/code\u003e pseudo-elements\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eUTF-8 bytes\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e0xE2 0x86 0x92\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eWorking with raw byte streams or file encoding\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"related-tools\"\u003eRelated Tools\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://productivity-works.com/tools/html-entity-encoder/\"\u003eHTML Entity Encoder\u003c/a\u003e\n — encode and decode HTML entities in bulk\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://productivity-works.com/tools/emoji-picker/\"\u003eEmoji Picker\u003c/a\u003e\n — browse and copy emoji with skin-tone and keyword search\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://productivity-works.com/tools/morse-code-translator/\"\u003eMorse Code Translator\u003c/a\u003e\n — convert text to Morse code and back\u003c/li\u003e\n\u003c/ul\u003e","title":"Unicode Character Map — Symbol Finder"},{"content":" Length SI (1 KB = 1,000 B) Binary (1 KiB = 1,024 B) From \u0026lt;!-- Swap --\u0026gt; \u0026lt;div class=\u0026quot;uc-swap-col\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;uc-swap-btn\u0026quot; onclick=\u0026quot;ucSwap()\u0026quot; title=\u0026quot;Swap units\u0026quot;\u0026gt;\u0026amp;#8646;\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Output --\u0026gt; \u0026lt;div class=\u0026quot;uc-field\u0026quot;\u0026gt; \u0026lt;label\u0026gt;To\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;uc-to-unit\u0026quot; onchange=\u0026quot;ucConvert()\u0026quot;\u0026gt;\u0026lt;/select\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;uc-to-val\u0026quot; readonly placeholder=\u0026quot;Result\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; Enter a value to see the conversion formula. Common Length Conversions From To Multiply by How to Use This Unit Converter Select a category using the tabs at the top — Length, Weight, Temperature, Volume, Area, Speed, Time, or Data. Choose your units from the \u0026ldquo;From\u0026rdquo; and \u0026ldquo;To\u0026rdquo; dropdown menus. Type a value in the left input field. The result appears instantly on the right. Swap the direction at any time using the swap button between the two fields — the result becomes the new input. Read the formula shown below the fields to understand exactly how the conversion is calculated. For Data conversions, you can switch between SI (decimal, base-1000) used by storage manufacturers and Binary (base-1024) used by operating systems.\nCommon Conversion References From To Multiply By 1 inch 2.54 centimeters × 2.54 1 mile 1.60934 kilometers × 1.60934 1 kilogram 2.20462 pounds × 2.20462 1 liter 0.264172 US gallons × 0.264172 1 acre 0.404686 hectares × 0.404686 0 °C 32 °F (°C × 9/5) + 32 1 m/s 3.6 km/h × 3.6 1 GB (SI) 1,000 MB × 1,000 Related Tools Calculate percentages instantly → Percentage Calculator Check your BMI → BMI Calculator Convert hourly and annual salary → Hourly to Salary Calculator ","permalink":"https://productivity-works.com/tools/unit-converter/","summary":"\u003cdiv id=\"converter-app\"\u003e\n\u003cstyle\u003e\n  #converter-app {\n    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n    max-width: 820px;\n    margin: 0 auto;\n    color: #1e293b;\n  }\n\n  /* Category Tabs */\n  .uc-tabs {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 6px;\n    margin-bottom: 24px;\n  }\n\n  .uc-tab {\n    padding: 8px 16px;\n    border: 2px solid #e2e8f0;\n    border-radius: 9999px;\n    background: #fff;\n    color: #64748b;\n    font-size: 14px;\n    font-weight: 500;\n    cursor: pointer;\n    transition: all 0.2s;\n    white-space: nowrap;\n  }\n\n  .uc-tab:hover {\n    border-color: #0891b2;\n    color: #0891b2;\n  }\n\n  .uc-tab.active {\n    background: #0891b2;\n    border-color: #0891b2;\n    color: #fff;\n  }\n\n  /* Main Converter Card */\n  .uc-card {\n    background: #fff;\n    border-radius: 16px;\n    padding: 32px;\n    box-shadow: 0 1px 3px rgba(0,0,0,0.08), 0 4px 16px rgba(8,145,178,0.08);\n    margin-bottom: 24px;\n  }\n\n  .uc-category-label {\n    font-size: 13px;\n    font-weight: 600;\n    color: #0891b2;\n    text-transform: uppercase;\n    letter-spacing: 0.08em;\n    margin-bottom: 20px;\n  }\n\n  /* Converter Row */\n  .uc-converter-row {\n    display: grid;\n    grid-template-columns: 1fr auto 1fr;\n    gap: 16px;\n    align-items: end;\n    margin-bottom: 20px;\n  }\n\n  .uc-field {\n    display: flex;\n    flex-direction: column;\n    gap: 8px;\n  }\n\n  .uc-field label {\n    font-size: 13px;\n    font-weight: 600;\n    color: #64748b;\n    text-transform: uppercase;\n    letter-spacing: 0.05em;\n  }\n\n  .uc-field select {\n    padding: 10px 12px;\n    border: 2px solid #e2e8f0;\n    border-radius: 10px;\n    font-size: 15px;\n    color: #1e293b;\n    background: #f8fafc;\n    appearance: none;\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E\");\n    background-repeat: no-repeat;\n    background-position: right 10px center;\n    padding-right: 36px;\n    cursor: pointer;\n    transition: border-color 0.2s;\n  }\n\n  .uc-field select:focus {\n    outline: none;\n    border-color: #0891b2;\n    background-color: #fff;\n  }\n\n  .uc-field input[type=\"number\"] {\n    padding: 14px 16px;\n    border: 2px solid #e2e8f0;\n    border-radius: 10px;\n    font-size: 22px;\n    font-weight: 600;\n    color: #1e293b;\n    background: #f8fafc;\n    width: 100%;\n    box-sizing: border-box;\n    transition: border-color 0.2s;\n    -moz-appearance: textfield;\n  }\n\n  .uc-field input[type=\"number\"]::-webkit-outer-spin-button,\n  .uc-field input[type=\"number\"]::-webkit-inner-spin-button {\n    -webkit-appearance: none;\n  }\n\n  .uc-field input[type=\"number\"]:focus {\n    outline: none;\n    border-color: #0891b2;\n    background-color: #fff;\n    box-shadow: 0 0 0 3px rgba(8,145,178,0.12);\n  }\n\n  .uc-field input[readonly] {\n    background: #f0f9ff;\n    border-color: #bae6fd;\n    color: #0891b2;\n    cursor: default;\n  }\n\n  /* Swap Button */\n  .uc-swap-col {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: flex-end;\n    padding-bottom: 4px;\n  }\n\n  .uc-swap-btn {\n    width: 44px;\n    height: 44px;\n    border-radius: 50%;\n    border: 2px solid #0891b2;\n    background: #fff;\n    color: #0891b2;\n    font-size: 20px;\n    cursor: pointer;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    transition: all 0.2s;\n    flex-shrink: 0;\n  }\n\n  .uc-swap-btn:hover {\n    background: #0891b2;\n    color: #fff;\n    transform: rotate(180deg);\n  }\n\n  /* Formula display */\n  .uc-formula {\n    background: #f0f9ff;\n    border: 1px solid #bae6fd;\n    border-radius: 10px;\n    padding: 12px 16px;\n    font-size: 13px;\n    color: #0369a1;\n    font-family: 'Courier New', monospace;\n    min-height: 40px;\n    display: flex;\n    align-items: center;\n    flex-wrap: wrap;\n    gap: 4px;\n    word-break: break-word;\n  }\n\n  .uc-formula strong {\n    color: #0891b2;\n  }\n\n  /* Quick Reference Table */\n  .uc-quick-ref {\n    background: #fff;\n    border-radius: 16px;\n    padding: 28px 32px;\n    box-shadow: 0 1px 3px rgba(0,0,0,0.08), 0 4px 16px rgba(8,145,178,0.08);\n    margin-bottom: 24px;\n  }\n\n  .uc-quick-ref h3 {\n    margin: 0 0 16px 0;\n    font-size: 16px;\n    font-weight: 700;\n    color: #1e293b;\n  }\n\n  .uc-ref-table {\n    width: 100%;\n    border-collapse: collapse;\n    font-size: 14px;\n  }\n\n  .uc-ref-table th {\n    text-align: left;\n    padding: 8px 12px;\n    background: #f0f9ff;\n    color: #0891b2;\n    font-weight: 600;\n    font-size: 12px;\n    text-transform: uppercase;\n    letter-spacing: 0.05em;\n  }\n\n  .uc-ref-table th:first-child { border-radius: 8px 0 0 8px; }\n  .uc-ref-table th:last-child  { border-radius: 0 8px 8px 0; }\n\n  .uc-ref-table td {\n    padding: 9px 12px;\n    border-bottom: 1px solid #f1f5f9;\n    color: #334155;\n  }\n\n  .uc-ref-table tr:last-child td {\n    border-bottom: none;\n  }\n\n  .uc-ref-table tr:hover td {\n    background: #f8fafc;\n  }\n\n  /* Data base toggle */\n  .uc-base-toggle {\n    display: flex;\n    gap: 8px;\n    margin-bottom: 16px;\n    flex-wrap: wrap;\n  }\n\n  .uc-base-btn {\n    padding: 6px 14px;\n    border-radius: 8px;\n    border: 2px solid #e2e8f0;\n    background: #fff;\n    color: #64748b;\n    font-size: 13px;\n    font-weight: 600;\n    cursor: pointer;\n    transition: all 0.2s;\n  }\n\n  .uc-base-btn.active {\n    background: #0891b2;\n    border-color: #0891b2;\n    color: #fff;\n  }\n\n  /* Responsive */\n  @media (max-width: 600px) {\n    .uc-card, .uc-quick-ref {\n      padding: 20px 16px;\n    }\n\n    .uc-converter-row {\n      grid-template-columns: 1fr;\n      gap: 12px;\n    }\n\n    .uc-swap-col {\n      flex-direction: row;\n      justify-content: center;\n      padding-bottom: 0;\n    }\n\n    .uc-swap-btn {\n      transform: rotate(90deg);\n    }\n\n    .uc-swap-btn:hover {\n      transform: rotate(270deg);\n    }\n\n    .uc-tabs {\n      gap: 4px;\n    }\n\n    .uc-tab {\n      padding: 6px 12px;\n      font-size: 13px;\n    }\n\n    .uc-field input[type=\"number\"] {\n      font-size: 18px;\n    }\n  }\n\u003c/style\u003e\n\u003c!-- Tabs --\u003e\n\u003cdiv class=\"uc-tabs\" id=\"uc-tabs\"\u003e\u003c/div\u003e\n\u003c!-- Main Converter Card --\u003e\n\u003cdiv class=\"uc-card\"\u003e\n  \u003cdiv class=\"uc-category-label\" id=\"uc-category-label\"\u003eLength\u003c/div\u003e\n  \u003c!-- Data base toggle (shown only for Data category) --\u003e\n  \u003cdiv class=\"uc-base-toggle\" id=\"uc-base-toggle\" style=\"display:none;\"\u003e\n    \u003cbutton class=\"uc-base-btn active\" data-base=\"1000\" onclick=\"ucSetBase(1000,this)\"\u003eSI (1 KB = 1,000 B)\u003c/button\u003e\n    \u003cbutton class=\"uc-base-btn\" data-base=\"1024\" onclick=\"ucSetBase(1024,this)\"\u003eBinary (1 KiB = 1,024 B)\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"uc-converter-row\"\u003e\n    \u003c!-- Input --\u003e\n    \u003cdiv class=\"uc-field\"\u003e\n      \u003clabel\u003eFrom\u003c/label\u003e\n      \u003cselect id=\"uc-from-unit\" onchange=\"ucConvert()\"\u003e\u003c/select\u003e\n      \u003cinput type=\"number\" id=\"uc-from-val\" value=\"1\" oninput=\"ucConvert()\" placeholder=\"Enter value\" /\u003e\n    \u003c/div\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;!-- Swap --\u0026gt;\n\u0026lt;div class=\u0026quot;uc-swap-col\u0026quot;\u0026gt;\n  \u0026lt;button class=\u0026quot;uc-swap-btn\u0026quot; onclick=\u0026quot;ucSwap()\u0026quot; title=\u0026quot;Swap units\u0026quot;\u0026gt;\u0026amp;#8646;\u0026lt;/button\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;!-- Output --\u0026gt;\n\u0026lt;div class=\u0026quot;uc-field\u0026quot;\u0026gt;\n  \u0026lt;label\u0026gt;To\u0026lt;/label\u0026gt;\n  \u0026lt;select id=\u0026quot;uc-to-unit\u0026quot; onchange=\u0026quot;ucConvert()\u0026quot;\u0026gt;\u0026lt;/select\u0026gt;\n  \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;uc-to-val\u0026quot; readonly placeholder=\u0026quot;Result\u0026quot; /\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n  \u003c!-- Formula --\u003e\n  \u003cdiv class=\"uc-formula\" id=\"uc-formula\"\u003eEnter a value to see the conversion formula.\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Quick Reference --\u003e\n\u003cdiv class=\"uc-quick-ref\"\u003e\n  \u003ch3 id=\"uc-ref-title\"\u003eCommon Length Conversions\u003c/h3\u003e\n  \u003ctable class=\"uc-ref-table\"\u003e\n    \u003cthead\u003e\n      \u003ctr\u003e\n        \u003cth\u003eFrom\u003c/th\u003e\n        \u003cth\u003eTo\u003c/th\u003e\n        \u003cth\u003eMultiply by\u003c/th\u003e\n      \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody id=\"uc-ref-body\"\u003e\u003c/tbody\u003e\n  \u003c/table\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  /* ===== UNIT DEFINITIONS ===== */\n  /* Each unit has a factor to convert TO the base unit of that category */\n  /* For temperature: special handling via functions */\n\n  const CATEGORIES = [\n    { id: 'length',      label: 'Length',       base: 'm' },\n    { id: 'weight',      label: 'Weight',       base: 'kg' },\n    { id: 'temperature', label: 'Temperature',  base: 'C' },\n    { id: 'volume',      label: 'Volume',       base: 'L' },\n    { id: 'area',        label: 'Area',         base: 'sqm' },\n    { id: 'speed',       label: 'Speed',        base: 'ms' },\n    { id: 'time',        label: 'Time',         base: 'sec' },\n    { id: 'data',        label: 'Data',         base: 'byte' },\n  ];\n\n  const UNITS = {\n    length: [\n      { id: 'mm',   label: 'Millimeter (mm)',  factor: 0.001 },\n      { id: 'cm',   label: 'Centimeter (cm)',  factor: 0.01 },\n      { id: 'm',    label: 'Meter (m)',         factor: 1 },\n      { id: 'km',   label: 'Kilometer (km)',    factor: 1000 },\n      { id: 'in',   label: 'Inch (in)',         factor: 0.0254 },\n      { id: 'ft',   label: 'Foot (ft)',         factor: 0.3048 },\n      { id: 'yd',   label: 'Yard (yd)',         factor: 0.9144 },\n      { id: 'mi',   label: 'Mile (mi)',         factor: 1609.344 },\n    ],\n    weight: [\n      { id: 'mg',   label: 'Milligram (mg)',    factor: 0.000001 },\n      { id: 'g',    label: 'Gram (g)',           factor: 0.001 },\n      { id: 'kg',   label: 'Kilogram (kg)',      factor: 1 },\n      { id: 'oz',   label: 'Ounce (oz)',         factor: 0.0283495 },\n      { id: 'lb',   label: 'Pound (lb)',         factor: 0.453592 },\n      { id: 'ton',  label: 'Metric Ton (t)',     factor: 1000 },\n    ],\n    temperature: [\n      { id: 'C',  label: 'Celsius (°C)' },\n      { id: 'F',  label: 'Fahrenheit (°F)' },\n      { id: 'K',  label: 'Kelvin (K)' },\n    ],\n    volume: [\n      { id: 'ml',    label: 'Milliliter (ml)',   factor: 0.001 },\n      { id: 'L',     label: 'Liter (L)',          factor: 1 },\n      { id: 'cup',   label: 'Cup (US)',           factor: 0.2365882 },\n      { id: 'pt',    label: 'Pint (US)',          factor: 0.4731765 },\n      { id: 'qt',    label: 'Quart (US)',         factor: 0.9463529 },\n      { id: 'gal',   label: 'Gallon (US)',        factor: 3.785412 },\n      { id: 'floz',  label: 'Fl Oz (US)',         factor: 0.0295735 },\n    ],\n    area: [\n      { id: 'sqcm',  label: 'Sq Centimeter (cm²)', factor: 0.0001 },\n      { id: 'sqm',   label: 'Sq Meter (m²)',        factor: 1 },\n      { id: 'sqkm',  label: 'Sq Kilometer (km²)',   factor: 1e6 },\n      { id: 'sqft',  label: 'Sq Foot (ft²)',        factor: 0.092903 },\n      { id: 'sqyd',  label: 'Sq Yard (yd²)',        factor: 0.836127 },\n      { id: 'acre',  label: 'Acre',                 factor: 4046.856 },\n      { id: 'ha',    label: 'Hectare (ha)',          factor: 10000 },\n    ],\n    speed: [\n      { id: 'ms',   label: 'Meter/sec (m/s)',    factor: 1 },\n      { id: 'kmh',  label: 'Kilometer/h (km/h)', factor: 0.277778 },\n      { id: 'mph',  label: 'Miles/h (mph)',       factor: 0.44704 },\n      { id: 'kn',   label: 'Knot (kn)',           factor: 0.514444 },\n    ],\n    time: [\n      { id: 'sec',   label: 'Second (s)',   factor: 1 },\n      { id: 'min',   label: 'Minute (min)', factor: 60 },\n      { id: 'hr',    label: 'Hour (hr)',    factor: 3600 },\n      { id: 'day',   label: 'Day',          factor: 86400 },\n      { id: 'week',  label: 'Week',         factor: 604800 },\n    ],\n    data: [\n      { id: 'byte', label: 'Byte (B)',       si: 1,          bin: 1 },\n      { id: 'kb',   label: 'Kilobyte (KB)',  si: 1e3,        bin: 1024 },\n      { id: 'mb',   label: 'Megabyte (MB)',  si: 1e6,        bin: 1048576 },\n      { id: 'gb',   label: 'Gigabyte (GB)',  si: 1e9,        bin: 1073741824 },\n      { id: 'tb',   label: 'Terabyte (TB)',  si: 1e12,       bin: 1099511627776 },\n    ],\n  };\n\n  /* Quick Reference rows per category */\n  const QUICK_REF = {\n    length: [\n      ['1 inch', '2.54 cm',       '× 2.54'],\n      ['1 foot', '30.48 cm',      '× 30.48'],\n      ['1 mile', '1.609 km',      '× 1.60934'],\n      ['1 yard', '0.9144 m',      '× 0.9144'],\n      ['1 km',   '0.621 miles',   '× 0.62137'],\n      ['1 m',    '3.281 feet',    '× 3.28084'],\n    ],\n    weight: [\n      ['1 kg',   '2.205 lb',      '× 2.20462'],\n      ['1 lb',   '453.6 g',       '× 453.592'],\n      ['1 oz',   '28.35 g',       '× 28.3495'],\n      ['1 ton',  '1,000 kg',      '× 1000'],\n      ['1 lb',   '16 oz',         '× 16'],\n    ],\n    temperature: [\n      ['0 °C',    '32 °F',         '(°C × 9/5) + 32'],\n      ['100 °C',  '212 °F',        '(°C × 9/5) + 32'],\n      ['0 °C',    '273.15 K',      '°C + 273.15'],\n      ['98.6 °F', '37 °C',         '(°F − 32) × 5/9'],\n      ['72 °F',   '22.2 °C',       '(°F − 32) × 5/9'],\n    ],\n    volume: [\n      ['1 L',     '4.227 cups',    '× 4.22675'],\n      ['1 gal',   '3.785 L',       '× 3.78541'],\n      ['1 cup',   '236.6 ml',      '× 236.588'],\n      ['1 pint',  '2 cups',        '× 2'],\n      ['1 quart', '2 pints',       '× 2'],\n      ['1 fl oz', '29.57 ml',      '× 29.5735'],\n    ],\n    area: [\n      ['1 acre',    '4,047 m²',     '× 4046.86'],\n      ['1 hectare', '10,000 m²',    '× 10000'],\n      ['1 sq mile', '640 acres',    '× 640'],\n      ['1 sq ft',   '0.0929 m²',   '× 0.092903'],\n      ['1 sq km',   '100 ha',       '× 100'],\n    ],\n    speed: [\n      ['1 m/s',   '3.6 km/h',      '× 3.6'],\n      ['1 mph',   '1.609 km/h',    '× 1.60934'],\n      ['1 knot',  '1.852 km/h',    '× 1.852'],\n      ['60 mph',  '96.56 km/h',    '× 1.60934'],\n      ['1 km/h',  '0.2778 m/s',   '× 0.27778'],\n    ],\n    time: [\n      ['1 min',   '60 sec',        '× 60'],\n      ['1 hour',  '3,600 sec',     '× 3600'],\n      ['1 day',   '24 hours',      '× 24'],\n      ['1 week',  '7 days',        '× 7'],\n      ['1 week',  '168 hours',     '× 168'],\n    ],\n    data: [\n      ['1 KB (SI)',  '1,000 B',       '× 1,000'],\n      ['1 MB (SI)',  '1,000 KB',      '× 1,000'],\n      ['1 GB (SI)',  '1,000 MB',      '× 1,000'],\n      ['1 KiB',      '1,024 B',       '× 1,024'],\n      ['1 MiB',      '1,024 KiB',    '× 1,024'],\n      ['1 GiB',      '1,024 MiB',    '× 1,024'],\n    ],\n  };\n\n  /* ===== STATE ===== */\n  let currentCat = 'length';\n  let dataBase = 1000; // SI by default\n\n  /* ===== INIT ===== */\n  function init() {\n    buildTabs();\n    selectCategory('length');\n  }\n\n  function buildTabs() {\n    const container = document.getElementById('uc-tabs');\n    CATEGORIES.forEach(cat =\u003e {\n      const btn = document.createElement('button');\n      btn.className = 'uc-tab' + (cat.id === 'length' ? ' active' : '');\n      btn.textContent = cat.label;\n      btn.addEventListener('click', () =\u003e selectCategory(cat.id));\n      btn.id = 'tab-' + cat.id;\n      container.appendChild(btn);\n    });\n  }\n\n  function selectCategory(catId) {\n    currentCat = catId;\n\n    /* Update tabs */\n    document.querySelectorAll('.uc-tab').forEach(t =\u003e t.classList.remove('active'));\n    const activeTab = document.getElementById('tab-' + catId);\n    if (activeTab) activeTab.classList.add('active');\n\n    /* Update label */\n    const cat = CATEGORIES.find(c =\u003e c.id === catId);\n    document.getElementById('uc-category-label').textContent = cat.label;\n\n    /* Show/hide data base toggle */\n    document.getElementById('uc-base-toggle').style.display = catId === 'data' ? 'flex' : 'none';\n\n    /* Populate selects */\n    populateSelects(catId);\n\n    /* Update quick ref */\n    updateQuickRef(catId);\n\n    /* Run conversion */\n    ucConvert();\n  }\n\n  function populateSelects(catId) {\n    const units = UNITS[catId];\n    const fromSel = document.getElementById('uc-from-unit');\n    const toSel   = document.getElementById('uc-to-unit');\n\n    fromSel.innerHTML = '';\n    toSel.innerHTML   = '';\n\n    units.forEach((u, i) =\u003e {\n      const opt1 = new Option(u.label, u.id);\n      const opt2 = new Option(u.label, u.id);\n      fromSel.appendChild(opt1);\n      toSel.appendChild(opt2);\n    });\n\n    /* Default: first unit from, second unit to */\n    fromSel.selectedIndex = 0;\n    toSel.selectedIndex   = Math.min(1, units.length - 1);\n  }\n\n  function updateQuickRef(catId) {\n    const cat = CATEGORIES.find(c =\u003e c.id === catId);\n    document.getElementById('uc-ref-title').textContent = 'Common ' + cat.label + ' Conversions';\n    const tbody = document.getElementById('uc-ref-body');\n    tbody.innerHTML = '';\n    (QUICK_REF[catId] || []).forEach(row =\u003e {\n      const tr = document.createElement('tr');\n      tr.innerHTML = row.map(cell =\u003e `\u003ctd\u003e${cell}\u003c/td\u003e`).join('');\n      tbody.appendChild(tr);\n    });\n  }\n\n  /* ===== CONVERSION LOGIC ===== */\n  function toBase(value, unitId, catId) {\n    if (catId === 'temperature') {\n      if (unitId === 'C') return value;\n      if (unitId === 'F') return (value - 32) * 5 / 9;\n      if (unitId === 'K') return value - 273.15;\n    }\n    if (catId === 'data') {\n      const u = UNITS.data.find(x =\u003e x.id === unitId);\n      return value * (dataBase === 1024 ? u.bin : u.si);\n    }\n    const u = UNITS[catId].find(x =\u003e x.id === unitId);\n    return value * u.factor;\n  }\n\n  function fromBase(baseValue, unitId, catId) {\n    if (catId === 'temperature') {\n      if (unitId === 'C') return baseValue;\n      if (unitId === 'F') return baseValue * 9 / 5 + 32;\n      if (unitId === 'K') return baseValue + 273.15;\n    }\n    if (catId === 'data') {\n      const u = UNITS.data.find(x =\u003e x.id === unitId);\n      return baseValue / (dataBase === 1024 ? u.bin : u.si);\n    }\n    const u = UNITS[catId].find(x =\u003e x.id === unitId);\n    return baseValue / u.factor;\n  }\n\n  function buildFormula(value, fromId, toId, result, catId) {\n    const fromUnit = UNITS[catId].find(x =\u003e x.id === fromId);\n    const toUnit   = UNITS[catId].find(x =\u003e x.id === toId);\n    const fromName = fromUnit ? fromUnit.label : fromId;\n    const toName   = toUnit   ? toUnit.label   : toId;\n\n    if (catId === 'temperature') {\n      let expr = '';\n      if (fromId === 'C' \u0026\u0026 toId === 'F') expr = `(${fmtNum(value)} × 9/5) + 32 = \u003cstrong\u003e${fmtNum(result)} °F\u003c/strong\u003e`;\n      else if (fromId === 'F' \u0026\u0026 toId === 'C') expr = `(${fmtNum(value)} − 32) × 5/9 = \u003cstrong\u003e${fmtNum(result)} °C\u003c/strong\u003e`;\n      else if (fromId === 'C' \u0026\u0026 toId === 'K') expr = `${fmtNum(value)} + 273.15 = \u003cstrong\u003e${fmtNum(result)} K\u003c/strong\u003e`;\n      else if (fromId === 'K' \u0026\u0026 toId === 'C') expr = `${fmtNum(value)} − 273.15 = \u003cstrong\u003e${fmtNum(result)} °C\u003c/strong\u003e`;\n      else if (fromId === 'F' \u0026\u0026 toId === 'K') expr = `(${fmtNum(value)} − 32) × 5/9 + 273.15 = \u003cstrong\u003e${fmtNum(result)} K\u003c/strong\u003e`;\n      else if (fromId === 'K' \u0026\u0026 toId === 'F') expr = `(${fmtNum(value)} − 273.15) × 9/5 + 32 = \u003cstrong\u003e${fmtNum(result)} °F\u003c/strong\u003e`;\n      else expr = `\u003cstrong\u003e${fmtNum(result)}\u003c/strong\u003e`;\n      return expr;\n    }\n\n    if (fromId === toId) {\n      return `Same unit — no conversion needed: \u003cstrong\u003e${fmtNum(result)}\u003c/strong\u003e`;\n    }\n\n    /* Calculate the direct multiplier for display */\n    let multiplier;\n    if (catId === 'data') {\n      const fu = UNITS.data.find(x =\u003e x.id === fromId);\n      const tu = UNITS.data.find(x =\u003e x.id === toId);\n      const fromFactor = dataBase === 1024 ? fu.bin : fu.si;\n      const toFactor   = dataBase === 1024 ? tu.bin : tu.si;\n      multiplier = fromFactor / toFactor;\n    } else {\n      const fu = UNITS[catId].find(x =\u003e x.id === fromId);\n      const tu = UNITS[catId].find(x =\u003e x.id === toId);\n      multiplier = fu.factor / tu.factor;\n    }\n\n    return `${fmtNum(value)} × ${fmtFactor(multiplier)} = \u003cstrong\u003e${fmtNum(result)}\u003c/strong\u003e`;\n  }\n\n  function fmtNum(n) {\n    if (n === null || n === undefined || isNaN(n)) return '—';\n    if (Math.abs(n) \u003e= 1e9 || (Math.abs(n) \u003c 1e-4 \u0026\u0026 n !== 0)) {\n      return n.toExponential(6);\n    }\n    /* Show up to 8 significant digits */\n    const s = parseFloat(n.toPrecision(8)).toString();\n    return s;\n  }\n\n  function fmtFactor(f) {\n    if (Math.abs(f) \u003e= 1e6 || (Math.abs(f) \u003c 1e-4 \u0026\u0026 f !== 0)) {\n      return f.toExponential(4);\n    }\n    return parseFloat(f.toPrecision(7)).toString();\n  }\n\n  /* ===== PUBLIC FUNCTIONS (called from HTML) ===== */\n  window.ucConvert = function() {\n    const fromId  = document.getElementById('uc-from-unit').value;\n    const toId    = document.getElementById('uc-to-unit').value;\n    const rawVal  = document.getElementById('uc-from-val').value;\n    const value   = rawVal === '' ? null : parseFloat(rawVal);\n\n    if (value === null || isNaN(value)) {\n      document.getElementById('uc-to-val').value = '';\n      document.getElementById('uc-formula').innerHTML = 'Enter a value to see the conversion formula.';\n      return;\n    }\n\n    const base   = toBase(value, fromId, currentCat);\n    const result = fromBase(base, toId, currentCat);\n\n    document.getElementById('uc-to-val').value = fmtNum(result);\n    document.getElementById('uc-formula').innerHTML = buildFormula(value, fromId, toId, result, currentCat);\n  };\n\n  window.ucSwap = function() {\n    const fromSel = document.getElementById('uc-from-unit');\n    const toSel   = document.getElementById('uc-to-unit');\n    const fromVal = document.getElementById('uc-from-val');\n    const toVal   = document.getElementById('uc-to-val');\n\n    const tmpUnit = fromSel.value;\n    fromSel.value = toSel.value;\n    toSel.value   = tmpUnit;\n\n    /* Swap values: result becomes new input */\n    const toValNum = toVal.value;\n    if (toValNum !== '' \u0026\u0026 !isNaN(parseFloat(toValNum))) {\n      fromVal.value = toValNum;\n    }\n\n    ucConvert();\n  };\n\n  window.ucSetBase = function(base, btn) {\n    dataBase = base;\n    document.querySelectorAll('.uc-base-btn').forEach(b =\u003e b.classList.remove('active'));\n    btn.classList.add('active');\n    ucConvert();\n  };\n\n  /* ===== BOOT ===== */\n  init();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-to-use-this-unit-converter\"\u003eHow to Use This Unit Converter\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eSelect a category\u003c/strong\u003e using the tabs at the top — Length, Weight, Temperature, Volume, Area, Speed, Time, or Data.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eChoose your units\u003c/strong\u003e from the \u0026ldquo;From\u0026rdquo; and \u0026ldquo;To\u0026rdquo; dropdown menus.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eType a value\u003c/strong\u003e in the left input field. The result appears instantly on the right.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSwap the direction\u003c/strong\u003e at any time using the swap button between the two fields — the result becomes the new input.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eRead the formula\u003c/strong\u003e shown below the fields to understand exactly how the conversion is calculated.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eFor Data conversions, you can switch between \u003cstrong\u003eSI (decimal, base-1000)\u003c/strong\u003e used by storage manufacturers and \u003cstrong\u003eBinary (base-1024)\u003c/strong\u003e used by operating systems.\u003c/p\u003e","title":"Unit Converter - Free Online Measurement Converter"},{"content":" Encoding Format Format: Base64 URL Encode HTML Entities Hex Binary Octal ASCII Codes Unicode Escape (\\uXXXX) JWT Decode (header.payload) Chain: — Step 1 — Base64 URL Encode HTML Entities Hex Binary Unicode Escape \u0026#8594; — Step 2 — Base64 URL Encode HTML Entities Hex Binary Unicode Escape Run Chain Auto-detect format AUTO Input 0 chars Encode Decode Clear \u0026#8645; Swap Input / Output Output 0 chars Copy Output Recent Conversions (Last 5) No conversions yet. What is an Encoder/Decoder? Encoding transforms text or binary data into a different representation so it can be safely transported, stored, or embedded in a target medium. Decoding reverses the process to recover the original content. Unlike encryption, encoding is not a security mechanism — the transformation is entirely reversible and publicly defined. Common uses include transmitting binary data over text-based protocols, embedding special characters safely in HTML or URLs, and inspecting authentication tokens.\nSupported Formats Format Encode example Use case Base64 Hello → SGVsbG8= Email attachments, data URIs, JWT URL Encode a b → a%20b Query strings, form data HTML Entities \u0026lt;b\u0026gt; → \u0026amp;lt;b\u0026amp;gt; Safe HTML output Hex A → 41 Debugging, binary inspection Binary A → 01000001 Low-level analysis Octal A → \\101 Unix file permissions, C escape sequences ASCII Codes Hi → 72 105 Protocol debugging Unicode Escape © → \\u00A9 JavaScript source, JSON JWT Decode eyJ… → header + payload JSON Inspect tokens without a library Chain Encoding The chain feature applies two encoding steps in sequence. For example, selecting Hex → Base64 first converts each character to its hex code, then Base64-encodes the result. This is useful for simulating multi-layer encoding found in obfuscated payloads or CTF challenges.\nRelated Tools Encode Base64 → Base64 Encoder Encode URLs → URL Encoder Encode HTML entities → HTML Entity Encoder ","permalink":"https://productivity-works.com/tools/encoder-decoder/","summary":"\u003cdiv id=\"enc-app\"\u003e\n\u003cstyle\u003e\n#enc-app {\n  font-family: ui-sans-serif, system-ui, -apple-system, sans-serif;\n  color: #e2e8f0;\n  max-width: 880px;\n  margin: 0 auto;\n  padding: 0;\n}\n\n#enc-app * {\n  box-sizing: border-box;\n}\n\n.enc-card {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 12px;\n  padding: 22px 24px;\n  margin-bottom: 14px;\n}\n\n.enc-section-title {\n  font-size: 15px;\n  font-weight: 700;\n  color: #cbd5e1;\n  margin-bottom: 14px;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n\n.enc-section-title::before {\n  content: '';\n  display: block;\n  width: 3px;\n  height: 16px;\n  background: #06b6d4;\n  border-radius: 2px;\n  flex-shrink: 0;\n}\n\n/* Mode selector row */\n.enc-mode-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  align-items: center;\n  margin-bottom: 16px;\n}\n\n.enc-mode-label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #94a3b8;\n  white-space: nowrap;\n}\n\n.enc-select {\n  background: #0f172a;\n  border: 1px solid #334155;\n  border-radius: 8px;\n  color: #e2e8f0;\n  font-size: 14px;\n  padding: 8px 12px;\n  cursor: pointer;\n  outline: none;\n  transition: border-color 0.2s;\n  min-width: 220px;\n}\n\n.enc-select:focus {\n  border-color: #06b6d4;\n}\n\n.enc-auto-badge {\n  display: inline-block;\n  background: #164e63;\n  color: #67e8f9;\n  font-size: 11px;\n  font-weight: 700;\n  padding: 2px 9px;\n  border-radius: 10px;\n  letter-spacing: 0.03em;\n  margin-left: 4px;\n  vertical-align: middle;\n}\n\n/* Chain row */\n.enc-chain-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  align-items: center;\n  margin-bottom: 14px;\n  padding: 10px 14px;\n  background: #0f172a;\n  border: 1px solid #334155;\n  border-radius: 8px;\n}\n\n.enc-chain-label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #94a3b8;\n  white-space: nowrap;\n}\n\n.enc-chain-select {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 6px;\n  color: #e2e8f0;\n  font-size: 13px;\n  padding: 6px 10px;\n  cursor: pointer;\n  outline: none;\n  transition: border-color 0.2s;\n}\n\n.enc-chain-select:focus {\n  border-color: #06b6d4;\n}\n\n.enc-chain-arrow {\n  color: #475569;\n  font-size: 16px;\n  font-weight: 700;\n}\n\n/* Textarea area */\n.enc-textarea-label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #94a3b8;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  margin-bottom: 7px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n\n.enc-char-count {\n  font-size: 12px;\n  font-weight: 400;\n  color: #64748b;\n  text-transform: none;\n  letter-spacing: 0;\n}\n\n.enc-textarea {\n  width: 100%;\n  min-height: 140px;\n  background: #0f172a;\n  border: 1px solid #334155;\n  border-radius: 8px;\n  color: #e2e8f0;\n  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n  font-size: 14px;\n  line-height: 1.6;\n  padding: 12px;\n  resize: vertical;\n  transition: border-color 0.2s;\n  outline: none;\n}\n\n.enc-textarea:focus {\n  border-color: #06b6d4;\n}\n\n.enc-textarea.enc-error-border {\n  border-color: #f87171;\n}\n\n.enc-textarea::placeholder {\n  color: #475569;\n}\n\n/* Button rows */\n.enc-btn-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  margin: 14px 0 0;\n  align-items: center;\n}\n\n.enc-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  padding: 9px 20px;\n  border-radius: 8px;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  border: none;\n  transition: background 0.15s, transform 0.1s, opacity 0.15s;\n  white-space: nowrap;\n  outline: none;\n}\n\n.enc-btn:active {\n  transform: scale(0.97);\n}\n\n.enc-btn-primary {\n  background: #06b6d4;\n  color: #0f172a;\n}\n\n.enc-btn-primary:hover {\n  background: #22d3ee;\n}\n\n.enc-btn-violet {\n  background: #7c3aed;\n  color: #fff;\n}\n\n.enc-btn-violet:hover {\n  background: #8b5cf6;\n}\n\n.enc-btn-secondary {\n  background: #475569;\n  color: #e2e8f0;\n}\n\n.enc-btn-secondary:hover {\n  background: #64748b;\n}\n\n.enc-btn-ghost {\n  background: transparent;\n  color: #94a3b8;\n  border: 1px solid #334155;\n}\n\n.enc-btn-ghost:hover {\n  background: #1e293b;\n  color: #e2e8f0;\n  border-color: #475569;\n}\n\n.enc-btn-success {\n  background: #10b981 !important;\n  color: #fff !important;\n}\n\n/* Swap row */\n.enc-swap-row {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  margin: 4px 0;\n}\n\n.enc-swap-btn {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  padding: 7px 16px;\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 20px;\n  color: #94a3b8;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.15s;\n}\n\n.enc-swap-btn:hover {\n  background: #334155;\n  color: #e2e8f0;\n  border-color: #475569;\n}\n\n/* Error */\n.enc-error {\n  background: #450a0a;\n  border: 1px solid #f87171;\n  border-radius: 8px;\n  color: #fca5a5;\n  font-size: 13px;\n  padding: 10px 14px;\n  margin-top: 10px;\n  display: none;\n}\n\n.enc-error.visible {\n  display: block;\n}\n\n/* History */\n.enc-history-list {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n}\n\n.enc-history-item {\n  background: #0f172a;\n  border: 1px solid #1e293b;\n  border-radius: 8px;\n  padding: 10px 14px;\n  font-size: 13px;\n  cursor: pointer;\n  transition: border-color 0.15s, background 0.15s;\n  display: flex;\n  flex-direction: column;\n  gap: 3px;\n}\n\n.enc-history-item:hover {\n  border-color: #334155;\n  background: #1e293b;\n}\n\n.enc-history-meta {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  flex-wrap: wrap;\n}\n\n.enc-history-mode-badge {\n  display: inline-block;\n  background: #1e3a5f;\n  color: #7dd3fc;\n  font-size: 11px;\n  font-weight: 700;\n  padding: 2px 8px;\n  border-radius: 6px;\n}\n\n.enc-history-dir-badge {\n  display: inline-block;\n  font-size: 11px;\n  font-weight: 700;\n  padding: 2px 7px;\n  border-radius: 6px;\n}\n\n.enc-history-dir-badge.enc {\n  background: #14532d;\n  color: #86efac;\n}\n\n.enc-history-dir-badge.dec {\n  background: #3b1f6e;\n  color: #c4b5fd;\n}\n\n.enc-history-text {\n  color: #94a3b8;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  font-family: ui-monospace, Menlo, monospace;\n  font-size: 12px;\n}\n\n.enc-history-empty {\n  color: #475569;\n  font-size: 13px;\n  font-style: italic;\n}\n\n/* Auto-detect toggle */\n.enc-toggle-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 14px;\n  align-items: center;\n  padding-top: 12px;\n  border-top: 1px solid #1e293b;\n  margin-top: 14px;\n}\n\n.enc-toggle-item {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  cursor: pointer;\n  user-select: none;\n}\n\n.enc-toggle-item input[type=\"checkbox\"] {\n  appearance: none;\n  -webkit-appearance: none;\n  width: 36px;\n  height: 20px;\n  background: #334155;\n  border-radius: 10px;\n  position: relative;\n  cursor: pointer;\n  transition: background 0.2s;\n  flex-shrink: 0;\n}\n\n.enc-toggle-item input[type=\"checkbox\"]:checked {\n  background: #06b6d4;\n}\n\n.enc-toggle-item input[type=\"checkbox\"]::after {\n  content: '';\n  position: absolute;\n  width: 14px;\n  height: 14px;\n  background: #fff;\n  border-radius: 50%;\n  top: 3px;\n  left: 3px;\n  transition: left 0.2s;\n}\n\n.enc-toggle-item input[type=\"checkbox\"]:checked::after {\n  left: 19px;\n}\n\n.enc-toggle-label-text {\n  font-size: 13px;\n  color: #94a3b8;\n}\n\n@media (max-width: 600px) {\n  .enc-card {\n    padding: 16px;\n  }\n  .enc-btn {\n    padding: 8px 14px;\n    font-size: 13px;\n  }\n  .enc-textarea {\n    min-height: 110px;\n    font-size: 13px;\n  }\n  .enc-btn-row {\n    gap: 8px;\n  }\n  .enc-select {\n    min-width: 0;\n    width: 100%;\n  }\n  .enc-mode-row {\n    flex-direction: column;\n    align-items: flex-start;\n  }\n}\n\u003c/style\u003e\n\u003c!-- MODE SELECTOR + AUTO-DETECT --\u003e\n\u003cdiv class=\"enc-card\"\u003e\n  \u003cdiv class=\"enc-section-title\"\u003eEncoding Format\u003c/div\u003e\n  \u003cdiv class=\"enc-mode-row\"\u003e\n    \u003cspan class=\"enc-mode-label\"\u003eFormat:\u003c/span\u003e\n    \u003cselect class=\"enc-select\" id=\"enc-mode\"\u003e\n      \u003coption value=\"base64\"\u003eBase64\u003c/option\u003e\n      \u003coption value=\"url\"\u003eURL Encode\u003c/option\u003e\n      \u003coption value=\"html\"\u003eHTML Entities\u003c/option\u003e\n      \u003coption value=\"hex\"\u003eHex\u003c/option\u003e\n      \u003coption value=\"binary\"\u003eBinary\u003c/option\u003e\n      \u003coption value=\"octal\"\u003eOctal\u003c/option\u003e\n      \u003coption value=\"ascii\"\u003eASCII Codes\u003c/option\u003e\n      \u003coption value=\"unicode\"\u003eUnicode Escape (\\uXXXX)\u003c/option\u003e\n      \u003coption value=\"jwt\"\u003eJWT Decode (header.payload)\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n  \u003c!-- Chain encoding --\u003e\n  \u003cdiv class=\"enc-chain-row\"\u003e\n    \u003cspan class=\"enc-chain-label\"\u003eChain:\u003c/span\u003e\n    \u003cselect class=\"enc-chain-select\" id=\"enc-chain-1\"\u003e\n      \u003coption value=\"none\"\u003e— Step 1 —\u003c/option\u003e\n      \u003coption value=\"base64\"\u003eBase64\u003c/option\u003e\n      \u003coption value=\"url\"\u003eURL Encode\u003c/option\u003e\n      \u003coption value=\"html\"\u003eHTML Entities\u003c/option\u003e\n      \u003coption value=\"hex\"\u003eHex\u003c/option\u003e\n      \u003coption value=\"binary\"\u003eBinary\u003c/option\u003e\n      \u003coption value=\"unicode\"\u003eUnicode Escape\u003c/option\u003e\n    \u003c/select\u003e\n    \u003cspan class=\"enc-chain-arrow\"\u003e\u0026#8594;\u003c/span\u003e\n    \u003cselect class=\"enc-chain-select\" id=\"enc-chain-2\"\u003e\n      \u003coption value=\"none\"\u003e— Step 2 —\u003c/option\u003e\n      \u003coption value=\"base64\"\u003eBase64\u003c/option\u003e\n      \u003coption value=\"url\"\u003eURL Encode\u003c/option\u003e\n      \u003coption value=\"html\"\u003eHTML Entities\u003c/option\u003e\n      \u003coption value=\"hex\"\u003eHex\u003c/option\u003e\n      \u003coption value=\"binary\"\u003eBinary\u003c/option\u003e\n      \u003coption value=\"unicode\"\u003eUnicode Escape\u003c/option\u003e\n    \u003c/select\u003e\n    \u003cbutton class=\"enc-btn enc-btn-violet\" id=\"enc-chain-run-btn\"\u003eRun Chain\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"enc-toggle-row\"\u003e\n    \u003clabel class=\"enc-toggle-item\"\u003e\n      \u003cinput type=\"checkbox\" id=\"enc-auto-detect\"\u003e\n      \u003cspan class=\"enc-toggle-label-text\"\u003eAuto-detect format \u003cspan class=\"enc-auto-badge\" id=\"enc-auto-indicator\" style=\"display:none;\"\u003eAUTO\u003c/span\u003e\u003c/span\u003e\n    \u003c/label\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- INPUT --\u003e\n\u003cdiv class=\"enc-card\"\u003e\n  \u003cdiv class=\"enc-textarea-label\"\u003e\n    \u003cspan\u003eInput\u003c/span\u003e\n    \u003cspan class=\"enc-char-count\" id=\"enc-input-count\"\u003e0 chars\u003c/span\u003e\n  \u003c/div\u003e\n  \u003ctextarea\n    class=\"enc-textarea\"\n    id=\"enc-input\"\n    placeholder=\"Type or paste text here...\"\n    spellcheck=\"false\"\n  \u003e\u003c/textarea\u003e\n  \u003cdiv class=\"enc-btn-row\"\u003e\n    \u003cbutton class=\"enc-btn enc-btn-primary\" id=\"enc-encode-btn\"\u003eEncode\u003c/button\u003e\n    \u003cbutton class=\"enc-btn enc-btn-primary\" id=\"enc-decode-btn\"\u003eDecode\u003c/button\u003e\n    \u003cbutton class=\"enc-btn enc-btn-ghost\" id=\"enc-clear-btn\"\u003eClear\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- SWAP --\u003e\n\u003cdiv class=\"enc-swap-row\"\u003e\n  \u003cbutton class=\"enc-swap-btn\" id=\"enc-swap-btn\"\u003e\u0026#8645; Swap Input / Output\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- OUTPUT --\u003e\n\u003cdiv class=\"enc-card\"\u003e\n  \u003cdiv class=\"enc-textarea-label\"\u003e\n    \u003cspan\u003eOutput\u003c/span\u003e\n    \u003cspan class=\"enc-char-count\" id=\"enc-output-count\"\u003e0 chars\u003c/span\u003e\n  \u003c/div\u003e\n  \u003ctextarea\n    class=\"enc-textarea\"\n    id=\"enc-output\"\n    placeholder=\"Result will appear here...\"\n    spellcheck=\"false\"\n    readonly\n  \u003e\u003c/textarea\u003e\n  \u003cdiv class=\"enc-error\" id=\"enc-error-msg\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"enc-btn-row\"\u003e\n    \u003cbutton class=\"enc-btn enc-btn-secondary\" id=\"enc-copy-btn\"\u003eCopy Output\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- HISTORY --\u003e\n\u003cdiv class=\"enc-card\"\u003e\n  \u003cdiv class=\"enc-section-title\"\u003eRecent Conversions (Last 5)\u003c/div\u003e\n  \u003cul class=\"enc-history-list\" id=\"enc-history-list\"\u003e\n    \u003cli class=\"enc-history-empty\"\u003eNo conversions yet.\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  'use strict';\n\n  /* ── DOM refs ── */\n  var inputEl     = document.getElementById('enc-input');\n  var outputEl    = document.getElementById('enc-output');\n  var inputCnt    = document.getElementById('enc-input-count');\n  var outputCnt   = document.getElementById('enc-output-count');\n  var errorEl     = document.getElementById('enc-error-msg');\n  var modeSelect  = document.getElementById('enc-mode');\n  var encodeBtn   = document.getElementById('enc-encode-btn');\n  var decodeBtn   = document.getElementById('enc-decode-btn');\n  var clearBtn    = document.getElementById('enc-clear-btn');\n  var swapBtn     = document.getElementById('enc-swap-btn');\n  var copyBtn     = document.getElementById('enc-copy-btn');\n  var autoDetect  = document.getElementById('enc-auto-detect');\n  var autoInd     = document.getElementById('enc-auto-indicator');\n  var chain1      = document.getElementById('enc-chain-1');\n  var chain2      = document.getElementById('enc-chain-2');\n  var chainRunBtn = document.getElementById('enc-chain-run-btn');\n  var historyList = document.getElementById('enc-history-list');\n\n  /* ── History ── */\n  var history = [];\n\n  function addHistory(mode, direction, input, output) {\n    history.unshift({ mode: mode, dir: direction, input: input, output: output });\n    if (history.length \u003e 5) history = history.slice(0, 5);\n    renderHistory();\n  }\n\n  function renderHistory() {\n    if (history.length === 0) {\n      historyList.innerHTML = '\u003cli class=\"enc-history-empty\"\u003eNo conversions yet.\u003c/li\u003e';\n      return;\n    }\n    historyList.innerHTML = history.map(function (h, i) {\n      var dirCls  = h.dir === 'encode' ? 'enc' : 'dec';\n      var dirText = h.dir === 'encode' ? 'ENC' : 'DEC';\n      var preview = (h.input || '').substring(0, 60) + (h.input.length \u003e 60 ? '…' : '');\n      return '\u003cli class=\"enc-history-item\" data-idx=\"' + i + '\"\u003e' +\n        '\u003cdiv class=\"enc-history-meta\"\u003e' +\n          '\u003cspan class=\"enc-history-mode-badge\"\u003e' + h.mode.toUpperCase() + '\u003c/span\u003e' +\n          '\u003cspan class=\"enc-history-dir-badge ' + dirCls + '\"\u003e' + dirText + '\u003c/span\u003e' +\n        '\u003c/div\u003e' +\n        '\u003cdiv class=\"enc-history-text\"\u003e' + escapeHtml(preview) + '\u003c/div\u003e' +\n      '\u003c/li\u003e';\n    }).join('');\n\n    historyList.querySelectorAll('.enc-history-item').forEach(function (el) {\n      el.addEventListener('click', function () {\n        var idx = parseInt(this.getAttribute('data-idx'), 10);\n        var h = history[idx];\n        if (h) {\n          inputEl.value  = h.input;\n          outputEl.value = h.output;\n          modeSelect.value = h.mode;\n          updateCounts();\n          clearError();\n        }\n      });\n    });\n  }\n\n  function escapeHtml(str) {\n    return str.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n  }\n\n  /* ── Counts ── */\n  function updateCounts() {\n    inputCnt.textContent  = inputEl.value.length.toLocaleString() + ' chars';\n    outputCnt.textContent = outputEl.value.length.toLocaleString() + ' chars';\n  }\n\n  /* ── Error ── */\n  function showError(msg) {\n    errorEl.textContent = msg;\n    errorEl.classList.add('visible');\n    outputEl.classList.add('enc-error-border');\n  }\n\n  function clearError() {\n    errorEl.textContent = '';\n    errorEl.classList.remove('visible');\n    outputEl.classList.remove('enc-error-border');\n  }\n\n  function setOutput(val) {\n    outputEl.value = val;\n    updateCounts();\n  }\n\n  /* ══════════════════════════════════════════\n     ENCODING FUNCTIONS\n  ══════════════════════════════════════════ */\n\n  function encodeBase64(str) {\n    return btoa(unescape(encodeURIComponent(str)));\n  }\n\n  function decodeBase64(str) {\n    var raw = str.trim().replace(/^data:[^;]+;base64,/, '');\n    raw = raw.replace(/-/g, '+').replace(/_/g, '/');\n    while (raw.length % 4 !== 0) raw += '=';\n    return decodeURIComponent(escape(atob(raw)));\n  }\n\n  function encodeUrl(str) {\n    return encodeURIComponent(str);\n  }\n\n  function decodeUrl(str) {\n    return decodeURIComponent(str.replace(/\\+/g, ' '));\n  }\n\n  function encodeHtml(str) {\n    return str\n      .replace(/\u0026/g, '\u0026amp;')\n      .replace(/\u003c/g, '\u0026lt;')\n      .replace(/\u003e/g, '\u0026gt;')\n      .replace(/\"/g, '\u0026quot;')\n      .replace(/'/g, '\u0026#39;')\n      .replace(/\\//g, '\u0026#47;');\n  }\n\n  function decodeHtml(str) {\n    var txt = document.createElement('textarea');\n    txt.innerHTML = str;\n    return txt.value;\n  }\n\n  function encodeHex(str) {\n    var result = '';\n    for (var i = 0; i \u003c str.length; i++) {\n      var code = str.charCodeAt(i);\n      result += (code \u003c 256 ? '' : '\\\\u') + code.toString(16).padStart(code \u003c 256 ? 2 : 4, '0') + ' ';\n    }\n    return result.trim();\n  }\n\n  function decodeHex(str) {\n    return str.trim().split(/\\s+/).map(function (token) {\n      var code = parseInt(token.replace(/^\\\\u/, ''), 16);\n      if (isNaN(code)) throw new Error('Invalid hex token: ' + token);\n      return String.fromCharCode(code);\n    }).join('');\n  }\n\n  function encodeBinary(str) {\n    var result = [];\n    for (var i = 0; i \u003c str.length; i++) {\n      result.push(str.charCodeAt(i).toString(2).padStart(8, '0'));\n    }\n    return result.join(' ');\n  }\n\n  function decodeBinary(str) {\n    return str.trim().split(/\\s+/).map(function (b) {\n      var code = parseInt(b, 2);\n      if (isNaN(code)) throw new Error('Invalid binary sequence: ' + b);\n      return String.fromCharCode(code);\n    }).join('');\n  }\n\n  function encodeOctal(str) {\n    var result = [];\n    for (var i = 0; i \u003c str.length; i++) {\n      result.push('\\\\' + str.charCodeAt(i).toString(8).padStart(3, '0'));\n    }\n    return result.join(' ');\n  }\n\n  function decodeOctal(str) {\n    return str.trim().split(/\\s+/).map(function (o) {\n      var clean = o.replace(/^\\\\/, '');\n      var code = parseInt(clean, 8);\n      if (isNaN(code)) throw new Error('Invalid octal token: ' + o);\n      return String.fromCharCode(code);\n    }).join('');\n  }\n\n  function encodeAscii(str) {\n    var result = [];\n    for (var i = 0; i \u003c str.length; i++) {\n      result.push(str.charCodeAt(i));\n    }\n    return result.join(' ');\n  }\n\n  function decodeAscii(str) {\n    return str.trim().split(/\\s+/).map(function (n) {\n      var code = parseInt(n, 10);\n      if (isNaN(code)) throw new Error('Invalid ASCII code: ' + n);\n      return String.fromCharCode(code);\n    }).join('');\n  }\n\n  function encodeUnicode(str) {\n    var result = '';\n    for (var i = 0; i \u003c str.length; i++) {\n      var code = str.charCodeAt(i);\n      if (code \u003e 127) {\n        result += '\\\\u' + code.toString(16).toUpperCase().padStart(4, '0');\n      } else {\n        result += str[i];\n      }\n    }\n    return result;\n  }\n\n  function decodeUnicode(str) {\n    return str.replace(/\\\\u([0-9a-fA-F]{4})/g, function (_, hex) {\n      return String.fromCharCode(parseInt(hex, 16));\n    });\n  }\n\n  function decodeJwt(str) {\n    var parts = str.trim().split('.');\n    if (parts.length \u003c 2) throw new Error('Not a valid JWT: expected at least two dot-separated parts.');\n    function decodeSegment(seg) {\n      seg = seg.replace(/-/g, '+').replace(/_/g, '/');\n      while (seg.length % 4 !== 0) seg += '=';\n      return JSON.parse(decodeURIComponent(escape(atob(seg))));\n    }\n    var header  = decodeSegment(parts[0]);\n    var payload = decodeSegment(parts[1]);\n    return JSON.stringify({ header: header, payload: payload }, null, 2);\n  }\n\n  /* ── Dispatch encode/decode by mode ── */\n  function runEncode(text, mode) {\n    switch (mode) {\n      case 'base64':  return encodeBase64(text);\n      case 'url':     return encodeUrl(text);\n      case 'html':    return encodeHtml(text);\n      case 'hex':     return encodeHex(text);\n      case 'binary':  return encodeBinary(text);\n      case 'octal':   return encodeOctal(text);\n      case 'ascii':   return encodeAscii(text);\n      case 'unicode': return encodeUnicode(text);\n      case 'jwt':     throw new Error('JWT is decode-only. Switch to another format for encoding.');\n      default: throw new Error('Unknown mode: ' + mode);\n    }\n  }\n\n  function runDecode(text, mode) {\n    switch (mode) {\n      case 'base64':  return decodeBase64(text);\n      case 'url':     return decodeUrl(text);\n      case 'html':    return decodeHtml(text);\n      case 'hex':     return decodeHex(text);\n      case 'binary':  return decodeBinary(text);\n      case 'octal':   return decodeOctal(text);\n      case 'ascii':   return decodeAscii(text);\n      case 'unicode': return decodeUnicode(text);\n      case 'jwt':     return decodeJwt(text);\n      default: throw new Error('Unknown mode: ' + mode);\n    }\n  }\n\n  /* ── Auto-detect ── */\n  function detectMode(str) {\n    var s = str.trim();\n    if (/^[A-Za-z0-9\\-_]+\\.[A-Za-z0-9\\-_]+\\.[A-Za-z0-9\\-_]*$/.test(s)) return 'jwt';\n    if (/^[A-Za-z0-9+/\\-_]+=*$/.test(s) \u0026\u0026 s.length % 4 === 0 \u0026\u0026 s.length \u003e 3) return 'base64';\n    if (/%[0-9A-Fa-f]{2}/.test(s)) return 'url';\n    if (/\u0026(amp|lt|gt|quot|#\\d+|#x[0-9a-fA-F]+);/.test(s)) return 'html';\n    if (/^([01]{8}\\s*)+$/.test(s)) return 'binary';\n    if (/^(\\\\[0-7]{3}\\s*)+$/.test(s)) return 'octal';\n    if (/^([0-9a-fA-F]{2}\\s*)+$/.test(s)) return 'hex';\n    if (/^(\\d{1,3}\\s*)+$/.test(s) \u0026\u0026 s.trim().split(/\\s+/).every(function(n){var v=parseInt(n,10);return !isNaN(v)\u0026\u0026v\u003e=0\u0026\u0026v\u003c=127;})) return 'ascii';\n    if (/\\\\u[0-9a-fA-F]{4}/.test(s)) return 'unicode';\n    return null;\n  }\n\n  /* ── Main actions ── */\n  function doEncode() {\n    clearError();\n    var text = inputEl.value;\n    var mode = modeSelect.value;\n    if (!text) { setOutput(''); return; }\n    try {\n      var result = runEncode(text, mode);\n      setOutput(result);\n      addHistory(mode, 'encode', text, result);\n    } catch (e) {\n      showError('Encode error: ' + e.message);\n      setOutput('');\n    }\n  }\n\n  function doDecode() {\n    clearError();\n    var text = inputEl.value;\n    var mode = modeSelect.value;\n    if (!text) { setOutput(''); return; }\n    try {\n      var result = runDecode(text, mode);\n      setOutput(result);\n      addHistory(mode, 'decode', text, result);\n    } catch (e) {\n      showError('Decode error: ' + e.message);\n      setOutput('');\n    }\n  }\n\n  /* ── Chain encoding ── */\n  function doChain() {\n    clearError();\n    var text = inputEl.value;\n    if (!text) { setOutput(''); return; }\n    var steps = [chain1.value, chain2.value].filter(function(v){ return v !== 'none'; });\n    if (steps.length === 0) {\n      showError('Select at least one step in the chain.');\n      return;\n    }\n    try {\n      var current = text;\n      steps.forEach(function (step) {\n        current = runEncode(current, step);\n      });\n      setOutput(current);\n      addHistory(steps.join('+'), 'encode', text, current);\n    } catch (e) {\n      showError('Chain error: ' + e.message);\n      setOutput('');\n    }\n  }\n\n  /* ── Auto-detect handler ── */\n  function runAutoDetect() {\n    var text = inputEl.value.trim();\n    if (!text) return;\n    var detected = detectMode(text);\n    if (detected) {\n      modeSelect.value = detected;\n      doDecode();\n    }\n  }\n\n  /* ── Copy ── */\n  function doCopy() {\n    var val = outputEl.value;\n    if (!val) return;\n    navigator.clipboard.writeText(val).then(function () {\n      flashCopy();\n    }).catch(function () {\n      var ta = document.createElement('textarea');\n      ta.value = val;\n      ta.style.position = 'fixed';\n      ta.style.opacity  = '0';\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      flashCopy();\n    });\n  }\n\n  function flashCopy() {\n    var orig = copyBtn.textContent;\n    copyBtn.textContent = 'Copied!';\n    copyBtn.classList.add('enc-btn-success');\n    setTimeout(function () {\n      copyBtn.textContent = orig;\n      copyBtn.classList.remove('enc-btn-success');\n    }, 1800);\n  }\n\n  /* ── Event listeners ── */\n  encodeBtn.addEventListener('click', doEncode);\n  decodeBtn.addEventListener('click', doDecode);\n  chainRunBtn.addEventListener('click', doChain);\n\n  clearBtn.addEventListener('click', function () {\n    inputEl.value  = '';\n    setOutput('');\n    clearError();\n  });\n\n  swapBtn.addEventListener('click', function () {\n    var tmp = inputEl.value;\n    inputEl.value  = outputEl.value;\n    outputEl.value = tmp;\n    clearError();\n    updateCounts();\n  });\n\n  copyBtn.addEventListener('click', doCopy);\n\n  autoDetect.addEventListener('change', function () {\n    autoInd.style.display = this.checked ? 'inline-block' : 'none';\n    if (this.checked) runAutoDetect();\n  });\n\n  inputEl.addEventListener('input', function () {\n    updateCounts();\n    clearError();\n    if (autoDetect.checked) runAutoDetect();\n  });\n\n  renderHistory();\n  updateCounts();\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"what-is-an-encoderdecoder\"\u003eWhat is an Encoder/Decoder?\u003c/h2\u003e\n\u003cp\u003eEncoding transforms text or binary data into a different representation so it can be safely transported, stored, or embedded in a target medium. Decoding reverses the process to recover the original content. Unlike encryption, encoding is not a security mechanism — the transformation is entirely reversible and publicly defined. Common uses include transmitting binary data over text-based protocols, embedding special characters safely in HTML or URLs, and inspecting authentication tokens.\u003c/p\u003e","title":"Universal Encoder/Decoder — Multi-Format Tool"},{"content":"Convert Unix timestamps instantly — paste an epoch value to see the human-readable date, or pick a date to get its timestamp. Live clock, multiple output formats, milliseconds support, and full timezone control.\nCurrent Unix Timestamp — — Global Settings Unit: Seconds Milliseconds Timezone: Timestamp to Date Use Now Date to Timestamp Use Now Common Reference Timestamps Event Unix (seconds) Date (UTC) How Unix Timestamps Work A Unix timestamp (also called epoch time) counts the number of seconds — or milliseconds — elapsed since January 1, 1970, 00:00:00 UTC. It is the universal time standard in software: databases, APIs, log files, and programming languages all rely on it.\nFormat Example Seconds (standard) 1700000000 Milliseconds (JavaScript) 1700000000000 ISO 8601 2023-11-14T22:13:20+00:00 RFC 2822 Tue, 14 Nov 2023 22:13:20 +0000 Tip: If your timestamp has 13 digits it is in milliseconds. 10 digits means seconds. Toggle the unit selector above accordingly.\nRelated Tools Convert timezones → Timezone Converter Generate cron expressions → Cron Expression Generator Calculate age → Age Calculator ","permalink":"https://productivity-works.com/tools/timestamp-converter/","summary":"\u003cp\u003e\u003cstrong\u003eConvert Unix timestamps instantly\u003c/strong\u003e — paste an epoch value to see the human-readable date, or pick a date to get its timestamp. Live clock, multiple output formats, milliseconds support, and full timezone control.\u003c/p\u003e\n\u003cdiv id=\"ts-app\"\u003e\n\u003cstyle\u003e\n  #ts-app *,\n  #ts-app *::before,\n  #ts-app *::after {\n    box-sizing: border-box;\n    margin: 0;\n    padding: 0;\n  }\n  #ts-app {\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n    font-size: 15px;\n    line-height: 1.6;\n    color: #1a1a2e;\n    max-width: 800px;\n  }\n  #ts-app .ts-section {\n    background: #f8f9ff;\n    border: 1px solid #e0e4f0;\n    border-radius: 10px;\n    padding: 20px 24px;\n    margin-bottom: 20px;\n  }\n  #ts-app .ts-section-title {\n    font-size: 13px;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 0.08em;\n    color: #6366f1;\n    margin-bottom: 14px;\n  }\n  #ts-app .ts-live-clock {\n    background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);\n    border-radius: 10px;\n    padding: 20px 24px;\n    margin-bottom: 20px;\n    color: #fff;\n  }\n  #ts-app .ts-live-label {\n    font-size: 12px;\n    font-weight: 600;\n    text-transform: uppercase;\n    letter-spacing: 0.1em;\n    opacity: 0.8;\n    margin-bottom: 6px;\n  }\n  #ts-app .ts-live-value {\n    font-size: 36px;\n    font-weight: 700;\n    font-variant-numeric: tabular-nums;\n    letter-spacing: 0.02em;\n    word-break: break-all;\n  }\n  #ts-app .ts-live-sub {\n    font-size: 13px;\n    opacity: 0.75;\n    margin-top: 4px;\n    font-variant-numeric: tabular-nums;\n  }\n  #ts-app .ts-controls-row {\n    display: flex;\n    gap: 10px;\n    align-items: center;\n    flex-wrap: wrap;\n    margin-bottom: 16px;\n  }\n  #ts-app .ts-toggle-group {\n    display: flex;\n    border: 1px solid #c7d0ea;\n    border-radius: 6px;\n    overflow: hidden;\n  }\n  #ts-app .ts-toggle-btn {\n    padding: 7px 16px;\n    font-size: 13px;\n    font-weight: 600;\n    border: none;\n    background: #fff;\n    color: #555;\n    cursor: pointer;\n    transition: background 0.15s, color 0.15s;\n  }\n  #ts-app .ts-toggle-btn.active {\n    background: #6366f1;\n    color: #fff;\n  }\n  #ts-app .ts-toggle-btn:hover:not(.active) {\n    background: #eef0ff;\n  }\n  #ts-app select,\n  #ts-app input[type=\"text\"],\n  #ts-app input[type=\"number\"],\n  #ts-app input[type=\"datetime-local\"] {\n    padding: 8px 12px;\n    font-size: 14px;\n    border: 1px solid #c7d0ea;\n    border-radius: 6px;\n    background: #fff;\n    color: #1a1a2e;\n    outline: none;\n    transition: border-color 0.15s;\n  }\n  #ts-app select:focus,\n  #ts-app input[type=\"text\"]:focus,\n  #ts-app input[type=\"number\"]:focus,\n  #ts-app input[type=\"datetime-local\"]:focus {\n    border-color: #6366f1;\n    box-shadow: 0 0 0 3px rgba(99,102,241,0.12);\n  }\n  #ts-app .ts-input-row {\n    display: flex;\n    gap: 10px;\n    align-items: center;\n    flex-wrap: wrap;\n    margin-bottom: 14px;\n  }\n  #ts-app .ts-input-wide {\n    flex: 1;\n    min-width: 200px;\n    font-family: \"SFMono-Regular\", Consolas, monospace;\n  }\n  #ts-app .ts-btn {\n    padding: 8px 18px;\n    font-size: 14px;\n    font-weight: 600;\n    border: none;\n    border-radius: 6px;\n    cursor: pointer;\n    background: #6366f1;\n    color: #fff;\n    transition: background 0.15s, transform 0.1s;\n    white-space: nowrap;\n  }\n  #ts-app .ts-btn:hover {\n    background: #4f46e5;\n  }\n  #ts-app .ts-btn:active {\n    transform: scale(0.97);\n  }\n  #ts-app .ts-btn-secondary {\n    background: #fff;\n    color: #6366f1;\n    border: 1px solid #c7d0ea;\n  }\n  #ts-app .ts-btn-secondary:hover {\n    background: #eef0ff;\n  }\n  #ts-app .ts-results {\n    display: flex;\n    flex-direction: column;\n    gap: 8px;\n  }\n  #ts-app .ts-result-row {\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    background: #fff;\n    border: 1px solid #e0e4f0;\n    border-radius: 7px;\n    padding: 10px 14px;\n    flex-wrap: wrap;\n  }\n  #ts-app .ts-result-label {\n    font-size: 12px;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 0.06em;\n    color: #888;\n    min-width: 110px;\n    flex-shrink: 0;\n  }\n  #ts-app .ts-result-value {\n    font-family: \"SFMono-Regular\", Consolas, monospace;\n    font-size: 13.5px;\n    color: #1a1a2e;\n    flex: 1;\n    word-break: break-all;\n    min-width: 150px;\n  }\n  #ts-app .ts-copy-btn {\n    padding: 4px 12px;\n    font-size: 12px;\n    font-weight: 600;\n    border: 1px solid #c7d0ea;\n    border-radius: 5px;\n    background: #fff;\n    color: #6366f1;\n    cursor: pointer;\n    transition: background 0.15s, color 0.15s;\n    flex-shrink: 0;\n  }\n  #ts-app .ts-copy-btn:hover {\n    background: #6366f1;\n    color: #fff;\n    border-color: #6366f1;\n  }\n  #ts-app .ts-copy-btn.copied {\n    background: #10b981;\n    color: #fff;\n    border-color: #10b981;\n  }\n  #ts-app .ts-error {\n    color: #ef4444;\n    font-size: 13px;\n    margin-top: 6px;\n    min-height: 18px;\n  }\n  #ts-app .ts-ref-table {\n    width: 100%;\n    border-collapse: collapse;\n    font-size: 13.5px;\n  }\n  #ts-app .ts-ref-table th {\n    text-align: left;\n    font-size: 12px;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 0.06em;\n    color: #888;\n    padding: 6px 10px;\n    border-bottom: 1px solid #e0e4f0;\n  }\n  #ts-app .ts-ref-table td {\n    padding: 9px 10px;\n    border-bottom: 1px solid #f0f2fa;\n    vertical-align: middle;\n  }\n  #ts-app .ts-ref-table tr:last-child td {\n    border-bottom: none;\n  }\n  #ts-app .ts-ref-table .ts-mono {\n    font-family: \"SFMono-Regular\", Consolas, monospace;\n  }\n  #ts-app .ts-ref-table .ts-ref-copy {\n    padding: 3px 10px;\n    font-size: 11px;\n    font-weight: 600;\n    border: 1px solid #c7d0ea;\n    border-radius: 4px;\n    background: #fff;\n    color: #6366f1;\n    cursor: pointer;\n    transition: background 0.15s, color 0.15s;\n  }\n  #ts-app .ts-ref-table .ts-ref-copy:hover {\n    background: #6366f1;\n    color: #fff;\n    border-color: #6366f1;\n  }\n  #ts-app .ts-divider {\n    border: none;\n    border-top: 1px solid #e0e4f0;\n    margin: 6px 0 14px;\n  }\n  #ts-app .ts-tz-row {\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    flex-wrap: wrap;\n    margin-bottom: 14px;\n  }\n  #ts-app .ts-tz-label {\n    font-size: 13px;\n    font-weight: 600;\n    color: #555;\n    white-space: nowrap;\n  }\n  #ts-app select.ts-tz-select {\n    min-width: 220px;\n  }\n  @media (max-width: 540px) {\n    #ts-app .ts-live-value { font-size: 26px; }\n    #ts-app .ts-result-label { min-width: 90px; }\n    #ts-app .ts-ref-table { font-size: 12px; }\n    #ts-app .ts-ref-table th, #ts-app .ts-ref-table td { padding: 7px 6px; }\n  }\n\u003c/style\u003e\n\u003c!-- Live Clock --\u003e\n\u003cdiv class=\"ts-live-clock\"\u003e\n  \u003cdiv class=\"ts-live-label\"\u003eCurrent Unix Timestamp\u003c/div\u003e\n  \u003cdiv class=\"ts-live-value\" id=\"ts-live-val\"\u003e—\u003c/div\u003e\n  \u003cdiv class=\"ts-live-sub\" id=\"ts-live-sub\"\u003e—\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Controls: unit + timezone --\u003e\n\u003cdiv class=\"ts-section\"\u003e\n  \u003cdiv class=\"ts-section-title\"\u003eGlobal Settings\u003c/div\u003e\n  \u003cdiv class=\"ts-controls-row\"\u003e\n    \u003cspan style=\"font-size:13px;font-weight:600;color:#555;\"\u003eUnit:\u003c/span\u003e\n    \u003cdiv class=\"ts-toggle-group\"\u003e\n      \u003cbutton class=\"ts-toggle-btn active\" id=\"ts-unit-sec\" onclick=\"tsSetUnit('s')\"\u003eSeconds\u003c/button\u003e\n      \u003cbutton class=\"ts-toggle-btn\" id=\"ts-unit-ms\" onclick=\"tsSetUnit('ms')\"\u003eMilliseconds\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ts-tz-row\"\u003e\n    \u003cspan class=\"ts-tz-label\"\u003eTimezone:\u003c/span\u003e\n    \u003cselect class=\"ts-tz-select\" id=\"ts-tz-select\" onchange=\"tsOnTzChange()\"\u003e\u003c/select\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Timestamp → Date --\u003e\n\u003cdiv class=\"ts-section\"\u003e\n  \u003cdiv class=\"ts-section-title\"\u003eTimestamp to Date\u003c/div\u003e\n  \u003cdiv class=\"ts-input-row\"\u003e\n    \u003cinput type=\"number\" class=\"ts-input-wide\" id=\"ts-ts-input\" placeholder=\"Enter Unix timestamp (e.g. 1700000000)\" oninput=\"tsConvertTs()\" /\u003e\n    \u003cbutton class=\"ts-btn ts-btn-secondary\" onclick=\"tsUseNow()\"\u003eUse Now\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ts-error\" id=\"ts-ts-error\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"ts-results\" id=\"ts-ts-results\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Date → Timestamp --\u003e\n\u003cdiv class=\"ts-section\"\u003e\n  \u003cdiv class=\"ts-section-title\"\u003eDate to Timestamp\u003c/div\u003e\n  \u003cdiv class=\"ts-input-row\"\u003e\n    \u003cinput type=\"datetime-local\" id=\"ts-dt-input\" oninput=\"tsConvertDt()\" /\u003e\n    \u003cbutton class=\"ts-btn ts-btn-secondary\" onclick=\"tsDtUseNow()\"\u003eUse Now\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ts-results\" id=\"ts-dt-results\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Common Reference Timestamps --\u003e\n\u003cdiv class=\"ts-section\"\u003e\n  \u003cdiv class=\"ts-section-title\"\u003eCommon Reference Timestamps\u003c/div\u003e\n  \u003ctable class=\"ts-ref-table\"\u003e\n    \u003cthead\u003e\n      \u003ctr\u003e\n        \u003cth\u003eEvent\u003c/th\u003e\n        \u003cth\u003eUnix (seconds)\u003c/th\u003e\n        \u003cth\u003eDate (UTC)\u003c/th\u003e\n        \u003cth\u003e\u003c/th\u003e\n      \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody id=\"ts-ref-body\"\u003e\u003c/tbody\u003e\n  \u003c/table\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  \"use strict\";\n\n  // ── State ────────────────────────────────────────────────\n  var unit = \"s\"; // \"s\" | \"ms\"\n  var tz = Intl.DateTimeFormat().resolvedOptions().timeZone;\n  var liveTimer = null;\n\n  // ── Timezone list ────────────────────────────────────────\n  var commonTzList = [\n    \"UTC\",\n    \"America/New_York\",\"America/Chicago\",\"America/Denver\",\"America/Los_Angeles\",\n    \"America/Sao_Paulo\",\"America/Toronto\",\"America/Vancouver\",\n    \"Europe/London\",\"Europe/Paris\",\"Europe/Berlin\",\"Europe/Moscow\",\"Europe/Istanbul\",\n    \"Asia/Dubai\",\"Asia/Kolkata\",\"Asia/Dhaka\",\"Asia/Bangkok\",\"Asia/Singapore\",\n    \"Asia/Shanghai\",\"Asia/Tokyo\",\"Asia/Seoul\",\n    \"Australia/Sydney\",\"Pacific/Auckland\",\"Pacific/Honolulu\"\n  ];\n\n  function buildTzSelect() {\n    var sel = document.getElementById(\"ts-tz-select\");\n    // Best effort: list Intl-supported timezones\n    var allTz = [];\n    try {\n      if (Intl.supportedValuesOf) {\n        allTz = Intl.supportedValuesOf(\"timeZone\");\n      }\n    } catch(e) {}\n    if (allTz.length === 0) allTz = commonTzList;\n    // Deduplicate and sort\n    var seen = {};\n    var sorted = [];\n    allTz.forEach(function(z) { if (!seen[z]) { seen[z]=1; sorted.push(z); } });\n    sorted.sort();\n    // Detect local\n    var localTz = Intl.DateTimeFormat().resolvedOptions().timeZone;\n    sel.innerHTML = \"\";\n    sorted.forEach(function(z) {\n      var opt = document.createElement(\"option\");\n      opt.value = z;\n      opt.textContent = z;\n      if (z === localTz) opt.selected = true;\n      sel.appendChild(opt);\n    });\n    tz = sel.value;\n  }\n\n  function tsOnTzChange() {\n    tz = document.getElementById(\"ts-tz-select\").value;\n    tsConvertTs();\n    tsConvertDt();\n    renderRef();\n  }\n\n  // ── Unit ─────────────────────────────────────────────────\n  function tsSetUnit(u) {\n    unit = u;\n    document.getElementById(\"ts-unit-sec\").classList.toggle(\"active\", u === \"s\");\n    document.getElementById(\"ts-unit-ms\").classList.toggle(\"active\", u === \"ms\");\n    startLiveClock();\n    tsConvertTs();\n    tsConvertDt();\n    renderRef();\n  }\n  window.tsSetUnit = tsSetUnit;\n  window.tsOnTzChange = tsOnTzChange;\n\n  // ── Live clock ───────────────────────────────────────────\n  function startLiveClock() {\n    if (liveTimer) clearInterval(liveTimer);\n    tickClock();\n    liveTimer = setInterval(tickClock, unit === \"ms\" ? 100 : 1000);\n  }\n\n  function tickClock() {\n    var now = Date.now();\n    var val = unit === \"ms\" ? now : Math.floor(now / 1000);\n    document.getElementById(\"ts-live-val\").textContent = val.toLocaleString(\"en-US\");\n    document.getElementById(\"ts-live-sub\").textContent =\n      formatInTz(new Date(now), tz, {\n        weekday:\"short\", year:\"numeric\", month:\"short\",\n        day:\"numeric\", hour:\"2-digit\", minute:\"2-digit\", second:\"2-digit\",\n        timeZoneName:\"short\"\n      });\n  }\n\n  // ── Helpers ──────────────────────────────────────────────\n  function formatInTz(date, timezone, opts) {\n    try {\n      return date.toLocaleString(\"en-US\", Object.assign({timeZone: timezone}, opts));\n    } catch(e) {\n      return date.toLocaleString(\"en-US\", opts);\n    }\n  }\n\n  function toIso8601(date, timezone) {\n    // Approximate ISO 8601 with offset\n    try {\n      var fmt = new Intl.DateTimeFormat(\"sv-SE\", {\n        timeZone: timezone,\n        year:\"numeric\", month:\"2-digit\", day:\"2-digit\",\n        hour:\"2-digit\", minute:\"2-digit\", second:\"2-digit\"\n      });\n      var parts = {};\n      fmt.formatToParts(date).forEach(function(p) { parts[p.type] = p.value; });\n      var base = parts.year+\"-\"+parts.month+\"-\"+parts.day+\"T\"+\n                 parts.hour+\":\"+parts.minute+\":\"+parts.second;\n      // Offset\n      var utcMs = date.getTime();\n      var localMs = new Date(date.toLocaleString(\"en-US\", {timeZone: timezone})).getTime();\n      var diffMin = Math.round((localMs - new Date(date.toLocaleString(\"en-US\", {timeZone: \"UTC\"})).getTime()) / 60000);\n      var sign = diffMin \u003e= 0 ? \"+\" : \"-\";\n      var abs = Math.abs(diffMin);\n      var hh = String(Math.floor(abs/60)).padStart(2,\"0\");\n      var mm = String(abs%60).padStart(2,\"0\");\n      return base + sign + hh + \":\" + mm;\n    } catch(e) {\n      return date.toISOString();\n    }\n  }\n\n  function toRfc2822(date) {\n    // RFC 2822 is always in local or UTC\n    var days = [\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"];\n    var months = [\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"];\n    var d = new Date(date.toLocaleString(\"en-US\", {timeZone: tz}));\n    // We'll use UTC representation of the localized time\n    try {\n      var parts = {};\n      new Intl.DateTimeFormat(\"en-US\", {\n        timeZone: tz,\n        weekday:\"short\", day:\"2-digit\", month:\"short\", year:\"numeric\",\n        hour:\"2-digit\", minute:\"2-digit\", second:\"2-digit\", hour12:false\n      }).formatToParts(date).forEach(function(p){ parts[p.type] = p.value; });\n      // offset\n      var utcMs2 = Date.parse(date.toLocaleString(\"en-US\", {timeZone:\"UTC\"}));\n      var localMs2 = Date.parse(date.toLocaleString(\"en-US\", {timeZone:tz}));\n      var diffMin2 = Math.round((localMs2 - utcMs2) / 60000);\n      var sign2 = diffMin2 \u003e= 0 ? \"+\" : \"-\";\n      var abs2 = Math.abs(diffMin2);\n      var hh2 = String(Math.floor(abs2/60)).padStart(2,\"0\");\n      var mm2 = String(abs2%60).padStart(2,\"0\");\n      return (parts.weekday||\"\")+ \", \"+(parts.day||\"\").replace(/\\D/g,\"\")+\" \"+\n             (parts.month||\"\")+\" \"+(parts.year||\"\")+\" \"+\n             (parts.hour||\"\").replace(/\\D/g,\"\").padStart(2,\"0\")+\":\"+\n             (parts.minute||\"\").padStart(2,\"0\")+\":\"+\n             (parts.second||\"\").padStart(2,\"0\")+\" \"+sign2+hh2+mm2;\n    } catch(e) {\n      return date.toString();\n    }\n  }\n\n  function relativeTime(date) {\n    var diffMs = Date.now() - date.getTime();\n    var abs = Math.abs(diffMs);\n    var suffix = diffMs \u003e= 0 ? \" ago\" : \" from now\";\n    if (abs \u003c 5000)   return \"just now\";\n    if (abs \u003c 60000)  return Math.round(abs/1000)+\" seconds\"+suffix;\n    if (abs \u003c 3600000) return Math.round(abs/60000)+\" minutes\"+suffix;\n    if (abs \u003c 86400000) return Math.round(abs/3600000)+\" hours\"+suffix;\n    if (abs \u003c 2592000000) return Math.round(abs/86400000)+\" days\"+suffix;\n    if (abs \u003c 31536000000) return Math.round(abs/2592000000)+\" months\"+suffix;\n    return Math.round(abs/31536000000)+\" years\"+suffix;\n  }\n\n  function tsToDate(val) {\n    var n = parseFloat(val);\n    if (isNaN(n)) return null;\n    var ms = unit === \"ms\" ? n : n * 1000;\n    return new Date(ms);\n  }\n\n  // ── Timestamp → Date ─────────────────────────────────────\n  function tsConvertTs() {\n    var raw = document.getElementById(\"ts-ts-input\").value.trim();\n    var errEl = document.getElementById(\"ts-ts-error\");\n    var resEl = document.getElementById(\"ts-ts-results\");\n    errEl.textContent = \"\";\n    if (!raw) { resEl.innerHTML = \"\"; return; }\n    var date = tsToDate(raw);\n    if (!date || isNaN(date.getTime())) {\n      errEl.textContent = \"Invalid timestamp. Please enter a valid number.\";\n      resEl.innerHTML = \"\";\n      return;\n    }\n    renderDateResults(resEl, date);\n  }\n  window.tsConvertTs = tsConvertTs;\n\n  function renderDateResults(container, date) {\n    var rows = [\n      { label: \"UTC\",        value: date.toUTCString() },\n      { label: \"Local Time\", value: formatInTz(date, tz, {\n          weekday:\"long\", year:\"numeric\", month:\"long\", day:\"numeric\",\n          hour:\"2-digit\", minute:\"2-digit\", second:\"2-digit\", timeZoneName:\"short\"\n        })\n      },\n      { label: \"ISO 8601\",   value: toIso8601(date, tz) },\n      { label: \"RFC 2822\",   value: toRfc2822(date) },\n      { label: \"Relative\",   value: relativeTime(date) },\n      { label: \"Unix (sec)\", value: String(Math.floor(date.getTime()/1000)) },\n      { label: \"Unix (ms)\",  value: String(date.getTime()) }\n    ];\n    container.innerHTML = rows.map(function(r) {\n      return '\u003cdiv class=\"ts-result-row\"\u003e' +\n        '\u003cspan class=\"ts-result-label\"\u003e'+r.label+'\u003c/span\u003e' +\n        '\u003cspan class=\"ts-result-value\" id=\"tsrv-'+r.label.replace(/\\s/g,\"_\")+'\"\u003e'+escHtml(r.value)+'\u003c/span\u003e' +\n        '\u003cbutton class=\"ts-copy-btn\" onclick=\"tsCopy(this,\\'tsrv-'+r.label.replace(/\\s/g,\"_\")+'\\')\"\u003eCopy\u003c/button\u003e' +\n        '\u003c/div\u003e';\n    }).join(\"\");\n  }\n\n  // ── Date → Timestamp ─────────────────────────────────────\n  function tsConvertDt() {\n    var raw = document.getElementById(\"ts-dt-input\").value;\n    var resEl = document.getElementById(\"ts-dt-results\");\n    if (!raw) { resEl.innerHTML = \"\"; return; }\n    // datetime-local gives us local browser time; treat as chosen tz\n    // We parse it as if it's in the selected timezone\n    var date = parseDatetimeLocalAsTz(raw, tz);\n    if (!date || isNaN(date.getTime())) { resEl.innerHTML = \"\"; return; }\n    renderDateResults(resEl, date);\n  }\n  window.tsConvertDt = tsConvertDt;\n\n  function parseDatetimeLocalAsTz(s, timezone) {\n    // s = \"YYYY-MM-DDTHH:mm\"\n    // We construct a date assuming s is wall-clock time in timezone\n    if (!s) return null;\n    var parts = s.match(/^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2})(?::(\\d{2}))?$/);\n    if (!parts) return null;\n    var iso = parts[1]+\"-\"+parts[2]+\"-\"+parts[3]+\"T\"+(parts[4]||\"00\")+\":\"+(parts[5]||\"00\")+\":\"+(parts[6]||\"00\");\n    // Use a trick: format a reference date in the timezone, find the offset\n    // Simple approach: treat the input as UTC first, then adjust\n    var naive = new Date(iso + \"Z\"); // UTC parse\n    if (isNaN(naive.getTime())) return null;\n    // Get what wall-clock time this UTC moment shows in the target tz\n    try {\n      var wallStr = naive.toLocaleString(\"sv-SE\", {timeZone: timezone});\n      // wallStr = \"YYYY-MM-DD HH:mm:ss\"\n      var wallParts = wallStr.match(/(\\d{4})-(\\d{2})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2})/);\n      if (!wallParts) return naive;\n      var wallMs = Date.UTC(+wallParts[1],+wallParts[2]-1,+wallParts[3],+wallParts[4],+wallParts[5],+wallParts[6]);\n      var naiveMs = Date.UTC(+parts[1],+parts[2]-1,+parts[3],+parts[4],+parts[5],+(parts[6]||0));\n      var offsetMs = naiveMs - wallMs; // tz offset\n      return new Date(naive.getTime() + offsetMs);\n    } catch(e) {\n      return naive;\n    }\n  }\n\n  // ── Use Now buttons ──────────────────────────────────────\n  function tsUseNow() {\n    var now = Date.now();\n    var val = unit === \"ms\" ? now : Math.floor(now / 1000);\n    document.getElementById(\"ts-ts-input\").value = val;\n    tsConvertTs();\n  }\n  window.tsUseNow = tsUseNow;\n\n  function tsDtUseNow() {\n    var now = new Date();\n    // Format for datetime-local input (no seconds needed)\n    var y = now.getFullYear();\n    var mo = String(now.getMonth()+1).padStart(2,\"0\");\n    var d  = String(now.getDate()).padStart(2,\"0\");\n    var h  = String(now.getHours()).padStart(2,\"0\");\n    var mi = String(now.getMinutes()).padStart(2,\"0\");\n    document.getElementById(\"ts-dt-input\").value = y+\"-\"+mo+\"-\"+d+\"T\"+h+\":\"+mi;\n    tsConvertDt();\n  }\n  window.tsDtUseNow = tsDtUseNow;\n\n  // ── Copy ─────────────────────────────────────────────────\n  function tsCopy(btn, id) {\n    var el = document.getElementById(id);\n    if (!el) return;\n    var text = el.textContent;\n    if (navigator.clipboard) {\n      navigator.clipboard.writeText(text).then(function() { flashCopy(btn); });\n    } else {\n      var ta = document.createElement(\"textarea\");\n      ta.value = text;\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand(\"copy\");\n      document.body.removeChild(ta);\n      flashCopy(btn);\n    }\n  }\n  window.tsCopy = tsCopy;\n\n  function flashCopy(btn) {\n    btn.textContent = \"Copied!\";\n    btn.classList.add(\"copied\");\n    setTimeout(function() {\n      btn.textContent = \"Copy\";\n      btn.classList.remove(\"copied\");\n    }, 1500);\n  }\n\n  // ── Reference table ──────────────────────────────────────\n  var refEvents = [\n    { name: \"Unix Epoch\",         ts: 0 },\n    { name: \"Y2K (2000-01-01)\",   ts: 946684800 },\n    { name: \"9/11 (2001-09-11)\",  ts: 1000166400 },\n    { name: \"Unix 1 Billion\",     ts: 1000000000 },\n    { name: \"Unix 1.5 Billion\",   ts: 1500000000 },\n    { name: \"Unix 2 Billion\",     ts: 2000000000 },\n    { name: \"2030-01-01\",         ts: 1893456000 },\n    { name: \"Current Time\",       ts: -1 /* live */ },\n  ];\n\n  function renderRef() {\n    var body = document.getElementById(\"ts-ref-body\");\n    if (!body) return;\n    body.innerHTML = refEvents.map(function(ev) {\n      var secVal = ev.ts === -1 ? Math.floor(Date.now()/1000) : ev.ts;\n      var msVal  = ev.ts === -1 ? Date.now() : ev.ts * 1000;\n      var displayVal = unit === \"ms\" ? msVal : secVal;\n      var date = new Date(secVal * 1000);\n      var utcStr = date.toUTCString();\n      var copyId = \"tsref-\"+ev.name.replace(/\\W/g,\"_\");\n      return '\u003ctr\u003e' +\n        '\u003ctd\u003e'+escHtml(ev.name)+'\u003c/td\u003e' +\n        '\u003ctd class=\"ts-mono\" id=\"'+copyId+'\"\u003e'+displayVal+'\u003c/td\u003e' +\n        '\u003ctd\u003e'+escHtml(utcStr)+'\u003c/td\u003e' +\n        '\u003ctd\u003e\u003cbutton class=\"ts-ref-copy\" onclick=\"tsCopy(this,\\''+copyId+'\\')\"\u003eCopy\u003c/button\u003e\u003c/td\u003e' +\n        '\u003c/tr\u003e';\n    }).join(\"\");\n  }\n\n  function escHtml(s) {\n    return String(s)\n      .replace(/\u0026/g,\"\u0026amp;\")\n      .replace(/\u003c/g,\"\u0026lt;\")\n      .replace(/\u003e/g,\"\u0026gt;\")\n      .replace(/\"/g,\"\u0026quot;\");\n  }\n\n  // ── Init ─────────────────────────────────────────────────\n  buildTzSelect();\n  startLiveClock();\n  renderRef();\n\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch3 id=\"how-unix-timestamps-work\"\u003eHow Unix Timestamps Work\u003c/h3\u003e\n\u003cp\u003eA \u003cstrong\u003eUnix timestamp\u003c/strong\u003e (also called epoch time) counts the number of seconds — or milliseconds — elapsed since \u003cstrong\u003eJanuary 1, 1970, 00:00:00 UTC\u003c/strong\u003e. It is the universal time standard in software: databases, APIs, log files, and programming languages all rely on it.\u003c/p\u003e","title":"Unix Timestamp Converter — Epoch Time Tool"},{"content":" URL Encoder / Decoder Mode: Encode Decode Scope: Component (encodeURIComponent) Full URL (encodeURI) Input 0 chars Encoded Output 0 chars Encode Copy Result Clear \u0026#8645; Swap Auto-detect ON encodeURIComponent — Encodes all characters except: A–Z a–z 0–9 - _ . ! ~ * ' ( ). Best for encoding individual query parameters or path segments. Encoded Character Breakdown Original Encoded Unicode Note How It Works URL encoding (percent-encoding) converts characters that are not safe for use in URLs into a %XX format, where XX is the hexadecimal value of the character\u0026rsquo;s UTF-8 byte(s).\nWhen to use which mode Mode Function Safe characters (not encoded) encodeURIComponent Encode a single query param or path segment A–Z a–z 0–9 - _ . ! ~ * ' ( ) encodeURI Encode a full URL, preserving its structure All of the above + ; , / ? : @ \u0026amp; = + $ # Common encoded characters Character Encoded Use case Space %20 Separates words in a path \u0026amp; %26 Ampersand in query value = %3D Equals sign in query value + %2B Plus sign in query value # %23 Hash character in URL ? %3F Question mark in URL Encode HTML entities → HTML Entity Encoder Convert encodings → Universal Encoder/Decoder ","permalink":"https://productivity-works.com/tools/url-encoder-decoder/","summary":"\u003cdiv id=\"ue-app\"\u003e\n\u003cstyle\u003e\n#ue-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 820px;\n  margin: 0 auto;\n  color: #1e293b;\n}\n#ue-app * {\n  box-sizing: border-box;\n}\n#ue-app h2.ue-title {\n  font-size: 1.1rem;\n  font-weight: 700;\n  color: #0f172a;\n  margin: 0 0 16px 0;\n}\n#ue-app .ue-card {\n  background: #ffffff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 24px;\n  margin-bottom: 20px;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.05);\n}\n/* Mode toggle */\n#ue-app .ue-mode-row {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  flex-wrap: wrap;\n  margin-bottom: 20px;\n}\n#ue-app .ue-mode-label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #475569;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n#ue-app .ue-toggle-group {\n  display: flex;\n  background: #f1f5f9;\n  border-radius: 8px;\n  padding: 3px;\n  gap: 2px;\n}\n#ue-app .ue-toggle-btn {\n  padding: 7px 18px;\n  border: none;\n  border-radius: 6px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  background: transparent;\n  color: #64748b;\n  transition: background 0.15s, color 0.15s;\n}\n#ue-app .ue-toggle-btn.active {\n  background: #ffffff;\n  color: #0f172a;\n  box-shadow: 0 1px 3px rgba(0,0,0,0.12);\n}\n#ue-app .ue-scope-group {\n  display: flex;\n  gap: 8px;\n  flex-wrap: wrap;\n  align-items: center;\n}\n#ue-app .ue-scope-label {\n  font-size: 13px;\n  color: #64748b;\n  font-weight: 500;\n}\n#ue-app .ue-scope-btn {\n  padding: 5px 14px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 12px;\n  font-weight: 600;\n  cursor: pointer;\n  background: #fff;\n  color: #475569;\n  transition: border-color 0.15s, background 0.15s, color 0.15s;\n}\n#ue-app .ue-scope-btn.active {\n  border-color: #3b82f6;\n  background: #eff6ff;\n  color: #1d4ed8;\n}\n/* Textarea area */\n#ue-app .ue-io-row {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 16px;\n}\n@media (max-width: 600px) {\n  #ue-app .ue-io-row { grid-template-columns: 1fr; }\n}\n#ue-app .ue-io-block {\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n}\n#ue-app .ue-io-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n#ue-app .ue-io-title {\n  font-size: 12px;\n  font-weight: 700;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n}\n#ue-app .ue-char-count {\n  font-size: 11px;\n  color: #94a3b8;\n}\n#ue-app textarea {\n  width: 100%;\n  min-height: 160px;\n  padding: 12px 14px;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 8px;\n  font-family: \"SFMono-Regular\", \"Fira Code\", \"Consolas\", monospace;\n  font-size: 13px;\n  line-height: 1.6;\n  color: #1e293b;\n  background: #f8fafc;\n  resize: vertical;\n  transition: border-color 0.15s;\n  outline: none;\n}\n#ue-app textarea:focus {\n  border-color: #3b82f6;\n  background: #fff;\n}\n#ue-app textarea.ue-output {\n  background: #f0fdf4;\n  border-color: #bbf7d0;\n  color: #14532d;\n}\n#ue-app textarea.ue-output-decode {\n  background: #fefce8;\n  border-color: #fde68a;\n  color: #78350f;\n}\n/* Action bar */\n#ue-app .ue-action-bar {\n  display: flex;\n  gap: 10px;\n  align-items: center;\n  flex-wrap: wrap;\n  margin-top: 16px;\n}\n#ue-app .ue-btn {\n  padding: 9px 20px;\n  border: none;\n  border-radius: 7px;\n  font-size: 13px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: opacity 0.15s, transform 0.1s;\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n#ue-app .ue-btn:active { transform: scale(0.97); }\n#ue-app .ue-btn-primary {\n  background: #3b82f6;\n  color: #fff;\n}\n#ue-app .ue-btn-primary:hover { opacity: 0.88; }\n#ue-app .ue-btn-secondary {\n  background: #f1f5f9;\n  color: #475569;\n  border: 1.5px solid #e2e8f0;\n}\n#ue-app .ue-btn-secondary:hover { background: #e2e8f0; }\n#ue-app .ue-btn-copy {\n  background: #10b981;\n  color: #fff;\n}\n#ue-app .ue-btn-copy:hover { opacity: 0.88; }\n#ue-app .ue-auto-badge {\n  margin-left: auto;\n  font-size: 11px;\n  color: #6366f1;\n  font-weight: 600;\n  background: #eef2ff;\n  border-radius: 5px;\n  padding: 3px 9px;\n  border: 1px solid #c7d2fe;\n}\n/* Breakdown table */\n#ue-app .ue-breakdown {\n  margin-top: 20px;\n}\n#ue-app .ue-breakdown-title {\n  font-size: 12px;\n  font-weight: 700;\n  color: #64748b;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  margin-bottom: 10px;\n}\n#ue-app .ue-table-wrap {\n  overflow-x: auto;\n  border-radius: 8px;\n  border: 1.5px solid #e2e8f0;\n}\n#ue-app table.ue-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 13px;\n}\n#ue-app table.ue-table th {\n  background: #f8fafc;\n  color: #64748b;\n  font-weight: 700;\n  padding: 8px 14px;\n  text-align: left;\n  border-bottom: 1.5px solid #e2e8f0;\n}\n#ue-app table.ue-table td {\n  padding: 7px 14px;\n  border-bottom: 1px solid #f1f5f9;\n  font-family: \"SFMono-Regular\", \"Fira Code\", monospace;\n  vertical-align: middle;\n}\n#ue-app table.ue-table tr:last-child td { border-bottom: none; }\n#ue-app table.ue-table tr:nth-child(even) td { background: #f8fafc; }\n#ue-app .ue-char-orig { color: #1e293b; font-weight: 600; }\n#ue-app .ue-char-enc  { color: #1d4ed8; }\n#ue-app .ue-char-note { color: #64748b; font-size: 12px; font-family: inherit; }\n/* Toast */\n#ue-app .ue-toast {\n  position: fixed;\n  bottom: 28px;\n  right: 28px;\n  background: #0f172a;\n  color: #fff;\n  padding: 11px 20px;\n  border-radius: 8px;\n  font-size: 13px;\n  font-weight: 600;\n  opacity: 0;\n  pointer-events: none;\n  transition: opacity 0.25s;\n  z-index: 9999;\n  box-shadow: 0 4px 16px rgba(0,0,0,0.25);\n}\n#ue-app .ue-toast.show { opacity: 1; }\n/* Info box */\n#ue-app .ue-info {\n  background: #eff6ff;\n  border: 1px solid #bfdbfe;\n  border-radius: 8px;\n  padding: 12px 16px;\n  font-size: 13px;\n  color: #1d4ed8;\n  margin-top: 16px;\n  line-height: 1.6;\n}\n\u003c/style\u003e\n\u003cdiv class=\"ue-card\"\u003e\n  \u003ch2 class=\"ue-title\"\u003eURL Encoder / Decoder\u003c/h2\u003e\n  \u003c!-- Mode toggle --\u003e\n  \u003cdiv class=\"ue-mode-row\"\u003e\n    \u003cspan class=\"ue-mode-label\"\u003eMode:\u003c/span\u003e\n    \u003cdiv class=\"ue-toggle-group\"\u003e\n      \u003cbutton class=\"ue-toggle-btn active\" id=\"ue-mode-encode\" onclick=\"ueSetMode('encode')\"\u003eEncode\u003c/button\u003e\n      \u003cbutton class=\"ue-toggle-btn\" id=\"ue-mode-decode\" onclick=\"ueSetMode('decode')\"\u003eDecode\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ue-scope-group\" id=\"ue-scope-group\"\u003e\n      \u003cspan class=\"ue-scope-label\"\u003eScope:\u003c/span\u003e\n      \u003cbutton class=\"ue-scope-btn active\" id=\"ue-scope-component\" onclick=\"ueSetScope('component')\"\u003eComponent \u003cspan style=\"font-weight:400;font-size:11px;\"\u003e(encodeURIComponent)\u003c/span\u003e\u003c/button\u003e\n      \u003cbutton class=\"ue-scope-btn\" id=\"ue-scope-full\" onclick=\"ueSetScope('full')\"\u003eFull URL \u003cspan style=\"font-weight:400;font-size:11px;\"\u003e(encodeURI)\u003c/span\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- I/O --\u003e\n  \u003cdiv class=\"ue-io-row\"\u003e\n    \u003cdiv class=\"ue-io-block\"\u003e\n      \u003cdiv class=\"ue-io-header\"\u003e\n        \u003cspan class=\"ue-io-title\" id=\"ue-input-label\"\u003eInput\u003c/span\u003e\n        \u003cspan class=\"ue-char-count\" id=\"ue-input-count\"\u003e0 chars\u003c/span\u003e\n      \u003c/div\u003e\n      \u003ctextarea id=\"ue-input\" placeholder=\"Paste your text or URL here…\" oninput=\"ueOnInput()\" spellcheck=\"false\"\u003e\u003c/textarea\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"ue-io-block\"\u003e\n      \u003cdiv class=\"ue-io-header\"\u003e\n        \u003cspan class=\"ue-io-title\" id=\"ue-output-label\"\u003eEncoded Output\u003c/span\u003e\n        \u003cspan class=\"ue-char-count\" id=\"ue-output-count\"\u003e0 chars\u003c/span\u003e\n      \u003c/div\u003e\n      \u003ctextarea id=\"ue-output\" class=\"ue-output\" readonly placeholder=\"Result will appear here…\" spellcheck=\"false\"\u003e\u003c/textarea\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003c!-- Actions --\u003e\n  \u003cdiv class=\"ue-action-bar\"\u003e\n    \u003cbutton class=\"ue-btn ue-btn-primary\" onclick=\"ueProcess()\"\u003e\n      \u003csvg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\u003e\u003cpolyline points=\"5 12 12 5 19 12\"/\u003e\u003cpolyline points=\"5 19 12 12 19 19\"/\u003e\u003c/svg\u003e\n      \u003cspan id=\"ue-process-label\"\u003eEncode\u003c/span\u003e\n    \u003c/button\u003e\n    \u003cbutton class=\"ue-btn ue-btn-copy\" onclick=\"ueCopyResult()\"\u003e\n      \u003csvg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\"/\u003e\u003cpath d=\"M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1\"/\u003e\u003c/svg\u003e\n      Copy Result\n    \u003c/button\u003e\n    \u003cbutton class=\"ue-btn ue-btn-secondary\" onclick=\"ueClear()\"\u003eClear\u003c/button\u003e\n    \u003cbutton class=\"ue-btn ue-btn-secondary\" onclick=\"ueSwap()\"\u003e\u0026#8645; Swap\u003c/button\u003e\n    \u003cspan class=\"ue-auto-badge\" id=\"ue-auto-badge\"\u003eAuto-detect ON\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ue-info\" id=\"ue-scope-info\"\u003e\n    \u003cstrong\u003eencodeURIComponent\u003c/strong\u003e — Encodes all characters except: \u003ccode\u003eA–Z a–z 0–9 - _ . ! ~ * ' ( )\u003c/code\u003e. Best for encoding individual query parameters or path segments.\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Breakdown table --\u003e\n\u003cdiv class=\"ue-card ue-breakdown\" id=\"ue-breakdown-card\" style=\"display:none;\"\u003e\n  \u003cdiv class=\"ue-breakdown-title\"\u003eEncoded Character Breakdown\u003c/div\u003e\n  \u003cdiv class=\"ue-table-wrap\"\u003e\n    \u003ctable class=\"ue-table\"\u003e\n      \u003cthead\u003e\n        \u003ctr\u003e\n          \u003cth\u003eOriginal\u003c/th\u003e\n          \u003cth\u003eEncoded\u003c/th\u003e\n          \u003cth\u003eUnicode\u003c/th\u003e\n          \u003cth\u003eNote\u003c/th\u003e\n        \u003c/tr\u003e\n      \u003c/thead\u003e\n      \u003ctbody id=\"ue-breakdown-body\"\u003e\u003c/tbody\u003e\n    \u003c/table\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"ue-toast\" id=\"ue-toast\"\u003e\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var mode = 'encode';\n  var scope = 'component';\n\n  var charNotes = {\n    ' ': 'Space',\n    '!': 'Exclamation mark',\n    '\"': 'Quotation mark',\n    '#': 'Hash / fragment',\n    '$': 'Dollar sign',\n    '%': 'Percent sign',\n    '\u0026': 'Ampersand',\n    \"'\": 'Apostrophe',\n    '(': 'Left paren',\n    ')': 'Right paren',\n    '*': 'Asterisk',\n    '+': 'Plus sign',\n    ',': 'Comma',\n    '/': 'Forward slash',\n    ':': 'Colon',\n    ';': 'Semicolon',\n    '=': 'Equals sign',\n    '?': 'Question mark',\n    '@': 'At sign',\n    '[': 'Left bracket',\n    ']': 'Right bracket',\n  };\n\n  window.ueSetMode = function(m) {\n    mode = m;\n    document.getElementById('ue-mode-encode').classList.toggle('active', m === 'encode');\n    document.getElementById('ue-mode-decode').classList.toggle('active', m === 'decode');\n    document.getElementById('ue-scope-group').style.display = m === 'encode' ? '' : 'none';\n    document.getElementById('ue-scope-info').style.display = m === 'encode' ? '' : 'none';\n    document.getElementById('ue-process-label').textContent = m === 'encode' ? 'Encode' : 'Decode';\n    document.getElementById('ue-input-label').textContent = m === 'encode' ? 'Plain Text / URL' : 'Encoded URL';\n    document.getElementById('ue-output-label').textContent = m === 'encode' ? 'Encoded Output' : 'Decoded Output';\n    var out = document.getElementById('ue-output');\n    out.className = m === 'encode' ? 'ue-output' : 'ue-output-decode';\n    ueOnInput();\n  };\n\n  window.ueSetScope = function(s) {\n    scope = s;\n    document.getElementById('ue-scope-component').classList.toggle('active', s === 'component');\n    document.getElementById('ue-scope-full').classList.toggle('active', s === 'full');\n    var info = document.getElementById('ue-scope-info');\n    if (s === 'component') {\n      info.innerHTML = '\u003cstrong\u003eencodeURIComponent\u003c/strong\u003e — Encodes all characters except: \u003ccode\u003eA–Z a–z 0–9 - _ . ! ~ * \\' ( )\u003c/code\u003e. Best for encoding individual query parameters or path segments.';\n    } else {\n      info.innerHTML = '\u003cstrong\u003eencodeURI\u003c/strong\u003e — Encodes all characters except those that are legal in a URI: \u003ccode\u003eA–Z a–z 0–9 ; , / ? : @ \u0026 = + $ - _ . ! ~ * \\' ( ) #\u003c/code\u003e. Use for encoding a complete URL while preserving its structure.';\n    }\n    ueOnInput();\n  };\n\n  function ueEncode(text) {\n    if (scope === 'component') return encodeURIComponent(text);\n    return encodeURI(text);\n  }\n\n  function ueDecode(text) {\n    try {\n      return decodeURIComponent(text);\n    } catch(e) {\n      try { return decodeURI(text); } catch(e2) { return text; }\n    }\n  }\n\n  function ueAutoDetect(text) {\n    return /%[0-9A-Fa-f]{2}/.test(text) ? 'decode' : 'encode';\n  }\n\n  window.ueOnInput = function() {\n    var input = document.getElementById('ue-input').value;\n    document.getElementById('ue-input-count').textContent = input.length + ' chars';\n    // Auto-detect\n    if (input.length \u003e 0) {\n      var detected = ueAutoDetect(input);\n      var badge = document.getElementById('ue-auto-badge');\n      badge.textContent = 'Auto-detected: ' + (detected === 'decode' ? 'Decode' : 'Encode');\n    } else {\n      document.getElementById('ue-auto-badge').textContent = 'Auto-detect ON';\n    }\n    ueProcess();\n  };\n\n  window.ueProcess = function() {\n    var input = document.getElementById('ue-input').value;\n    if (!input) {\n      document.getElementById('ue-output').value = '';\n      document.getElementById('ue-output-count').textContent = '0 chars';\n      document.getElementById('ue-breakdown-card').style.display = 'none';\n      return;\n    }\n    var result;\n    var effectiveMode = mode;\n    // Auto-detect override\n    if (mode === 'encode' \u0026\u0026 ueAutoDetect(input) === 'decode') {\n      // Still respect user-chosen mode\n    }\n    try {\n      result = effectiveMode === 'encode' ? ueEncode(input) : ueDecode(input);\n    } catch(e) {\n      result = 'Error: ' + e.message;\n    }\n    document.getElementById('ue-output').value = result;\n    document.getElementById('ue-output-count').textContent = result.length + ' chars';\n    if (effectiveMode === 'encode') ueRenderBreakdown(input, result);\n    else document.getElementById('ue-breakdown-card').style.display = 'none';\n  };\n\n  function ueRenderBreakdown(original, encoded) {\n    var rows = [];\n    var seen = {};\n    for (var i = 0; i \u003c original.length; i++) {\n      var ch = original[i];\n      var encCh;\n      try {\n        encCh = scope === 'component' ? encodeURIComponent(ch) : encodeURI(ch);\n      } catch(e) { encCh = ch; }\n      if (encCh !== ch \u0026\u0026 !seen[ch]) {\n        seen[ch] = true;\n        var codePoint = ch.codePointAt ? ch.codePointAt(0) : ch.charCodeAt(0);\n        var note = charNotes[ch] || (codePoint \u003e 127 ? 'Non-ASCII character' : 'Special character');\n        rows.push({ orig: ch, enc: encCh, uni: 'U+' + codePoint.toString(16).toUpperCase().padStart(4,'0'), note: note });\n      }\n    }\n    var tbody = document.getElementById('ue-breakdown-body');\n    var card = document.getElementById('ue-breakdown-card');\n    if (rows.length === 0) { card.style.display = 'none'; return; }\n    card.style.display = '';\n    tbody.innerHTML = rows.map(function(r) {\n      var disp = r.orig === ' ' ? '\u0026nbsp;(space)' : r.orig.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;');\n      return '\u003ctr\u003e\u003ctd class=\"ue-char-orig\"\u003e' + disp + '\u003c/td\u003e\u003ctd class=\"ue-char-enc\"\u003e' + r.enc + '\u003c/td\u003e\u003ctd class=\"ue-char-enc\"\u003e' + r.uni + '\u003c/td\u003e\u003ctd class=\"ue-char-note\"\u003e' + r.note + '\u003c/td\u003e\u003c/tr\u003e';\n    }).join('');\n  }\n\n  window.ueCopyResult = function() {\n    var val = document.getElementById('ue-output').value;\n    if (!val) return;\n    if (navigator.clipboard) {\n      navigator.clipboard.writeText(val).then(function() { ueShowToast('Copied to clipboard!'); });\n    } else {\n      var ta = document.getElementById('ue-output');\n      ta.select();\n      document.execCommand('copy');\n      ueShowToast('Copied to clipboard!');\n    }\n  };\n\n  window.ueClear = function() {\n    document.getElementById('ue-input').value = '';\n    document.getElementById('ue-output').value = '';\n    document.getElementById('ue-input-count').textContent = '0 chars';\n    document.getElementById('ue-output-count').textContent = '0 chars';\n    document.getElementById('ue-breakdown-card').style.display = 'none';\n    document.getElementById('ue-auto-badge').textContent = 'Auto-detect ON';\n  };\n\n  window.ueSwap = function() {\n    var inp = document.getElementById('ue-input').value;\n    var out = document.getElementById('ue-output').value;\n    document.getElementById('ue-input').value = out;\n    ueOnInput();\n  };\n\n  function ueShowToast(msg) {\n    var t = document.getElementById('ue-toast');\n    t.textContent = msg;\n    t.classList.add('show');\n    setTimeout(function() { t.classList.remove('show'); }, 2000);\n  }\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-it-works\"\u003eHow It Works\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eURL encoding\u003c/strong\u003e (percent-encoding) converts characters that are not safe for use in URLs into a \u003ccode\u003e%XX\u003c/code\u003e format, where \u003ccode\u003eXX\u003c/code\u003e is the hexadecimal value of the character\u0026rsquo;s UTF-8 byte(s).\u003c/p\u003e","title":"URL Encoder / Decoder"},{"content":" Encode / Decode URL Parser Query Builder Bulk Mode Mode: encodeURIComponent encodeURI Real-time encoding Input 0 chars \u0026#x1F512; Encode \u0026#x1F513; Decode \u0026#x21C5; Swap \u0026#x2715; Clear Output Copied! 0 chars Copy encodeURIComponent encodes all special chars including :/?#[]@!$\u0026amp;'()*+,;= — use for encoding individual query parameter values.\nencodeURI preserves URL structure chars — use for encoding a full URL while keeping it valid. Parse a URL into its components Clear Enter a URL above to see its components Query String Builder Base URL (optional): \u0026#x2715; \u0026#x2715; + Add Parameter Clear All Generated URL / Query String — Copy Result Copied! Bulk Encode / Decode Enter one URL or string per line. All lines will be processed together.\nMode: encodeURIComponent encodeURI Input (one per line) Encode All Decode All \u0026#x21C5; Swap Clear Output Copied! 0 lines Copy What Is URL Encoding? URL encoding (also called percent-encoding) converts characters that are not allowed in URLs into a safe format. Each unsafe character is replaced with a % followed by two hexadecimal digits representing the character\u0026rsquo;s UTF-8 byte value.\nFor example:\nSpace becomes %20 \u0026amp; becomes %26 = becomes %3D This is essential when passing data through query strings, form submissions, or API requests.\nencodeURIComponent vs encodeURI Function Preserves Best For encodeURIComponent Letters, digits, -_.!~*'() Encoding individual query parameter values encodeURI URL structure chars (:/?#[]@!$\u0026amp;'()*+,;=) Encoding a full URL while keeping it navigable Common Use Cases API requests — Encode query parameter values before appending to a URL Form data — Encode user input before sending via GET requests Sharing URLs — Ensure URLs with special characters work across all systems Debugging — Decode percent-encoded URLs to make them human-readable Related Tools Base64 Encoder \u0026amp; Decoder — Encode and decode Base64 strings, useful for binary data in URLs and APIs JSON Formatter \u0026amp; Validator — Prettify and validate JSON, commonly found in API query parameters Regex Tester — Test regular expressions for parsing and validating URL patterns ","permalink":"https://productivity-works.com/tools/url-encoder/","summary":"\u003cdiv id=\"url-app\"\u003e\n\u003cstyle\u003e\n  #url-app *,#url-app *::before,#url-app *::after{box-sizing:border-box;margin:0;padding:0}\n  #url-app{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:#1e293b;background:#f8fafc;border-radius:12px;padding:24px;max-width:900px;margin:0 auto}\n  #url-app h2{font-size:1.1rem;font-weight:700;color:#1e293b;margin-bottom:12px}\n  #url-app .tabs{display:flex;gap:4px;background:#e2e8f0;padding:4px;border-radius:8px;margin-bottom:20px;flex-wrap:wrap}\n  #url-app .tab-btn{flex:1;min-width:120px;padding:8px 12px;border:none;border-radius:6px;font-size:0.85rem;font-weight:600;cursor:pointer;background:transparent;color:#64748b;transition:all .2s}\n  #url-app .tab-btn.active{background:#fff;color:#2563eb;box-shadow:0 1px 3px rgba(0,0,0,.1)}\n  #url-app .tab-btn:hover:not(.active){color:#2563eb}\n  #url-app .panel{display:none}\n  #url-app .panel.active{display:block}\n\u003cp\u003e/* Encode/Decode Panel */\n#url-app .mode-row{display:flex;align-items:center;gap:10px;margin-bottom:14px;flex-wrap:wrap}\n#url-app .mode-label{font-size:0.82rem;font-weight:600;color:#64748b;white-space:nowrap}\n#url-app .toggle-group{display:flex;border:1.5px solid #2563eb;border-radius:6px;overflow:hidden}\n#url-app .toggle-btn{padding:6px 14px;border:none;font-size:0.82rem;font-weight:600;cursor:pointer;background:#fff;color:#2563eb;transition:all .15s}\n#url-app .toggle-btn.active{background:#2563eb;color:#fff}\n#url-app .rt-label{font-size:0.78rem;color:#94a3b8;margin-left:auto}\u003c/p\u003e\n\u003cp\u003e#url-app textarea{width:100%;border:1.5px solid #e2e8f0;border-radius:8px;padding:12px;font-size:0.88rem;font-family:monospace;resize:vertical;min-height:110px;outline:none;transition:border-color .2s;background:#fff;color:#1e293b}\n#url-app textarea:focus{border-color:#2563eb;box-shadow:0 0 0 3px rgba(37,99,235,.1)}\n#url-app .textarea-label{font-size:0.8rem;font-weight:600;color:#64748b;margin-bottom:6px;display:block}\u003c/p\u003e\n\u003cp\u003e#url-app .btn-row{display:flex;gap:8px;margin:12px 0;flex-wrap:wrap}\n#url-app .btn{padding:9px 18px;border:none;border-radius:7px;font-size:0.85rem;font-weight:700;cursor:pointer;transition:all .18s;display:flex;align-items:center;gap:6px}\n#url-app .btn-primary{background:#2563eb;color:#fff}\n#url-app .btn-primary:hover{background:#1d4ed8}\n#url-app .btn-secondary{background:#f1f5f9;color:#475569;border:1.5px solid #e2e8f0}\n#url-app .btn-secondary:hover{background:#e2e8f0}\n#url-app .btn-danger{background:#fff;color:#ef4444;border:1.5px solid #fca5a5}\n#url-app .btn-danger:hover{background:#fef2f2}\n#url-app .btn-swap{background:#eff6ff;color:#2563eb;border:1.5px solid #bfdbfe}\n#url-app .btn-swap:hover{background:#dbeafe}\n#url-app .btn-copy{background:#f0fdf4;color:#16a34a;border:1.5px solid #bbf7d0}\n#url-app .btn-copy:hover{background:#dcfce7}\u003c/p\u003e","title":"URL Encoder \u0026 Decoder - Free Online URL Encoding Tool"},{"content":" Single Bulk Input Text \u0026lt;div class=\u0026quot;sg-options-grid\u0026quot; style=\u0026quot;margin-bottom:16px;\u0026quot;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;label for=\u0026quot;sg-separator\u0026quot;\u0026gt;Separator\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;sg-separator\u0026quot; onchange=\u0026quot;sgConvert()\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;-\u0026quot;\u0026gt;Hyphen ( - )\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;_\u0026quot;\u0026gt;Underscore ( _ )\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;.\u0026quot;\u0026gt;Dot ( . )\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div\u0026gt; \u0026lt;label for=\u0026quot;sg-maxlen\u0026quot;\u0026gt;Max Length\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;sg-maxlen\u0026quot; value=\u0026quot;80\u0026quot; min=\u0026quot;10\u0026quot; max=\u0026quot;500\u0026quot; oninput=\u0026quot;sgConvert()\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div style=\u0026quot;display:flex;flex-direction:column;justify-content:flex-end;\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;sg-checkbox-row\u0026quot; style=\u0026quot;margin-bottom:6px;\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;sg-stopwords\u0026quot; onchange=\u0026quot;sgConvert()\u0026quot; /\u0026gt; \u0026lt;label for=\u0026quot;sg-stopwords\u0026quot;\u0026gt;Remove stop words\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sg-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;sg-translit\u0026quot; checked onchange=\u0026quot;sgConvert()\u0026quot; /\u0026gt; \u0026lt;label for=\u0026quot;sg-translit\u0026quot;\u0026gt;Transliterate accents\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div style=\u0026quot;margin-bottom:10px;\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;sg-section-label\u0026quot;\u0026gt;Preview Base URL\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sg-base-url-row\u0026quot;\u0026gt; \u0026lt;span class=\u0026quot;sg-base-url-prefix\u0026quot;\u0026gt;https://\u0026lt;/span\u0026gt; \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;sg-baseurl\u0026quot; class=\u0026quot;sg-base-url-input\u0026quot; value=\u0026quot;example.com/\u0026quot; placeholder=\u0026quot;example.com/\u0026quot; oninput=\u0026quot;sgConvert()\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div style=\u0026quot;margin-bottom:8px;\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;sg-section-label\u0026quot;\u0026gt;Generated Slug\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sg-result-box empty\u0026quot; id=\u0026quot;sg-result\u0026quot;\u0026gt;Your slug will appear here…\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sg-preview-box empty\u0026quot; id=\u0026quot;sg-preview\u0026quot;\u0026gt;Full URL preview will appear here…\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sg-row\u0026quot; style=\u0026quot;margin-top:12px;\u0026quot;\u0026gt; \u0026lt;button class=\u0026quot;sg-copy-btn\u0026quot; id=\u0026quot;sg-copy-btn\u0026quot; onclick=\u0026quot;sgCopySlug()\u0026quot;\u0026gt; \u0026lt;svg width=\u0026quot;14\u0026quot; height=\u0026quot;14\u0026quot; viewBox=\u0026quot;0 0 24 24\u0026quot; fill=\u0026quot;none\u0026quot; stroke=\u0026quot;currentColor\u0026quot; stroke-width=\u0026quot;2.5\u0026quot; stroke-linecap=\u0026quot;round\u0026quot; stroke-linejoin=\u0026quot;round\u0026quot;\u0026gt;\u0026lt;rect x=\u0026quot;9\u0026quot; y=\u0026quot;9\u0026quot; width=\u0026quot;13\u0026quot; height=\u0026quot;13\u0026quot; rx=\u0026quot;2\u0026quot;/\u0026gt;\u0026lt;path d=\u0026quot;M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt; Copy Slug \u0026lt;/button\u0026gt; \u0026lt;button class=\u0026quot;sg-copy-btn\u0026quot; id=\u0026quot;sg-copy-url-btn\u0026quot; onclick=\u0026quot;sgCopyUrl()\u0026quot; style=\u0026quot;background:#0284c7;\u0026quot;\u0026gt; \u0026lt;svg width=\u0026quot;14\u0026quot; height=\u0026quot;14\u0026quot; viewBox=\u0026quot;0 0 24 24\u0026quot; fill=\u0026quot;none\u0026quot; stroke=\u0026quot;currentColor\u0026quot; stroke-width=\u0026quot;2.5\u0026quot; stroke-linecap=\u0026quot;round\u0026quot; stroke-linejoin=\u0026quot;round\u0026quot;\u0026gt;\u0026lt;path d=\u0026quot;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\u0026quot;/\u0026gt;\u0026lt;path d=\u0026quot;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt; Copy Full URL \u0026lt;/button\u0026gt; \u0026lt;span class=\u0026quot;sg-char-badge\u0026quot; id=\u0026quot;sg-char-count\u0026quot;\u0026gt;0 chars\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; Paste multiple lines (one title per line) \u0026lt;div class=\u0026quot;sg-options-grid\u0026quot; style=\u0026quot;margin-bottom:16px;\u0026quot;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;label for=\u0026quot;sg-bulk-separator\u0026quot;\u0026gt;Separator\u0026lt;/label\u0026gt; \u0026lt;select id=\u0026quot;sg-bulk-separator\u0026quot; onchange=\u0026quot;sgBulkConvert()\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;-\u0026quot;\u0026gt;Hyphen ( - )\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;_\u0026quot;\u0026gt;Underscore ( _ )\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;.\u0026quot;\u0026gt;Dot ( . )\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div\u0026gt; \u0026lt;label for=\u0026quot;sg-bulk-maxlen\u0026quot;\u0026gt;Max Length\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;sg-bulk-maxlen\u0026quot; value=\u0026quot;80\u0026quot; min=\u0026quot;10\u0026quot; max=\u0026quot;500\u0026quot; oninput=\u0026quot;sgBulkConvert()\u0026quot; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div style=\u0026quot;display:flex;flex-direction:column;justify-content:flex-end;\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;sg-checkbox-row\u0026quot; style=\u0026quot;margin-bottom:6px;\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;sg-bulk-stopwords\u0026quot; onchange=\u0026quot;sgBulkConvert()\u0026quot; /\u0026gt; \u0026lt;label for=\u0026quot;sg-bulk-stopwords\u0026quot;\u0026gt;Remove stop words\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sg-checkbox-row\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;sg-bulk-translit\u0026quot; checked onchange=\u0026quot;sgBulkConvert()\u0026quot; /\u0026gt; \u0026lt;label for=\u0026quot;sg-bulk-translit\u0026quot;\u0026gt;Transliterate accents\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;sg-bulk-results\u0026quot; id=\u0026quot;sg-bulk-results\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; Encode URLs → URL Encoder Generate meta tags → Meta Tag Generator ","permalink":"https://productivity-works.com/tools/slug-generator/","summary":"\u003cdiv id=\"sg-app\"\u003e\n\u003cstyle\u003e\n#sg-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 700px;\n  margin: 0 auto;\n  color: #1e293b;\n  box-sizing: border-box;\n}\n#sg-app *, #sg-app *::before, #sg-app *::after {\n  box-sizing: border-box;\n}\n#sg-app h2 {\n  font-size: 1.1rem;\n  font-weight: 700;\n  margin: 0 0 14px 0;\n  color: #0f172a;\n}\n#sg-app .sg-card {\n  background: #ffffff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 22px 20px;\n  margin-bottom: 18px;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.05);\n}\n#sg-app label {\n  display: block;\n  font-size: 13px;\n  font-weight: 600;\n  color: #475569;\n  margin-bottom: 6px;\n}\n#sg-app input[type=\"text\"],\n#sg-app textarea,\n#sg-app select,\n#sg-app input[type=\"number\"] {\n  width: 100%;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  padding: 10px 12px;\n  font-size: 15px;\n  color: #1e293b;\n  background: #f8fafc;\n  outline: none;\n  transition: border-color 0.15s;\n  font-family: inherit;\n}\n#sg-app input[type=\"text\"]:focus,\n#sg-app textarea:focus,\n#sg-app select:focus,\n#sg-app input[type=\"number\"]:focus {\n  border-color: #6366f1;\n  background: #fff;\n}\n#sg-app textarea {\n  resize: vertical;\n  min-height: 90px;\n}\n#sg-app .sg-options-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr 1fr;\n  gap: 14px;\n  margin-bottom: 0;\n}\n@media (max-width: 560px) {\n  #sg-app .sg-options-grid {\n    grid-template-columns: 1fr 1fr;\n  }\n}\n@media (max-width: 380px) {\n  #sg-app .sg-options-grid {\n    grid-template-columns: 1fr;\n  }\n}\n#sg-app .sg-checkbox-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin-top: 4px;\n}\n#sg-app .sg-checkbox-row input[type=\"checkbox\"] {\n  width: 16px;\n  height: 16px;\n  accent-color: #6366f1;\n  cursor: pointer;\n  flex-shrink: 0;\n}\n#sg-app .sg-checkbox-row label {\n  margin: 0;\n  font-size: 13px;\n  font-weight: 600;\n  color: #475569;\n  cursor: pointer;\n}\n#sg-app .sg-result-box {\n  background: #f1f5f9;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  padding: 11px 14px;\n  font-size: 15px;\n  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;\n  color: #1e293b;\n  word-break: break-all;\n  min-height: 42px;\n  line-height: 1.5;\n}\n#sg-app .sg-result-box.empty {\n  color: #94a3b8;\n  font-family: inherit;\n  font-size: 14px;\n}\n#sg-app .sg-preview-box {\n  background: #eff6ff;\n  border: 1.5px solid #bfdbfe;\n  border-radius: 8px;\n  padding: 10px 14px;\n  font-size: 13px;\n  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;\n  color: #1d4ed8;\n  word-break: break-all;\n  margin-top: 10px;\n  min-height: 38px;\n  line-height: 1.5;\n}\n#sg-app .sg-preview-box.empty {\n  color: #94a3b8;\n  font-family: inherit;\n  font-size: 13px;\n}\n#sg-app .sg-row {\n  display: flex;\n  gap: 10px;\n  align-items: center;\n  flex-wrap: wrap;\n}\n#sg-app .sg-copy-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  padding: 9px 18px;\n  background: #6366f1;\n  color: #fff;\n  border: none;\n  border-radius: 7px;\n  font-size: 13px;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.15s;\n  white-space: nowrap;\n}\n#sg-app .sg-copy-btn:hover {\n  background: #4f46e5;\n}\n#sg-app .sg-copy-btn.copied {\n  background: #16a34a;\n}\n#sg-app .sg-char-badge {\n  font-size: 12px;\n  color: #64748b;\n  background: #f1f5f9;\n  border: 1px solid #e2e8f0;\n  border-radius: 20px;\n  padding: 3px 10px;\n}\n#sg-app .sg-tab-row {\n  display: flex;\n  gap: 0;\n  margin-bottom: 16px;\n  border-bottom: 2px solid #e2e8f0;\n}\n#sg-app .sg-tab {\n  padding: 8px 20px;\n  font-size: 13px;\n  font-weight: 700;\n  color: #64748b;\n  cursor: pointer;\n  border: none;\n  background: none;\n  border-bottom: 2.5px solid transparent;\n  margin-bottom: -2px;\n  transition: color 0.12s, border-color 0.12s;\n}\n#sg-app .sg-tab.active {\n  color: #6366f1;\n  border-bottom-color: #6366f1;\n}\n#sg-app .sg-bulk-results {\n  margin-top: 12px;\n}\n#sg-app .sg-bulk-row {\n  display: flex;\n  align-items: flex-start;\n  gap: 8px;\n  padding: 7px 0;\n  border-bottom: 1px solid #f1f5f9;\n}\n#sg-app .sg-bulk-row:last-child {\n  border-bottom: none;\n}\n#sg-app .sg-bulk-input {\n  font-size: 13px;\n  color: #64748b;\n  flex: 1;\n  word-break: break-all;\n  padding-top: 1px;\n}\n#sg-app .sg-bulk-arrow {\n  font-size: 13px;\n  color: #94a3b8;\n  flex-shrink: 0;\n  padding-top: 1px;\n}\n#sg-app .sg-bulk-slug {\n  font-size: 13px;\n  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;\n  color: #4f46e5;\n  flex: 1.5;\n  word-break: break-all;\n  padding-top: 1px;\n}\n#sg-app .sg-bulk-copy {\n  background: none;\n  border: 1px solid #c7d2fe;\n  border-radius: 5px;\n  color: #6366f1;\n  font-size: 11px;\n  font-weight: 700;\n  padding: 2px 9px;\n  cursor: pointer;\n  flex-shrink: 0;\n  transition: background 0.12s;\n}\n#sg-app .sg-bulk-copy:hover {\n  background: #eef2ff;\n}\n#sg-app .sg-bulk-copy-all {\n  display: inline-flex;\n  align-items: center;\n  gap: 5px;\n  padding: 7px 16px;\n  background: #6366f1;\n  color: #fff;\n  border: none;\n  border-radius: 7px;\n  font-size: 13px;\n  font-weight: 700;\n  cursor: pointer;\n  margin-top: 12px;\n  transition: background 0.15s;\n}\n#sg-app .sg-bulk-copy-all:hover {\n  background: #4f46e5;\n}\n#sg-app .sg-section-label {\n  font-size: 12px;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  color: #94a3b8;\n  margin-bottom: 10px;\n}\n#sg-app .sg-base-url-row {\n  display: flex;\n  align-items: center;\n  gap: 0;\n  margin-top: 10px;\n}\n#sg-app .sg-base-url-prefix {\n  background: #e2e8f0;\n  border: 1.5px solid #cbd5e1;\n  border-right: none;\n  border-radius: 8px 0 0 8px;\n  padding: 10px 10px;\n  font-size: 13px;\n  color: #64748b;\n  white-space: nowrap;\n  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;\n}\n#sg-app .sg-base-url-input {\n  border-radius: 0 8px 8px 0 !important;\n}\n\u003c/style\u003e\n\u003cdiv class=\"sg-card\"\u003e\n  \u003cdiv class=\"sg-tab-row\"\u003e\n    \u003cbutton class=\"sg-tab active\" id=\"sg-tab-single\" onclick=\"sgSwitchTab('single')\"\u003eSingle\u003c/button\u003e\n    \u003cbutton class=\"sg-tab\" id=\"sg-tab-bulk\" onclick=\"sgSwitchTab('bulk')\"\u003eBulk\u003c/button\u003e\n  \u003c/div\u003e\n  \u003c!-- Single mode --\u003e\n  \u003cdiv id=\"sg-panel-single\"\u003e\n    \u003cdiv style=\"margin-bottom:14px;\"\u003e\n      \u003clabel for=\"sg-input\"\u003eInput Text\u003c/label\u003e\n      \u003cinput type=\"text\" id=\"sg-input\" placeholder=\"e.g. Hello World! My Blog Post Title\" oninput=\"sgConvert()\" /\u003e\n    \u003c/div\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;div class=\u0026quot;sg-options-grid\u0026quot; style=\u0026quot;margin-bottom:16px;\u0026quot;\u0026gt;\n  \u0026lt;div\u0026gt;\n    \u0026lt;label for=\u0026quot;sg-separator\u0026quot;\u0026gt;Separator\u0026lt;/label\u0026gt;\n    \u0026lt;select id=\u0026quot;sg-separator\u0026quot; onchange=\u0026quot;sgConvert()\u0026quot;\u0026gt;\n      \u0026lt;option value=\u0026quot;-\u0026quot;\u0026gt;Hyphen ( - )\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;_\u0026quot;\u0026gt;Underscore ( _ )\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;.\u0026quot;\u0026gt;Dot ( . )\u0026lt;/option\u0026gt;\n    \u0026lt;/select\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div\u0026gt;\n    \u0026lt;label for=\u0026quot;sg-maxlen\u0026quot;\u0026gt;Max Length\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;sg-maxlen\u0026quot; value=\u0026quot;80\u0026quot; min=\u0026quot;10\u0026quot; max=\u0026quot;500\u0026quot; oninput=\u0026quot;sgConvert()\u0026quot; /\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div style=\u0026quot;display:flex;flex-direction:column;justify-content:flex-end;\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;sg-checkbox-row\u0026quot; style=\u0026quot;margin-bottom:6px;\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;sg-stopwords\u0026quot; onchange=\u0026quot;sgConvert()\u0026quot; /\u0026gt;\n      \u0026lt;label for=\u0026quot;sg-stopwords\u0026quot;\u0026gt;Remove stop words\u0026lt;/label\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;sg-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;sg-translit\u0026quot; checked onchange=\u0026quot;sgConvert()\u0026quot; /\u0026gt;\n      \u0026lt;label for=\u0026quot;sg-translit\u0026quot;\u0026gt;Transliterate accents\u0026lt;/label\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div style=\u0026quot;margin-bottom:10px;\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;sg-section-label\u0026quot;\u0026gt;Preview Base URL\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;sg-base-url-row\u0026quot;\u0026gt;\n    \u0026lt;span class=\u0026quot;sg-base-url-prefix\u0026quot;\u0026gt;https://\u0026lt;/span\u0026gt;\n    \u0026lt;input type=\u0026quot;text\u0026quot; id=\u0026quot;sg-baseurl\u0026quot; class=\u0026quot;sg-base-url-input\u0026quot; value=\u0026quot;example.com/\u0026quot; placeholder=\u0026quot;example.com/\u0026quot; oninput=\u0026quot;sgConvert()\u0026quot; /\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div style=\u0026quot;margin-bottom:8px;\u0026quot;\u0026gt;\n  \u0026lt;div class=\u0026quot;sg-section-label\u0026quot;\u0026gt;Generated Slug\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;sg-result-box empty\u0026quot; id=\u0026quot;sg-result\u0026quot;\u0026gt;Your slug will appear here…\u0026lt;/div\u0026gt;\n  \u0026lt;div class=\u0026quot;sg-preview-box empty\u0026quot; id=\u0026quot;sg-preview\u0026quot;\u0026gt;Full URL preview will appear here…\u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;sg-row\u0026quot; style=\u0026quot;margin-top:12px;\u0026quot;\u0026gt;\n  \u0026lt;button class=\u0026quot;sg-copy-btn\u0026quot; id=\u0026quot;sg-copy-btn\u0026quot; onclick=\u0026quot;sgCopySlug()\u0026quot;\u0026gt;\n    \u0026lt;svg width=\u0026quot;14\u0026quot; height=\u0026quot;14\u0026quot; viewBox=\u0026quot;0 0 24 24\u0026quot; fill=\u0026quot;none\u0026quot; stroke=\u0026quot;currentColor\u0026quot; stroke-width=\u0026quot;2.5\u0026quot; stroke-linecap=\u0026quot;round\u0026quot; stroke-linejoin=\u0026quot;round\u0026quot;\u0026gt;\u0026lt;rect x=\u0026quot;9\u0026quot; y=\u0026quot;9\u0026quot; width=\u0026quot;13\u0026quot; height=\u0026quot;13\u0026quot; rx=\u0026quot;2\u0026quot;/\u0026gt;\u0026lt;path d=\u0026quot;M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt;\n    Copy Slug\n  \u0026lt;/button\u0026gt;\n  \u0026lt;button class=\u0026quot;sg-copy-btn\u0026quot; id=\u0026quot;sg-copy-url-btn\u0026quot; onclick=\u0026quot;sgCopyUrl()\u0026quot; style=\u0026quot;background:#0284c7;\u0026quot;\u0026gt;\n    \u0026lt;svg width=\u0026quot;14\u0026quot; height=\u0026quot;14\u0026quot; viewBox=\u0026quot;0 0 24 24\u0026quot; fill=\u0026quot;none\u0026quot; stroke=\u0026quot;currentColor\u0026quot; stroke-width=\u0026quot;2.5\u0026quot; stroke-linecap=\u0026quot;round\u0026quot; stroke-linejoin=\u0026quot;round\u0026quot;\u0026gt;\u0026lt;path d=\u0026quot;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\u0026quot;/\u0026gt;\u0026lt;path d=\u0026quot;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\u0026quot;/\u0026gt;\u0026lt;/svg\u0026gt;\n    Copy Full URL\n  \u0026lt;/button\u0026gt;\n  \u0026lt;span class=\u0026quot;sg-char-badge\u0026quot; id=\u0026quot;sg-char-count\u0026quot;\u0026gt;0 chars\u0026lt;/span\u0026gt;\n\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n  \u003c!-- Bulk mode --\u003e\n  \u003cdiv id=\"sg-panel-bulk\" style=\"display:none;\"\u003e\n    \u003cdiv style=\"margin-bottom:14px;\"\u003e\n      \u003clabel for=\"sg-bulk-input\"\u003ePaste multiple lines (one title per line)\u003c/label\u003e\n      \u003ctextarea id=\"sg-bulk-input\" placeholder=\"My First Post Title\u0026#10;Hello World Example\u0026#10;Another Great Article\" oninput=\"sgBulkConvert()\" rows=\"6\"\u003e\u003c/textarea\u003e\n    \u003c/div\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;div class=\u0026quot;sg-options-grid\u0026quot; style=\u0026quot;margin-bottom:16px;\u0026quot;\u0026gt;\n  \u0026lt;div\u0026gt;\n    \u0026lt;label for=\u0026quot;sg-bulk-separator\u0026quot;\u0026gt;Separator\u0026lt;/label\u0026gt;\n    \u0026lt;select id=\u0026quot;sg-bulk-separator\u0026quot; onchange=\u0026quot;sgBulkConvert()\u0026quot;\u0026gt;\n      \u0026lt;option value=\u0026quot;-\u0026quot;\u0026gt;Hyphen ( - )\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;_\u0026quot;\u0026gt;Underscore ( _ )\u0026lt;/option\u0026gt;\n      \u0026lt;option value=\u0026quot;.\u0026quot;\u0026gt;Dot ( . )\u0026lt;/option\u0026gt;\n    \u0026lt;/select\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div\u0026gt;\n    \u0026lt;label for=\u0026quot;sg-bulk-maxlen\u0026quot;\u0026gt;Max Length\u0026lt;/label\u0026gt;\n    \u0026lt;input type=\u0026quot;number\u0026quot; id=\u0026quot;sg-bulk-maxlen\u0026quot; value=\u0026quot;80\u0026quot; min=\u0026quot;10\u0026quot; max=\u0026quot;500\u0026quot; oninput=\u0026quot;sgBulkConvert()\u0026quot; /\u0026gt;\n  \u0026lt;/div\u0026gt;\n  \u0026lt;div style=\u0026quot;display:flex;flex-direction:column;justify-content:flex-end;\u0026quot;\u0026gt;\n    \u0026lt;div class=\u0026quot;sg-checkbox-row\u0026quot; style=\u0026quot;margin-bottom:6px;\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;sg-bulk-stopwords\u0026quot; onchange=\u0026quot;sgBulkConvert()\u0026quot; /\u0026gt;\n      \u0026lt;label for=\u0026quot;sg-bulk-stopwords\u0026quot;\u0026gt;Remove stop words\u0026lt;/label\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;div class=\u0026quot;sg-checkbox-row\u0026quot;\u0026gt;\n      \u0026lt;input type=\u0026quot;checkbox\u0026quot; id=\u0026quot;sg-bulk-translit\u0026quot; checked onchange=\u0026quot;sgBulkConvert()\u0026quot; /\u0026gt;\n      \u0026lt;label for=\u0026quot;sg-bulk-translit\u0026quot;\u0026gt;Transliterate accents\u0026lt;/label\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/div\u0026gt;\n\n\u0026lt;div class=\u0026quot;sg-bulk-results\u0026quot; id=\u0026quot;sg-bulk-results\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  // --- Stop words list ---\n  var STOP_WORDS = new Set([\n    'a','an','the','and','or','but','in','on','at','to','for','of','with',\n    'by','from','up','about','into','through','during','before','after',\n    'above','below','between','out','off','over','under','again','then',\n    'once','is','are','was','were','be','been','being','have','has','had',\n    'do','does','did','will','would','shall','should','may','might','must',\n    'can','could','not','no','nor','so','yet','both','either','neither',\n    'this','that','these','those','it','its','my','your','his','her',\n    'our','their','we','they','he','she','i','you','as','if','than'\n  ]);\n\n  // --- Transliteration map ---\n  var TRANSLIT = {\n    'à':'a','á':'a','â':'a','ã':'a','ä':'a','å':'a','æ':'ae',\n    'ç':'c','è':'e','é':'e','ê':'e','ë':'e',\n    'ì':'i','í':'i','î':'i','ï':'i',\n    'ð':'d','ñ':'n',\n    'ò':'o','ó':'o','ô':'o','õ':'o','ö':'o','ø':'o',\n    'ù':'u','ú':'u','û':'u','ü':'u',\n    'ý':'y','þ':'th','ß':'ss',\n    'À':'a','Á':'a','Â':'a','Ã':'a','Ä':'a','Å':'a','Æ':'ae',\n    'Ç':'c','È':'e','É':'e','Ê':'e','Ë':'e',\n    'Ì':'i','Í':'i','Î':'i','Ï':'i',\n    'Ð':'d','Ñ':'n',\n    'Ò':'o','Ó':'o','Ô':'o','Õ':'o','Ö':'o','Ø':'o',\n    'Ù':'u','Ú':'u','Û':'u','Ü':'u',\n    'Ý':'y','Þ':'th',\n    'ā':'a','ē':'e','ī':'i','ō':'o','ū':'u',\n    'Ā':'a','Ē':'e','Ī':'i','Ō':'o','Ū':'u',\n    'ă':'a','ĕ':'e','ĭ':'i','ŏ':'o','ŭ':'u',\n    'ć':'c','ĉ':'c','č':'c','ď':'d','ě':'e',\n    'ğ':'g','ĝ':'g','ġ':'g','ģ':'g',\n    'ĥ':'h','ħ':'h','ĵ':'j','ķ':'k','ĺ':'l','ļ':'l','ľ':'l','ł':'l',\n    'ń':'n','ņ':'n','ň':'n','ŕ':'r','ř':'r','ś':'s','ŝ':'s','ş':'s','š':'s',\n    'ţ':'t','ť':'t','ź':'z','ż':'z','ž':'z',\n    'œ':'oe','Œ':'oe','ĳ':'ij','Ĳ':'ij',\n    '\u0026':'and',\"'\":'','\"':''\n  };\n\n  function transliterate(str) {\n    return str.replace(/[^\\u0000-\\u007E]/g, function(ch) {\n      return TRANSLIT[ch] || '';\n    }).replace(/[\u0026'\"]/g, function(ch) {\n      return TRANSLIT[ch] !== undefined ? TRANSLIT[ch] : '';\n    });\n  }\n\n  function makeSlug(text, sep, maxLen, removeStop, doTranslit) {\n    if (!text || !text.trim()) return '';\n    var s = text;\n    // Replace \u0026 early\n    s = s.replace(/\u0026/g, ' and ');\n    // Transliterate\n    if (doTranslit) s = transliterate(s);\n    // Lowercase\n    s = s.toLowerCase();\n    // Remove apostrophes and quotes\n    s = s.replace(/[''\"\"\"]/g, '');\n    // Replace non-alphanumeric with sep\n    s = s.replace(/[^a-z0-9]+/g, sep);\n    // Trim sep from edges\n    var re = new RegExp('^[' + escRe(sep) + ']+|[' + escRe(sep) + ']+$', 'g');\n    s = s.replace(re, '');\n    // Remove stop words\n    if (removeStop \u0026\u0026 sep !== '') {\n      var parts = s.split(sep).filter(function(w) { return w.length \u003e 0; });\n      var filtered = parts.filter(function(w, i) {\n        // Keep at least one word\n        return !STOP_WORDS.has(w) || (i === 0 \u0026\u0026 parts.length === 1);\n      });\n      // If filtering removed everything, revert\n      if (filtered.length === 0) filtered = parts;\n      s = filtered.join(sep);\n    }\n    // Max length: truncate at sep boundary\n    if (maxLen \u0026\u0026 s.length \u003e maxLen) {\n      s = s.substring(0, maxLen);\n      var lastSep = s.lastIndexOf(sep);\n      if (lastSep \u003e 0) s = s.substring(0, lastSep);\n    }\n    // Final trim of sep\n    s = s.replace(re, '');\n    return s;\n  }\n\n  function escRe(ch) {\n    return ch.replace(/[-\\.]/g, '\\\\$\u0026');\n  }\n\n  function getBaseUrl() {\n    var raw = (document.getElementById('sg-baseurl').value || 'example.com/').trim();\n    if (!raw.startsWith('http://') \u0026\u0026 !raw.startsWith('https://')) raw = 'https://' + raw;\n    if (!raw.endsWith('/')) raw += '/';\n    return raw;\n  }\n\n  window.sgConvert = function() {\n    var text = document.getElementById('sg-input').value;\n    var sep = document.getElementById('sg-separator').value;\n    var maxLen = parseInt(document.getElementById('sg-maxlen').value) || 80;\n    var removeStop = document.getElementById('sg-stopwords').checked;\n    var doTranslit = document.getElementById('sg-translit').checked;\n\n    var slug = makeSlug(text, sep, maxLen, removeStop, doTranslit);\n\n    var resultEl = document.getElementById('sg-result');\n    var previewEl = document.getElementById('sg-preview');\n    var countEl = document.getElementById('sg-char-count');\n\n    if (slug) {\n      resultEl.textContent = slug;\n      resultEl.classList.remove('empty');\n      var fullUrl = getBaseUrl() + slug;\n      previewEl.textContent = fullUrl;\n      previewEl.classList.remove('empty');\n      countEl.textContent = slug.length + ' chars';\n    } else {\n      resultEl.textContent = 'Your slug will appear here\\u2026';\n      resultEl.classList.add('empty');\n      previewEl.textContent = 'Full URL preview will appear here\\u2026';\n      previewEl.classList.add('empty');\n      countEl.textContent = '0 chars';\n    }\n  };\n\n  window.sgCopySlug = function() {\n    var slug = document.getElementById('sg-result').textContent;\n    if (!slug || document.getElementById('sg-result').classList.contains('empty')) return;\n    copyText(slug, 'sg-copy-btn', 'Copy Slug', 'Copied!');\n  };\n\n  window.sgCopyUrl = function() {\n    var url = document.getElementById('sg-preview').textContent;\n    if (!url || document.getElementById('sg-preview').classList.contains('empty')) return;\n    copyText(url, 'sg-copy-url-btn', 'Copy Full URL', 'Copied!');\n  };\n\n  function copyText(text, btnId, origLabel, doneLabel) {\n    var btn = document.getElementById(btnId);\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(text).then(function() { flashBtn(btn, doneLabel, origLabel); });\n    } else {\n      var ta = document.createElement('textarea');\n      ta.value = text;\n      ta.style.position = 'fixed';\n      ta.style.opacity = '0';\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      flashBtn(btn, doneLabel, origLabel);\n    }\n  }\n\n  function flashBtn(btn, doneLabel, origLabel) {\n    var orig = btn.innerHTML;\n    btn.classList.add('copied');\n    btn.innerHTML = '\u003csvg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"/\u003e\u003c/svg\u003e ' + doneLabel;\n    setTimeout(function() {\n      btn.classList.remove('copied');\n      btn.innerHTML = orig;\n    }, 1600);\n  }\n\n  window.sgSwitchTab = function(tab) {\n    document.getElementById('sg-panel-single').style.display = tab === 'single' ? '' : 'none';\n    document.getElementById('sg-panel-bulk').style.display = tab === 'bulk' ? '' : 'none';\n    document.getElementById('sg-tab-single').classList.toggle('active', tab === 'single');\n    document.getElementById('sg-tab-bulk').classList.toggle('active', tab === 'bulk');\n  };\n\n  window.sgBulkConvert = function() {\n    var raw = document.getElementById('sg-bulk-input').value;\n    var sep = document.getElementById('sg-bulk-separator').value;\n    var maxLen = parseInt(document.getElementById('sg-bulk-maxlen').value) || 80;\n    var removeStop = document.getElementById('sg-bulk-stopwords').checked;\n    var doTranslit = document.getElementById('sg-bulk-translit').checked;\n\n    var lines = raw.split('\\n');\n    var container = document.getElementById('sg-bulk-results');\n\n    if (!raw.trim()) {\n      container.innerHTML = '';\n      return;\n    }\n\n    var html = '';\n    var results = [];\n    lines.forEach(function(line, idx) {\n      if (!line.trim()) return;\n      var slug = makeSlug(line, sep, maxLen, removeStop, doTranslit);\n      results.push(slug);\n      html += '\u003cdiv class=\"sg-bulk-row\"\u003e' +\n        '\u003cspan class=\"sg-bulk-input\"\u003e' + esc(line) + '\u003c/span\u003e' +\n        '\u003cspan class=\"sg-bulk-arrow\"\u003e\u0026rarr;\u003c/span\u003e' +\n        '\u003cspan class=\"sg-bulk-slug\" id=\"sg-br-' + idx + '\"\u003e' + esc(slug) + '\u003c/span\u003e' +\n        '\u003cbutton class=\"sg-bulk-copy\" onclick=\"sgBulkCopyOne(' + idx + ',\\'' + esc(slug).replace(/'/g,\"\\\\'\") + '\\',this)\"\u003eCopy\u003c/button\u003e' +\n        '\u003c/div\u003e';\n    });\n\n    if (results.length \u003e 0) {\n      html += '\u003cbutton class=\"sg-bulk-copy-all\" onclick=\"sgBulkCopyAll()\"\u003eCopy All Slugs\u003c/button\u003e';\n    }\n    container.innerHTML = html;\n    container._results = results;\n  };\n\n  window.sgBulkCopyOne = function(idx, slug, btn) {\n    var origText = btn.textContent;\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(slug).then(function() { btn.textContent = 'Copied!'; setTimeout(function(){btn.textContent=origText;},1400); });\n    } else {\n      fallbackCopy(slug);\n      btn.textContent = 'Copied!';\n      setTimeout(function(){btn.textContent=origText;},1400);\n    }\n  };\n\n  window.sgBulkCopyAll = function() {\n    var container = document.getElementById('sg-bulk-results');\n    var results = container._results || [];\n    var text = results.join('\\n');\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(text);\n    } else {\n      fallbackCopy(text);\n    }\n    var btn = container.querySelector('.sg-bulk-copy-all');\n    if (btn) { var o=btn.textContent; btn.textContent='All Copied!'; setTimeout(function(){btn.textContent=o;},1600); }\n  };\n\n  function fallbackCopy(text) {\n    var ta = document.createElement('textarea');\n    ta.value = text;\n    ta.style.position = 'fixed';\n    ta.style.opacity = '0';\n    document.body.appendChild(ta);\n    ta.select();\n    document.execCommand('copy');\n    document.body.removeChild(ta);\n  }\n\n  function esc(s) {\n    return String(s).replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;').replace(/\"/g,'\u0026quot;');\n  }\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003eEncode URLs → \u003ca href=\"https://productivity-works.com/tools/url-encoder/\"\u003eURL Encoder\u003c/a\u003e\n\u003c/p\u003e","title":"URL Slug Generator"},{"content":"Check your current browser viewport dimensions in real time, identify which CSS breakpoint is active, and compare against common device presets — all without leaving this page.\nRelated tools: Screen Resolution → Screen Resolution Checker | Media Queries → CSS Media Query Generator — Width (px) × — Height (px) — Breakpoint — — DPR — Breakpoint Indicator 0640768102412801536+ xs\u0026lt;640px sm640–767 md768–1023 lg1024–1279 xl1280–1535 2xl1536px+ window.innerWidth — Viewport width incl. scrollbar window.innerHeight — Viewport height incl. scrollbar document.documentElement — × — clientWidth × clientHeight screen.width × screen.height — Physical screen resolution devicePixelRatio — CSS px to physical px ratio Orientation — Portrait / Landscape Copy All Info Copied to clipboard! Common Device Viewport Reference Device Viewport (CSS px) Tailwind BP Notes iPhone 15 Pro 393 × 852 xs Portrait, @3x iPhone 15 / 14 390 × 844 xs Portrait, @3x iPhone SE (3rd gen) 375 × 667 xs Portrait, @2x iPad (10th gen) 820 × 1180 md Portrait, @2x iPad Pro 12.9\" 1024 × 1366 lg Portrait, @2x Pixel 7 412 × 915 xs Portrait, @2.625x Galaxy S24 360 × 780 xs Portrait, @3x MacBook Air 13\" 1280 × 800 xl @2x Retina MacBook Pro 14\" 1512 × 982 xl @2x Retina MacBook Pro 16\" 1728 × 1117 2xl @2x Retina Desktop 1080p 1920 × 1080 2xl @1x standard How to Use Read the live numbers at the top — they update instantly as you resize the browser window. Check the breakpoint bar to see where you fall in the Tailwind CSS grid (xs / sm / md / lg / xl / 2xl). Compare to device presets in the table to understand how your page will look on real devices. Click \u0026ldquo;Copy All Info\u0026rdquo; to grab a text snapshot for bug reports or design handoffs. What Each Measurement Means Property What it measures window.innerWidth/Height The visible viewport, including the scrollbar area clientWidth/Height The viewport minus the scrollbar — matches your CSS media queries screen.width/Height The physical display resolution (not affected by zoom) devicePixelRatio How many physical pixels make up one CSS pixel (2 = Retina) Tailwind CSS Breakpoints Reference Prefix Min-width CSS rule (none — xs) 0 px default styles sm 640 px @media (min-width: 640px) md 768 px @media (min-width: 768px) lg 1024 px @media (min-width: 1024px) xl 1280 px @media (min-width: 1280px) 2xl 1536 px @media (min-width: 1536px) Related tools: Screen Resolution → Screen Resolution Checker | Media Queries → CSS Media Query Generator ","permalink":"https://productivity-works.com/tools/viewport-tester/","summary":"\u003cp\u003eCheck your current browser viewport dimensions in real time, identify which CSS breakpoint is active, and compare against common device presets — all without leaving this page.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eRelated tools:\u003c/strong\u003e Screen Resolution → \u003ca href=\"https://productivity-works.com/tools/screen-resolution/\"\u003eScreen Resolution Checker\u003c/a\u003e\n | Media Queries → \u003ca href=\"https://productivity-works.com/tools/media-query-generator/\"\u003eCSS Media Query Generator\u003c/a\u003e\n\u003c/p\u003e\n\u003chr\u003e\n\u003cdiv id=\"vt-app\"\u003e\n\u003cstyle\u003e\n#vt-app * {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#vt-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n  font-size: 15px;\n  color: #1e293b;\n  line-height: 1.5;\n  max-width: 860px;\n}\n#vt-app h2 {\n  font-size: 17px;\n  font-weight: 700;\n  color: #0f172a;\n  margin-bottom: 12px;\n  padding-bottom: 6px;\n  border-bottom: 2px solid #e2e8f0;\n}\n#vt-app .vt-card {\n  background: #fff;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 12px;\n  padding: 20px 22px;\n  margin-bottom: 20px;\n  box-shadow: 0 1px 4px rgba(0,0,0,0.05);\n}\n#vt-app .vt-hero {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  gap: 24px;\n  background: linear-gradient(135deg, #0f172a 0%, #1e3a5f 100%);\n  border-radius: 14px;\n  padding: 28px 24px;\n  margin-bottom: 20px;\n  flex-wrap: wrap;\n}\n#vt-app .vt-big-dim {\n  text-align: center;\n}\n#vt-app .vt-big-dim .vt-num {\n  font-size: 52px;\n  font-weight: 800;\n  color: #38bdf8;\n  letter-spacing: -2px;\n  line-height: 1;\n}\n#vt-app .vt-big-dim .vt-label {\n  font-size: 12px;\n  color: #94a3b8;\n  text-transform: uppercase;\n  letter-spacing: 1px;\n  margin-top: 4px;\n}\n#vt-app .vt-sep {\n  font-size: 36px;\n  color: #475569;\n  font-weight: 300;\n  padding: 0 4px;\n}\n#vt-app .vt-bp-badge {\n  background: #0284c7;\n  color: #fff;\n  font-size: 22px;\n  font-weight: 800;\n  border-radius: 10px;\n  padding: 10px 22px;\n  letter-spacing: 1px;\n  text-align: center;\n}\n#vt-app .vt-bp-name {\n  font-size: 11px;\n  color: #bae6fd;\n  margin-top: 4px;\n  text-align: center;\n  letter-spacing: 1px;\n  text-transform: uppercase;\n}\n#vt-app .vt-meta-row {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  gap: 16px;\n  flex-wrap: wrap;\n  margin-top: 10px;\n}\n#vt-app .vt-pill {\n  background: rgba(255,255,255,0.08);\n  border: 1px solid rgba(255,255,255,0.15);\n  border-radius: 20px;\n  padding: 4px 14px;\n  font-size: 12px;\n  color: #cbd5e1;\n}\n#vt-app .vt-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 10px;\n  margin-bottom: 20px;\n}\n@media (max-width: 560px) {\n  #vt-app .vt-grid { grid-template-columns: 1fr; }\n}\n#vt-app .vt-stat-card {\n  background: #f8fafc;\n  border: 1.5px solid #e2e8f0;\n  border-radius: 10px;\n  padding: 14px 16px;\n}\n#vt-app .vt-stat-card .vt-stat-title {\n  font-size: 11px;\n  text-transform: uppercase;\n  letter-spacing: 0.8px;\n  color: #64748b;\n  margin-bottom: 4px;\n}\n#vt-app .vt-stat-card .vt-stat-val {\n  font-size: 17px;\n  font-weight: 700;\n  color: #0f172a;\n  font-variant-numeric: tabular-nums;\n}\n#vt-app .vt-stat-card .vt-stat-sub {\n  font-size: 12px;\n  color: #94a3b8;\n  margin-top: 2px;\n}\n/* Breakpoint bar */\n#vt-app .vt-bpbar-wrap {\n  margin-bottom: 20px;\n}\n#vt-app .vt-bpbar-track {\n  position: relative;\n  height: 36px;\n  background: #e2e8f0;\n  border-radius: 8px;\n  overflow: hidden;\n  margin-bottom: 6px;\n}\n#vt-app .vt-bpbar-fill {\n  position: absolute;\n  left: 0; top: 0; bottom: 0;\n  background: linear-gradient(90deg, #0284c7, #38bdf8);\n  border-radius: 8px;\n  transition: width 0.2s ease;\n  min-width: 4px;\n}\n#vt-app .vt-bpbar-label {\n  position: absolute;\n  right: 8px; top: 0; bottom: 0;\n  display: flex;\n  align-items: center;\n  font-size: 12px;\n  font-weight: 600;\n  color: #fff;\n  text-shadow: 0 1px 2px rgba(0,0,0,0.3);\n}\n#vt-app .vt-bpbar-ticks {\n  display: flex;\n  justify-content: space-between;\n  font-size: 10px;\n  color: #94a3b8;\n  padding: 0 2px;\n}\n#vt-app .vt-bp-segments {\n  display: flex;\n  gap: 6px;\n  flex-wrap: wrap;\n  margin-top: 10px;\n}\n#vt-app .vt-bp-seg {\n  flex: 1;\n  min-width: 60px;\n  text-align: center;\n  padding: 6px 4px;\n  border-radius: 7px;\n  border: 1.5px solid #e2e8f0;\n  font-size: 12px;\n  font-weight: 600;\n  color: #64748b;\n  background: #f8fafc;\n  transition: all 0.15s;\n}\n#vt-app .vt-bp-seg.active {\n  background: #0284c7;\n  border-color: #0284c7;\n  color: #fff;\n  box-shadow: 0 2px 8px rgba(2,132,199,0.25);\n}\n#vt-app .vt-bp-seg .vt-seg-range {\n  display: block;\n  font-size: 10px;\n  font-weight: 400;\n  opacity: 0.8;\n  margin-top: 2px;\n}\n/* Device presets table */\n#vt-app .vt-preset-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 13px;\n}\n#vt-app .vt-preset-table th {\n  text-align: left;\n  padding: 8px 10px;\n  background: #f1f5f9;\n  color: #475569;\n  font-size: 11px;\n  text-transform: uppercase;\n  letter-spacing: 0.6px;\n  border-bottom: 1.5px solid #e2e8f0;\n}\n#vt-app .vt-preset-table td {\n  padding: 9px 10px;\n  border-bottom: 1px solid #f1f5f9;\n  color: #334155;\n}\n#vt-app .vt-preset-table tr:last-child td {\n  border-bottom: none;\n}\n#vt-app .vt-preset-table tr:hover td {\n  background: #f8fafc;\n}\n#vt-app .vt-preset-table .vt-device-name {\n  font-weight: 600;\n  color: #0f172a;\n}\n#vt-app .vt-preset-table .vt-bp-chip {\n  display: inline-block;\n  padding: 2px 8px;\n  border-radius: 10px;\n  font-size: 11px;\n  font-weight: 700;\n  background: #e0f2fe;\n  color: #0369a1;\n}\n/* Copy button */\n#vt-app .vt-actions {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n  margin-bottom: 20px;\n}\n#vt-app .vt-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 7px;\n  padding: 10px 20px;\n  border-radius: 8px;\n  font-size: 14px;\n  font-weight: 600;\n  cursor: pointer;\n  border: none;\n  transition: all 0.15s;\n  font-family: inherit;\n}\n#vt-app .vt-btn-primary {\n  background: #0284c7;\n  color: #fff;\n}\n#vt-app .vt-btn-primary:hover {\n  background: #0369a1;\n}\n#vt-app .vt-btn-secondary {\n  background: #f1f5f9;\n  color: #334155;\n  border: 1.5px solid #e2e8f0;\n}\n#vt-app .vt-btn-secondary:hover {\n  background: #e2e8f0;\n}\n#vt-app .vt-copy-msg {\n  display: none;\n  font-size: 13px;\n  color: #16a34a;\n  align-items: center;\n  gap: 5px;\n  padding: 10px 16px;\n  background: #f0fdf4;\n  border-radius: 8px;\n  border: 1px solid #bbf7d0;\n}\n#vt-app .vt-copy-msg.show { display: flex; }\n\u003c/style\u003e\n\u003c!-- HERO: Live dimensions --\u003e\n\u003cdiv class=\"vt-hero\" id=\"vt-hero\"\u003e\n  \u003cdiv class=\"vt-big-dim\"\u003e\n    \u003cdiv class=\"vt-num\" id=\"vt-width\"\u003e—\u003c/div\u003e\n    \u003cdiv class=\"vt-label\"\u003eWidth (px)\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"vt-sep\"\u003e×\u003c/div\u003e\n  \u003cdiv class=\"vt-big-dim\"\u003e\n    \u003cdiv class=\"vt-num\" id=\"vt-height\"\u003e—\u003c/div\u003e\n    \u003cdiv class=\"vt-label\"\u003eHeight (px)\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"vt-bp-badge\" id=\"vt-bp-badge\"\u003e—\u003c/div\u003e\n    \u003cdiv class=\"vt-bp-name\" id=\"vt-bp-name\"\u003eBreakpoint\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv style=\"width:100%;\"\u003e\n    \u003cdiv class=\"vt-meta-row\"\u003e\n      \u003cspan class=\"vt-pill\" id=\"vt-orientation\"\u003e—\u003c/span\u003e\n      \u003cspan class=\"vt-pill\" id=\"vt-touch\"\u003e—\u003c/span\u003e\n      \u003cspan class=\"vt-pill\" id=\"vt-dpr\"\u003eDPR —\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Breakpoint visual bar --\u003e\n\u003cdiv class=\"vt-card vt-bpbar-wrap\"\u003e\n  \u003ch2\u003eBreakpoint Indicator\u003c/h2\u003e\n  \u003cdiv class=\"vt-bpbar-track\"\u003e\n    \u003cdiv class=\"vt-bpbar-fill\" id=\"vt-bar-fill\" style=\"width:50%;\"\u003e\n      \u003cspan class=\"vt-bpbar-label\" id=\"vt-bar-label\"\u003e\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"vt-bpbar-ticks\"\u003e\n    \u003cspan\u003e0\u003c/span\u003e\u003cspan\u003e640\u003c/span\u003e\u003cspan\u003e768\u003c/span\u003e\u003cspan\u003e1024\u003c/span\u003e\u003cspan\u003e1280\u003c/span\u003e\u003cspan\u003e1536+\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"vt-bp-segments\"\u003e\n    \u003cdiv class=\"vt-bp-seg\" id=\"seg-xs\"\u003exs\u003cspan class=\"vt-seg-range\"\u003e\u0026lt;640px\u003c/span\u003e\u003c/div\u003e\n    \u003cdiv class=\"vt-bp-seg\" id=\"seg-sm\"\u003esm\u003cspan class=\"vt-seg-range\"\u003e640–767\u003c/span\u003e\u003c/div\u003e\n    \u003cdiv class=\"vt-bp-seg\" id=\"seg-md\"\u003emd\u003cspan class=\"vt-seg-range\"\u003e768–1023\u003c/span\u003e\u003c/div\u003e\n    \u003cdiv class=\"vt-bp-seg\" id=\"seg-lg\"\u003elg\u003cspan class=\"vt-seg-range\"\u003e1024–1279\u003c/span\u003e\u003c/div\u003e\n    \u003cdiv class=\"vt-bp-seg\" id=\"seg-xl\"\u003exl\u003cspan class=\"vt-seg-range\"\u003e1280–1535\u003c/span\u003e\u003c/div\u003e\n    \u003cdiv class=\"vt-bp-seg\" id=\"seg-2xl\"\u003e2xl\u003cspan class=\"vt-seg-range\"\u003e1536px+\u003c/span\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Detail stats grid --\u003e\n\u003cdiv class=\"vt-grid\"\u003e\n  \u003cdiv class=\"vt-stat-card\"\u003e\n    \u003cdiv class=\"vt-stat-title\"\u003ewindow.innerWidth\u003c/div\u003e\n    \u003cdiv class=\"vt-stat-val\" id=\"stat-iw\"\u003e—\u003c/div\u003e\n    \u003cdiv class=\"vt-stat-sub\"\u003eViewport width incl. scrollbar\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"vt-stat-card\"\u003e\n    \u003cdiv class=\"vt-stat-title\"\u003ewindow.innerHeight\u003c/div\u003e\n    \u003cdiv class=\"vt-stat-val\" id=\"stat-ih\"\u003e—\u003c/div\u003e\n    \u003cdiv class=\"vt-stat-sub\"\u003eViewport height incl. scrollbar\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"vt-stat-card\"\u003e\n    \u003cdiv class=\"vt-stat-title\"\u003edocument.documentElement\u003c/div\u003e\n    \u003cdiv class=\"vt-stat-val\" id=\"stat-doc\"\u003e— × —\u003c/div\u003e\n    \u003cdiv class=\"vt-stat-sub\"\u003eclientWidth × clientHeight\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"vt-stat-card\"\u003e\n    \u003cdiv class=\"vt-stat-title\"\u003escreen.width × screen.height\u003c/div\u003e\n    \u003cdiv class=\"vt-stat-val\" id=\"stat-scr\"\u003e—\u003c/div\u003e\n    \u003cdiv class=\"vt-stat-sub\"\u003ePhysical screen resolution\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"vt-stat-card\"\u003e\n    \u003cdiv class=\"vt-stat-title\"\u003edevicePixelRatio\u003c/div\u003e\n    \u003cdiv class=\"vt-stat-val\" id=\"stat-dpr\"\u003e—\u003c/div\u003e\n    \u003cdiv class=\"vt-stat-sub\"\u003eCSS px to physical px ratio\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"vt-stat-card\"\u003e\n    \u003cdiv class=\"vt-stat-title\"\u003eOrientation\u003c/div\u003e\n    \u003cdiv class=\"vt-stat-val\" id=\"stat-orient\"\u003e—\u003c/div\u003e\n    \u003cdiv class=\"vt-stat-sub\"\u003ePortrait / Landscape\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Actions --\u003e\n\u003cdiv class=\"vt-actions\"\u003e\n  \u003cbutton class=\"vt-btn vt-btn-primary\" onclick=\"vtCopyAll()\"\u003e\n    \u003csvg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\"/\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"/\u003e\u003c/svg\u003e\n    Copy All Info\n  \u003c/button\u003e\n  \u003cdiv class=\"vt-copy-msg\" id=\"vt-copy-msg\"\u003e\n    \u003csvg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"/\u003e\u003c/svg\u003e\n    Copied to clipboard!\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Device presets --\u003e\n\u003cdiv class=\"vt-card\"\u003e\n  \u003ch2\u003eCommon Device Viewport Reference\u003c/h2\u003e\n  \u003cdiv style=\"overflow-x:auto;\"\u003e\n    \u003ctable class=\"vt-preset-table\"\u003e\n      \u003cthead\u003e\n        \u003ctr\u003e\n          \u003cth\u003eDevice\u003c/th\u003e\n          \u003cth\u003eViewport (CSS px)\u003c/th\u003e\n          \u003cth\u003eTailwind BP\u003c/th\u003e\n          \u003cth\u003eNotes\u003c/th\u003e\n        \u003c/tr\u003e\n      \u003c/thead\u003e\n      \u003ctbody\u003e\n        \u003ctr\u003e\n          \u003ctd class=\"vt-device-name\"\u003eiPhone 15 Pro\u003c/td\u003e\n          \u003ctd\u003e393 × 852\u003c/td\u003e\n          \u003ctd\u003e\u003cspan class=\"vt-bp-chip\"\u003exs\u003c/span\u003e\u003c/td\u003e\n          \u003ctd\u003ePortrait, @3x\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd class=\"vt-device-name\"\u003eiPhone 15 / 14\u003c/td\u003e\n          \u003ctd\u003e390 × 844\u003c/td\u003e\n          \u003ctd\u003e\u003cspan class=\"vt-bp-chip\"\u003exs\u003c/span\u003e\u003c/td\u003e\n          \u003ctd\u003ePortrait, @3x\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd class=\"vt-device-name\"\u003eiPhone SE (3rd gen)\u003c/td\u003e\n          \u003ctd\u003e375 × 667\u003c/td\u003e\n          \u003ctd\u003e\u003cspan class=\"vt-bp-chip\"\u003exs\u003c/span\u003e\u003c/td\u003e\n          \u003ctd\u003ePortrait, @2x\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd class=\"vt-device-name\"\u003eiPad (10th gen)\u003c/td\u003e\n          \u003ctd\u003e820 × 1180\u003c/td\u003e\n          \u003ctd\u003e\u003cspan class=\"vt-bp-chip\"\u003emd\u003c/span\u003e\u003c/td\u003e\n          \u003ctd\u003ePortrait, @2x\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd class=\"vt-device-name\"\u003eiPad Pro 12.9\"\u003c/td\u003e\n          \u003ctd\u003e1024 × 1366\u003c/td\u003e\n          \u003ctd\u003e\u003cspan class=\"vt-bp-chip\"\u003elg\u003c/span\u003e\u003c/td\u003e\n          \u003ctd\u003ePortrait, @2x\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd class=\"vt-device-name\"\u003ePixel 7\u003c/td\u003e\n          \u003ctd\u003e412 × 915\u003c/td\u003e\n          \u003ctd\u003e\u003cspan class=\"vt-bp-chip\"\u003exs\u003c/span\u003e\u003c/td\u003e\n          \u003ctd\u003ePortrait, @2.625x\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd class=\"vt-device-name\"\u003eGalaxy S24\u003c/td\u003e\n          \u003ctd\u003e360 × 780\u003c/td\u003e\n          \u003ctd\u003e\u003cspan class=\"vt-bp-chip\"\u003exs\u003c/span\u003e\u003c/td\u003e\n          \u003ctd\u003ePortrait, @3x\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd class=\"vt-device-name\"\u003eMacBook Air 13\"\u003c/td\u003e\n          \u003ctd\u003e1280 × 800\u003c/td\u003e\n          \u003ctd\u003e\u003cspan class=\"vt-bp-chip\"\u003exl\u003c/span\u003e\u003c/td\u003e\n          \u003ctd\u003e@2x Retina\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd class=\"vt-device-name\"\u003eMacBook Pro 14\"\u003c/td\u003e\n          \u003ctd\u003e1512 × 982\u003c/td\u003e\n          \u003ctd\u003e\u003cspan class=\"vt-bp-chip\"\u003exl\u003c/span\u003e\u003c/td\u003e\n          \u003ctd\u003e@2x Retina\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd class=\"vt-device-name\"\u003eMacBook Pro 16\"\u003c/td\u003e\n          \u003ctd\u003e1728 × 1117\u003c/td\u003e\n          \u003ctd\u003e\u003cspan class=\"vt-bp-chip\"\u003e2xl\u003c/span\u003e\u003c/td\u003e\n          \u003ctd\u003e@2x Retina\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd class=\"vt-device-name\"\u003eDesktop 1080p\u003c/td\u003e\n          \u003ctd\u003e1920 × 1080\u003c/td\u003e\n          \u003ctd\u003e\u003cspan class=\"vt-bp-chip\"\u003e2xl\u003c/span\u003e\u003c/td\u003e\n          \u003ctd\u003e@1x standard\u003c/td\u003e\n        \u003c/tr\u003e\n      \u003c/tbody\u003e\n    \u003c/table\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  var BP_BREAKS = [\n    { id: 'xs',  name: 'xs (\u003c 640)',    min: 0,    max: 639  },\n    { id: 'sm',  name: 'sm (640–767)',  min: 640,  max: 767  },\n    { id: 'md',  name: 'md (768–1023)', min: 768,  max: 1023 },\n    { id: 'lg',  name: 'lg (1024–1279)',min: 1024, max: 1279 },\n    { id: 'xl',  name: 'xl (1280–1535)',min: 1280, max: 1535 },\n    { id: '2xl', name: '2xl (≥ 1536)',  min: 1536, max: Infinity }\n  ];\n\n  var MAX_BAR_W = 1536;\n\n  function getBP(w) {\n    for (var i = BP_BREAKS.length - 1; i \u003e= 0; i--) {\n      if (w \u003e= BP_BREAKS[i].min) return BP_BREAKS[i];\n    }\n    return BP_BREAKS[0];\n  }\n\n  function update() {\n    var iw = window.innerWidth;\n    var ih = window.innerHeight;\n    var dw = document.documentElement.clientWidth;\n    var dh = document.documentElement.clientHeight;\n    var sw = screen.width;\n    var sh = screen.height;\n    var dpr = window.devicePixelRatio || 1;\n    var isTouch = ('ontouchstart' in window) || navigator.maxTouchPoints \u003e 0;\n    var orient = iw \u003e= ih ? 'Landscape' : 'Portrait';\n    var bp = getBP(iw);\n\n    // Hero\n    document.getElementById('vt-width').textContent = iw;\n    document.getElementById('vt-height').textContent = ih;\n    document.getElementById('vt-bp-badge').textContent = bp.id;\n    document.getElementById('vt-bp-name').textContent = bp.name;\n    document.getElementById('vt-orientation').textContent = orient;\n    document.getElementById('vt-touch').textContent = isTouch ? 'Touch Device' : 'Non-Touch';\n    document.getElementById('vt-dpr').textContent = 'DPR ' + dpr.toFixed(2);\n\n    // Bar\n    var pct = Math.min(100, (iw / MAX_BAR_W) * 100);\n    document.getElementById('vt-bar-fill').style.width = pct + '%';\n    document.getElementById('vt-bar-label').textContent = iw + 'px';\n\n    // Segments\n    BP_BREAKS.forEach(function(b) {\n      var el = document.getElementById('seg-' + b.id);\n      if (el) {\n        el.classList.toggle('active', b.id === bp.id);\n      }\n    });\n\n    // Stats\n    document.getElementById('stat-iw').textContent = iw + ' px';\n    document.getElementById('stat-ih').textContent = ih + ' px';\n    document.getElementById('stat-doc').textContent = dw + ' × ' + dh + ' px';\n    document.getElementById('stat-scr').textContent = sw + ' × ' + sh + ' px';\n    document.getElementById('stat-dpr').textContent = dpr.toFixed(3);\n    document.getElementById('stat-orient').textContent = orient;\n  }\n\n  window.vtCopyAll = function() {\n    var iw = window.innerWidth;\n    var ih = window.innerHeight;\n    var dw = document.documentElement.clientWidth;\n    var dh = document.documentElement.clientHeight;\n    var dpr = (window.devicePixelRatio || 1).toFixed(3);\n    var isTouch = ('ontouchstart' in window) || navigator.maxTouchPoints \u003e 0;\n    var orient = iw \u003e= ih ? 'Landscape' : 'Portrait';\n    var bp = getBP(iw);\n\n    var text = [\n      '=== Viewport Info ===',\n      'Viewport (innerWidth x innerHeight): ' + iw + ' x ' + ih + ' px',\n      'Document (clientWidth x clientHeight): ' + dw + ' x ' + dh + ' px',\n      'Screen: ' + screen.width + ' x ' + screen.height + ' px',\n      'devicePixelRatio: ' + dpr,\n      'Tailwind Breakpoint: ' + bp.id + ' (' + bp.name + ')',\n      'Orientation: ' + orient,\n      'Touch Device: ' + (isTouch ? 'Yes' : 'No'),\n      'Captured at: ' + new Date().toISOString()\n    ].join('\\n');\n\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(text).then(function() { vtShowCopied(); });\n    } else {\n      var ta = document.createElement('textarea');\n      ta.value = text;\n      ta.style.position = 'fixed';\n      ta.style.opacity = '0';\n      document.body.appendChild(ta);\n      ta.select();\n      document.execCommand('copy');\n      document.body.removeChild(ta);\n      vtShowCopied();\n    }\n  };\n\n  window.vtShowCopied = function() {\n    var msg = document.getElementById('vt-copy-msg');\n    msg.classList.add('show');\n    setTimeout(function() { msg.classList.remove('show'); }, 2500);\n  };\n\n  update();\n  window.addEventListener('resize', update);\n  if (window.screen \u0026\u0026 window.screen.orientation) {\n    screen.orientation.addEventListener('change', update);\n  }\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 id=\"how-to-use\"\u003eHow to Use\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eRead the live numbers\u003c/strong\u003e at the top — they update instantly as you resize the browser window.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eCheck the breakpoint bar\u003c/strong\u003e to see where you fall in the Tailwind CSS grid (xs / sm / md / lg / xl / 2xl).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eCompare to device presets\u003c/strong\u003e in the table to understand how your page will look on real devices.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eClick \u0026ldquo;Copy All Info\u0026rdquo;\u003c/strong\u003e to grab a text snapshot for bug reports or design handoffs.\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"what-each-measurement-means\"\u003eWhat Each Measurement Means\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eProperty\u003c/th\u003e\n          \u003cth\u003eWhat it measures\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003ewindow.innerWidth/Height\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eThe visible viewport, including the scrollbar area\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eclientWidth/Height\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eThe viewport minus the scrollbar — matches your CSS media queries\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003escreen.width/Height\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eThe physical display resolution (not affected by zoom)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003edevicePixelRatio\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eHow many physical pixels make up one CSS pixel (2 = Retina)\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"tailwind-css-breakpoints-reference\"\u003eTailwind CSS Breakpoints Reference\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003ePrefix\u003c/th\u003e\n          \u003cth\u003eMin-width\u003c/th\u003e\n          \u003cth\u003eCSS rule\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cem\u003e(none — xs)\u003c/em\u003e\u003c/td\u003e\n          \u003ctd\u003e0 px\u003c/td\u003e\n          \u003ctd\u003edefault styles\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003esm\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e640 px\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e@media (min-width: 640px)\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003emd\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e768 px\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e@media (min-width: 768px)\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003elg\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e1024 px\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e@media (min-width: 1024px)\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003exl\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e1280 px\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e@media (min-width: 1280px)\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e2xl\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e1536 px\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e@media (min-width: 1536px)\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e\u003cstrong\u003eRelated tools:\u003c/strong\u003e Screen Resolution → \u003ca href=\"https://productivity-works.com/tools/screen-resolution/\"\u003eScreen Resolution Checker\u003c/a\u003e\n | Media Queries → \u003ca href=\"https://productivity-works.com/tools/media-query-generator/\"\u003eCSS Media Query Generator\u003c/a\u003e\n\u003c/p\u003e","title":"Viewport Size Tester"},{"content":" Enter Your Details kg lbs Body Weight (kg) Activity Level Sedentary (little/no exercise) Light (1-3 days/week) Moderate (3-5 days/week) Active (6-7 days/week) Very Active (intense daily) Climate Cool / Cold Moderate Hot Very Hot / Humid Calculate My Daily Water Intake — liters per day recommended Today's Hydration Tracker 0 / 0 glasses logged today 0% You've hit your daily hydration goal! Great work! + Log a Glass (250ml) Reset Day Tips for Staying Hydrated Start your day with a full glass of water before coffee or breakfast. Set hourly reminders on your phone to drink water throughout the day. Eat water-rich foods: cucumbers, watermelon, lettuce, and celery all count. Drink an extra 250–500 ml for every 30 minutes of exercise. Caffeinated drinks are mild diuretics — balance each cup with extra water. Check your urine color: pale yellow means good hydration; dark yellow means drink more. Keep a reusable water bottle visible at your desk as a constant reminder. In hot or humid weather, increase intake by at least 500 ml above your baseline. Related Free Tools\nBMI Calculator Calorie Calculator Sleep Calculator Pomodoro Timer Related Tools Bmi Calculator Body Fat Calculator Calorie Calculator ","permalink":"https://productivity-works.com/tools/water-intake-calculator/","summary":"\u003cdiv id=\"wi-app\"\u003e\n\u003cstyle\u003e\n#wi-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, sans-serif;\n  max-width: 720px;\n  margin: 0 auto;\n  color: #1a2b3c;\n}\n#wi-app h2 {\n  font-size: 1.4rem;\n  font-weight: 700;\n  margin: 1.5rem 0 0.75rem;\n  color: #0077cc;\n}\n#wi-app .wi-card {\n  background: #f0f8ff;\n  border: 1px solid #b3d9f5;\n  border-radius: 12px;\n  padding: 1.5rem;\n  margin-bottom: 1.25rem;\n}\n#wi-app .wi-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 1rem;\n}\n@media (max-width: 520px) {\n  #wi-app .wi-grid { grid-template-columns: 1fr; }\n}\n#wi-app label {\n  display: block;\n  font-size: 0.85rem;\n  font-weight: 600;\n  margin-bottom: 0.3rem;\n  color: #345;\n}\n#wi-app input[type=\"number\"],\n#wi-app select {\n  width: 100%;\n  padding: 0.55rem 0.75rem;\n  border: 1.5px solid #90c4e8;\n  border-radius: 8px;\n  font-size: 1rem;\n  background: #fff;\n  box-sizing: border-box;\n  transition: border-color 0.2s;\n}\n#wi-app input[type=\"number\"]:focus,\n#wi-app select:focus {\n  border-color: #0077cc;\n  outline: none;\n}\n#wi-app .wi-unit-toggle {\n  display: flex;\n  gap: 0.5rem;\n  margin-bottom: 0.75rem;\n}\n#wi-app .wi-unit-btn {\n  flex: 1;\n  padding: 0.45rem;\n  border: 1.5px solid #90c4e8;\n  border-radius: 8px;\n  background: #fff;\n  font-size: 0.9rem;\n  cursor: pointer;\n  font-weight: 600;\n  transition: all 0.2s;\n}\n#wi-app .wi-unit-btn.active {\n  background: #0077cc;\n  color: #fff;\n  border-color: #0077cc;\n}\n#wi-app .wi-calc-btn {\n  display: block;\n  width: 100%;\n  padding: 0.9rem;\n  background: linear-gradient(135deg, #0077cc, #00aaff);\n  color: #fff;\n  border: none;\n  border-radius: 10px;\n  font-size: 1.1rem;\n  font-weight: 700;\n  cursor: pointer;\n  margin-top: 1rem;\n  letter-spacing: 0.02em;\n  transition: opacity 0.2s;\n}\n#wi-app .wi-calc-btn:hover { opacity: 0.9; }\n#wi-app .wi-result-box {\n  display: none;\n  background: linear-gradient(135deg, #e8f5ff, #d0eeff);\n  border: 2px solid #0077cc;\n  border-radius: 12px;\n  padding: 1.25rem 1.5rem;\n  margin-bottom: 1.25rem;\n  text-align: center;\n}\n#wi-app .wi-result-box.show { display: block; }\n#wi-app .wi-result-liters {\n  font-size: 3rem;\n  font-weight: 800;\n  color: #0055aa;\n  line-height: 1.1;\n}\n#wi-app .wi-result-label {\n  font-size: 1rem;\n  color: #0077cc;\n  font-weight: 600;\n  margin-top: 0.2rem;\n}\n#wi-app .wi-result-glasses {\n  font-size: 0.95rem;\n  color: #345;\n  margin-top: 0.5rem;\n}\n#wi-app .wi-tracker {\n  display: none;\n  margin-bottom: 1.25rem;\n}\n#wi-app .wi-tracker.show { display: block; }\n#wi-app .wi-progress-bar-wrap {\n  background: #d0e8f5;\n  border-radius: 999px;\n  height: 22px;\n  overflow: hidden;\n  margin: 0.6rem 0 0.4rem;\n}\n#wi-app .wi-progress-bar-fill {\n  height: 100%;\n  background: linear-gradient(90deg, #00aaff, #0077cc);\n  border-radius: 999px;\n  transition: width 0.4s ease;\n  width: 0%;\n}\n#wi-app .wi-progress-label {\n  font-size: 0.85rem;\n  color: #456;\n  text-align: right;\n}\n#wi-app .wi-glasses-area {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem;\n  margin: 1rem 0 0.75rem;\n}\n#wi-app .wi-glass {\n  font-size: 2rem;\n  cursor: pointer;\n  opacity: 0.25;\n  transition: opacity 0.2s, transform 0.15s;\n  user-select: none;\n  line-height: 1;\n}\n#wi-app .wi-glass.filled { opacity: 1; }\n#wi-app .wi-glass:hover { transform: scale(1.15); }\n#wi-app .wi-tracker-status {\n  font-size: 1rem;\n  font-weight: 700;\n  color: #0055aa;\n  margin-bottom: 0.5rem;\n}\n#wi-app .wi-tracker-actions {\n  display: flex;\n  gap: 0.75rem;\n  margin-top: 0.75rem;\n}\n#wi-app .wi-log-btn {\n  flex: 1;\n  padding: 0.65rem;\n  background: #0077cc;\n  color: #fff;\n  border: none;\n  border-radius: 8px;\n  font-size: 0.95rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: opacity 0.2s;\n}\n#wi-app .wi-log-btn:hover { opacity: 0.85; }\n#wi-app .wi-reset-btn {\n  padding: 0.65rem 1.1rem;\n  background: #fff;\n  color: #0077cc;\n  border: 1.5px solid #0077cc;\n  border-radius: 8px;\n  font-size: 0.95rem;\n  font-weight: 700;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n#wi-app .wi-reset-btn:hover { background: #e8f4ff; }\n#wi-app .wi-congrats {\n  display: none;\n  background: #e6fff0;\n  border: 2px solid #22bb66;\n  border-radius: 10px;\n  padding: 0.9rem 1.2rem;\n  font-weight: 700;\n  color: #117733;\n  text-align: center;\n  margin-bottom: 0.75rem;\n  font-size: 1rem;\n}\n#wi-app .wi-congrats.show { display: block; }\n#wi-app .wi-tips-list {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n}\n#wi-app .wi-tips-list li {\n  padding: 0.5rem 0 0.5rem 1.75rem;\n  border-bottom: 1px solid #c8e4f5;\n  position: relative;\n  font-size: 0.95rem;\n  line-height: 1.5;\n}\n#wi-app .wi-tips-list li:last-child { border-bottom: none; }\n#wi-app .wi-tips-list li::before {\n  content: attr(data-icon);\n  position: absolute;\n  left: 0;\n  font-size: 1.1rem;\n}\n#wi-app .wi-related {\n  background: #f5f9fd;\n  border: 1px solid #c0d8ec;\n  border-radius: 10px;\n  padding: 1rem 1.25rem;\n  margin-top: 1rem;\n}\n#wi-app .wi-related p {\n  margin: 0 0 0.5rem;\n  font-weight: 700;\n  color: #0055aa;\n  font-size: 0.95rem;\n}\n#wi-app .wi-related a {\n  display: inline-block;\n  margin: 0.2rem 0.4rem 0.2rem 0;\n  padding: 0.3rem 0.75rem;\n  background: #fff;\n  border: 1.5px solid #90c4e8;\n  border-radius: 20px;\n  color: #0077cc;\n  text-decoration: none;\n  font-size: 0.88rem;\n  font-weight: 600;\n  transition: background 0.2s;\n}\n#wi-app .wi-related a:hover { background: #e0f0ff; }\n\u003c/style\u003e\n\u003ch2\u003eEnter Your Details\u003c/h2\u003e\n\u003cdiv class=\"wi-card\"\u003e\n  \u003cdiv class=\"wi-unit-toggle\"\u003e\n    \u003cbutton class=\"wi-unit-btn active\" id=\"wi-kg-btn\" onclick=\"wiSetUnit('kg')\"\u003ekg\u003c/button\u003e\n    \u003cbutton class=\"wi-unit-btn\" id=\"wi-lbs-btn\" onclick=\"wiSetUnit('lbs')\"\u003elbs\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wi-grid\"\u003e\n    \u003cdiv\u003e\n      \u003clabel for=\"wi-weight\"\u003eBody Weight (\u003cspan id=\"wi-unit-label\"\u003ekg\u003c/span\u003e)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"wi-weight\" min=\"1\" max=\"500\" placeholder=\"e.g. 70\" /\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003clabel for=\"wi-activity\"\u003eActivity Level\u003c/label\u003e\n      \u003cselect id=\"wi-activity\"\u003e\n        \u003coption value=\"1.0\"\u003eSedentary (little/no exercise)\u003c/option\u003e\n        \u003coption value=\"1.1\"\u003eLight (1-3 days/week)\u003c/option\u003e\n        \u003coption value=\"1.2\" selected\u003eModerate (3-5 days/week)\u003c/option\u003e\n        \u003coption value=\"1.35\"\u003eActive (6-7 days/week)\u003c/option\u003e\n        \u003coption value=\"1.5\"\u003eVery Active (intense daily)\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003clabel for=\"wi-climate\"\u003eClimate\u003c/label\u003e\n      \u003cselect id=\"wi-climate\"\u003e\n        \u003coption value=\"0\"\u003eCool / Cold\u003c/option\u003e\n        \u003coption value=\"0.3\" selected\u003eModerate\u003c/option\u003e\n        \u003coption value=\"0.5\"\u003eHot\u003c/option\u003e\n        \u003coption value=\"0.75\"\u003eVery Hot / Humid\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cbutton class=\"wi-calc-btn\" onclick=\"wiCalculate()\"\u003eCalculate My Daily Water Intake\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"wi-result-box\" id=\"wi-result-box\"\u003e\n  \u003cdiv class=\"wi-result-liters\" id=\"wi-result-liters\"\u003e—\u003c/div\u003e\n  \u003cdiv class=\"wi-result-label\"\u003eliters per day recommended\u003c/div\u003e\n  \u003cdiv class=\"wi-result-glasses\" id=\"wi-result-glasses\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"wi-tracker wi-card\" id=\"wi-tracker\"\u003e\n  \u003ch2 style=\"margin-top:0\"\u003eToday's Hydration Tracker\u003c/h2\u003e\n  \u003cdiv class=\"wi-tracker-status\" id=\"wi-tracker-status\"\u003e0 / 0 glasses logged today\u003c/div\u003e\n  \u003cdiv class=\"wi-progress-bar-wrap\"\u003e\n    \u003cdiv class=\"wi-progress-bar-fill\" id=\"wi-progress-fill\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wi-progress-label\" id=\"wi-progress-label\"\u003e0%\u003c/div\u003e\n  \u003cdiv class=\"wi-glasses-area\" id=\"wi-glasses-area\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"wi-congrats\" id=\"wi-congrats\"\u003eYou've hit your daily hydration goal! Great work!\u003c/div\u003e\n  \u003cdiv class=\"wi-tracker-actions\"\u003e\n    \u003cbutton class=\"wi-log-btn\" onclick=\"wiLogGlass()\"\u003e+ Log a Glass (250ml)\u003c/button\u003e\n    \u003cbutton class=\"wi-reset-btn\" onclick=\"wiResetDay()\"\u003eReset Day\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003ch2\u003eTips for Staying Hydrated\u003c/h2\u003e\n\u003cdiv class=\"wi-card\"\u003e\n  \u003cul class=\"wi-tips-list\"\u003e\n    \u003cli data-icon=\"🌅\"\u003eStart your day with a full glass of water before coffee or breakfast.\u003c/li\u003e\n    \u003cli data-icon=\"⏰\"\u003eSet hourly reminders on your phone to drink water throughout the day.\u003c/li\u003e\n    \u003cli data-icon=\"🍃\"\u003eEat water-rich foods: cucumbers, watermelon, lettuce, and celery all count.\u003c/li\u003e\n    \u003cli data-icon=\"🏃\"\u003eDrink an extra 250–500 ml for every 30 minutes of exercise.\u003c/li\u003e\n    \u003cli data-icon=\"☕\"\u003eCaffeinated drinks are mild diuretics — balance each cup with extra water.\u003c/li\u003e\n    \u003cli data-icon=\"💛\"\u003eCheck your urine color: pale yellow means good hydration; dark yellow means drink more.\u003c/li\u003e\n    \u003cli data-icon=\"🥤\"\u003eKeep a reusable water bottle visible at your desk as a constant reminder.\u003c/li\u003e\n    \u003cli data-icon=\"🌡️\"\u003eIn hot or humid weather, increase intake by at least 500 ml above your baseline.\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cdiv class=\"wi-related\"\u003e\n  \u003cp\u003eRelated Free Tools\u003c/p\u003e","title":"Water Intake Calculator - Daily Hydration Guide"},{"content":" Watermark Generator Upload an image, customize your text watermark (font, size, color, opacity, position, rotation), then download as PNG. No sign-up. No uploads to servers — all processing happens in your browser.\n🖼️ Drop your image here or click to browse \u0026mdash; JPG, PNG, GIF, WebP supported\nWatermark Text Watermark Text Font Size (px) 36 Font Family Arial Georgia Times New Roman Courier New Verdana Impact Trebuchet MS Palatino Text Color Opacity (%) 50% Rotation (deg) -30° Position \u0026amp; Layout Position (single watermark) Top Left Top Center Top Right Mid Left Center Mid Right Bot Left Bot Center Bot Right Repeat / Tile Pattern Tile watermark across image Tile Spacing (px) 150 \u0026#8595; Download PNG Upload New Image Related Tools Resize images to exact dimensions → Image Resizer Apply Instagram-style photo effects → Photo Filter Generate placeholder images → Placeholder Image ","permalink":"https://productivity-works.com/tools/watermark-generator/","summary":"\u003cdiv id=\"wm-app\"\u003e\n\u003cstyle\u003e\n#wm-app {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  padding: 0 0 48px 0;\n  color: #1a202c;\n}\n\n#wm-app * {\n  box-sizing: border-box;\n}\n\n/* Hero */\n.wm-hero {\n  background: linear-gradient(135deg, #0369a1 0%, #0284c7 50%, #0ea5e9 100%);\n  border-radius: 16px;\n  padding: 32px 28px;\n  margin-bottom: 24px;\n  color: #fff;\n}\n\n.wm-hero h2 {\n  margin: 0 0 6px 0;\n  font-size: 24px;\n  font-weight: 800;\n}\n\n.wm-hero p {\n  margin: 0;\n  font-size: 14px;\n  opacity: 0.88;\n  line-height: 1.6;\n}\n\n/* Drop Zone */\n.wm-dropzone {\n  border: 3px dashed #0369a1;\n  border-radius: 14px;\n  padding: 48px 24px;\n  text-align: center;\n  background: #f0f9ff;\n  cursor: pointer;\n  transition: background 0.2s, border-color 0.2s;\n  margin-bottom: 24px;\n}\n\n.wm-dropzone:hover,\n.wm-dropzone.wm-drag-over {\n  background: #e0f2fe;\n  border-color: #0284c7;\n}\n\n.wm-dropzone-icon {\n  font-size: 48px;\n  margin-bottom: 12px;\n}\n\n.wm-dropzone h3 {\n  margin: 0 0 6px 0;\n  font-size: 17px;\n  font-weight: 700;\n  color: #0c4a6e;\n}\n\n.wm-dropzone p {\n  margin: 0;\n  font-size: 13px;\n  color: #0369a1;\n}\n\n/* Editor layout */\n.wm-editor {\n  display: none;\n}\n\n.wm-editor.wm-visible {\n  display: block;\n}\n\n.wm-canvas-wrap {\n  background: #f8fafc;\n  border: 1px solid #e2e8f0;\n  border-radius: 14px;\n  padding: 16px;\n  margin-bottom: 20px;\n  text-align: center;\n  overflow: auto;\n}\n\n#wm-canvas {\n  max-width: 100%;\n  border-radius: 8px;\n  box-shadow: 0 4px 20px rgba(0,0,0,0.12);\n  display: block;\n  margin: 0 auto;\n}\n\n/* Controls panel */\n.wm-panel {\n  background: #fff;\n  border: 1px solid #e2e8f0;\n  border-radius: 14px;\n  padding: 24px;\n  margin-bottom: 20px;\n}\n\n.wm-panel h3 {\n  margin: 0 0 16px 0;\n  font-size: 15px;\n  font-weight: 700;\n  color: #0c4a6e;\n  border-bottom: 2px solid #e0f2fe;\n  padding-bottom: 8px;\n}\n\n.wm-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 14px;\n}\n\n@media (max-width: 600px) {\n  .wm-grid {\n    grid-template-columns: 1fr;\n  }\n}\n\n.wm-field {\n  display: flex;\n  flex-direction: column;\n  gap: 5px;\n}\n\n.wm-field.wm-full {\n  grid-column: 1 / -1;\n}\n\n.wm-field label {\n  font-size: 12px;\n  font-weight: 600;\n  color: #374151;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n\n.wm-field input[type=\"text\"],\n.wm-field input[type=\"number\"],\n.wm-field select {\n  padding: 8px 10px;\n  border: 1.5px solid #d1d5db;\n  border-radius: 8px;\n  font-size: 14px;\n  color: #1a202c;\n  background: #fff;\n  transition: border-color 0.2s;\n  width: 100%;\n}\n\n.wm-field input[type=\"text\"]:focus,\n.wm-field input[type=\"number\"]:focus,\n.wm-field select:focus {\n  outline: none;\n  border-color: #0369a1;\n}\n\n.wm-range-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n\n.wm-range-row input[type=\"range\"] {\n  flex: 1;\n  accent-color: #0369a1;\n}\n\n.wm-range-val {\n  font-size: 13px;\n  font-weight: 700;\n  color: #0369a1;\n  min-width: 36px;\n  text-align: right;\n}\n\n.wm-color-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n\n.wm-color-row input[type=\"color\"] {\n  width: 42px;\n  height: 36px;\n  border: 1.5px solid #d1d5db;\n  border-radius: 8px;\n  padding: 2px;\n  cursor: pointer;\n  background: #fff;\n}\n\n.wm-color-row input[type=\"text\"] {\n  flex: 1;\n  padding: 8px 10px;\n  border: 1.5px solid #d1d5db;\n  border-radius: 8px;\n  font-size: 14px;\n  color: #1a202c;\n}\n\n/* Position grid */\n.wm-pos-grid {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 6px;\n}\n\n.wm-pos-btn {\n  padding: 8px 4px;\n  border: 1.5px solid #d1d5db;\n  border-radius: 8px;\n  background: #fff;\n  font-size: 11px;\n  color: #374151;\n  cursor: pointer;\n  transition: all 0.15s;\n  text-align: center;\n  font-weight: 600;\n}\n\n.wm-pos-btn:hover {\n  border-color: #0369a1;\n  background: #f0f9ff;\n  color: #0369a1;\n}\n\n.wm-pos-btn.wm-pos-active {\n  border-color: #0369a1;\n  background: #0369a1;\n  color: #fff;\n}\n\n/* Toggle */\n.wm-toggle-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n\n.wm-toggle {\n  position: relative;\n  width: 44px;\n  height: 24px;\n  flex-shrink: 0;\n}\n\n.wm-toggle input {\n  opacity: 0;\n  width: 0;\n  height: 0;\n}\n\n.wm-toggle-slider {\n  position: absolute;\n  inset: 0;\n  background: #d1d5db;\n  border-radius: 24px;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n\n.wm-toggle-slider::before {\n  content: '';\n  position: absolute;\n  width: 18px;\n  height: 18px;\n  left: 3px;\n  top: 3px;\n  background: #fff;\n  border-radius: 50%;\n  transition: transform 0.2s;\n}\n\n.wm-toggle input:checked + .wm-toggle-slider {\n  background: #0369a1;\n}\n\n.wm-toggle input:checked + .wm-toggle-slider::before {\n  transform: translateX(20px);\n}\n\n.wm-toggle-label {\n  font-size: 13px;\n  color: #374151;\n  font-weight: 600;\n}\n\n/* Actions */\n.wm-actions {\n  display: flex;\n  gap: 12px;\n  flex-wrap: wrap;\n}\n\n.wm-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  padding: 11px 22px;\n  border-radius: 10px;\n  font-size: 14px;\n  font-weight: 700;\n  cursor: pointer;\n  border: none;\n  transition: all 0.18s;\n  text-decoration: none;\n}\n\n.wm-btn-primary {\n  background: #0369a1;\n  color: #fff;\n}\n\n.wm-btn-primary:hover {\n  background: #0284c7;\n  transform: translateY(-1px);\n  box-shadow: 0 4px 12px rgba(3,105,161,0.3);\n}\n\n.wm-btn-secondary {\n  background: #f1f5f9;\n  color: #374151;\n  border: 1.5px solid #d1d5db;\n}\n\n.wm-btn-secondary:hover {\n  background: #e2e8f0;\n}\n\n/* Status badge */\n.wm-status {\n  display: inline-block;\n  background: #dcfce7;\n  color: #15803d;\n  font-size: 12px;\n  font-weight: 700;\n  padding: 4px 10px;\n  border-radius: 20px;\n  margin-left: 8px;\n}\n\u003c/style\u003e\n\u003cdiv class=\"wm-hero\"\u003e\n  \u003ch2\u003eWatermark Generator\u003c/h2\u003e\n  \u003cp\u003eUpload an image, customize your text watermark (font, size, color, opacity, position, rotation), then download as PNG. No sign-up. No uploads to servers — all processing happens in your browser.\u003c/p\u003e","title":"Watermark Generator - Add Text to Images"},{"content":"Convert all weather measurement units in real time — temperature, wind speed, pressure, precipitation, and visibility — with the conversion formula shown for each result.\nConvert units → Unit Converter Temperature Wind Speed Pressure Precipitation Visibility \u0026#127777; Temperature Celsius (°C) Fahrenheit (°F) Kelvin (K) Formulas °F = °C × 9/5 + 32 K = °C + 273.15 °C = (°F − 32) × 5/9 °C = K − 273.15 Reset \u0026#127744; Wind Speed m/s km/h mph Knots (kn) Formulas (base: m/s) km/h = m/s × 3.6 mph = m/s × 2.23694 knots = m/s × 1.94384 Reset \u0026#9729; Pressure hPa (mbar) mmHg (Torr) inHg atm psi Formulas (base: hPa) mmHg = hPa × 0.750062 inHg = hPa × 0.02953 atm = hPa / 1013.25 psi = hPa × 0.0145038 Reset \u0026#127783; Precipitation Millimetres (mm) Inches (in) Formulas inches = mm / 25.4 mm = inches × 25.4 Reset \u0026#128065; Visibility Kilometres (km) Miles (mi) Formulas miles = km / 1.60934 km = miles × 1.60934 Reset Conversion references:\nCategory Units covered Temperature °C, °F, K — all three directions Wind speed m/s, km/h, mph, knots Pressure hPa/mbar, mmHg/Torr, inHg, atm, psi Precipitation mm, inches Visibility km, miles All conversions use standard meteorological definitions. Pressure base unit is hPa (hectopascal, equivalent to millibar), as used by most weather services worldwide.\n","permalink":"https://productivity-works.com/tools/weather-unit-converter/","summary":"\u003cp\u003eConvert all weather measurement units in real time — temperature, wind speed, pressure, precipitation, and visibility — with the conversion formula shown for each result.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eConvert units → \u003ca href=\"https://productivity-works.com/tools/unit-converter/\"\u003eUnit Converter\u003c/a\u003e\n\u003c/p\u003e\u003c/blockquote\u003e\n\u003cdiv id=\"wu-app\"\u003e\n\u003cstyle\u003e\n#wu-app *,#wu-app *::before,#wu-app *::after{box-sizing:border-box;margin:0;padding:0}\n#wu-app{font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;font-size:14px;color:#1e293b;max-width:860px}\n#wu-app .wu-tabs{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:20px}\n#wu-app .wu-tab{padding:8px 18px;border-radius:8px;border:1.5px solid #cbd5e1;background:#fff;color:#334155;font-size:13px;font-weight:600;cursor:pointer;transition:all .15s}\n#wu-app .wu-tab:hover{border-color:#0ea5e9;color:#0ea5e9}\n#wu-app .wu-tab.active{background:#0ea5e9;border-color:#0ea5e9;color:#fff}\n#wu-app .wu-section{display:none;background:#f8fafc;border:1px solid #e2e8f0;border-radius:12px;padding:20px}\n#wu-app .wu-section.active{display:block}\n#wu-app .wu-section-title{font-size:16px;font-weight:700;color:#0f172a;margin-bottom:16px;display:flex;align-items:center;gap:8px}\n#wu-app .wu-section-icon{font-size:20px;line-height:1}\n/* Temperature grid */\n#wu-app .wu-temp-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}\n@media(max-width:560px){#wu-app .wu-temp-grid{grid-template-columns:1fr}}\n/* Generic converter: row of inputs */\n#wu-app .wu-conv-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:12px}\n#wu-app .wu-field{display:flex;flex-direction:column;gap:4px}\n#wu-app .wu-field label{font-size:12px;font-weight:600;color:#64748b;letter-spacing:.03em}\n#wu-app .wu-field input{padding:10px 12px;border:1.5px solid #e2e8f0;border-radius:8px;font-size:15px;font-weight:600;color:#1e293b;background:#fff;outline:none;transition:border-color .15s;width:100%}\n#wu-app .wu-field input:focus{border-color:#0ea5e9}\n#wu-app .wu-field input.source{border-color:#0ea5e9;background:#f0f9ff}\n#wu-app .wu-formula{margin-top:14px;padding:10px 14px;background:#fff;border:1px solid #e2e8f0;border-radius:8px;font-size:12px;color:#64748b;line-height:1.7;font-family:\"SFMono-Regular\",Consolas,monospace}\n#wu-app .wu-formula-title{font-size:11px;font-weight:700;color:#94a3b8;letter-spacing:.06em;text-transform:uppercase;margin-bottom:4px}\n#wu-app .wu-formula-row{color:#475569}\n#wu-app .wu-formula-row strong{color:#0369a1}\n#wu-app .wu-reset-row{margin-top:14px;display:flex;gap:8px;align-items:center}\n#wu-app .wu-btn{padding:7px 16px;border-radius:7px;border:1.5px solid #cbd5e1;background:#f1f5f9;color:#475569;font-size:13px;font-weight:500;cursor:pointer;transition:all .15s}\n#wu-app .wu-btn:hover{border-color:#0ea5e9;color:#0ea5e9}\n#wu-app .wu-divider{border:none;border-top:1px solid #e2e8f0;margin:16px 0}\n\u003c/style\u003e\n\u003c!-- Category tabs --\u003e\n\u003cdiv class=\"wu-tabs\"\u003e\n  \u003cbutton class=\"wu-tab active\" onclick=\"wuTab('temp')\"\u003eTemperature\u003c/button\u003e\n  \u003cbutton class=\"wu-tab\" onclick=\"wuTab('wind')\"\u003eWind Speed\u003c/button\u003e\n  \u003cbutton class=\"wu-tab\" onclick=\"wuTab('pres')\"\u003ePressure\u003c/button\u003e\n  \u003cbutton class=\"wu-tab\" onclick=\"wuTab('precip')\"\u003ePrecipitation\u003c/button\u003e\n  \u003cbutton class=\"wu-tab\" onclick=\"wuTab('vis')\"\u003eVisibility\u003c/button\u003e\n\u003c/div\u003e\n\u003c!-- TEMPERATURE --\u003e\n\u003cdiv class=\"wu-section active\" id=\"wu-sec-temp\"\u003e\n  \u003cdiv class=\"wu-section-title\"\u003e\u003cspan class=\"wu-section-icon\"\u003e\u0026#127777;\u003c/span\u003e Temperature\u003c/div\u003e\n  \u003cdiv class=\"wu-temp-grid\"\u003e\n    \u003cdiv class=\"wu-field\"\u003e\n      \u003clabel\u003eCelsius (°C)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"wu-tc\" placeholder=\"0\" oninput=\"wuTempFrom('c')\" onfocus=\"wuTempFocus('c')\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"wu-field\"\u003e\n      \u003clabel\u003eFahrenheit (°F)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"wu-tf\" placeholder=\"32\" oninput=\"wuTempFrom('f')\" onfocus=\"wuTempFocus('f')\"\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"wu-field\"\u003e\n      \u003clabel\u003eKelvin (K)\u003c/label\u003e\n      \u003cinput type=\"number\" id=\"wu-tk\" placeholder=\"273.15\" oninput=\"wuTempFrom('k')\" onfocus=\"wuTempFocus('k')\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wu-formula\" id=\"wu-temp-formula\"\u003e\n    \u003cdiv class=\"wu-formula-title\"\u003eFormulas\u003c/div\u003e\n    \u003cdiv class=\"wu-formula-row\"\u003e°F = °C × 9/5 + 32\u003c/div\u003e\n    \u003cdiv class=\"wu-formula-row\"\u003eK = °C + 273.15\u003c/div\u003e\n    \u003cdiv class=\"wu-formula-row\"\u003e°C = (°F − 32) × 5/9\u003c/div\u003e\n    \u003cdiv class=\"wu-formula-row\"\u003e°C = K − 273.15\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wu-reset-row\"\u003e\u003cbutton class=\"wu-btn\" onclick=\"wuTempReset()\"\u003eReset\u003c/button\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- WIND SPEED --\u003e\n\u003cdiv class=\"wu-section\" id=\"wu-sec-wind\"\u003e\n  \u003cdiv class=\"wu-section-title\"\u003e\u003cspan class=\"wu-section-icon\"\u003e\u0026#127744;\u003c/span\u003e Wind Speed\u003c/div\u003e\n  \u003cdiv class=\"wu-conv-grid\"\u003e\n    \u003cdiv class=\"wu-field\"\u003e\u003clabel\u003em/s\u003c/label\u003e\u003cinput type=\"number\" id=\"wu-wms\" placeholder=\"0\" oninput=\"wuWindFrom('ms')\" onfocus=\"wuWindFocus('ms')\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"wu-field\"\u003e\u003clabel\u003ekm/h\u003c/label\u003e\u003cinput type=\"number\" id=\"wu-wkh\" placeholder=\"0\" oninput=\"wuWindFrom('kh')\" onfocus=\"wuWindFocus('kh')\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"wu-field\"\u003e\u003clabel\u003emph\u003c/label\u003e\u003cinput type=\"number\" id=\"wu-wmp\" placeholder=\"0\" oninput=\"wuWindFrom('mp')\" onfocus=\"wuWindFocus('mp')\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"wu-field\"\u003e\u003clabel\u003eKnots (kn)\u003c/label\u003e\u003cinput type=\"number\" id=\"wu-wkn\" placeholder=\"0\" oninput=\"wuWindFrom('kn')\" onfocus=\"wuWindFocus('kn')\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wu-formula\"\u003e\n    \u003cdiv class=\"wu-formula-title\"\u003eFormulas (base: m/s)\u003c/div\u003e\n    \u003cdiv class=\"wu-formula-row\"\u003ekm/h = m/s × 3.6\u003c/div\u003e\n    \u003cdiv class=\"wu-formula-row\"\u003emph = m/s × 2.23694\u003c/div\u003e\n    \u003cdiv class=\"wu-formula-row\"\u003eknots = m/s × 1.94384\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wu-reset-row\"\u003e\u003cbutton class=\"wu-btn\" onclick=\"wuWindReset()\"\u003eReset\u003c/button\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- PRESSURE --\u003e\n\u003cdiv class=\"wu-section\" id=\"wu-sec-pres\"\u003e\n  \u003cdiv class=\"wu-section-title\"\u003e\u003cspan class=\"wu-section-icon\"\u003e\u0026#9729;\u003c/span\u003e Pressure\u003c/div\u003e\n  \u003cdiv class=\"wu-conv-grid\"\u003e\n    \u003cdiv class=\"wu-field\"\u003e\u003clabel\u003ehPa (mbar)\u003c/label\u003e\u003cinput type=\"number\" id=\"wu-phpa\" placeholder=\"1013.25\" oninput=\"wuPresFrom('hpa')\" onfocus=\"wuPresFocus('hpa')\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"wu-field\"\u003e\u003clabel\u003emmHg (Torr)\u003c/label\u003e\u003cinput type=\"number\" id=\"wu-pmmhg\" placeholder=\"760\" oninput=\"wuPresFrom('mmhg')\" onfocus=\"wuPresFocus('mmhg')\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"wu-field\"\u003e\u003clabel\u003einHg\u003c/label\u003e\u003cinput type=\"number\" id=\"wu-pinhg\" placeholder=\"29.92\" oninput=\"wuPresFrom('inhg')\" onfocus=\"wuPresFocus('inhg')\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"wu-field\"\u003e\u003clabel\u003eatm\u003c/label\u003e\u003cinput type=\"number\" id=\"wu-patm\" placeholder=\"1\" oninput=\"wuPresFrom('atm')\" onfocus=\"wuPresFocus('atm')\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"wu-field\"\u003e\u003clabel\u003epsi\u003c/label\u003e\u003cinput type=\"number\" id=\"wu-ppsi\" placeholder=\"14.696\" oninput=\"wuPresFrom('psi')\" onfocus=\"wuPresFocus('psi')\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wu-formula\"\u003e\n    \u003cdiv class=\"wu-formula-title\"\u003eFormulas (base: hPa)\u003c/div\u003e\n    \u003cdiv class=\"wu-formula-row\"\u003emmHg = hPa × 0.750062\u003c/div\u003e\n    \u003cdiv class=\"wu-formula-row\"\u003einHg = hPa × 0.02953\u003c/div\u003e\n    \u003cdiv class=\"wu-formula-row\"\u003eatm = hPa / 1013.25\u003c/div\u003e\n    \u003cdiv class=\"wu-formula-row\"\u003epsi = hPa × 0.0145038\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wu-reset-row\"\u003e\u003cbutton class=\"wu-btn\" onclick=\"wuPresReset()\"\u003eReset\u003c/button\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- PRECIPITATION --\u003e\n\u003cdiv class=\"wu-section\" id=\"wu-sec-precip\"\u003e\n  \u003cdiv class=\"wu-section-title\"\u003e\u003cspan class=\"wu-section-icon\"\u003e\u0026#127783;\u003c/span\u003e Precipitation\u003c/div\u003e\n  \u003cdiv class=\"wu-conv-grid\"\u003e\n    \u003cdiv class=\"wu-field\"\u003e\u003clabel\u003eMillimetres (mm)\u003c/label\u003e\u003cinput type=\"number\" id=\"wu-pmm\" placeholder=\"0\" oninput=\"wuPrecipFrom('mm')\" onfocus=\"wuPrecipFocus('mm')\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"wu-field\"\u003e\u003clabel\u003eInches (in)\u003c/label\u003e\u003cinput type=\"number\" id=\"wu-pin\" placeholder=\"0\" oninput=\"wuPrecipFrom('in')\" onfocus=\"wuPrecipFocus('in')\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wu-formula\"\u003e\n    \u003cdiv class=\"wu-formula-title\"\u003eFormulas\u003c/div\u003e\n    \u003cdiv class=\"wu-formula-row\"\u003einches = mm / 25.4\u003c/div\u003e\n    \u003cdiv class=\"wu-formula-row\"\u003emm = inches × 25.4\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wu-reset-row\"\u003e\u003cbutton class=\"wu-btn\" onclick=\"wuPrecipReset()\"\u003eReset\u003c/button\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- VISIBILITY --\u003e\n\u003cdiv class=\"wu-section\" id=\"wu-sec-vis\"\u003e\n  \u003cdiv class=\"wu-section-title\"\u003e\u003cspan class=\"wu-section-icon\"\u003e\u0026#128065;\u003c/span\u003e Visibility\u003c/div\u003e\n  \u003cdiv class=\"wu-conv-grid\"\u003e\n    \u003cdiv class=\"wu-field\"\u003e\u003clabel\u003eKilometres (km)\u003c/label\u003e\u003cinput type=\"number\" id=\"wu-vkm\" placeholder=\"0\" oninput=\"wuVisFrom('km')\" onfocus=\"wuVisFocus('km')\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"wu-field\"\u003e\u003clabel\u003eMiles (mi)\u003c/label\u003e\u003cinput type=\"number\" id=\"wu-vmi\" placeholder=\"0\" oninput=\"wuVisFrom('mi')\" onfocus=\"wuVisFocus('mi')\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wu-formula\"\u003e\n    \u003cdiv class=\"wu-formula-title\"\u003eFormulas\u003c/div\u003e\n    \u003cdiv class=\"wu-formula-row\"\u003emiles = km / 1.60934\u003c/div\u003e\n    \u003cdiv class=\"wu-formula-row\"\u003ekm = miles × 1.60934\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wu-reset-row\"\u003e\u003cbutton class=\"wu-btn\" onclick=\"wuVisReset()\"\u003eReset\u003c/button\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n  var currentTab = 'temp';\n  var tempSrc = 'c';\n  var windSrc = 'ms';\n  var presSrc = 'hpa';\n  var precipSrc = 'mm';\n  var visSrc = 'km';\n\n  /* ---- Tab switching ---- */\n  function tab(id){\n    currentTab = id;\n    document.querySelectorAll('#wu-app .wu-tab').forEach(function(b,i){\n      var ids=['temp','wind','pres','precip','vis'];\n      b.classList.toggle('active', ids[i]===id);\n    });\n    document.querySelectorAll('#wu-app .wu-section').forEach(function(s){\n      s.classList.remove('active');\n    });\n    document.getElementById('wu-sec-'+id).classList.add('active');\n  }\n\n  /* ---- Helpers ---- */\n  function r(v, dp){ return Math.round(v * Math.pow(10,dp)) / Math.pow(10,dp); }\n  function setVal(id, v){ var el=document.getElementById(id); if(el) el.value = isNaN(v)||!isFinite(v)?'':r(v,6); }\n  function setFocus(prefix, src, ids){\n    ids.forEach(function(k){\n      var el = document.getElementById(prefix+k);\n      if(el) el.classList.toggle('source', k===src);\n    });\n  }\n\n  /* ---- TEMPERATURE ---- */\n  function tempFrom(src){\n    tempSrc = src;\n    var vc = parseFloat(document.getElementById('wu-tc').value);\n    var vf = parseFloat(document.getElementById('wu-tf').value);\n    var vk = parseFloat(document.getElementById('wu-tk').value);\n    var c;\n    if(src==='c'){ c = vc; }\n    else if(src==='f'){ c = (vf-32)*5/9; }\n    else { c = vk-273.15; }\n    if(isNaN(c)){ return; }\n    if(src!=='c') setVal('wu-tc', c);\n    if(src!=='f') setVal('wu-tf', c*9/5+32);\n    if(src!=='k') setVal('wu-tk', c+273.15);\n    updateTempFormula(src, c);\n  }\n  function tempFocus(src){ tempSrc=src; setFocus('wu-t', src, ['c','f','k']); }\n  function updateTempFormula(src, c){\n    var el = document.getElementById('wu-temp-formula');\n    var f = r(c*9/5+32,4), k = r(c+273.15,4), cv = r(c,4);\n    var lines = [];\n    if(src==='c'){\n      lines.push('°F = '+cv+' × 9/5 + 32 = \u003cstrong\u003e'+f+' °F\u003c/strong\u003e');\n      lines.push('K = '+cv+' + 273.15 = \u003cstrong\u003e'+k+' K\u003c/strong\u003e');\n    } else if(src==='f'){\n      var fv=r(parseFloat(document.getElementById('wu-tf').value),4);\n      lines.push('°C = ('+fv+' − 32) × 5/9 = \u003cstrong\u003e'+cv+' °C\u003c/strong\u003e');\n      lines.push('K = '+cv+' + 273.15 = \u003cstrong\u003e'+k+' K\u003c/strong\u003e');\n    } else {\n      var kv=r(parseFloat(document.getElementById('wu-tk').value),4);\n      lines.push('°C = '+kv+' − 273.15 = \u003cstrong\u003e'+cv+' °C\u003c/strong\u003e');\n      lines.push('°F = '+cv+' × 9/5 + 32 = \u003cstrong\u003e'+f+' °F\u003c/strong\u003e');\n    }\n    el.innerHTML = '\u003cdiv class=\"wu-formula-title\"\u003eActive formulas\u003c/div\u003e' +\n      lines.map(function(l){ return '\u003cdiv class=\"wu-formula-row\"\u003e'+l+'\u003c/div\u003e'; }).join('');\n  }\n  function tempReset(){ ['wu-tc','wu-tf','wu-tk'].forEach(function(id){ document.getElementById(id).value=''; }); ['wu-tc','wu-tf','wu-tk'].forEach(function(id){ document.getElementById(id).classList.remove('source'); }); document.getElementById('wu-temp-formula').innerHTML='\u003cdiv class=\"wu-formula-title\"\u003eFormulas\u003c/div\u003e\u003cdiv class=\"wu-formula-row\"\u003e°F = °C × 9/5 + 32\u003c/div\u003e\u003cdiv class=\"wu-formula-row\"\u003eK = °C + 273.15\u003c/div\u003e\u003cdiv class=\"wu-formula-row\"\u003e°C = (°F − 32) × 5/9\u003c/div\u003e\u003cdiv class=\"wu-formula-row\"\u003e°C = K − 273.15\u003c/div\u003e'; }\n\n  /* ---- WIND ---- */\n  // base: m/s\n  var windFactors = {ms:1, kh:3.6, mp:2.23694, kn:1.94384};\n  var windIds = {ms:'wu-wms', kh:'wu-wkh', mp:'wu-wmp', kn:'wu-wkn'};\n  function windFrom(src){\n    windSrc = src;\n    var v = parseFloat(document.getElementById(windIds[src]).value);\n    if(isNaN(v)) return;\n    var ms = v / windFactors[src];\n    Object.keys(windIds).forEach(function(k){\n      if(k!==src) setVal(windIds[k], ms*windFactors[k]);\n    });\n  }\n  function windFocus(src){ windSrc=src; Object.keys(windIds).forEach(function(k){ document.getElementById(windIds[k]).classList.toggle('source',k===src); }); }\n  function windReset(){ Object.keys(windIds).forEach(function(k){ document.getElementById(windIds[k]).value=''; document.getElementById(windIds[k]).classList.remove('source'); }); }\n\n  /* ---- PRESSURE ---- */\n  // base: hPa\n  var presFactors = {hpa:1, mmhg:0.750062, inhg:0.02953, atm:1/1013.25, psi:0.0145038};\n  var presIds = {hpa:'wu-phpa', mmhg:'wu-pmmhg', inhg:'wu-pinhg', atm:'wu-patm', psi:'wu-ppsi'};\n  function presFrom(src){\n    presSrc = src;\n    var v = parseFloat(document.getElementById(presIds[src]).value);\n    if(isNaN(v)) return;\n    var hpa = v / presFactors[src];\n    Object.keys(presIds).forEach(function(k){\n      if(k!==src) setVal(presIds[k], hpa*presFactors[k]);\n    });\n  }\n  function presFocus(src){ presSrc=src; Object.keys(presIds).forEach(function(k){ document.getElementById(presIds[k]).classList.toggle('source',k===src); }); }\n  function presReset(){ Object.keys(presIds).forEach(function(k){ document.getElementById(presIds[k]).value=''; document.getElementById(presIds[k]).classList.remove('source'); }); }\n\n  /* ---- PRECIPITATION ---- */\n  function precipFrom(src){\n    precipSrc = src;\n    var v = parseFloat(document.getElementById(src==='mm'?'wu-pmm':'wu-pin').value);\n    if(isNaN(v)) return;\n    if(src==='mm') setVal('wu-pin', v/25.4);\n    else setVal('wu-pmm', v*25.4);\n  }\n  function precipFocus(src){ precipSrc=src; ['wu-pmm','wu-pin'].forEach(function(id){ document.getElementById(id).classList.remove('source'); }); document.getElementById(src==='mm'?'wu-pmm':'wu-pin').classList.add('source'); }\n  function precipReset(){ ['wu-pmm','wu-pin'].forEach(function(id){ document.getElementById(id).value=''; document.getElementById(id).classList.remove('source'); }); }\n\n  /* ---- VISIBILITY ---- */\n  function visFrom(src){\n    visSrc = src;\n    var v = parseFloat(document.getElementById(src==='km'?'wu-vkm':'wu-vmi').value);\n    if(isNaN(v)) return;\n    if(src==='km') setVal('wu-vmi', v/1.60934);\n    else setVal('wu-vkm', v*1.60934);\n  }\n  function visFocus(src){ visSrc=src; ['wu-vkm','wu-vmi'].forEach(function(id){ document.getElementById(id).classList.remove('source'); }); document.getElementById(src==='km'?'wu-vkm':'wu-vmi').classList.add('source'); }\n  function visReset(){ ['wu-vkm','wu-vmi'].forEach(function(id){ document.getElementById(id).value=''; document.getElementById(id).classList.remove('source'); }); }\n\n  /* ---- Expose ---- */\n  window.wuTab = tab;\n  window.wuTempFrom = tempFrom; window.wuTempFocus = tempFocus; window.wuTempReset = tempReset;\n  window.wuWindFrom = windFrom; window.wuWindFocus = windFocus; window.wuWindReset = windReset;\n  window.wuPresFrom = presFrom; window.wuPresFocus = presFocus; window.wuPresReset = presReset;\n  window.wuPrecipFrom = precipFrom; window.wuPrecipFocus = precipFocus; window.wuPrecipReset = precipReset;\n  window.wuVisFrom = visFrom; window.wuVisFocus = visFocus; window.wuVisReset = visReset;\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cstrong\u003eConversion references:\u003c/strong\u003e\u003c/p\u003e","title":"Weather Unit Converter"},{"content":" Plan your perfect wedding without the financial stress. Enter your total budget, adjust each category with sliders, and see your full breakdown instantly — including a live pie chart and per-guest cost. Total Wedding Budget ($) Guest Count Budget $30,000 Allocated $30,000 Balanced Allocation 100% of budget Warning: category percentages add up to more or less than 100%. Adjust sliders to balance. Breakdown Per-guest cost:\n$300.00 Related Free Tools Monthly Budget Calculator Savings Goal Calculator Debt Payoff Calculator Net Worth Calculator ","permalink":"https://productivity-works.com/tools/wedding-budget-calculator/","summary":"\u003cdiv id=\"wb-app\"\u003e\n\u003cstyle\u003e\n#wb-app {\n  font-family: 'Segoe UI', Arial, sans-serif;\n  max-width: 860px;\n  margin: 0 auto;\n  color: #2d2d2d;\n  padding: 0 8px;\n}\n#wb-app h2 {\n  font-size: 1.4rem;\n  font-weight: 700;\n  color: #1a1a1a;\n  margin: 1.5rem 0 0.5rem;\n  border-bottom: 2px solid #f0c4d4;\n  padding-bottom: 0.3rem;\n}\n#wb-app .wb-intro {\n  background: linear-gradient(135deg, #fff5f8 0%, #fdf0f5 100%);\n  border: 1px solid #f0c4d4;\n  border-radius: 10px;\n  padding: 1.2rem 1.4rem;\n  margin-bottom: 1.5rem;\n  font-size: 0.97rem;\n  color: #555;\n}\n#wb-app .wb-top-row {\n  display: flex;\n  gap: 16px;\n  flex-wrap: wrap;\n  margin-bottom: 1.4rem;\n}\n#wb-app .wb-top-card {\n  flex: 1 1 200px;\n  background: #fff;\n  border: 1px solid #e8d0d8;\n  border-radius: 10px;\n  padding: 1rem 1.2rem;\n}\n#wb-app .wb-top-card label {\n  display: block;\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #888;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  margin-bottom: 0.4rem;\n}\n#wb-app .wb-top-card input {\n  width: 100%;\n  border: 1.5px solid #e0c8d0;\n  border-radius: 6px;\n  padding: 0.45rem 0.7rem;\n  font-size: 1.1rem;\n  font-weight: 600;\n  color: #c0396b;\n  background: #fffafb;\n  box-sizing: border-box;\n  outline: none;\n  transition: border-color 0.2s;\n}\n#wb-app .wb-top-card input:focus {\n  border-color: #c0396b;\n  background: #fff;\n}\n#wb-app .wb-status-bar {\n  background: #fff;\n  border: 2px solid #e8d0d8;\n  border-radius: 10px;\n  padding: 1rem 1.4rem;\n  margin-bottom: 1.4rem;\n  display: flex;\n  align-items: center;\n  gap: 20px;\n  flex-wrap: wrap;\n}\n#wb-app .wb-status-label {\n  font-size: 0.85rem;\n  color: #888;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n}\n#wb-app .wb-status-amount {\n  font-size: 1.5rem;\n  font-weight: 800;\n  color: #c0396b;\n}\n#wb-app .wb-status-diff {\n  font-size: 1rem;\n  font-weight: 700;\n  padding: 0.3rem 0.8rem;\n  border-radius: 20px;\n}\n#wb-app .wb-status-diff.under {\n  background: #e8f5e9;\n  color: #2e7d32;\n}\n#wb-app .wb-status-diff.over {\n  background: #fdecea;\n  color: #c62828;\n}\n#wb-app .wb-status-diff.exact {\n  background: #e8eaf6;\n  color: #3949ab;\n}\n#wb-app .wb-progress-wrap {\n  flex: 1 1 200px;\n  min-width: 120px;\n}\n#wb-app .wb-progress-track {\n  background: #f0e0e8;\n  border-radius: 6px;\n  height: 12px;\n  overflow: hidden;\n}\n#wb-app .wb-progress-fill {\n  height: 100%;\n  border-radius: 6px;\n  transition: width 0.4s, background 0.4s;\n}\n#wb-app .wb-main {\n  display: flex;\n  gap: 24px;\n  flex-wrap: wrap;\n}\n#wb-app .wb-categories {\n  flex: 1 1 340px;\n}\n#wb-app .wb-chart-col {\n  flex: 0 0 240px;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n}\n#wb-app .wb-category-row {\n  background: #fff;\n  border: 1px solid #ecdde4;\n  border-radius: 8px;\n  padding: 0.75rem 1rem;\n  margin-bottom: 0.7rem;\n}\n#wb-app .wb-cat-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 0.3rem;\n}\n#wb-app .wb-cat-name {\n  font-weight: 600;\n  font-size: 0.95rem;\n  color: #333;\n}\n#wb-app .wb-cat-amounts {\n  font-size: 0.82rem;\n  color: #888;\n  text-align: right;\n}\n#wb-app .wb-cat-pct {\n  font-size: 0.8rem;\n  font-weight: 700;\n  color: #c0396b;\n  min-width: 36px;\n  text-align: right;\n}\n#wb-app .wb-cat-input-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n#wb-app .wb-cat-slider {\n  -webkit-appearance: none;\n  appearance: none;\n  flex: 1;\n  height: 5px;\n  border-radius: 3px;\n  background: #f0c4d4;\n  outline: none;\n  cursor: pointer;\n}\n#wb-app .wb-cat-slider::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  width: 16px;\n  height: 16px;\n  border-radius: 50%;\n  background: #c0396b;\n  cursor: pointer;\n  box-shadow: 0 1px 3px rgba(0,0,0,0.2);\n}\n#wb-app .wb-cat-slider::-moz-range-thumb {\n  width: 16px;\n  height: 16px;\n  border-radius: 50%;\n  background: #c0396b;\n  cursor: pointer;\n  border: none;\n}\n#wb-app .wb-warning {\n  font-size: 0.8rem;\n  color: #e65100;\n  background: #fff3e0;\n  border: 1px solid #ffcc80;\n  border-radius: 6px;\n  padding: 0.4rem 0.7rem;\n  margin-bottom: 0.8rem;\n  display: none;\n}\n#wb-app .wb-chart-title {\n  font-size: 0.85rem;\n  font-weight: 600;\n  color: #888;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  margin-bottom: 0.6rem;\n  text-align: center;\n}\n#wb-app canvas#wb-pie {\n  border-radius: 50%;\n  box-shadow: 0 2px 12px rgba(192,57,107,0.10);\n}\n#wb-app .wb-legend {\n  margin-top: 1rem;\n  width: 100%;\n}\n#wb-app .wb-legend-item {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  font-size: 0.78rem;\n  color: #555;\n  margin-bottom: 0.3rem;\n}\n#wb-app .wb-legend-dot {\n  width: 11px;\n  height: 11px;\n  border-radius: 3px;\n  flex-shrink: 0;\n}\n#wb-app .wb-per-person {\n  background: #fff5f8;\n  border: 1px solid #f0c4d4;\n  border-radius: 8px;\n  padding: 0.8rem 1rem;\n  margin-top: 1rem;\n  font-size: 0.88rem;\n  color: #555;\n  text-align: center;\n}\n#wb-app .wb-per-person strong {\n  font-size: 1.1rem;\n  color: #c0396b;\n}\n#wb-app .wb-related {\n  margin-top: 2rem;\n  background: #f8f9fa;\n  border: 1px solid #e0e0e0;\n  border-radius: 10px;\n  padding: 1rem 1.3rem;\n}\n#wb-app .wb-related h3 {\n  font-size: 0.95rem;\n  font-weight: 700;\n  color: #444;\n  margin: 0 0 0.6rem;\n}\n#wb-app .wb-related ul {\n  margin: 0;\n  padding-left: 1.2rem;\n  font-size: 0.88rem;\n  color: #555;\n}\n#wb-app .wb-related ul li {\n  margin-bottom: 0.3rem;\n}\n#wb-app .wb-related a {\n  color: #c0396b;\n  text-decoration: none;\n}\n#wb-app .wb-related a:hover {\n  text-decoration: underline;\n}\n@media (max-width: 600px) {\n  #wb-app .wb-chart-col {\n    flex: 1 1 100%;\n    align-items: center;\n  }\n  #wb-app .wb-status-bar {\n    gap: 10px;\n  }\n}\n\u003c/style\u003e\n\u003cdiv class=\"wb-intro\"\u003e\n  Plan your perfect wedding without the financial stress. Enter your total budget, adjust each category with sliders, and see your full breakdown instantly — including a live pie chart and per-guest cost.\n\u003c/div\u003e\n\u003cdiv class=\"wb-top-row\"\u003e\n  \u003cdiv class=\"wb-top-card\"\u003e\n    \u003clabel for=\"wb-total-budget\"\u003eTotal Wedding Budget ($)\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"wb-total-budget\" value=\"30000\" min=\"0\" step=\"500\" placeholder=\"30000\"\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wb-top-card\"\u003e\n    \u003clabel for=\"wb-guest-count\"\u003eGuest Count\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"wb-guest-count\" value=\"100\" min=\"1\" step=\"1\" placeholder=\"100\"\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"wb-status-bar\"\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"wb-status-label\"\u003eBudget\u003c/div\u003e\n    \u003cdiv class=\"wb-status-amount\" id=\"wb-disp-budget\"\u003e$30,000\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"wb-status-label\"\u003eAllocated\u003c/div\u003e\n    \u003cdiv class=\"wb-status-amount\" id=\"wb-disp-allocated\"\u003e$30,000\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv id=\"wb-disp-diff\" class=\"wb-status-diff exact\"\u003eBalanced\u003c/div\u003e\n  \u003cdiv class=\"wb-progress-wrap\"\u003e\n    \u003cdiv class=\"wb-status-label\" style=\"margin-bottom:0.3rem;\"\u003eAllocation\u003c/div\u003e\n    \u003cdiv class=\"wb-progress-track\"\u003e\n      \u003cdiv class=\"wb-progress-fill\" id=\"wb-progress-fill\" style=\"width:100%;background:#c0396b;\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv style=\"font-size:0.78rem;color:#aaa;margin-top:3px;\" id=\"wb-pct-total-label\"\u003e100% of budget\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"wb-pct-warning\" class=\"wb-warning\"\u003e\n  Warning: category percentages add up to more or less than 100%. Adjust sliders to balance.\n\u003c/div\u003e\n\u003cdiv class=\"wb-main\"\u003e\n  \u003cdiv class=\"wb-categories\" id=\"wb-categories-list\"\u003e\n    \u003c!-- Rows injected by JS --\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wb-chart-col\"\u003e\n    \u003cdiv class=\"wb-chart-title\"\u003eBreakdown\u003c/div\u003e\n    \u003ccanvas id=\"wb-pie\" width=\"220\" height=\"220\"\u003e\u003c/canvas\u003e\n    \u003cdiv class=\"wb-legend\" id=\"wb-legend\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"wb-per-person\" id=\"wb-per-person\"\u003e\n      Per-guest cost:\u003cbr\u003e\u003cstrong id=\"wb-per-person-val\"\u003e$300.00\u003c/strong\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"wb-related\"\u003e\n  \u003ch3\u003eRelated Free Tools\u003c/h3\u003e\n  \u003cul\u003e\n    \u003cli\u003e\u003ca href=\"/tools/budget-planner/\"\u003eMonthly Budget Calculator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/savings-goal-calculator/\"\u003eSavings Goal Calculator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/debt-payoff-calculator/\"\u003eDebt Payoff Calculator\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"/tools/net-worth-calculator/\"\u003eNet Worth Calculator\u003c/a\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  'use strict';\n\n  const CATEGORIES = [\n    { name: 'Venue',         pct: 40, color: '#c0396b' },\n    { name: 'Catering',      pct: 25, color: '#e05c8a' },\n    { name: 'Photography',   pct: 10, color: '#f4a0b5' },\n    { name: 'Flowers',       pct:  5, color: '#f7c5d5' },\n    { name: 'Music',         pct:  5, color: '#a0c4ff' },\n    { name: 'Attire',        pct:  5, color: '#b5ead7' },\n    { name: 'Invitations',   pct:  3, color: '#ffd6a5' },\n    { name: 'Favors',        pct:  2, color: '#caffbf' },\n    { name: 'Other',         pct:  5, color: '#c9c9ff' },\n  ];\n\n  let state = {\n    budget: 30000,\n    guests: 100,\n    pcts: CATEGORIES.map(c =\u003e c.pct),\n  };\n\n  function fmt(n) {\n    return '$' + n.toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 0 });\n  }\n  function fmtDec(n) {\n    return '$' + n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });\n  }\n\n  // Build category rows\n  const list = document.getElementById('wb-categories-list');\n  CATEGORIES.forEach((cat, i) =\u003e {\n    const row = document.createElement('div');\n    row.className = 'wb-category-row';\n    row.innerHTML = `\n      \u003cdiv class=\"wb-cat-header\"\u003e\n        \u003cspan class=\"wb-cat-name\"\u003e${cat.name}\u003c/span\u003e\n        \u003cdiv style=\"text-align:right\"\u003e\n          \u003cspan class=\"wb-cat-amounts\" id=\"wb-amt-${i}\"\u003e\u003c/span\u003e\n          \u003cspan class=\"wb-cat-pct\" id=\"wb-pct-${i}\"\u003e${cat.pct}%\u003c/span\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"wb-cat-input-row\"\u003e\n        \u003cinput type=\"range\" class=\"wb-cat-slider\" id=\"wb-slider-${i}\"\n          min=\"0\" max=\"60\" step=\"1\" value=\"${cat.pct}\"\n          aria-label=\"${cat.name} percentage\"\u003e\n      \u003c/div\u003e\n    `;\n    list.appendChild(row);\n\n    document.getElementById(`wb-slider-${i}`).addEventListener('input', function() {\n      state.pcts[i] = parseInt(this.value, 10);\n      render();\n    });\n  });\n\n  document.getElementById('wb-total-budget').addEventListener('input', function() {\n    const v = parseFloat(this.value);\n    state.budget = isNaN(v) ? 0 : v;\n    render();\n  });\n  document.getElementById('wb-guest-count').addEventListener('input', function() {\n    const v = parseInt(this.value, 10);\n    state.guests = isNaN(v) || v \u003c 1 ? 1 : v;\n    render();\n  });\n\n  function drawPie(data, colors) {\n    const canvas = document.getElementById('wb-pie');\n    const ctx = canvas.getContext('2d');\n    const W = canvas.width, H = canvas.height;\n    const cx = W / 2, cy = H / 2, r = Math.min(cx, cy) - 6;\n    ctx.clearRect(0, 0, W, H);\n\n    const total = data.reduce((a, b) =\u003e a + b, 0);\n    if (total === 0) return;\n\n    let startAngle = -Math.PI / 2;\n    data.forEach((val, i) =\u003e {\n      if (val \u003c= 0) return;\n      const slice = (val / total) * 2 * Math.PI;\n      ctx.beginPath();\n      ctx.moveTo(cx, cy);\n      ctx.arc(cx, cy, r, startAngle, startAngle + slice);\n      ctx.closePath();\n      ctx.fillStyle = colors[i];\n      ctx.fill();\n      ctx.strokeStyle = '#fff';\n      ctx.lineWidth = 2;\n      ctx.stroke();\n      startAngle += slice;\n    });\n\n    // center circle (donut)\n    ctx.beginPath();\n    ctx.arc(cx, cy, r * 0.44, 0, 2 * Math.PI);\n    ctx.fillStyle = '#fff';\n    ctx.fill();\n  }\n\n  function buildLegend(amounts) {\n    const leg = document.getElementById('wb-legend');\n    leg.innerHTML = '';\n    CATEGORIES.forEach((cat, i) =\u003e {\n      if (amounts[i] \u003c= 0) return;\n      const item = document.createElement('div');\n      item.className = 'wb-legend-item';\n      item.innerHTML = `\n        \u003cspan class=\"wb-legend-dot\" style=\"background:${cat.color}\"\u003e\u003c/span\u003e\n        \u003cspan\u003e${cat.name}: ${fmt(amounts[i])}\u003c/span\u003e\n      `;\n      leg.appendChild(item);\n    });\n  }\n\n  function render() {\n    const budget = state.budget;\n    const guests = state.guests;\n    const pctTotal = state.pcts.reduce((a, b) =\u003e a + b, 0);\n    const amounts = state.pcts.map(p =\u003e budget * p / 100);\n    const allocated = amounts.reduce((a, b) =\u003e a + b, 0);\n\n    // Update slider labels\n    CATEGORIES.forEach((cat, i) =\u003e {\n      document.getElementById(`wb-pct-${i}`).textContent = state.pcts[i] + '%';\n      document.getElementById(`wb-slider-${i}`).value = state.pcts[i];\n      document.getElementById(`wb-amt-${i}`).textContent = fmt(amounts[i]);\n    });\n\n    // Status bar\n    document.getElementById('wb-disp-budget').textContent = fmt(budget);\n    document.getElementById('wb-disp-allocated').textContent = fmt(allocated);\n\n    const diff = budget - allocated;\n    const diffEl = document.getElementById('wb-disp-diff');\n    if (Math.abs(diff) \u003c 1) {\n      diffEl.textContent = 'Balanced';\n      diffEl.className = 'wb-status-diff exact';\n    } else if (diff \u003e 0) {\n      diffEl.textContent = fmt(diff) + ' under budget';\n      diffEl.className = 'wb-status-diff under';\n    } else {\n      diffEl.textContent = fmt(Math.abs(diff)) + ' over budget';\n      diffEl.className = 'wb-status-diff over';\n    }\n\n    // Progress bar\n    const pf = document.getElementById('wb-progress-fill');\n    const pPct = budget \u003e 0 ? Math.min((allocated / budget) * 100, 120) : 0;\n    pf.style.width = Math.min(pPct, 100) + '%';\n    pf.style.background = pPct \u003e 100 ? '#c62828' : pPct \u003e 90 ? '#e65100' : '#c0396b';\n    document.getElementById('wb-pct-total-label').textContent = pctTotal + '% of budget';\n\n    // Warning\n    const warn = document.getElementById('wb-pct-warning');\n    warn.style.display = (pctTotal !== 100) ? 'block' : 'none';\n    warn.textContent = pctTotal \u003e 100\n      ? `Warning: category percentages add up to ${pctTotal}% (${pctTotal - 100}% over 100%). Adjust sliders to balance.`\n      : `Note: category percentages add up to ${pctTotal}% (${100 - pctTotal}% unallocated). Adjust sliders to balance.`;\n\n    // Per guest\n    document.getElementById('wb-per-person-val').textContent = guests \u003e 0\n      ? fmtDec(allocated / guests)\n      : '$0.00';\n\n    // Pie chart\n    drawPie(amounts, CATEGORIES.map(c =\u003e c.color));\n    buildLegend(amounts);\n  }\n\n  render();\n})();\n\u003c/script\u003e\n\u003c/div\u003e","title":"Wedding Budget Calculator - Plan Your Wedding Expenses"},{"content":"Block out distractions and create your ideal sound environment — mix white, pink, brown, and rain noise directly in your browser with no downloads or sign-ups required.\nWhite 70% Pink 70% Brown 70% Rain 70% Master vol 80% \u0026#9654; \u0026lt;select id=\u0026quot;ng-timer-select\u0026quot; aria-label=\u0026quot;Auto-stop timer\u0026quot;\u0026gt; \u0026lt;option value=\u0026quot;0\u0026quot;\u0026gt;No timer\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;900\u0026quot;\u0026gt;15 min\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;1800\u0026quot;\u0026gt;30 min\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;3600\u0026quot;\u0026gt;60 min\u0026lt;/option\u0026gt; \u0026lt;option value=\u0026quot;7200\u0026quot;\u0026gt;2 hr\u0026lt;/option\u0026gt; \u0026lt;/select\u0026gt; \u0026lt;span class=\u0026quot;ng-timer-display\u0026quot; id=\u0026quot;ng-timer-display\u0026quot; aria-live=\u0026quot;polite\u0026quot;\u0026gt;\u0026lt;/span\u0026gt; Press play to start\nFocus with Pomodoro → Pomodoro Timer Test typing speed → Typing Speed Test Generate lorem ipsum → Lorem Ipsum Generator ","permalink":"https://productivity-works.com/tools/noise-generator/","summary":"\u003cp\u003eBlock out distractions and create your ideal sound environment — mix white, pink, brown, and rain noise directly in your browser with no downloads or sign-ups required.\u003c/p\u003e\n\u003cdiv id=\"ng-app\"\u003e\n\u003cstyle\u003e\n#ng-app {\n  font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n  max-width: 680px;\n  margin: 0 auto;\n  color: #e2e8f0;\n  background: #0f172a;\n  border-radius: 16px;\n  padding: 28px 24px 32px;\n  box-shadow: 0 8px 32px rgba(0,0,0,0.45);\n}\n\n#ng-app * {\n  box-sizing: border-box;\n}\n\n#ng-app h2 {\n  margin: 0 0 4px;\n  font-size: 1.25rem;\n  font-weight: 700;\n  color: #f8fafc;\n  letter-spacing: -0.01em;\n}\n\n#ng-app .ng-subtitle {\n  font-size: 0.82rem;\n  color: #94a3b8;\n  margin: 0 0 24px;\n}\n\n/* Canvas */\n#ng-app canvas {\n  display: block;\n  width: 100%;\n  height: 90px;\n  background: #1e293b;\n  border-radius: 10px;\n  margin-bottom: 24px;\n}\n\n/* Channel cards */\n#ng-app .ng-channels {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 12px;\n  margin-bottom: 24px;\n}\n\n@media (max-width: 480px) {\n  #ng-app .ng-channels {\n    grid-template-columns: 1fr;\n  }\n}\n\n#ng-app .ng-channel {\n  background: #1e293b;\n  border: 1.5px solid #334155;\n  border-radius: 12px;\n  padding: 14px 16px;\n  transition: border-color 0.2s;\n}\n\n#ng-app .ng-channel.ng-active {\n  border-color: #38bdf8;\n  background: #0c2a3f;\n}\n\n#ng-app .ng-channel-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 10px;\n}\n\n#ng-app .ng-channel-label {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  font-size: 0.9rem;\n  font-weight: 600;\n  color: #cbd5e1;\n}\n\n#ng-app .ng-channel-label .ng-dot {\n  width: 10px;\n  height: 10px;\n  border-radius: 50%;\n  flex-shrink: 0;\n}\n\n#ng-app .dot-white  { background: #f1f5f9; }\n#ng-app .dot-pink   { background: #f472b6; }\n#ng-app .dot-brown  { background: #a16207; }\n#ng-app .dot-rain   { background: #38bdf8; }\n\n/* Toggle switch */\n#ng-app .ng-toggle {\n  position: relative;\n  width: 38px;\n  height: 21px;\n  flex-shrink: 0;\n}\n\n#ng-app .ng-toggle input {\n  opacity: 0;\n  width: 0;\n  height: 0;\n  position: absolute;\n}\n\n#ng-app .ng-toggle-track {\n  position: absolute;\n  inset: 0;\n  background: #334155;\n  border-radius: 21px;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n\n#ng-app .ng-toggle input:checked + .ng-toggle-track {\n  background: #0ea5e9;\n}\n\n#ng-app .ng-toggle-track::after {\n  content: \"\";\n  position: absolute;\n  left: 3px;\n  top: 3px;\n  width: 15px;\n  height: 15px;\n  background: #fff;\n  border-radius: 50%;\n  transition: transform 0.2s;\n}\n\n#ng-app .ng-toggle input:checked + .ng-toggle-track::after {\n  transform: translateX(17px);\n}\n\n/* Volume slider */\n#ng-app .ng-slider-row {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n\n#ng-app .ng-slider-row span {\n  font-size: 0.75rem;\n  color: #64748b;\n  width: 28px;\n  text-align: right;\n  flex-shrink: 0;\n}\n\n#ng-app input[type=\"range\"] {\n  -webkit-appearance: none;\n  appearance: none;\n  flex: 1;\n  height: 5px;\n  background: #334155;\n  border-radius: 5px;\n  outline: none;\n  cursor: pointer;\n}\n\n#ng-app input[type=\"range\"]::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  width: 16px;\n  height: 16px;\n  border-radius: 50%;\n  background: #38bdf8;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n\n#ng-app input[type=\"range\"]::-moz-range-thumb {\n  width: 16px;\n  height: 16px;\n  border-radius: 50%;\n  background: #38bdf8;\n  border: none;\n  cursor: pointer;\n}\n\n#ng-app input[type=\"range\"]:hover::-webkit-slider-thumb {\n  background: #7dd3fc;\n}\n\n/* Master controls */\n#ng-app .ng-master {\n  background: #1e293b;\n  border-radius: 12px;\n  padding: 18px 20px;\n}\n\n#ng-app .ng-master-row {\n  display: flex;\n  align-items: center;\n  gap: 14px;\n  margin-bottom: 16px;\n}\n\n#ng-app .ng-master-label {\n  font-size: 0.8rem;\n  font-weight: 600;\n  color: #94a3b8;\n  width: 90px;\n  flex-shrink: 0;\n}\n\n#ng-app .ng-controls-row {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  gap: 16px;\n  flex-wrap: wrap;\n  margin-top: 4px;\n}\n\n/* Play button */\n#ng-app #ng-play-btn {\n  width: 64px;\n  height: 64px;\n  border-radius: 50%;\n  border: none;\n  background: linear-gradient(135deg, #0ea5e9, #6366f1);\n  color: #fff;\n  font-size: 1.6rem;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  flex-shrink: 0;\n  box-shadow: 0 4px 16px rgba(14,165,233,0.35);\n  transition: transform 0.15s, box-shadow 0.15s;\n  line-height: 1;\n}\n\n#ng-app #ng-play-btn:hover {\n  transform: scale(1.06);\n  box-shadow: 0 6px 22px rgba(14,165,233,0.5);\n}\n\n#ng-app #ng-play-btn:active {\n  transform: scale(0.97);\n}\n\n/* Timer select */\n#ng-app #ng-timer-select {\n  background: #0f172a;\n  border: 1.5px solid #334155;\n  color: #cbd5e1;\n  font-family: inherit;\n  font-size: 0.82rem;\n  border-radius: 8px;\n  padding: 8px 10px;\n  cursor: pointer;\n  outline: none;\n  transition: border-color 0.2s;\n}\n\n#ng-app #ng-timer-select:hover,\n#ng-app #ng-timer-select:focus {\n  border-color: #38bdf8;\n}\n\n#ng-app .ng-timer-display {\n  font-size: 0.8rem;\n  color: #38bdf8;\n  font-variant-numeric: tabular-nums;\n  min-width: 52px;\n  text-align: center;\n}\n\n/* Status */\n#ng-app .ng-status {\n  text-align: center;\n  font-size: 0.75rem;\n  color: #475569;\n  margin-top: 14px;\n}\n\u003c/style\u003e\n\u003cp\u003e\u003ccanvas id=\"ng-canvas\" width=\"640\" height=\"90\" aria-hidden=\"true\"\u003e\u003c/canvas\u003e\u003c/p\u003e","title":"White Noise Generator"},{"content":" White Noise Generator Generate ambient sounds to boost focus, mask distractions, and improve sleep — all in your browser. No downloads needed.\nNoise Type 🌫White 🌸Pink 🌿Brown Volume 🔈 70% Timer Continuous 15 min 30 min 60 min ▶ Start Playing Select a noise type and press play Which noise type should I choose? White Noise — Equal energy at all frequencies. Best for blocking office chatter, AC hum, or general distractions. Great for concentration tasks.\nPink Noise — Energy decreases with frequency (1/f). Sounds more natural and balanced. Research suggests it may improve memory consolidation during sleep.\nBrown Noise — Heavier bass, similar to distant thunder or ocean surf. Many people find it deeply relaxing and ideal for sleep or meditation.\nRelated Free Tools Pomodoro Timer — Pair focused work sessions with white noise Time Blocking Planner — Schedule deep-work blocks Focus Session Tracker — Track distraction-free streaks ","permalink":"https://productivity-works.com/tools/white-noise-generator/","summary":"\u003cdiv id=\"wn-app\"\u003e\n\u003cstyle\u003e\n#wn-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 680px;\n  margin: 0 auto;\n  padding: 24px 16px;\n  color: #1a1a2e;\n}\n#wn-app h2 {\n  font-size: 1.4rem;\n  margin: 0 0 6px;\n  color: #1a1a2e;\n}\n#wn-app .wn-subtitle {\n  font-size: 0.9rem;\n  color: #666;\n  margin: 0 0 28px;\n}\n#wn-app .wn-card {\n  background: #f8f9ff;\n  border: 1px solid #e0e4f0;\n  border-radius: 16px;\n  padding: 28px 24px;\n  margin-bottom: 20px;\n}\n#wn-app .wn-noise-types {\n  display: flex;\n  gap: 10px;\n  margin-bottom: 24px;\n  flex-wrap: wrap;\n}\n#wn-app .wn-noise-btn {\n  flex: 1;\n  min-width: 90px;\n  padding: 14px 8px;\n  border: 2px solid #d0d6f0;\n  border-radius: 12px;\n  background: #fff;\n  cursor: pointer;\n  text-align: center;\n  font-size: 0.85rem;\n  font-weight: 600;\n  color: #555;\n  transition: all 0.2s ease;\n  user-select: none;\n}\n#wn-app .wn-noise-btn:hover {\n  border-color: #6c63ff;\n  color: #6c63ff;\n  background: #f0eeff;\n}\n#wn-app .wn-noise-btn.active {\n  border-color: #6c63ff;\n  background: #6c63ff;\n  color: #fff;\n}\n#wn-app .wn-noise-btn .wn-icon {\n  display: block;\n  font-size: 1.6rem;\n  margin-bottom: 4px;\n}\n#wn-app .wn-section-label {\n  font-size: 0.78rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: #888;\n  margin-bottom: 10px;\n}\n#wn-app .wn-volume-row {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  margin-bottom: 24px;\n}\n#wn-app .wn-volume-icon {\n  font-size: 1.2rem;\n  width: 24px;\n  text-align: center;\n  flex-shrink: 0;\n}\n#wn-app input[type=\"range\"] {\n  flex: 1;\n  -webkit-appearance: none;\n  appearance: none;\n  height: 6px;\n  border-radius: 3px;\n  background: linear-gradient(to right, #6c63ff 0%, #6c63ff var(--vol-pct, 70%), #d0d6f0 var(--vol-pct, 70%), #d0d6f0 100%);\n  outline: none;\n  cursor: pointer;\n}\n#wn-app input[type=\"range\"]::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 20px;\n  height: 20px;\n  border-radius: 50%;\n  background: #6c63ff;\n  box-shadow: 0 2px 6px rgba(108,99,255,0.4);\n  cursor: pointer;\n  transition: transform 0.15s;\n}\n#wn-app input[type=\"range\"]::-webkit-slider-thumb:hover {\n  transform: scale(1.15);\n}\n#wn-app .wn-vol-label {\n  font-size: 0.85rem;\n  font-weight: 700;\n  color: #6c63ff;\n  width: 36px;\n  text-align: right;\n  flex-shrink: 0;\n}\n#wn-app .wn-timer-row {\n  display: flex;\n  gap: 8px;\n  margin-bottom: 28px;\n  flex-wrap: wrap;\n}\n#wn-app .wn-timer-btn {\n  padding: 8px 16px;\n  border: 2px solid #d0d6f0;\n  border-radius: 8px;\n  background: #fff;\n  cursor: pointer;\n  font-size: 0.82rem;\n  font-weight: 600;\n  color: #666;\n  transition: all 0.2s;\n}\n#wn-app .wn-timer-btn:hover {\n  border-color: #6c63ff;\n  color: #6c63ff;\n}\n#wn-app .wn-timer-btn.active {\n  border-color: #6c63ff;\n  background: #eeecff;\n  color: #6c63ff;\n}\n#wn-app .wn-play-btn {\n  display: block;\n  width: 100%;\n  padding: 18px;\n  border: none;\n  border-radius: 14px;\n  background: linear-gradient(135deg, #6c63ff, #a78bfa);\n  color: #fff;\n  font-size: 1.1rem;\n  font-weight: 700;\n  cursor: pointer;\n  letter-spacing: 0.02em;\n  box-shadow: 0 4px 16px rgba(108,99,255,0.35);\n  transition: all 0.2s;\n}\n#wn-app .wn-play-btn:hover {\n  transform: translateY(-2px);\n  box-shadow: 0 6px 22px rgba(108,99,255,0.45);\n}\n#wn-app .wn-play-btn:active {\n  transform: translateY(0);\n}\n#wn-app .wn-play-btn.playing {\n  background: linear-gradient(135deg, #e53e3e, #fc8181);\n  box-shadow: 0 4px 16px rgba(229,62,62,0.35);\n}\n#wn-app .wn-canvas-wrap {\n  margin-bottom: 20px;\n  border-radius: 12px;\n  overflow: hidden;\n  background: #1a1a2e;\n}\n#wn-app canvas {\n  display: block;\n  width: 100%;\n  height: 80px;\n}\n#wn-app .wn-status {\n  text-align: center;\n  font-size: 0.88rem;\n  color: #888;\n  min-height: 1.4em;\n  margin-top: 14px;\n}\n#wn-app .wn-status.active {\n  color: #6c63ff;\n  font-weight: 600;\n}\n#wn-app .wn-info {\n  background: #fff;\n  border: 1px solid #e0e4f0;\n  border-radius: 12px;\n  padding: 20px;\n  margin-bottom: 20px;\n}\n#wn-app .wn-info h3 {\n  font-size: 1rem;\n  margin: 0 0 10px;\n  color: #333;\n}\n#wn-app .wn-info p {\n  font-size: 0.85rem;\n  color: #555;\n  margin: 0 0 8px;\n  line-height: 1.6;\n}\n#wn-app .wn-info p:last-child { margin-bottom: 0; }\n#wn-app .wn-related {\n  background: #f0eeff;\n  border-radius: 12px;\n  padding: 18px 20px;\n}\n#wn-app .wn-related h3 {\n  font-size: 0.9rem;\n  font-weight: 700;\n  color: #6c63ff;\n  margin: 0 0 10px;\n}\n#wn-app .wn-related ul {\n  margin: 0;\n  padding: 0 0 0 18px;\n}\n#wn-app .wn-related li {\n  font-size: 0.85rem;\n  margin-bottom: 6px;\n  color: #444;\n}\n#wn-app .wn-related a {\n  color: #6c63ff;\n  text-decoration: none;\n  font-weight: 600;\n}\n#wn-app .wn-related a:hover { text-decoration: underline; }\n\u003c/style\u003e\n\u003ch2\u003eWhite Noise Generator\u003c/h2\u003e\n\u003cp class=\"wn-subtitle\"\u003eGenerate ambient sounds to boost focus, mask distractions, and improve sleep — all in your browser. No downloads needed.\u003c/p\u003e","title":"White Noise Generator - Focus \u0026 Sleep Sounds"},{"content":" \u0026#9998; \u0026#9135; \u0026#9645; \u0026#9711; T \u0026#9003; Color Width 3 \u0026#8630; Undo \u0026#8631; Redo \u0026#10005; Clear \u0026#8595; PNG Also try: Pixel Art Editor SVG Path Editor Placeholder Image Generator ","permalink":"https://productivity-works.com/tools/whiteboard-drawing/","summary":"\u003cdiv id=\"wb-app\"\u003e\n\u003cstyle\u003e\n  #wb-app * {\n    box-sizing: border-box;\n    margin: 0;\n    padding: 0;\n  }\n\u003cp\u003e#wb-app {\nfont-family: system-ui, -apple-system, BlinkMacSystemFont, \u0026ldquo;Segoe UI\u0026rdquo;, sans-serif;\nbackground: #1e293b;\nborder-radius: 10px;\noverflow: hidden;\nuser-select: none;\n}\u003c/p\u003e\n\u003cp\u003e#wb-app .wb-toolbar {\ndisplay: flex;\nflex-wrap: wrap;\nalign-items: center;\ngap: 6px;\npadding: 10px 12px;\nbackground: #0f172a;\nborder-bottom: 2px solid #334155;\n}\u003c/p\u003e\n\u003cp\u003e#wb-app .wb-toolbar-group {\ndisplay: flex;\nalign-items: center;\ngap: 4px;\npadding-right: 10px;\nborder-right: 1px solid #334155;\n}\u003c/p\u003e\n\u003cp\u003e#wb-app .wb-toolbar-group:last-child {\nborder-right: none;\n}\u003c/p\u003e\n\u003cp\u003e#wb-app .wb-tool-btn {\ndisplay: flex;\nalign-items: center;\njustify-content: center;\nwidth: 36px;\nheight: 36px;\nborder: 2px solid transparent;\nborder-radius: 6px;\nbackground: #1e293b;\ncolor: #94a3b8;\ncursor: pointer;\nfont-size: 16px;\ntransition: background 0.15s, border-color 0.15s, color 0.15s;\nflex-shrink: 0;\n}\u003c/p\u003e","title":"Whiteboard Drawing Tool"},{"content":" Trim leading/trailing spaces Remove double spaces Remove blank lines Trim each line Remove tabs Remove line breaks Remove ALL whitespace Input Text Cleaned Output 0 Characters before 0 Characters after 0 Characters removed 0% Reduction Live Diff Preview (strikethrough = removed) Copy Output Use Output as Input Clear Copied! Related: Count cleaned text with our Word Counter Related Tools Convert text between UPPERCASE, lowercase, camelCase, and more → Case Converter Count words, characters, and lines in your text → Word Counter Encode special HTML characters safely → HTML Entity Encoder ","permalink":"https://productivity-works.com/tools/whitespace-remover/","summary":"\u003cdiv id=\"ws-app\"\u003e\n\u003cstyle\u003e\n#ws-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  max-width: 880px;\n  margin: 0 auto;\n  color: #134e4a;\n}\n\n#ws-app * {\n  box-sizing: border-box;\n}\n\n/* Options panel */\n#ws-options {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  margin-bottom: 16px;\n  padding: 14px 16px;\n  background: #f0fdfa;\n  border: 1.5px solid #99f6e4;\n  border-radius: 12px;\n}\n\n.ws-opt-label {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 0.875rem;\n  font-weight: 500;\n  color: #134e4a;\n  cursor: pointer;\n  user-select: none;\n  padding: 4px 10px;\n  border-radius: 8px;\n  transition: background 0.15s;\n}\n\n.ws-opt-label:hover {\n  background: #ccfbf1;\n}\n\n.ws-opt-label input[type=\"checkbox\"] {\n  accent-color: #0d9488;\n  width: 16px;\n  height: 16px;\n  cursor: pointer;\n}\n\n/* Textareas row */\n#ws-io-row {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 16px;\n  margin-bottom: 12px;\n}\n\n@media (max-width: 640px) {\n  #ws-io-row {\n    grid-template-columns: 1fr;\n  }\n}\n\n.ws-panel-label {\n  font-size: 0.8rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  color: #0f766e;\n  margin-bottom: 6px;\n}\n\n#ws-input, #ws-output {\n  width: 100%;\n  min-height: 220px;\n  padding: 14px 16px;\n  font-size: 0.95rem;\n  line-height: 1.6;\n  border: 2px solid #99f6e4;\n  border-radius: 10px;\n  resize: vertical;\n  outline: none;\n  transition: border-color 0.2s;\n  background: #f0fdfa;\n  color: #134e4a;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n}\n\n#ws-input:focus {\n  border-color: #0d9488;\n  background: #fff;\n  box-shadow: 0 0 0 3px rgba(13,148,136,0.12);\n}\n\n#ws-output {\n  background: #fafffe;\n  color: #134e4a;\n}\n\n/* Diff preview */\n#ws-diff-wrap {\n  margin-bottom: 16px;\n}\n\n#ws-diff-title {\n  font-size: 0.8rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  color: #0f766e;\n  margin-bottom: 6px;\n}\n\n#ws-diff {\n  width: 100%;\n  min-height: 100px;\n  max-height: 260px;\n  overflow-y: auto;\n  padding: 14px 16px;\n  font-size: 0.9rem;\n  line-height: 1.7;\n  border: 2px solid #99f6e4;\n  border-radius: 10px;\n  background: #f0fdfa;\n  white-space: pre-wrap;\n  word-break: break-word;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n}\n\n#ws-diff .ws-removed {\n  background: #fecaca;\n  color: #991b1b;\n  text-decoration: line-through;\n  border-radius: 2px;\n  padding: 0 1px;\n}\n\n/* Stats row */\n#ws-stats {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 12px;\n  margin-bottom: 14px;\n}\n\n.ws-stat-box {\n  flex: 1 1 140px;\n  padding: 12px 16px;\n  border-radius: 10px;\n  border: 1.5px solid #99f6e4;\n  background: #f0fdfa;\n  text-align: center;\n}\n\n.ws-stat-box .ws-stat-val {\n  font-size: 1.6rem;\n  font-weight: 700;\n  color: #0d9488;\n  line-height: 1.1;\n}\n\n.ws-stat-box .ws-stat-lbl {\n  font-size: 0.78rem;\n  color: #5eead4;\n  margin-top: 3px;\n}\n\n/* Buttons */\n#ws-actions {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px;\n  margin-bottom: 18px;\n}\n\n.ws-btn {\n  padding: 9px 20px;\n  border: none;\n  border-radius: 8px;\n  font-size: 0.9rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s;\n}\n\n.ws-btn:active {\n  transform: scale(0.97);\n}\n\n.ws-btn-primary {\n  background: #0d9488;\n  color: #fff;\n}\n\n.ws-btn-primary:hover {\n  background: #0f766e;\n}\n\n.ws-btn-secondary {\n  background: #ccfbf1;\n  color: #0f766e;\n}\n\n.ws-btn-secondary:hover {\n  background: #99f6e4;\n}\n\n.ws-btn-danger {\n  background: #fee2e2;\n  color: #b91c1c;\n}\n\n.ws-btn-danger:hover {\n  background: #fecaca;\n}\n\n/* Copy toast */\n#ws-toast {\n  display: none;\n  padding: 8px 16px;\n  background: #0d9488;\n  color: #fff;\n  border-radius: 8px;\n  font-size: 0.85rem;\n  font-weight: 600;\n  align-items: center;\n}\n\n#ws-toast.ws-show {\n  display: inline-flex;\n}\n\n/* Related */\n#ws-related {\n  margin-top: 24px;\n  padding: 12px 16px;\n  background: #f0fdfa;\n  border-left: 4px solid #0d9488;\n  border-radius: 0 8px 8px 0;\n  font-size: 0.9rem;\n  color: #134e4a;\n}\n\u003c/style\u003e\n\u003c!-- Options --\u003e\n\u003cdiv id=\"ws-options\"\u003e\n  \u003clabel class=\"ws-opt-label\"\u003e\u003cinput type=\"checkbox\" id=\"opt-trim\" checked\u003e Trim leading/trailing spaces\u003c/label\u003e\n  \u003clabel class=\"ws-opt-label\"\u003e\u003cinput type=\"checkbox\" id=\"opt-double-spaces\" checked\u003e Remove double spaces\u003c/label\u003e\n  \u003clabel class=\"ws-opt-label\"\u003e\u003cinput type=\"checkbox\" id=\"opt-blank-lines\" checked\u003e Remove blank lines\u003c/label\u003e\n  \u003clabel class=\"ws-opt-label\"\u003e\u003cinput type=\"checkbox\" id=\"opt-trim-lines\"\u003e Trim each line\u003c/label\u003e\n  \u003clabel class=\"ws-opt-label\"\u003e\u003cinput type=\"checkbox\" id=\"opt-tabs\"\u003e Remove tabs\u003c/label\u003e\n  \u003clabel class=\"ws-opt-label\"\u003e\u003cinput type=\"checkbox\" id=\"opt-linebreaks\"\u003e Remove line breaks\u003c/label\u003e\n  \u003clabel class=\"ws-opt-label\"\u003e\u003cinput type=\"checkbox\" id=\"opt-all-ws\"\u003e Remove ALL whitespace\u003c/label\u003e\n\u003c/div\u003e\n\u003c!-- Input / Output --\u003e\n\u003cdiv id=\"ws-io-row\"\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"ws-panel-label\"\u003eInput Text\u003c/div\u003e\n    \u003ctextarea id=\"ws-input\" placeholder=\"Paste or type text here...\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"ws-panel-label\"\u003eCleaned Output\u003c/div\u003e\n    \u003ctextarea id=\"ws-output\" readonly placeholder=\"Cleaned text will appear here...\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Stats --\u003e\n\u003cdiv id=\"ws-stats\"\u003e\n  \u003cdiv class=\"ws-stat-box\"\u003e\n    \u003cdiv class=\"ws-stat-val\" id=\"stat-before\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"ws-stat-lbl\"\u003eCharacters before\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ws-stat-box\"\u003e\n    \u003cdiv class=\"ws-stat-val\" id=\"stat-after\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"ws-stat-lbl\"\u003eCharacters after\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ws-stat-box\"\u003e\n    \u003cdiv class=\"ws-stat-val\" id=\"stat-removed\"\u003e0\u003c/div\u003e\n    \u003cdiv class=\"ws-stat-lbl\"\u003eCharacters removed\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"ws-stat-box\"\u003e\n    \u003cdiv class=\"ws-stat-val\" id=\"stat-pct\"\u003e0%\u003c/div\u003e\n    \u003cdiv class=\"ws-stat-lbl\"\u003eReduction\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Diff preview --\u003e\n\u003cdiv id=\"ws-diff-wrap\"\u003e\n  \u003cdiv id=\"ws-diff-title\"\u003eLive Diff Preview (strikethrough = removed)\u003c/div\u003e\n  \u003cdiv id=\"ws-diff\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- Actions --\u003e\n\u003cdiv id=\"ws-actions\"\u003e\n  \u003cbutton class=\"ws-btn ws-btn-primary\" id=\"btn-copy\"\u003eCopy Output\u003c/button\u003e\n  \u003cbutton class=\"ws-btn ws-btn-secondary\" id=\"btn-swap\"\u003eUse Output as Input\u003c/button\u003e\n  \u003cbutton class=\"ws-btn ws-btn-danger\" id=\"btn-clear\"\u003eClear\u003c/button\u003e\n  \u003cspan id=\"ws-toast\"\u003eCopied!\u003c/span\u003e\n\u003c/div\u003e\n\u003cdiv id=\"ws-related\"\u003e\n  Related: Count cleaned text with our \u003ca href=\"/tools/word-counter/\" style=\"color:#0d9488;font-weight:600;\"\u003eWord Counter\u003c/a\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n  const input    = document.getElementById('ws-input');\n  const output   = document.getElementById('ws-output');\n  const diffEl   = document.getElementById('ws-diff');\n  const statBefore  = document.getElementById('stat-before');\n  const statAfter   = document.getElementById('stat-after');\n  const statRemoved = document.getElementById('stat-removed');\n  const statPct     = document.getElementById('stat-pct');\n  const toast    = document.getElementById('ws-toast');\n\n  const opts = {\n    trim:        document.getElementById('opt-trim'),\n    doubleSpaces:document.getElementById('opt-double-spaces'),\n    blankLines:  document.getElementById('opt-blank-lines'),\n    trimLines:   document.getElementById('opt-trim-lines'),\n    tabs:        document.getElementById('opt-tabs'),\n    linebreaks:  document.getElementById('opt-linebreaks'),\n    allWs:       document.getElementById('opt-all-ws'),\n  };\n\n  // When \"remove ALL whitespace\" is toggled, disable conflicting opts\n  opts.allWs.addEventListener('change', function() {\n    const state = this.checked;\n    [opts.trim, opts.doubleSpaces, opts.blankLines, opts.trimLines, opts.tabs, opts.linebreaks].forEach(o =\u003e {\n      o.disabled = state;\n    });\n    process();\n  });\n\n  function clean(text) {\n    if (opts.allWs.checked) {\n      return text.replace(/\\s/g, '');\n    }\n    let t = text;\n    if (opts.tabs.checked)        t = t.replace(/\\t/g, '');\n    if (opts.trimLines.checked)   t = t.split('\\n').map(l =\u003e l.trim()).join('\\n');\n    if (opts.linebreaks.checked)  t = t.replace(/\\r?\\n/g, ' ');\n    if (opts.blankLines.checked)  t = t.replace(/(\\r?\\n){2,}/g, '\\n');\n    if (opts.doubleSpaces.checked) t = t.replace(/ {2,}/g, ' ');\n    if (opts.trim.checked)        t = t.trim();\n    return t;\n  }\n\n  // Diff: character-level, marks removed chars with span\n  function buildDiff(original, cleaned) {\n    // Use LCS-based char diff (simple greedy for display)\n    // For performance, cap at 5000 chars\n    const a = original.slice(0, 5000);\n    const b = cleaned.slice(0, 5000);\n\n    const dp = [];\n    for (let i = 0; i \u003c= a.length; i++) {\n      dp[i] = new Array(b.length + 1).fill(0);\n    }\n    for (let i = 1; i \u003c= a.length; i++) {\n      for (let j = 1; j \u003c= b.length; j++) {\n        dp[i][j] = a[i-1] === b[j-1] ? dp[i-1][j-1] + 1 : Math.max(dp[i-1][j], dp[i][j-1]);\n      }\n    }\n\n    // Traceback\n    let i = a.length, j = b.length;\n    const ops = [];\n    while (i \u003e 0 || j \u003e 0) {\n      if (i \u003e 0 \u0026\u0026 j \u003e 0 \u0026\u0026 a[i-1] === b[j-1]) {\n        ops.push({ type: 'keep', ch: a[i-1] }); i--; j--;\n      } else if (j \u003e 0 \u0026\u0026 (i === 0 || dp[i][j-1] \u003e= dp[i-1][j])) {\n        j--; // inserted in b — ignore (shouldn't happen in our case)\n      } else {\n        ops.push({ type: 'del', ch: a[i-1] }); i--;\n      }\n    }\n    ops.reverse();\n\n    let html = '';\n    let run = null;\n    for (const op of ops) {\n      const safe = op.ch === '\u003c' ? '\u0026lt;' : op.ch === '\u003e' ? '\u0026gt;' : op.ch === '\u0026' ? '\u0026amp;' : op.ch === '\\n' ? '↵\\n' : op.ch === '\\t' ? '→' : op.ch === ' ' ? '·' : op.ch;\n      if (op.type === 'del') {\n        if (run !== 'del') { if (run) html += (run === 'keep' ? '' : '\u003c/span\u003e'); html += '\u003cspan class=\"ws-removed\"\u003e'; run = 'del'; }\n        html += safe;\n      } else {\n        if (run === 'del') { html += '\u003c/span\u003e'; }\n        run = 'keep';\n        html += op.ch === '\\n' ? '\\n' : op.ch === '\\t' ? '\\t' : op.ch === '\u003c' ? '\u0026lt;' : op.ch === '\u003e' ? '\u0026gt;' : op.ch === '\u0026' ? '\u0026amp;' : op.ch;\n      }\n    }\n    if (run === 'del') html += '\u003c/span\u003e';\n\n    if (original.length \u003e 5000) html += '\\n... (preview capped at 5000 chars)';\n    return html;\n  }\n\n  function process() {\n    const raw = input.value;\n    const result = clean(raw);\n    output.value = result;\n\n    const before = raw.length;\n    const after  = result.length;\n    const removed = before - after;\n    const pct = before \u003e 0 ? Math.round((removed / before) * 100) : 0;\n\n    statBefore.textContent  = before.toLocaleString();\n    statAfter.textContent   = after.toLocaleString();\n    statRemoved.textContent = removed.toLocaleString();\n    statPct.textContent     = pct + '%';\n\n    diffEl.innerHTML = raw.length \u003e 0 ? buildDiff(raw, result) : '\u003cspan style=\"color:#9ca3af;font-style:italic;\"\u003ePaste text above to see diff...\u003c/span\u003e';\n  }\n\n  input.addEventListener('input', process);\n  Object.values(opts).forEach(o =\u003e o.addEventListener('change', process));\n\n  document.getElementById('btn-copy').addEventListener('click', function() {\n    const text = output.value;\n    if (!text) return;\n    navigator.clipboard.writeText(text).then(() =\u003e {\n      toast.classList.add('ws-show');\n      setTimeout(() =\u003e toast.classList.remove('ws-show'), 1800);\n    });\n  });\n\n  document.getElementById('btn-swap').addEventListener('click', function() {\n    input.value = output.value;\n    process();\n  });\n\n  document.getElementById('btn-clear').addEventListener('click', function() {\n    input.value = '';\n    process();\n  });\n\n  process();\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003ch2 id=\"related-tools\"\u003eRelated Tools\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003eConvert text between UPPERCASE, lowercase, camelCase, and more → \u003ca href=\"https://productivity-works.com/tools/case-converter/\"\u003eCase Converter\u003c/a\u003e\n\u003c/p\u003e","title":"Whitespace Remover - Free Online Tool to Clean \u0026 Trim Text"},{"content":" Paste or type your text below Copy Text Clear 0Words 0Characters\n(with spaces) 0Characters\n(no spaces) 0Sentences 0Paragraphs 0Avg Word\nLength \u0026#128214; 0 sec Reading time (225 WPM) \u0026#127897; 0 sec Speaking time (130 WPM) Readability Score (Flesch-Kincaid) \u0026#8212; Enter text to calculate Very Difficult (0) Standard (60) Very Easy (100) Top 10 Keywords \u0026amp; Density Enter text to see keyword analysis Find \u0026amp; Replace Replace All Count Matches Clear Count characters → Character Counter Check word frequency → Word Frequency Counter Related Articles How to Use ChatGPT for Etsy Shop Descriptions (Complete 2026 Guide) Etsy SEO: How to Generate Perfect Tags with ChatGPT (Step-by-Step) AI Writing Tools Comparison 2026: Best Options Ranked and Reviewed ","permalink":"https://productivity-works.com/tools/word-counter/","summary":"\u003cdiv id=\"wc-app\"\u003e\n\u003cstyle\u003e\n#wc-app {\n  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;\n  background: #0f0f13;\n  color: #e2e8f0;\n  border-radius: 12px;\n  padding: 24px;\n  margin: 0 auto;\n  max-width: 960px;\n  box-sizing: border-box;\n}\n#wc-app * { box-sizing: border-box; }\n\n#wc-app h2 {\n  font-size: 1.05rem;\n  font-weight: 600;\n  color: #f1f5f9;\n  margin: 0 0 12px 0;\n  letter-spacing: 0.02em;\n}\n\n#wc-app h3 {\n  font-size: 0.85rem;\n  font-weight: 600;\n  color: #94a3b8;\n  margin: 0 0 10px 0;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n}\n\n#wc-textarea {\n  width: 100%;\n  min-height: 160px;\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  color: #e2e8f0;\n  font-size: 0.97rem;\n  padding: 12px 14px;\n  resize: vertical;\n  font-family: inherit;\n  transition: border-color 0.2s;\n  outline: none;\n  line-height: 1.6;\n}\n#wc-textarea:focus { border-color: #6366f1; }\n#wc-textarea::placeholder { color: #4a4a6a; }\n\n.wc-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-top: 10px;\n  margin-bottom: 20px;\n}\n.wc-btn {\n  padding: 7px 15px;\n  border-radius: 6px;\n  border: none;\n  font-size: 0.84rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.1s;\n}\n.wc-btn:active { transform: scale(0.97); }\n.wc-btn-primary { background: #6366f1; color: #fff; }\n.wc-btn-primary:hover { background: #4f46e5; }\n.wc-btn-danger { background: #1a1a24; color: #f87171; border: 1px solid #3d2020; }\n.wc-btn-danger:hover { background: #2d1a1a; }\n.wc-btn-secondary { background: #1e293b; color: #94a3b8; border: 1px solid #2d3748; }\n.wc-btn-secondary:hover { background: #253347; color: #e2e8f0; }\n.wc-copy-btn { background: #1a1a24; color: #a5b4fc; border: 1px solid #2d2d4d; }\n.wc-copy-btn:hover { background: #22224a; }\n\n.wc-stats-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));\n  gap: 10px;\n  margin-bottom: 20px;\n}\n.wc-stat-card {\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  padding: 12px 14px;\n  text-align: center;\n}\n.wc-stat-value {\n  font-size: 1.5rem;\n  font-weight: 700;\n  color: #a5b4fc;\n  line-height: 1.2;\n}\n.wc-stat-label {\n  font-size: 0.74rem;\n  color: #64748b;\n  margin-top: 4px;\n  line-height: 1.3;\n}\n\n.wc-time-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));\n  gap: 10px;\n  margin-bottom: 20px;\n}\n.wc-time-card {\n  background: #161b2e;\n  border: 1px solid #1e2a4a;\n  border-radius: 8px;\n  padding: 12px 16px;\n  display: flex;\n  align-items: center;\n  gap: 12px;\n}\n.wc-time-icon { font-size: 1.4rem; line-height: 1; }\n.wc-time-val {\n  font-size: 1.1rem;\n  font-weight: 700;\n  color: #7dd3fc;\n}\n.wc-time-desc { font-size: 0.74rem; color: #64748b; }\n\n.wc-readability-wrap {\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  padding: 16px;\n  margin-bottom: 20px;\n}\n.wc-rbar-track {\n  background: #0f0f18;\n  border-radius: 99px;\n  height: 10px;\n  margin: 10px 0 6px;\n  overflow: hidden;\n}\n.wc-rbar-fill {\n  height: 100%;\n  border-radius: 99px;\n  transition: width 0.4s ease, background 0.4s;\n}\n.wc-rbar-labels {\n  display: flex;\n  justify-content: space-between;\n  font-size: 0.7rem;\n  color: #475569;\n}\n.wc-rbar-score {\n  font-size: 1.2rem;\n  font-weight: 700;\n  color: #f1f5f9;\n}\n.wc-rbar-level {\n  font-size: 0.82rem;\n  color: #94a3b8;\n  margin-top: 4px;\n}\n\n.wc-section {\n  background: #1a1a24;\n  border: 1px solid #2d2d3d;\n  border-radius: 8px;\n  padding: 16px;\n  margin-bottom: 20px;\n}\n.wc-table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.86rem;\n}\n.wc-table th {\n  text-align: left;\n  color: #64748b;\n  font-size: 0.75rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  padding: 6px 10px;\n  border-bottom: 1px solid #2d2d3d;\n}\n.wc-table td {\n  padding: 7px 10px;\n  color: #e2e8f0;\n  border-bottom: 1px solid #1e1e2c;\n}\n.wc-table tr:last-child td { border-bottom: none; }\n.wc-table tr:hover td { background: #20202e; }\n.wc-density-bar {\n  display: inline-block;\n  height: 6px;\n  border-radius: 3px;\n  background: #6366f1;\n  vertical-align: middle;\n  margin-right: 6px;\n  transition: width 0.3s;\n}\n\n.wc-fr-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr auto;\n  gap: 8px;\n  align-items: center;\n}\n.wc-fr-input {\n  background: #0f0f18;\n  border: 1px solid #2d2d3d;\n  border-radius: 6px;\n  color: #e2e8f0;\n  font-size: 0.9rem;\n  padding: 8px 12px;\n  outline: none;\n  font-family: inherit;\n  transition: border-color 0.2s;\n}\n.wc-fr-input:focus { border-color: #6366f1; }\n.wc-fr-input::placeholder { color: #3a3a5a; }\n.wc-fr-actions {\n  display: flex;\n  gap: 6px;\n  flex-wrap: wrap;\n  margin-top: 8px;\n}\n.wc-fr-info {\n  font-size: 0.8rem;\n  color: #64748b;\n  margin-top: 6px;\n  min-height: 1.2em;\n}\n\n.wc-empty {\n  color: #3a3a5a;\n  font-size: 0.85rem;\n  text-align: center;\n  padding: 12px 0;\n}\n\n@media (max-width: 600px) {\n  #wc-app { padding: 16px 12px; }\n  .wc-stats-grid { grid-template-columns: repeat(2, 1fr); }\n  .wc-time-grid { grid-template-columns: 1fr 1fr; }\n  .wc-fr-grid { grid-template-columns: 1fr 1fr; }\n  .wc-fr-grid \u003e .wc-btn { grid-column: 1 / -1; }\n}\n\u003c/style\u003e\n\u003ch2\u003ePaste or type your text below\u003c/h2\u003e\n\u003ctextarea id=\"wc-textarea\" placeholder=\"Paste or type your text here to get instant statistics...\"\u003e\u003c/textarea\u003e\n\u003cdiv class=\"wc-toolbar\"\u003e\n  \u003cbutton class=\"wc-btn wc-copy-btn\" onclick=\"wcCopyText()\"\u003eCopy Text\u003c/button\u003e\n  \u003cbutton class=\"wc-btn wc-btn-danger\" onclick=\"wcClear()\"\u003eClear\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"wc-stats-grid\"\u003e\n  \u003cdiv class=\"wc-stat-card\"\u003e\u003cdiv class=\"wc-stat-value\" id=\"wc-words\"\u003e0\u003c/div\u003e\u003cdiv class=\"wc-stat-label\"\u003eWords\u003c/div\u003e\u003c/div\u003e\n  \u003cdiv class=\"wc-stat-card\"\u003e\u003cdiv class=\"wc-stat-value\" id=\"wc-chars\"\u003e0\u003c/div\u003e\u003cdiv class=\"wc-stat-label\"\u003eCharacters\u003cbr\u003e(with spaces)\u003c/div\u003e\u003c/div\u003e\n  \u003cdiv class=\"wc-stat-card\"\u003e\u003cdiv class=\"wc-stat-value\" id=\"wc-chars-no\"\u003e0\u003c/div\u003e\u003cdiv class=\"wc-stat-label\"\u003eCharacters\u003cbr\u003e(no spaces)\u003c/div\u003e\u003c/div\u003e\n  \u003cdiv class=\"wc-stat-card\"\u003e\u003cdiv class=\"wc-stat-value\" id=\"wc-sentences\"\u003e0\u003c/div\u003e\u003cdiv class=\"wc-stat-label\"\u003eSentences\u003c/div\u003e\u003c/div\u003e\n  \u003cdiv class=\"wc-stat-card\"\u003e\u003cdiv class=\"wc-stat-value\" id=\"wc-paragraphs\"\u003e0\u003c/div\u003e\u003cdiv class=\"wc-stat-label\"\u003eParagraphs\u003c/div\u003e\u003c/div\u003e\n  \u003cdiv class=\"wc-stat-card\"\u003e\u003cdiv class=\"wc-stat-value\" id=\"wc-avg-word\"\u003e0\u003c/div\u003e\u003cdiv class=\"wc-stat-label\"\u003eAvg Word\u003cbr\u003eLength\u003c/div\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"wc-time-grid\"\u003e\n  \u003cdiv class=\"wc-time-card\"\u003e\n    \u003cdiv class=\"wc-time-icon\"\u003e\u0026#128214;\u003c/div\u003e\n    \u003cdiv\u003e\n      \u003cdiv class=\"wc-time-val\" id=\"wc-read-time\"\u003e0 sec\u003c/div\u003e\n      \u003cdiv class=\"wc-time-desc\"\u003eReading time (225 WPM)\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wc-time-card\"\u003e\n    \u003cdiv class=\"wc-time-icon\"\u003e\u0026#127897;\u003c/div\u003e\n    \u003cdiv\u003e\n      \u003cdiv class=\"wc-time-val\" id=\"wc-speak-time\"\u003e0 sec\u003c/div\u003e\n      \u003cdiv class=\"wc-time-desc\"\u003eSpeaking time (130 WPM)\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"wc-readability-wrap\"\u003e\n  \u003ch3\u003eReadability Score (Flesch-Kincaid)\u003c/h3\u003e\n  \u003cdiv style=\"display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;\"\u003e\n    \u003cspan class=\"wc-rbar-score\" id=\"wc-fk-score\"\u003e\u0026#8212;\u003c/span\u003e\n    \u003cspan class=\"wc-rbar-level\" id=\"wc-fk-level\"\u003eEnter text to calculate\u003c/span\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wc-rbar-track\"\u003e\n    \u003cdiv class=\"wc-rbar-fill\" id=\"wc-rbar-fill\" style=\"width:0%;background:#6366f1;\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wc-rbar-labels\"\u003e\n    \u003cspan\u003eVery Difficult (0)\u003c/span\u003e\n    \u003cspan\u003eStandard (60)\u003c/span\u003e\n    \u003cspan\u003eVery Easy (100)\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"wc-section\"\u003e\n  \u003ch3\u003eTop 10 Keywords \u0026amp; Density\u003c/h3\u003e\n  \u003cdiv id=\"wc-keywords-wrap\"\u003e\u003cdiv class=\"wc-empty\"\u003eEnter text to see keyword analysis\u003c/div\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"wc-section\"\u003e\n  \u003ch3\u003eFind \u0026amp; Replace\u003c/h3\u003e\n  \u003cdiv class=\"wc-fr-grid\"\u003e\n    \u003cinput class=\"wc-fr-input\" id=\"wc-find\" placeholder=\"Find...\" /\u003e\n    \u003cinput class=\"wc-fr-input\" id=\"wc-replace\" placeholder=\"Replace with...\" /\u003e\n    \u003cbutton class=\"wc-btn wc-btn-primary\" onclick=\"wcReplaceAll()\"\u003eReplace All\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wc-fr-actions\"\u003e\n    \u003cbutton class=\"wc-btn wc-btn-secondary\" onclick=\"wcHighlightFind()\"\u003eCount Matches\u003c/button\u003e\n    \u003cbutton class=\"wc-btn wc-btn-secondary\" onclick=\"wcClearFrInfo()\"\u003eClear\u003c/button\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wc-fr-info\" id=\"wc-fr-info\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n  var STOP = new Set([\n    'a','an','the','and','but','or','nor','for','yet','so','in','on','at','to',\n    'of','by','up','as','is','it','its','be','was','are','were','been','being',\n    'have','has','had','do','does','did','will','would','could','should','may',\n    'might','shall','can','this','that','these','those','i','you','he','she','we',\n    'they','my','your','his','her','our','their','me','him','us','them','what',\n    'which','who','when','where','why','how','all','each','every','both','few',\n    'more','most','other','some','such','no','not','only','own','same','than',\n    'too','very','just','because','if','then','there','here','about','into',\n    'through','during','before','after','above','below','from','with','without',\n    'also','any','get','got','been','use','used','one','two','three','new','good'\n  ]);\n\n  function getWords(t) { return t.match(/\\b[a-zA-Z']+\\b/g) || []; }\n\n  function getSentences(t) {\n    var s = t.trim();\n    if (!s) return 0;\n    var m = s.match(/[^.!?]*[.!?]+/g);\n    if (!m) return s.length \u003e 0 ? 1 : 0;\n    return m.length;\n  }\n\n  function getParagraphs(t) {\n    var s = t.trim();\n    if (!s) return 0;\n    return s.split(/\\n\\s*\\n+/).filter(function(p){ return p.trim().length \u003e 0; }).length || 1;\n  }\n\n  function formatTime(mins) {\n    if (mins === 0) return '0 sec';\n    if (mins \u003c 1) return Math.round(mins * 60) + ' sec';\n    var m = Math.floor(mins);\n    var s = Math.round((mins - m) * 60);\n    if (s === 0) return m + ' min';\n    return m + ' min ' + s + ' sec';\n  }\n\n  function countSyllables(word) {\n    word = word.toLowerCase().replace(/[^a-z]/g, '');\n    if (!word) return 0;\n    if (word.length \u003c= 3) return 1;\n    word = word.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, '');\n    word = word.replace(/^y/, '');\n    var m = word.match(/[aeiouy]{1,2}/g);\n    return m ? Math.max(1, m.length) : 1;\n  }\n\n  function fleschKincaid(words, sentences) {\n    if (!words.length || !sentences) return null;\n    var syl = words.reduce(function(a, w){ return a + countSyllables(w); }, 0);\n    var asl = words.length / sentences;\n    var asw = syl / words.length;\n    var score = 206.835 - 1.015 * asl - 84.6 * asw;\n    return Math.round(Math.max(0, Math.min(100, score)));\n  }\n\n  function fkLabel(s) {\n    if (s \u003e= 90) return 'Very Easy — 5th grade level';\n    if (s \u003e= 80) return 'Easy — 6th grade level';\n    if (s \u003e= 70) return 'Fairly Easy — 7th grade level';\n    if (s \u003e= 60) return 'Standard — 8th–9th grade level';\n    if (s \u003e= 50) return 'Fairly Difficult — 10th–12th grade level';\n    if (s \u003e= 30) return 'Difficult — College level';\n    return 'Very Difficult — Professional level';\n  }\n\n  function fkColor(s) {\n    if (s \u003e= 70) return '#4ade80';\n    if (s \u003e= 50) return '#facc15';\n    if (s \u003e= 30) return '#fb923c';\n    return '#f87171';\n  }\n\n  function esc(s) {\n    return s.replace(/\u0026/g,'\u0026amp;').replace(/\u003c/g,'\u0026lt;').replace(/\u003e/g,'\u0026gt;');\n  }\n\n  function analyze() {\n    var text = document.getElementById('wc-textarea').value;\n    var words = getWords(text);\n    var wc = words.length;\n    var sentences = getSentences(text);\n    var paragraphs = getParagraphs(text);\n    var avgLen = wc \u003e 0\n      ? (words.reduce(function(a,w){ return a + w.replace(/'/g,'').length; }, 0) / wc).toFixed(1)\n      : '0.0';\n\n    document.getElementById('wc-words').textContent = wc.toLocaleString();\n    document.getElementById('wc-chars').textContent = text.length.toLocaleString();\n    document.getElementById('wc-chars-no').textContent = text.replace(/\\s/g,'').length.toLocaleString();\n    document.getElementById('wc-sentences').textContent = sentences.toLocaleString();\n    document.getElementById('wc-paragraphs').textContent = paragraphs.toLocaleString();\n    document.getElementById('wc-avg-word').textContent = avgLen;\n\n    document.getElementById('wc-read-time').textContent = formatTime(wc / 225);\n    document.getElementById('wc-speak-time').textContent = formatTime(wc / 130);\n\n    var fk = fleschKincaid(words, sentences);\n    var scoreEl = document.getElementById('wc-fk-score');\n    var levelEl = document.getElementById('wc-fk-level');\n    var fillEl  = document.getElementById('wc-rbar-fill');\n    if (fk === null) {\n      scoreEl.textContent = '\\u2014';\n      levelEl.textContent = 'Enter text to calculate';\n      fillEl.style.width = '0%';\n      fillEl.style.background = '#6366f1';\n    } else {\n      scoreEl.textContent = fk;\n      levelEl.textContent = fkLabel(fk);\n      fillEl.style.width = fk + '%';\n      fillEl.style.background = fkColor(fk);\n    }\n\n    // Keywords\n    var wrap = document.getElementById('wc-keywords-wrap');\n    if (!wc) {\n      wrap.innerHTML = '\u003cdiv class=\"wc-empty\"\u003eEnter text to see keyword analysis\u003c/div\u003e';\n      return;\n    }\n    var freq = {};\n    words.forEach(function(w) {\n      var lw = w.toLowerCase();\n      if (!STOP.has(lw) \u0026\u0026 lw.length \u003e 1) freq[lw] = (freq[lw] || 0) + 1;\n    });\n    var sorted = Object.keys(freq).sort(function(a,b){ return freq[b]-freq[a]; }).slice(0,10);\n    if (!sorted.length) {\n      wrap.innerHTML = '\u003cdiv class=\"wc-empty\"\u003eNo significant keywords found\u003c/div\u003e';\n      return;\n    }\n    var maxC = freq[sorted[0]];\n    var html = '\u003ctable class=\"wc-table\"\u003e\u003cthead\u003e\u003ctr\u003e\u003cth\u003e#\u003c/th\u003e\u003cth\u003eWord\u003c/th\u003e\u003cth\u003eCount\u003c/th\u003e\u003cth\u003eDensity\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e\u003ctbody\u003e';\n    sorted.forEach(function(word, i) {\n      var c = freq[word];\n      var d = ((c / wc) * 100).toFixed(2);\n      var bw = Math.round((c / maxC) * 80);\n      html += '\u003ctr\u003e\u003ctd style=\"color:#475569;\"\u003e' + (i+1) + '\u003c/td\u003e'\n        + '\u003ctd style=\"color:#a5b4fc;font-weight:600;\"\u003e' + esc(word) + '\u003c/td\u003e'\n        + '\u003ctd\u003e' + c + '\u003c/td\u003e'\n        + '\u003ctd\u003e\u003cspan class=\"wc-density-bar\" style=\"width:' + bw + 'px\"\u003e\u003c/span\u003e' + d + '%\u003c/td\u003e\u003c/tr\u003e';\n    });\n    html += '\u003c/tbody\u003e\u003c/table\u003e';\n    wrap.innerHTML = html;\n  }\n\n  window.wcAnalyze = analyze;\n\n  window.wcClear = function() {\n    document.getElementById('wc-textarea').value = '';\n    document.getElementById('wc-find').value = '';\n    document.getElementById('wc-replace').value = '';\n    document.getElementById('wc-fr-info').textContent = '';\n    analyze();\n  };\n\n  window.wcCopyText = function() {\n    var ta = document.getElementById('wc-textarea');\n    ta.select();\n    try { document.execCommand('copy'); } catch(e) {}\n    ta.setSelectionRange(0,0);\n  };\n\n  window.wcReplaceAll = function() {\n    var find = document.getElementById('wc-find').value;\n    var repl = document.getElementById('wc-replace').value;\n    var info = document.getElementById('wc-fr-info');\n    if (!find) { info.textContent = 'Please enter a search term.'; return; }\n    var ta = document.getElementById('wc-textarea');\n    var regex = new RegExp(find.replace(/[.*+?^${}()|[\\]\\\\]/g,'\\\\$\u0026'), 'g');\n    var m = (ta.value.match(regex) || []).length;\n    if (!m) { info.textContent = 'No matches found for \"' + find + '\".'; return; }\n    ta.value = ta.value.replace(regex, repl);\n    info.textContent = 'Replaced ' + m + ' occurrence(s).';\n    analyze();\n  };\n\n  window.wcHighlightFind = function() {\n    var find = document.getElementById('wc-find').value;\n    var info = document.getElementById('wc-fr-info');\n    if (!find) { info.textContent = 'Please enter a search term.'; return; }\n    var regex = new RegExp(find.replace(/[.*+?^${}()|[\\]\\\\]/g,'\\\\$\u0026'), 'gi');\n    var m = (document.getElementById('wc-textarea').value.match(regex) || []).length;\n    info.textContent = m \u003e 0\n      ? 'Found ' + m + ' match(es) for \"' + find + '\".'\n      : 'No matches found for \"' + find + '\".';\n  };\n\n  window.wcClearFrInfo = function() {\n    document.getElementById('wc-find').value = '';\n    document.getElementById('wc-replace').value = '';\n    document.getElementById('wc-fr-info').textContent = '';\n  };\n\n  document.getElementById('wc-textarea').addEventListener('input', analyze);\n  analyze();\n})();\n\u003c/script\u003e\n\u003cblockquote\u003e\n\u003cp\u003eCount characters → \u003ca href=\"https://productivity-works.com/tools/character-counter/\"\u003eCharacter Counter\u003c/a\u003e\n\nCheck word frequency → \u003ca href=\"https://productivity-works.com/tools/word-frequency-counter/\"\u003eWord Frequency Counter\u003c/a\u003e\n\u003c/p\u003e","title":"Word Counter \u0026 Text Statistics"},{"content":"Paste any text to instantly count word frequency. Visualize top words as a bar chart, filter stop words, and export results as CSV.\nCount words → Word Counter Input Text Top 10 25 50 100 words Ignore stop words Case-insensitive Min length Analyze Clear Copy Results Export CSV Paste text above and click Analyze to see word frequency. ","permalink":"https://productivity-works.com/tools/word-frequency/","summary":"\u003cp\u003ePaste any text to instantly count word frequency. Visualize top words as a bar chart, filter stop words, and export results as CSV.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eCount words → \u003ca href=\"https://productivity-works.com/tools/word-counter/\"\u003eWord Counter\u003c/a\u003e\n\u003c/p\u003e\u003c/blockquote\u003e\n\u003cdiv id=\"wf-app\"\u003e\n\u003cstyle\u003e\n#wf-app *,\n#wf-app *::before,\n#wf-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#wf-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  color: #1e293b;\n  max-width: 820px;\n}\n#wf-app h2 {\n  font-size: 1rem;\n  font-weight: 700;\n  color: #0f172a;\n  margin-bottom: 10px;\n}\n#wf-app textarea {\n  width: 100%;\n  height: 160px;\n  padding: 12px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  font-size: 14px;\n  line-height: 1.6;\n  resize: vertical;\n  font-family: inherit;\n  color: #1e293b;\n  background: #fff;\n  transition: border-color 0.2s;\n}\n#wf-app textarea:focus {\n  outline: none;\n  border-color: #6366f1;\n}\n#wf-app .wf-options {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 12px;\n  margin: 14px 0;\n  align-items: center;\n}\n#wf-app .wf-option-group {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 13px;\n  color: #475569;\n}\n#wf-app .wf-option-group label {\n  cursor: pointer;\n  user-select: none;\n}\n#wf-app .wf-option-group input[type=\"checkbox\"] {\n  width: 15px;\n  height: 15px;\n  accent-color: #6366f1;\n  cursor: pointer;\n}\n#wf-app .wf-option-group select,\n#wf-app .wf-option-group input[type=\"number\"] {\n  padding: 4px 8px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 6px;\n  font-size: 13px;\n  color: #1e293b;\n  background: #fff;\n  cursor: pointer;\n}\n#wf-app .wf-option-group input[type=\"number\"] {\n  width: 64px;\n}\n#wf-app .wf-btn-row {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin-bottom: 20px;\n}\n#wf-app button {\n  padding: 9px 20px;\n  border: none;\n  border-radius: 7px;\n  font-size: 13px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: opacity 0.15s, background 0.15s;\n}\n#wf-app button:hover { opacity: 0.88; }\n#wf-app .wf-btn-analyze {\n  background: #6366f1;\n  color: #fff;\n}\n#wf-app .wf-btn-clear {\n  background: #f1f5f9;\n  color: #475569;\n}\n#wf-app .wf-btn-copy {\n  background: #f1f5f9;\n  color: #475569;\n}\n#wf-app .wf-btn-csv {\n  background: #ecfdf5;\n  color: #065f46;\n  border: 1px solid #a7f3d0;\n}\n#wf-app .wf-stats-bar {\n  display: flex;\n  gap: 18px;\n  flex-wrap: wrap;\n  margin-bottom: 18px;\n  padding: 12px 16px;\n  background: #f8fafc;\n  border-radius: 8px;\n  font-size: 13px;\n  color: #475569;\n}\n#wf-app .wf-stats-bar span strong {\n  color: #1e293b;\n  font-size: 15px;\n}\n#wf-app .wf-canvas-wrap {\n  margin-bottom: 20px;\n  overflow-x: auto;\n}\n#wf-app canvas {\n  display: block;\n  max-width: 100%;\n  border-radius: 8px;\n  background: #fff;\n  border: 1px solid #e2e8f0;\n}\n#wf-app .wf-table-wrap {\n  overflow-x: auto;\n  border-radius: 8px;\n  border: 1px solid #e2e8f0;\n}\n#wf-app table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 13px;\n}\n#wf-app thead th {\n  background: #f8fafc;\n  padding: 10px 14px;\n  text-align: left;\n  font-weight: 700;\n  color: #475569;\n  border-bottom: 1.5px solid #e2e8f0;\n}\n#wf-app tbody tr:hover { background: #f8fafc; }\n#wf-app tbody td {\n  padding: 8px 14px;\n  border-bottom: 1px solid #f1f5f9;\n  color: #1e293b;\n}\n#wf-app tbody tr:last-child td { border-bottom: none; }\n#wf-app .wf-rank { color: #94a3b8; font-size: 12px; }\n#wf-app .wf-bar-cell { width: 160px; }\n#wf-app .wf-inline-bar {\n  height: 8px;\n  border-radius: 4px;\n  background: #6366f1;\n  min-width: 2px;\n  transition: width 0.3s;\n}\n#wf-app .wf-pct { color: #64748b; font-size: 12px; }\n#wf-app .wf-empty {\n  text-align: center;\n  padding: 40px 20px;\n  color: #94a3b8;\n  font-size: 14px;\n}\n\u003c/style\u003e\n\u003ch2\u003eInput Text\u003c/h2\u003e\n\u003ctextarea id=\"wf-input\" placeholder=\"Paste your text here...\"\u003e\u003c/textarea\u003e\n\u003cdiv class=\"wf-options\"\u003e\n  \u003cdiv class=\"wf-option-group\"\u003e\n    \u003clabel for=\"wf-top-n\"\u003eTop\u003c/label\u003e\n    \u003cselect id=\"wf-top-n\"\u003e\n      \u003coption value=\"10\"\u003e10\u003c/option\u003e\n      \u003coption value=\"25\" selected\u003e25\u003c/option\u003e\n      \u003coption value=\"50\"\u003e50\u003c/option\u003e\n      \u003coption value=\"100\"\u003e100\u003c/option\u003e\n    \u003c/select\u003e\n    \u003clabel for=\"wf-top-n\"\u003ewords\u003c/label\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wf-option-group\"\u003e\n    \u003cinput type=\"checkbox\" id=\"wf-stop\" checked\u003e\n    \u003clabel for=\"wf-stop\"\u003eIgnore stop words\u003c/label\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wf-option-group\"\u003e\n    \u003cinput type=\"checkbox\" id=\"wf-case\" checked\u003e\n    \u003clabel for=\"wf-case\"\u003eCase-insensitive\u003c/label\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wf-option-group\"\u003e\n    \u003clabel for=\"wf-minlen\"\u003eMin length\u003c/label\u003e\n    \u003cinput type=\"number\" id=\"wf-minlen\" value=\"2\" min=\"1\" max=\"20\"\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"wf-btn-row\"\u003e\n  \u003cbutton class=\"wf-btn-analyze\" id=\"wf-analyze-btn\"\u003eAnalyze\u003c/button\u003e\n  \u003cbutton class=\"wf-btn-clear\" id=\"wf-clear-btn\"\u003eClear\u003c/button\u003e\n  \u003cbutton class=\"wf-btn-copy\" id=\"wf-copy-btn\"\u003eCopy Results\u003c/button\u003e\n  \u003cbutton class=\"wf-btn-csv\" id=\"wf-csv-btn\"\u003eExport CSV\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv id=\"wf-stats\" class=\"wf-stats-bar\" style=\"display:none\"\u003e\u003c/div\u003e\n\u003cdiv class=\"wf-canvas-wrap\"\u003e\u003ccanvas id=\"wf-canvas\" style=\"display:none\"\u003e\u003c/canvas\u003e\u003c/div\u003e\n\u003cdiv id=\"wf-table-container\"\u003e\n  \u003cdiv class=\"wf-empty\"\u003ePaste text above and click Analyze to see word frequency.\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function(){\n  const STOP_WORDS = new Set([\n    \"a\",\"an\",\"the\",\"and\",\"or\",\"but\",\"in\",\"on\",\"at\",\"to\",\"for\",\"of\",\"with\",\n    \"by\",\"from\",\"is\",\"are\",\"was\",\"were\",\"be\",\"been\",\"being\",\"have\",\"has\",\"had\",\n    \"do\",\"does\",\"did\",\"will\",\"would\",\"could\",\"should\",\"may\",\"might\",\"shall\",\n    \"can\",\"not\",\"no\",\"nor\",\"so\",\"yet\",\"both\",\"either\",\"neither\",\"each\",\"few\",\n    \"more\",\"most\",\"other\",\"some\",\"such\",\"than\",\"then\",\"that\",\"this\",\"these\",\n    \"those\",\"it\",\"its\",\"i\",\"me\",\"my\",\"we\",\"our\",\"you\",\"your\",\"he\",\"him\",\"his\",\n    \"she\",\"her\",\"they\",\"them\",\"their\",\"what\",\"which\",\"who\",\"whom\",\"when\",\"where\",\n    \"why\",\"how\",\"all\",\"any\",\"as\",\"if\",\"up\",\"out\",\"about\",\"into\",\"through\",\n    \"during\",\"before\",\"after\",\"above\",\"below\",\"between\",\"same\",\"just\",\"also\",\n    \"very\",\"too\",\"much\",\"well\",\"only\",\"even\",\"still\",\"back\",\"here\",\"there\",\"now\"\n  ]);\n\n  let lastResults = [];\n\n  function tokenize(text, caseInsensitive, minLen, ignoreStop) {\n    if (caseInsensitive) text = text.toLowerCase();\n    const words = text.match(/[a-zA-Z']+/g) || [];\n    const freq = {};\n    for (let w of words) {\n      w = w.replace(/^'+|'+$/g, \"\");\n      if (w.length \u003c minLen) continue;\n      if (ignoreStop \u0026\u0026 STOP_WORDS.has(w.toLowerCase())) continue;\n      freq[w] = (freq[w] || 0) + 1;\n    }\n    return Object.entries(freq).sort((a,b) =\u003e b[1]-a[1]);\n  }\n\n  function drawChart(data, topN) {\n    const canvas = document.getElementById(\"wf-canvas\");\n    const items = data.slice(0, Math.min(topN, 20));\n    if (!items.length) { canvas.style.display = \"none\"; return; }\n    const barH = 28, padL = 110, padR = 70, padT = 20, padB = 20;\n    const w = Math.max(500, padL + 320 + padR);\n    const h = padT + items.length * barH + padB;\n    canvas.width = w;\n    canvas.height = h;\n    canvas.style.display = \"block\";\n    const ctx = canvas.getContext(\"2d\");\n    ctx.clearRect(0, 0, w, h);\n    const maxVal = items[0][1];\n    const barMaxW = w - padL - padR;\n    const colors = [\"#6366f1\",\"#8b5cf6\",\"#06b6d4\",\"#10b981\",\"#f59e0b\",\"#ef4444\",\"#ec4899\",\"#14b8a6\",\"#f97316\",\"#84cc16\"];\n    items.forEach(([word, count], i) =\u003e {\n      const y = padT + i * barH;\n      const bw = Math.max(4, (count / maxVal) * barMaxW);\n      ctx.fillStyle = colors[i % colors.length];\n      ctx.beginPath();\n      ctx.roundRect(padL, y + 4, bw, barH - 8, 4);\n      ctx.fill();\n      ctx.fillStyle = \"#475569\";\n      ctx.font = \"12px -apple-system, sans-serif\";\n      ctx.textAlign = \"right\";\n      ctx.fillText(word, padL - 8, y + barH / 2 + 4);\n      ctx.fillStyle = \"#1e293b\";\n      ctx.font = \"bold 12px -apple-system, sans-serif\";\n      ctx.textAlign = \"left\";\n      ctx.fillText(count, padL + bw + 6, y + barH / 2 + 4);\n    });\n  }\n\n  function renderTable(data, topN, total) {\n    const container = document.getElementById(\"wf-table-container\");\n    const items = data.slice(0, topN);\n    if (!items.length) {\n      container.innerHTML = '\u003cdiv class=\"wf-empty\"\u003eNo words found with current settings.\u003c/div\u003e';\n      return;\n    }\n    const maxCount = items[0][1];\n    let rows = items.map(([word, count], i) =\u003e {\n      const pct = total \u003e 0 ? ((count / total) * 100).toFixed(2) : 0;\n      const barW = Math.round((count / maxCount) * 100);\n      return `\u003ctr\u003e\n        \u003ctd class=\"wf-rank\"\u003e${i+1}\u003c/td\u003e\n        \u003ctd\u003e\u003cstrong\u003e${word}\u003c/strong\u003e\u003c/td\u003e\n        \u003ctd\u003e${count}\u003c/td\u003e\n        \u003ctd class=\"wf-bar-cell\"\u003e\u003cdiv class=\"wf-inline-bar\" style=\"width:${barW}%\"\u003e\u003c/div\u003e\u003c/td\u003e\n        \u003ctd class=\"wf-pct\"\u003e${pct}%\u003c/td\u003e\n      \u003c/tr\u003e`;\n    }).join(\"\");\n    container.innerHTML = `\u003cdiv class=\"wf-table-wrap\"\u003e\u003ctable\u003e\n      \u003cthead\u003e\u003ctr\u003e\u003cth\u003e#\u003c/th\u003e\u003cth\u003eWord\u003c/th\u003e\u003cth\u003eCount\u003c/th\u003e\u003cth\u003eFrequency\u003c/th\u003e\u003cth\u003e%\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e\n      \u003ctbody\u003e${rows}\u003c/tbody\u003e\n    \u003c/table\u003e\u003c/div\u003e`;\n  }\n\n  function analyze() {\n    const text = document.getElementById(\"wf-input\").value;\n    const topN = parseInt(document.getElementById(\"wf-top-n\").value);\n    const ignoreStop = document.getElementById(\"wf-stop\").checked;\n    const caseInsensitive = document.getElementById(\"wf-case\").checked;\n    const minLen = parseInt(document.getElementById(\"wf-minlen\").value) || 1;\n    if (!text.trim()) {\n      document.getElementById(\"wf-table-container\").innerHTML = '\u003cdiv class=\"wf-empty\"\u003ePaste text above and click Analyze to see word frequency.\u003c/div\u003e';\n      document.getElementById(\"wf-canvas\").style.display = \"none\";\n      document.getElementById(\"wf-stats\").style.display = \"none\";\n      return;\n    }\n    const results = tokenize(text, caseInsensitive, minLen, ignoreStop);\n    lastResults = results;\n    const totalWords = results.reduce((s,[,c]) =\u003e s+c, 0);\n    const uniqueWords = results.length;\n    const statsEl = document.getElementById(\"wf-stats\");\n    statsEl.style.display = \"flex\";\n    statsEl.innerHTML = `\n      \u003cspan\u003eTotal words: \u003cstrong\u003e${totalWords.toLocaleString()}\u003c/strong\u003e\u003c/span\u003e\n      \u003cspan\u003eUnique words: \u003cstrong\u003e${uniqueWords.toLocaleString()}\u003c/strong\u003e\u003c/span\u003e\n      \u003cspan\u003eShowing top: \u003cstrong\u003e${Math.min(topN, uniqueWords)}\u003c/strong\u003e\u003c/span\u003e\n    `;\n    drawChart(results, topN);\n    renderTable(results, topN, totalWords);\n  }\n\n  document.getElementById(\"wf-analyze-btn\").addEventListener(\"click\", analyze);\n  document.getElementById(\"wf-input\").addEventListener(\"keydown\", function(e){\n    if (e.ctrlKey \u0026\u0026 e.key === \"Enter\") analyze();\n  });\n\n  document.getElementById(\"wf-clear-btn\").addEventListener(\"click\", function(){\n    document.getElementById(\"wf-input\").value = \"\";\n    document.getElementById(\"wf-canvas\").style.display = \"none\";\n    document.getElementById(\"wf-stats\").style.display = \"none\";\n    document.getElementById(\"wf-table-container\").innerHTML = '\u003cdiv class=\"wf-empty\"\u003ePaste text above and click Analyze to see word frequency.\u003c/div\u003e';\n    lastResults = [];\n  });\n\n  document.getElementById(\"wf-copy-btn\").addEventListener(\"click\", function(){\n    if (!lastResults.length) return;\n    const topN = parseInt(document.getElementById(\"wf-top-n\").value);\n    const lines = lastResults.slice(0, topN).map(([w,c],i) =\u003e `${i+1}. ${w}: ${c}`);\n    navigator.clipboard.writeText(lines.join(\"\\n\")).then(() =\u003e {\n      const btn = document.getElementById(\"wf-copy-btn\");\n      const orig = btn.textContent;\n      btn.textContent = \"Copied!\";\n      setTimeout(() =\u003e btn.textContent = orig, 1500);\n    });\n  });\n\n  document.getElementById(\"wf-csv-btn\").addEventListener(\"click\", function(){\n    if (!lastResults.length) return;\n    const topN = parseInt(document.getElementById(\"wf-top-n\").value);\n    const total = lastResults.reduce((s,[,c]) =\u003e s+c, 0);\n    const rows = [[\"Rank\",\"Word\",\"Count\",\"Percentage\"]];\n    lastResults.slice(0, topN).forEach(([w,c],i) =\u003e {\n      rows.push([i+1, w, c, total \u003e 0 ? ((c/total)*100).toFixed(2)+\"%\" : \"0%\"]);\n    });\n    const csv = rows.map(r =\u003e r.map(v =\u003e `\"${v}\"`).join(\",\")).join(\"\\n\");\n    const blob = new Blob([csv], {type: \"text/csv\"});\n    const url = URL.createObjectURL(blob);\n    const a = document.createElement(\"a\");\n    a.href = url; a.download = \"word-frequency.csv\"; a.click();\n    URL.revokeObjectURL(url);\n  });\n\n  // Auto-analyze on option change\n  [\"wf-top-n\",\"wf-stop\",\"wf-case\",\"wf-minlen\"].forEach(id =\u003e {\n    document.getElementById(id).addEventListener(\"change\", function(){\n      if (lastResults.length || document.getElementById(\"wf-input\").value.trim()) analyze();\n    });\n  });\n})();\n\u003c/script\u003e\n\u003c/div\u003e","title":"Word Frequency Analyzer"},{"content":" Paste or type your text below Case-insensitive Exclude stop words Show n-gram analysis Analyze Text Clear Top 20 Words Full Table N-grams Word Cloud Top 20 Most Common Words\nExport Words CSV #\u0026#8597; Word\u0026#8597; Count\u0026#8597; %\u0026#8597; Bar Export Bigrams CSV Export Trigrams CSV Top Bigrams (2-word phrases)\n#PhraseCount%Bar Top Trigrams (3-word phrases)\n#PhraseCount%Bar Font size is proportional to word frequency. Top 60 words shown.\nRelated tools:\nConvert text case → Text Case Converter Count lines of code → Line Counter Check text differences → Text Diff Checker ","permalink":"https://productivity-works.com/tools/word-frequency-counter/","summary":"\u003cdiv id=\"wf-app\"\u003e\n\u003cstyle\u003e\n#wf-app {\n  font-family: system-ui, -apple-system, sans-serif;\n  max-width: 900px;\n  margin: 0 auto;\n  color: #e2e8f0;\n}\n#wf-app * {\n  box-sizing: border-box;\n}\n#wf-app .wf-section {\n  background: #1e293b;\n  border: 1px solid #334155;\n  border-radius: 10px;\n  padding: 20px;\n  margin-bottom: 20px;\n}\n#wf-app label {\n  display: block;\n  font-size: 0.85rem;\n  color: #94a3b8;\n  margin-bottom: 6px;\n  font-weight: 500;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n#wf-app textarea {\n  width: 100%;\n  height: 200px;\n  background: #0f172a;\n  border: 1px solid #334155;\n  border-radius: 8px;\n  color: #e2e8f0;\n  font-size: 0.95rem;\n  padding: 12px;\n  resize: vertical;\n  outline: none;\n  font-family: inherit;\n  line-height: 1.6;\n}\n#wf-app textarea:focus {\n  border-color: #6366f1;\n}\n#wf-app .wf-options {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 16px;\n  margin-top: 14px;\n  align-items: center;\n}\n#wf-app .wf-checkbox-group {\n  display: flex;\n  align-items: center;\n  gap: 7px;\n  cursor: pointer;\n  font-size: 0.9rem;\n  color: #cbd5e1;\n}\n#wf-app .wf-checkbox-group input[type=\"checkbox\"] {\n  width: 16px;\n  height: 16px;\n  accent-color: #6366f1;\n  cursor: pointer;\n}\n#wf-app .wf-btn-row {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n  margin-top: 16px;\n}\n#wf-app button {\n  padding: 10px 22px;\n  border-radius: 7px;\n  font-size: 0.9rem;\n  font-weight: 600;\n  cursor: pointer;\n  border: none;\n  transition: background 0.15s, transform 0.1s;\n}\n#wf-app button:active {\n  transform: scale(0.97);\n}\n#wf-app .wf-btn-primary {\n  background: #6366f1;\n  color: #fff;\n}\n#wf-app .wf-btn-primary:hover {\n  background: #4f46e5;\n}\n#wf-app .wf-btn-secondary {\n  background: #334155;\n  color: #e2e8f0;\n}\n#wf-app .wf-btn-secondary:hover {\n  background: #475569;\n}\n#wf-app .wf-btn-danger {\n  background: #1e293b;\n  color: #94a3b8;\n  border: 1px solid #334155;\n}\n#wf-app .wf-btn-danger:hover {\n  background: #334155;\n  color: #e2e8f0;\n}\n#wf-app .wf-stats-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n  gap: 12px;\n  margin-bottom: 20px;\n}\n#wf-app .wf-stat-card {\n  background: #0f172a;\n  border: 1px solid #334155;\n  border-radius: 8px;\n  padding: 14px 18px;\n  text-align: center;\n}\n#wf-app .wf-stat-value {\n  font-size: 1.8rem;\n  font-weight: 700;\n  color: #818cf8;\n  line-height: 1.1;\n}\n#wf-app .wf-stat-label {\n  font-size: 0.78rem;\n  color: #64748b;\n  margin-top: 4px;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n}\n#wf-app .wf-tabs {\n  display: flex;\n  gap: 4px;\n  border-bottom: 1px solid #334155;\n  margin-bottom: 18px;\n  flex-wrap: wrap;\n}\n#wf-app .wf-tab {\n  padding: 8px 18px;\n  border-radius: 7px 7px 0 0;\n  font-size: 0.88rem;\n  font-weight: 600;\n  cursor: pointer;\n  border: 1px solid transparent;\n  border-bottom: none;\n  background: transparent;\n  color: #64748b;\n  transition: color 0.15s, background 0.15s;\n}\n#wf-app .wf-tab:hover {\n  color: #cbd5e1;\n}\n#wf-app .wf-tab.active {\n  background: #1e293b;\n  border-color: #334155;\n  color: #818cf8;\n}\n#wf-app .wf-tab-panel {\n  display: none;\n}\n#wf-app .wf-tab-panel.active {\n  display: block;\n}\n#wf-app .wf-table-wrap {\n  overflow-x: auto;\n}\n#wf-app table {\n  width: 100%;\n  border-collapse: collapse;\n  font-size: 0.88rem;\n}\n#wf-app th {\n  background: #0f172a;\n  color: #94a3b8;\n  font-size: 0.78rem;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  padding: 10px 12px;\n  text-align: left;\n  border-bottom: 1px solid #334155;\n  cursor: pointer;\n  user-select: none;\n  white-space: nowrap;\n}\n#wf-app th:hover {\n  color: #e2e8f0;\n}\n#wf-app th .wf-sort-icon {\n  margin-left: 5px;\n  opacity: 0.4;\n  font-style: normal;\n}\n#wf-app th.sort-asc .wf-sort-icon,\n#wf-app th.sort-desc .wf-sort-icon {\n  opacity: 1;\n  color: #818cf8;\n}\n#wf-app td {\n  padding: 9px 12px;\n  border-bottom: 1px solid #1e293b;\n  color: #cbd5e1;\n}\n#wf-app tr:hover td {\n  background: #1e293b;\n}\n#wf-app .wf-bar-cell {\n  width: 120px;\n}\n#wf-app .wf-bar-bg {\n  background: #0f172a;\n  border-radius: 4px;\n  height: 8px;\n  overflow: hidden;\n}\n#wf-app .wf-bar-fill {\n  background: linear-gradient(90deg, #6366f1, #818cf8);\n  height: 100%;\n  border-radius: 4px;\n  transition: width 0.3s;\n}\n#wf-app .wf-rank {\n  color: #475569;\n  font-size: 0.8rem;\n}\n#wf-app canvas#wf-cloud {\n  display: block;\n  width: 100%;\n  max-width: 860px;\n  border-radius: 8px;\n  background: #0f172a;\n  border: 1px solid #334155;\n  margin: 0 auto;\n}\n#wf-app .wf-empty {\n  text-align: center;\n  color: #475569;\n  padding: 40px 20px;\n  font-size: 0.95rem;\n}\n#wf-app .wf-top20-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\n  gap: 10px;\n}\n#wf-app .wf-top20-item {\n  background: #0f172a;\n  border: 1px solid #334155;\n  border-radius: 7px;\n  padding: 10px 14px;\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n#wf-app .wf-top20-rank {\n  font-size: 0.72rem;\n  font-weight: 700;\n  color: #6366f1;\n  background: #1e293b;\n  border-radius: 5px;\n  padding: 2px 7px;\n  min-width: 28px;\n  text-align: center;\n}\n#wf-app .wf-top20-word {\n  font-weight: 600;\n  color: #e2e8f0;\n  flex: 1;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n#wf-app .wf-top20-count {\n  color: #818cf8;\n  font-size: 0.85rem;\n  font-weight: 600;\n}\n#wf-app .wf-section-title {\n  font-size: 1rem;\n  font-weight: 700;\n  color: #e2e8f0;\n  margin: 0 0 14px 0;\n}\n#wf-app .wf-export-row {\n  display: flex;\n  gap: 10px;\n  flex-wrap: wrap;\n  margin-top: 10px;\n}\n#wf-app .wf-notice {\n  font-size: 0.82rem;\n  color: #64748b;\n  margin-top: 8px;\n}\n@media (max-width: 600px) {\n  #wf-app .wf-stats-grid {\n    grid-template-columns: repeat(2, 1fr);\n  }\n  #wf-app .wf-bar-cell {\n    display: none;\n  }\n}\n\u003c/style\u003e\n\u003c!-- INPUT SECTION --\u003e\n\u003cdiv class=\"wf-section\"\u003e\n  \u003clabel for=\"wf-input\"\u003ePaste or type your text below\u003c/label\u003e\n  \u003ctextarea id=\"wf-input\" placeholder=\"Paste your text here to analyze word frequency...\"\u003e\u003c/textarea\u003e\n  \u003cdiv class=\"wf-options\"\u003e\n    \u003clabel class=\"wf-checkbox-group\"\u003e\n      \u003cinput type=\"checkbox\" id=\"wf-case-insensitive\" checked\u003e\n      Case-insensitive\n    \u003c/label\u003e\n    \u003clabel class=\"wf-checkbox-group\"\u003e\n      \u003cinput type=\"checkbox\" id=\"wf-stop-words\"\u003e\n      Exclude stop words\n    \u003c/label\u003e\n    \u003clabel class=\"wf-checkbox-group\"\u003e\n      \u003cinput type=\"checkbox\" id=\"wf-show-ngrams\" checked\u003e\n      Show n-gram analysis\n    \u003c/label\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"wf-btn-row\"\u003e\n    \u003cbutton class=\"wf-btn-primary\" onclick=\"wfAnalyze()\"\u003eAnalyze Text\u003c/button\u003e\n    \u003cbutton class=\"wf-btn-danger\" onclick=\"wfClear()\"\u003eClear\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- RESULTS SECTION --\u003e\n\u003cdiv class=\"wf-section\" id=\"wf-results\" style=\"display:none;\"\u003e\n  \u003c!-- STATS --\u003e\n  \u003cdiv class=\"wf-stats-grid\" id=\"wf-stats\"\u003e\u003c/div\u003e\n  \u003c!-- TABS --\u003e\n  \u003cdiv class=\"wf-tabs\"\u003e\n    \u003cbutton class=\"wf-tab active\" onclick=\"wfSwitchTab('top20', this)\"\u003eTop 20 Words\u003c/button\u003e\n    \u003cbutton class=\"wf-tab\" onclick=\"wfSwitchTab('table', this)\"\u003eFull Table\u003c/button\u003e\n    \u003cbutton class=\"wf-tab\" onclick=\"wfSwitchTab('ngrams', this)\"\u003eN-grams\u003c/button\u003e\n    \u003cbutton class=\"wf-tab\" onclick=\"wfSwitchTab('cloud', this)\"\u003eWord Cloud\u003c/button\u003e\n  \u003c/div\u003e\n  \u003c!-- TOP 20 --\u003e\n  \u003cdiv class=\"wf-tab-panel active\" id=\"wf-panel-top20\"\u003e\n    \u003cp class=\"wf-section-title\"\u003eTop 20 Most Common Words\u003c/p\u003e","title":"Word Frequency Counter"},{"content":"Convert between YAML and JSON instantly in your browser. Paste your input and the tool auto-detects the format.\nYAML ↔ JSON Converter Convert Sample YAML Sample JSON Clear Indent: 2 spaces 4 spaces Input (YAML or JSON) Output Copy Related: Format JSON → JSON Formatter ","permalink":"https://productivity-works.com/tools/yaml-json-converter/","summary":"\u003cp\u003eConvert between YAML and JSON instantly in your browser. Paste your input and the tool auto-detects the format.\u003c/p\u003e\n\u003cdiv id=\"yj-app\"\u003e\n\u003cstyle\u003e\n  #yj-app {\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n    background: #1a1a1a;\n    color: #e5e5e5;\n    border-radius: 12px;\n    padding: 24px;\n    margin: 24px 0;\n  }\n  #yj-app * { box-sizing: border-box; }\n  #yj-app h2 {\n    margin: 0 0 16px;\n    font-size: 1.1rem;\n    color: #ea580c;\n    letter-spacing: 0.02em;\n  }\n  #yj-app .yj-controls {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin-bottom: 18px;\n    align-items: center;\n  }\n  #yj-app button {\n    background: #ea580c;\n    color: #fff;\n    border: none;\n    border-radius: 6px;\n    padding: 8px 16px;\n    font-size: 0.9rem;\n    cursor: pointer;\n    font-weight: 600;\n    transition: background 0.15s;\n  }\n  #yj-app button:hover { background: #c2410c; }\n  #yj-app button.yj-secondary {\n    background: #2a2a2a;\n    color: #e5e5e5;\n    border: 1px solid #444;\n    font-weight: 400;\n  }\n  #yj-app button.yj-secondary:hover { background: #333; }\n  #yj-app .yj-indent-label {\n    font-size: 0.85rem;\n    color: #aaa;\n    margin-left: 4px;\n    display: flex;\n    align-items: center;\n    gap: 6px;\n  }\n  #yj-app select {\n    background: #2a2a2a;\n    color: #e5e5e5;\n    border: 1px solid #444;\n    border-radius: 6px;\n    padding: 7px 10px;\n    font-size: 0.9rem;\n    cursor: pointer;\n  }\n  #yj-app .yj-panes {\n    display: grid;\n    grid-template-columns: 1fr 1fr;\n    gap: 16px;\n  }\n  @media (max-width: 600px) {\n    #yj-app .yj-panes { grid-template-columns: 1fr; }\n  }\n  #yj-app .yj-pane-label {\n    font-size: 0.8rem;\n    color: #aaa;\n    margin-bottom: 6px;\n    text-transform: uppercase;\n    letter-spacing: 0.05em;\n  }\n  #yj-app textarea {\n    width: 100%;\n    height: 280px;\n    background: #111;\n    color: #e5e5e5;\n    border: 1.5px solid #333;\n    border-radius: 8px;\n    padding: 12px;\n    font-family: \"Fira Mono\", \"Consolas\", \"Menlo\", monospace;\n    font-size: 0.85rem;\n    line-height: 1.6;\n    resize: vertical;\n    outline: none;\n    transition: border-color 0.15s;\n  }\n  #yj-app textarea:focus { border-color: #ea580c; }\n  #yj-app .yj-output-wrapper { position: relative; }\n  #yj-app .yj-copy-btn {\n    position: absolute;\n    top: 8px;\n    right: 8px;\n    background: #2a2a2a;\n    color: #aaa;\n    border: 1px solid #444;\n    border-radius: 5px;\n    padding: 4px 10px;\n    font-size: 0.78rem;\n    cursor: pointer;\n    font-weight: 400;\n    transition: background 0.15s, color 0.15s;\n    z-index: 2;\n  }\n  #yj-app .yj-copy-btn:hover { background: #ea580c; color: #fff; border-color: #ea580c; }\n  #yj-app #yj-error {\n    margin-top: 12px;\n    background: #3b0000;\n    border: 1px solid #7f1d1d;\n    color: #fca5a5;\n    border-radius: 6px;\n    padding: 10px 14px;\n    font-size: 0.88rem;\n    display: none;\n  }\n  #yj-app #yj-detect-badge {\n    font-size: 0.8rem;\n    color: #ea580c;\n    background: #2a1200;\n    border: 1px solid #7c2d12;\n    border-radius: 4px;\n    padding: 3px 9px;\n    display: inline-block;\n  }\n\u003c/style\u003e\n\u003ch2\u003eYAML ↔ JSON Converter\u003c/h2\u003e\n\u003cdiv class=\"yj-controls\"\u003e\n  \u003cbutton onclick=\"yjConvert()\"\u003eConvert\u003c/button\u003e\n  \u003cbutton class=\"yj-secondary\" onclick=\"yjLoadSampleYAML()\"\u003eSample YAML\u003c/button\u003e\n  \u003cbutton class=\"yj-secondary\" onclick=\"yjLoadSampleJSON()\"\u003eSample JSON\u003c/button\u003e\n  \u003cbutton class=\"yj-secondary\" onclick=\"yjClear()\"\u003eClear\u003c/button\u003e\n  \u003cspan class=\"yj-indent-label\"\u003e\n    Indent:\n    \u003cselect id=\"yj-indent\"\u003e\n      \u003coption value=\"2\" selected\u003e2 spaces\u003c/option\u003e\n      \u003coption value=\"4\"\u003e4 spaces\u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/span\u003e\n  \u003cspan id=\"yj-detect-badge\" style=\"display:none\"\u003e\u003c/span\u003e\n\u003c/div\u003e\n\u003cdiv class=\"yj-panes\"\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"yj-pane-label\"\u003eInput (YAML or JSON)\u003c/div\u003e\n    \u003ctextarea id=\"yj-input\" placeholder=\"Paste YAML or JSON here…\" oninput=\"yjAutoDetect()\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n  \u003cdiv\u003e\n    \u003cdiv class=\"yj-pane-label\"\u003eOutput\u003c/div\u003e\n    \u003cdiv class=\"yj-output-wrapper\"\u003e\n      \u003cbutton class=\"yj-copy-btn\" onclick=\"yjCopyOutput()\"\u003eCopy\u003c/button\u003e\n      \u003ctextarea id=\"yj-output\" readonly placeholder=\"Output will appear here…\"\u003e\u003c/textarea\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv id=\"yj-error\"\u003e\u003c/div\u003e\n\u003cscript\u003e\n(function() {\n\n  // ── YAML Parser ──────────────────────────────────────────────────────────\n  // Supports: key: value, nested objects, arrays (- item), strings, numbers, booleans, null\n  function parseYAML(text) {\n    var lines = text.split('\\n');\n    var result = parseBlock(lines, 0, 0);\n    return result.value;\n  }\n\n  function getIndent(line) {\n    var m = line.match(/^(\\s*)/);\n    return m ? m[1].length : 0;\n  }\n\n  function parseScalar(raw) {\n    var s = raw.trim();\n    if (s === 'null' || s === '~') return null;\n    if (s === 'true') return true;\n    if (s === 'false') return false;\n    if (s === '') return null;\n    // Quoted strings\n    if ((s[0] === '\"' \u0026\u0026 s[s.length-1] === '\"') ||\n        (s[0] === \"'\" \u0026\u0026 s[s.length-1] === \"'\")) {\n      return s.slice(1, -1);\n    }\n    // Number\n    if (/^-?(\\d+\\.?\\d*|\\.\\d+)([eE][+-]?\\d+)?$/.test(s)) {\n      var n = Number(s);\n      if (!isNaN(n)) return n;\n    }\n    return s;\n  }\n\n  // Returns {value, nextLine}\n  function parseBlock(lines, startLine, baseIndent) {\n    var i = startLine;\n    // Skip empty lines\n    while (i \u003c lines.length \u0026\u0026 lines[i].trim() === '') i++;\n    if (i \u003e= lines.length) return {value: null, nextLine: i};\n\n    var firstLine = lines[i];\n    var firstIndent = getIndent(firstLine);\n    var firstTrimmed = firstLine.trim();\n\n    // Determine if block is a sequence or mapping\n    if (firstTrimmed.startsWith('- ') || firstTrimmed === '-') {\n      // Sequence\n      return parseSequence(lines, i, firstIndent);\n    } else {\n      // Mapping or scalar\n      return parseMapping(lines, i, firstIndent);\n    }\n  }\n\n  function parseSequence(lines, startLine, baseIndent) {\n    var arr = [];\n    var i = startLine;\n    while (i \u003c lines.length) {\n      var line = lines[i];\n      if (line.trim() === '') { i++; continue; }\n      var indent = getIndent(line);\n      if (indent \u003c baseIndent) break;\n      if (indent \u003e baseIndent) { i++; continue; }\n      var trimmed = line.trim();\n      if (!trimmed.startsWith('-')) break;\n      var afterDash = trimmed.slice(1);\n      if (afterDash.startsWith(' ')) afterDash = afterDash.slice(1);\n      // Inline value after dash?\n      if (afterDash.trim() !== '') {\n        // Could be a mapping on same line: \"- key: val\"\n        if (/^[^:]+:\\s/.test(afterDash) || afterDash.endsWith(':')) {\n          // Inline mapping — parse as block starting with this content\n          // Build synthetic lines\n          var subIndent = indent + 2;\n          var synLines = [' '.repeat(subIndent) + afterDash.trim()];\n          var j = i + 1;\n          while (j \u003c lines.length) {\n            if (lines[j].trim() === '') { synLines.push(''); j++; continue; }\n            if (getIndent(lines[j]) \u003c= indent) break;\n            synLines.push(lines[j]);\n            j++;\n          }\n          var subResult = parseBlock(synLines, 0, subIndent);\n          arr.push(subResult.value);\n          i = j;\n        } else {\n          arr.push(parseScalar(afterDash));\n          i++;\n        }\n      } else {\n        // Next lines belong to this item\n        i++;\n        while (i \u003c lines.length \u0026\u0026 lines[i].trim() === '') i++;\n        if (i \u003c lines.length) {\n          var childIndent = getIndent(lines[i]);\n          if (childIndent \u003e baseIndent) {\n            var res = parseBlock(lines, i, childIndent);\n            arr.push(res.value);\n            i = res.nextLine;\n          } else {\n            arr.push(null);\n          }\n        } else {\n          arr.push(null);\n        }\n      }\n    }\n    return {value: arr, nextLine: i};\n  }\n\n  function parseMapping(lines, startLine, baseIndent) {\n    var obj = {};\n    var i = startLine;\n    while (i \u003c lines.length) {\n      var line = lines[i];\n      if (line.trim() === '') { i++; continue; }\n      var indent = getIndent(line);\n      if (indent \u003c baseIndent) break;\n      if (indent \u003e baseIndent) { i++; continue; }\n      var trimmed = line.trim();\n      if (trimmed.startsWith('-')) break; // sequence starts\n      // Key: value\n      var colonIdx = trimmed.indexOf(': ');\n      var key, valStr;\n      if (colonIdx !== -1) {\n        key = trimmed.slice(0, colonIdx).trim();\n        valStr = trimmed.slice(colonIdx + 2).trim();\n      } else if (trimmed.endsWith(':')) {\n        key = trimmed.slice(0, -1).trim();\n        valStr = '';\n      } else {\n        // Not a key-value pair, treat as scalar\n        return {value: parseScalar(trimmed), nextLine: i + 1};\n      }\n      i++;\n      if (valStr !== '') {\n        obj[key] = parseScalar(valStr);\n      } else {\n        // Value is next block\n        while (i \u003c lines.length \u0026\u0026 lines[i].trim() === '') i++;\n        if (i \u003c lines.length) {\n          var childIndent = getIndent(lines[i]);\n          if (childIndent \u003e baseIndent) {\n            var childTrimmed = lines[i].trim();\n            if (childTrimmed.startsWith('- ') || childTrimmed === '-') {\n              var seqRes = parseSequence(lines, i, childIndent);\n              obj[key] = seqRes.value;\n              i = seqRes.nextLine;\n            } else {\n              var mapRes = parseMapping(lines, i, childIndent);\n              obj[key] = mapRes.value;\n              i = mapRes.nextLine;\n            }\n          } else {\n            obj[key] = null;\n          }\n        } else {\n          obj[key] = null;\n        }\n      }\n    }\n    return {value: obj, nextLine: i};\n  }\n\n  // ── JSON → YAML ──────────────────────────────────────────────────────────\n  function jsonToYAML(obj, indent, level) {\n    indent = indent || 2;\n    level = level || 0;\n    var pad = ' '.repeat(indent * level);\n    var childPad = ' '.repeat(indent * (level + 1));\n\n    if (obj === null) return 'null';\n    if (obj === true) return 'true';\n    if (obj === false) return 'false';\n    if (typeof obj === 'number') return String(obj);\n    if (typeof obj === 'string') {\n      // Quote if necessary\n      if (/[:#\\[\\]{}\u0026*!|\u003e'\"%@`,]/.test(obj) || obj.trim() !== obj ||\n          obj === '' || /^\\s*$/.test(obj) ||\n          ['true','false','null','~'].indexOf(obj.toLowerCase()) !== -1 ||\n          /^-?\\d/.test(obj)) {\n        return '\"' + obj.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"') + '\"';\n      }\n      return obj;\n    }\n    if (Array.isArray(obj)) {\n      if (obj.length === 0) return '[]';\n      var lines = obj.map(function(item) {\n        if (typeof item === 'object' \u0026\u0026 item !== null \u0026\u0026 !Array.isArray(item)) {\n          var keys = Object.keys(item);\n          if (keys.length === 0) return pad + '- {}';\n          var firstKey = keys[0];\n          var rest = '';\n          var subLines = [];\n          subLines.push(pad + '- ' + firstKey + ': ' + jsonToYAML(item[firstKey], indent, 0));\n          for (var k = 1; k \u003c keys.length; k++) {\n            subLines.push(pad + '  ' + keys[k] + ': ' + jsonToYAML(item[keys[k]], indent, 0));\n          }\n          return subLines.join('\\n');\n        }\n        if (Array.isArray(item)) {\n          return pad + '-\\n' + jsonToYAML(item, indent, level + 1);\n        }\n        return pad + '- ' + jsonToYAML(item, indent, 0);\n      });\n      return lines.join('\\n');\n    }\n    if (typeof obj === 'object') {\n      var keys = Object.keys(obj);\n      if (keys.length === 0) return '{}';\n      var lines = keys.map(function(k) {\n        var val = obj[k];\n        if (typeof val === 'object' \u0026\u0026 val !== null) {\n          if (Array.isArray(val)) {\n            if (val.length === 0) return pad + k + ': []';\n            return pad + k + ':\\n' + jsonToYAML(val, indent, level + 1);\n          } else {\n            if (Object.keys(val).length === 0) return pad + k + ': {}';\n            return pad + k + ':\\n' + jsonToYAML(val, indent, level + 1);\n          }\n        }\n        return pad + k + ': ' + jsonToYAML(val, indent, 0);\n      });\n      return lines.join('\\n');\n    }\n    return String(obj);\n  }\n\n  // ── Auto-detect ──────────────────────────────────────────────────────────\n  function detectFormat(text) {\n    var t = text.trim();\n    if (!t) return null;\n    if (t[0] === '{' || t[0] === '[') return 'JSON';\n    return 'YAML';\n  }\n\n  // ── UI Functions ─────────────────────────────────────────────────────────\n  window.yjAutoDetect = function() {\n    var input = document.getElementById('yj-input').value;\n    var badge = document.getElementById('yj-detect-badge');\n    var fmt = detectFormat(input);\n    if (fmt) {\n      badge.textContent = 'Detected: ' + fmt;\n      badge.style.display = 'inline-block';\n    } else {\n      badge.style.display = 'none';\n    }\n  };\n\n  window.yjConvert = function() {\n    var input = document.getElementById('yj-input').value.trim();\n    var errEl = document.getElementById('yj-error');\n    var outEl = document.getElementById('yj-output');\n    var indent = parseInt(document.getElementById('yj-indent').value, 10);\n\n    errEl.style.display = 'none';\n    outEl.value = '';\n\n    if (!input) {\n      showError('Input is empty. Paste YAML or JSON to convert.');\n      return;\n    }\n\n    var fmt = detectFormat(input);\n    try {\n      if (fmt === 'JSON') {\n        var parsed = JSON.parse(input);\n        var yaml = jsonToYAML(parsed, indent, 0);\n        outEl.value = yaml;\n      } else {\n        // YAML → JSON\n        var parsed = parseYAML(input);\n        outEl.value = JSON.stringify(parsed, null, indent);\n      }\n    } catch(e) {\n      showError('Parse error: ' + e.message);\n    }\n  };\n\n  window.yjCopyOutput = function() {\n    var outEl = document.getElementById('yj-output');\n    if (!outEl.value) return;\n    navigator.clipboard.writeText(outEl.value).then(function() {\n      var btn = document.querySelector('#yj-app .yj-copy-btn');\n      var orig = btn.textContent;\n      btn.textContent = 'Copied!';\n      setTimeout(function() { btn.textContent = orig; }, 1500);\n    }).catch(function() {\n      outEl.select();\n      document.execCommand('copy');\n    });\n  };\n\n  window.yjClear = function() {\n    document.getElementById('yj-input').value = '';\n    document.getElementById('yj-output').value = '';\n    document.getElementById('yj-error').style.display = 'none';\n    document.getElementById('yj-detect-badge').style.display = 'none';\n  };\n\n  window.yjLoadSampleYAML = function() {\n    document.getElementById('yj-input').value =\n'name: Productivity Works\\nversion: 2.1\\nactive: true\\nfeatures:\\n  - name: YAML Converter\\n    free: true\\n  - name: JSON Formatter\\n    free: true\\nsettings:\\n  theme: dark\\n  indent: 2\\n  maxSize: null\\ntags:\\n  - productivity\\n  - tools\\n  - free';\n    yjAutoDetect();\n    document.getElementById('yj-error').style.display = 'none';\n    document.getElementById('yj-output').value = '';\n  };\n\n  window.yjLoadSampleJSON = function() {\n    document.getElementById('yj-input').value =\nJSON.stringify({\n  \"name\": \"Productivity Works\",\n  \"version\": 2.1,\n  \"active\": true,\n  \"features\": [\n    {\"name\": \"YAML Converter\", \"free\": true},\n    {\"name\": \"JSON Formatter\", \"free\": true}\n  ],\n  \"settings\": {\n    \"theme\": \"dark\",\n    \"indent\": 2,\n    \"maxSize\": null\n  },\n  \"tags\": [\"productivity\", \"tools\", \"free\"]\n}, null, 2);\n    yjAutoDetect();\n    document.getElementById('yj-error').style.display = 'none';\n    document.getElementById('yj-output').value = '';\n  };\n\n  function showError(msg) {\n    var el = document.getElementById('yj-error');\n    el.textContent = msg;\n    el.style.display = 'block';\n  }\n\n})();\n\u003c/script\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cstrong\u003eRelated:\u003c/strong\u003e Format JSON → \u003ca href=\"https://productivity-works.com/tools/json-formatter/\"\u003eJSON Formatter\u003c/a\u003e\n\u003c/p\u003e","title":"YAML ↔ JSON Converter — Free Online Tool"},{"content":" Paste YAML or JSON below — validate, convert, or format it instantly. No data leaves your browser.\nInput type: YAML JSON Upload file Clear Input Output Copy Validate YAML → JSON JSON → YAML Format / Prettify Related Tools Validate JSON → JSON Validator Format HTML → HTML Beautifier ","permalink":"https://productivity-works.com/tools/yaml-validator/","summary":"\u003cdiv id=\"ym-app\"\u003e\n\u003cstyle\u003e\n#ym-app *,\n#ym-app *::before,\n#ym-app *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n#ym-app {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n  font-size: 15px;\n  color: #1e293b;\n  line-height: 1.6;\n  max-width: 860px;\n}\n#ym-app h2 {\n  font-size: 1.15rem;\n  font-weight: 700;\n  color: #1e293b;\n  margin-bottom: 10px;\n}\n#ym-app .ym-desc {\n  font-size: 14px;\n  color: #475569;\n  margin-bottom: 18px;\n}\n#ym-app .ym-grid {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 18px;\n}\n@media (max-width: 640px) {\n  #ym-app .ym-grid {\n    grid-template-columns: 1fr;\n  }\n}\n#ym-app .ym-panel {\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n}\n#ym-app .ym-panel-label {\n  font-size: 13px;\n  font-weight: 600;\n  color: #334155;\n}\n#ym-app textarea {\n  width: 100%;\n  height: 280px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 13px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  padding: 12px;\n  resize: vertical;\n  color: #1e293b;\n  background: #f8fafc;\n  line-height: 1.55;\n  outline: none;\n  transition: border-color 0.15s;\n}\n#ym-app textarea:focus {\n  border-color: #6366f1;\n  background: #fff;\n}\n#ym-app .ym-output-wrap {\n  position: relative;\n}\n#ym-app .ym-output {\n  width: 100%;\n  min-height: 280px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace;\n  font-size: 13px;\n  border: 1.5px solid #cbd5e1;\n  border-radius: 8px;\n  padding: 12px;\n  background: #0f172a;\n  color: #e2e8f0;\n  line-height: 1.55;\n  white-space: pre-wrap;\n  word-break: break-all;\n  overflow: auto;\n}\n#ym-app .ym-copy-btn {\n  position: absolute;\n  top: 8px;\n  right: 8px;\n  padding: 4px 10px;\n  font-size: 12px;\n  font-weight: 600;\n  background: #334155;\n  color: #e2e8f0;\n  border: none;\n  border-radius: 5px;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n#ym-app .ym-copy-btn:hover {\n  background: #475569;\n}\n#ym-app .ym-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 8px;\n  margin: 14px 0;\n  align-items: center;\n}\n#ym-app .ym-btn {\n  padding: 8px 16px;\n  font-size: 13px;\n  font-weight: 600;\n  border: none;\n  border-radius: 7px;\n  cursor: pointer;\n  transition: background 0.15s, transform 0.08s;\n}\n#ym-app .ym-btn:active {\n  transform: scale(0.97);\n}\n#ym-app .ym-btn-primary {\n  background: #6366f1;\n  color: #fff;\n}\n#ym-app .ym-btn-primary:hover {\n  background: #4f46e5;\n}\n#ym-app .ym-btn-green {\n  background: #10b981;\n  color: #fff;\n}\n#ym-app .ym-btn-green:hover {\n  background: #059669;\n}\n#ym-app .ym-btn-amber {\n  background: #f59e0b;\n  color: #fff;\n}\n#ym-app .ym-btn-amber:hover {\n  background: #d97706;\n}\n#ym-app .ym-btn-slate {\n  background: #64748b;\n  color: #fff;\n}\n#ym-app .ym-btn-slate:hover {\n  background: #475569;\n}\n#ym-app .ym-btn-red {\n  background: #ef4444;\n  color: #fff;\n}\n#ym-app .ym-btn-red:hover {\n  background: #dc2626;\n}\n#ym-app .ym-upload-label {\n  display: inline-block;\n  padding: 8px 16px;\n  font-size: 13px;\n  font-weight: 600;\n  background: #e2e8f0;\n  color: #334155;\n  border-radius: 7px;\n  cursor: pointer;\n  transition: background 0.15s;\n}\n#ym-app .ym-upload-label:hover {\n  background: #cbd5e1;\n}\n#ym-app #ym-file-input {\n  display: none;\n}\n#ym-app .ym-status {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  font-size: 13px;\n  font-weight: 600;\n  padding: 10px 14px;\n  border-radius: 8px;\n  min-height: 40px;\n}\n#ym-app .ym-status.ok {\n  background: #dcfce7;\n  color: #166534;\n  border: 1px solid #bbf7d0;\n}\n#ym-app .ym-status.err {\n  background: #fee2e2;\n  color: #991b1b;\n  border: 1px solid #fecaca;\n}\n#ym-app .ym-status.info {\n  background: #eff6ff;\n  color: #1e40af;\n  border: 1px solid #bfdbfe;\n}\n#ym-app .ym-status.hidden {\n  display: none;\n}\n/* Syntax highlight tokens */\n#ym-app .yt-key    { color: #a78bfa; }  /* purple */\n#ym-app .yt-str    { color: #4ade80; }  /* green  */\n#ym-app .yt-num    { color: #60a5fa; }  /* blue   */\n#ym-app .yt-bool   { color: #fb923c; }  /* orange */\n#ym-app .yt-null   { color: #94a3b8; }  /* slate  */\n#ym-app .yt-punct  { color: #e2e8f0; }\n#ym-app .yt-cmt    { color: #64748b; font-style: italic; }\n#ym-app .yt-dash   { color: #f472b6; }\n#ym-app .ym-mode-row {\n  display: flex;\n  gap: 8px;\n  align-items: center;\n  flex-wrap: wrap;\n  margin-bottom: 6px;\n}\n#ym-app .ym-mode-label {\n  font-size: 12px;\n  font-weight: 600;\n  color: #94a3b8;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n}\n#ym-app .ym-sep {\n  height: 1px;\n  background: #e2e8f0;\n  margin: 20px 0;\n}\n\u003c/style\u003e\n\u003cp class=\"ym-desc\"\u003ePaste YAML or JSON below — validate, convert, or format it instantly. No data leaves your browser.\u003c/p\u003e","title":"YAML Validator \u0026 Converter"}]