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: netCash, hourlyRate, compareGigs.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,26 @@
+# side-hustle-hourly
+
+True hourly rate math for side hustles and gig work.
+
+A gig's posted rate counts gross cash over paid hours. The real rate
+takes the platform cut and the costs of each job off the gross first,
+then divides by hours that include the unpaid admin around the paid
+ones. Messaging a client, invoicing, restocking supplies, the drive to
+the job: none of it bills, and all of it lands on the same denominator.
+
+The package follows the effective-hourly-rate method used on
+[Extra Hustles](https://extrahustles.com). It depends only on base.
+
+## Example
+
+A gig pays $200 gross, the platform keeps 15 percent, supplies run
+$40, the paid block is 5 hours, and admin eats 3 more:
+
+* net cash: `netCash 200 0.15 40` = $130
+* true hourly rate: `hourlyRate 130 5 3` = $16.25
+* the same net with zero admin hours: $26 an hour
+
+That last number is the trap. The two rates describe the same gig, and
+`compareGigs 16.25 12.00` says a second gig paying $12 an hour on an
+honest denominator still loses to this one by $4.25 an hour. Compare
+rates on hours that count everything, or the comparison is fiction.
diff --git a/side-hustle-hourly.cabal b/side-hustle-hourly.cabal
new file mode 100644
--- /dev/null
+++ b/side-hustle-hourly.cabal
@@ -0,0 +1,34 @@
+cabal-version:      2.2
+name:               side-hustle-hourly
+version:            0.1.0
+synopsis:           True hourly rate math for side hustles and gig work
+description:
+  The rate a gig actually pays: the platform cut and job costs come off
+  the gross first, and the net divides by hours that include the unpaid
+  admin work around the paid ones. Two gigs compare on that rate, and
+  the gap between them is the per-hour price of picking the wrong one.
+  .
+  Companion to Extra Hustles, which prices gig work by its effective
+  hourly rate after hidden costs. Three pure functions on Doubles, no
+  dependencies beyond base.
+license:            MIT
+author:             Poket Dev
+maintainer:         hello@poketdev.com
+homepage:           https://extrahustles.com
+bug-reports:        https://github.com/scrapertweeter3-prog/side-hustle-hourly-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/side-hustle-hourly-hs
+
+library
+  exposed-modules:  SideHustle.Hourly
+  hs-source-dirs:   src
+  build-depends:    base >=4 && <5
+  default-language: Haskell2010
+  ghc-options:      -Wall
diff --git a/src/SideHustle/Hourly.hs b/src/SideHustle/Hourly.hs
new file mode 100644
--- /dev/null
+++ b/src/SideHustle/Hourly.hs
@@ -0,0 +1,31 @@
+-- | True hourly rate math for side hustles and gig work.
+--
+-- A gig's posted rate is gross cash over paid hours. The real one comes
+-- off the gross in platform fees and job costs first, then divides by
+-- hours that include the unpaid admin around the paid ones: messaging
+-- clients, invoicing, restocking, the drive back from the job. This
+-- module computes that rate and compares two gigs on it.
+module SideHustle.Hourly
+  ( netCash
+  , hourlyRate
+  , compareGigs
+  ) where
+
+-- | Net cash a gig leaves after the platform cut and the costs of
+-- doing the job. A cut of 0.15 means the platform keeps 15 percent.
+netCash :: Double -> Double -> Double -> Double
+netCash gross platformCut costs = gross * (1 - platformCut) - costs
+
+-- | Net cash over total hours, paid and unpaid. Returns 0 when there
+-- are no hours at all.
+hourlyRate :: Double -> Double -> Double -> Double
+hourlyRate net paidHours unpaidHours
+  | paidHours + unpaidHours <= 0 = 0
+  | otherwise                    = net / (paidHours + unpaidHours)
+
+-- | The better of two hourly rates and the per-hour gap between them,
+-- returned as (better rate, how far ahead it is).
+compareGigs :: Double -> Double -> (Double, Double)
+compareGigs rateA rateB
+  | rateA >= rateB = (rateA, rateA - rateB)
+  | otherwise      = (rateB, rateB - rateA)
