diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Change log
+
+## 0.1.0
+
+* Initial release: fireNumber, gapToTarget, yearsToTarget, budgetCost.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,28 @@
+# fire-number-calc
+
+Fire number and timeline math for early retirement planning.
+
+A fire number is annual spending divided by a withdrawal rate. The
+honest version of the plan also counts the gap from today's balance to
+that target and the years of saving it takes to close the gap when the
+portfolio compounds at a fixed return.
+
+The package follows the withdrawal-rate and sequence-of-returns math
+published on [Firenomics](https://firenomics.com). It depends only on
+base.
+
+## Example
+
+With $48,000 of annual spending at a 4% withdrawal rate:
+
+* fire number: `fireNumber 48000 0.04` = $1,200,000
+* gap from a $450,000 balance: `gapToTarget 450000 1200000` = $750,000
+* years to close it saving $30,000 a year at a 6% return:
+  `yearsToTarget 450000 30000 1200000 0.06 0` = 10 years
+* the same plan with the spending budget growing 3% a year until
+  retirement: 17 years
+
+The last two numbers are the point. A budget that grows before
+retirement pushes the crossing point out by seven working years in
+this plan, and `budgetCost` returns both timelines so the comparison
+is one call.
diff --git a/fire-number-calc.cabal b/fire-number-calc.cabal
new file mode 100644
--- /dev/null
+++ b/fire-number-calc.cabal
@@ -0,0 +1,39 @@
+cabal-version:      2.2
+name:               fire-number-calc
+version:            0.1.0
+synopsis:           Fire number and timeline math for early retirement planning
+description:
+  The arithmetic behind a fire number: the balance a withdrawal rate has
+  to support, the gap between today's balance and that target, and the
+  years of saving it takes to close the gap when the portfolio compounds
+  at a fixed return.
+  .
+  The timeline function also prices a growing budget: a spending target
+  that rises each year before retirement pushes the crossing point out,
+  and the difference between the flat and growing timelines is the
+  working-year cost of that growth.
+  .
+  Companion to Firenomics, which publishes the withdrawal-rate and
+  sequence-of-returns math these functions follow. Four pure functions
+  on Doubles, no dependencies beyond base.
+license:            MIT
+author:             Poket Dev
+maintainer:         hello@poketdev.com
+homepage:           https://firenomics.com
+bug-reports:        https://github.com/scrapertweeter3-prog/fire-number-calc-hs/issues
+copyright:          2026 Poket Dev LLC
+category:           Math
+build-type:         Simple
+extra-doc-files:    README.md
+                  , CHANGELOG.md
+
+source-repository head
+  type:     git
+  location: https://github.com/scrapertweeter3-prog/fire-number-calc-hs
+
+library
+  exposed-modules:  FireNumber.Calc
+  hs-source-dirs:   src
+  build-depends:    base >=4 && <5
+  default-language: Haskell2010
+  ghc-options:      -Wall
diff --git a/src/FireNumber/Calc.hs b/src/FireNumber/Calc.hs
new file mode 100644
--- /dev/null
+++ b/src/FireNumber/Calc.hs
@@ -0,0 +1,55 @@
+-- | Fire number math for early retirement planning.
+--
+-- A fire number is annual spending divided by a withdrawal rate, and the
+-- honest version of the plan also counts the gap from your current
+-- balance, the years of saving it takes to close, and what happens to
+-- that timeline when the spending budget grows before you retire.
+module FireNumber.Calc
+  ( fireNumber
+  , gapToTarget
+  , yearsToTarget
+  , budgetCost
+  ) where
+
+-- | The balance a withdrawal rate has to support a given annual spending.
+-- A zero or negative rate guards to zero.
+fireNumber :: Double -> Double -> Double
+fireNumber annualSpending withdrawalRate
+  | withdrawalRate <= 0 = 0
+  | otherwise           = annualSpending / withdrawalRate
+
+-- | Dollars still missing between today's balance and the fire number.
+-- Never negative.
+gapToTarget :: Double -> Double -> Double
+gapToTarget currentBalance target = max 0 (target - currentBalance)
+
+-- | Whole years until the portfolio (balance plus annual saving,
+-- compounding at 'returnRate') covers the fire number. The target itself
+-- grows at 'spendingGrowth' each year, so a growing budget pushes the
+-- crossing point out. Returns 0 when you are already there and positive
+-- infinity when the budget outruns the savings forever.
+yearsToTarget :: Double -> Double -> Double -> Double -> Double -> Double
+yearsToTarget currentBalance annualSaving target returnRate spendingGrowth
+  | currentBalance >= target = 0
+  | otherwise                = go (1 :: Int)
+  where
+    go n
+      | n > 600   = 1 / 0
+      | bal >= tgt = fromIntegral n
+      | otherwise = go (n + 1)
+      where
+        grow = (1 + returnRate) ** fromIntegral n
+        bal  = currentBalance * grow
+             + if returnRate > 0
+                 then annualSaving * (grow - 1) / returnRate
+                 else annualSaving * fromIntegral n
+        tgt  = target * (1 + spendingGrowth) ** fromIntegral n
+
+-- | Both timelines: years to the target with a flat budget and with one
+-- that grows each year until retirement. The difference is the price of
+-- the growing budget in working years.
+budgetCost :: Double -> Double -> Double -> Double -> Double -> (Double, Double)
+budgetCost currentBalance annualSaving target returnRate spendingGrowth =
+  ( yearsToTarget currentBalance annualSaving target returnRate 0
+  , yearsToTarget currentBalance annualSaving target returnRate spendingGrowth
+  )
