Not all the topicss are present in the pdf give each and every topic in the chapter atomic structure,all the formulas like this

Reading File
Running Command

python3 << 'EOF' import pdfplumber pdf_path = "/tmp/workspace/attachments/4bd18996-a207-4cc6-9bd0-43009042dded/DOC-20260716-WA0037..pdf" with pdfplumber.open(pdf_path) as pdf: print(f"Total pages: {len(pdf.pages)}") # Extract all text from all pages for i, page in enumerate(pdf.pages): text = page.extract_text() print(f"\n===== PAGE {i+1} =====") if text: print(text) else: print("[No text extracted]") EOF

Here is the complete Atomic Structure chapter - every topic with every formula, symbol defined, and topics your PDF was missing added in full:

⚛️ ATOMIC STRUCTURE - Complete Formula & Topic Reference

Every Topic | Every Formula | Every Symbol Defined


GROUP 1 - BOHR'S MODEL FORMULAS

Formula 1: Orbital Radius

$$r_n = 0.529 \times \frac{n^2}{Z} \text{ Å}$$
SymbolMeaningUnit
rₙRadius of nth orbit (electron-nucleus distance)Angstroms (Å)
0.529 ÅBohr radius (a₀) - radius of 1st orbit of HFixed constant
nPrincipal quantum number = 1, 2, 3, 4...Dimensionless
ZAtomic number (number of protons)Dimensionless
When to use: Find orbital radius of any H-like ion (H, He⁺, Li²⁺, Be³⁺) in any orbit. Key rule: Radius ∝ n² (quadruples when n doubles). Radius ∝ 1/Z (He⁺ has half radius of H for same n).

Formula 2: Orbital Velocity

$$v_n = 2.18 \times 10^6 \times \frac{Z}{n} \text{ m/s}$$
SymbolMeaningUnit
vₙSpeed of electron in nth orbitm/s
2.18 × 10⁶ m/sVelocity in 1st orbit of HFixed constant
ZAtomic numberDimensionless
nPrincipal quantum numberDimensionless
Key rule: v ∝ Z/n. Outer electrons move slower (easier to remove).

Formula 3: Total Energy

$$E_n = -13.6 \times \frac{Z^2}{n^2} \text{ eV}$$
SymbolMeaningUnit
EₙTotal energy of electron in nth orbit - ALWAYS NEGATIVEeV or J
-13.6 eVEnergy in 1st orbit of H = ionisation energy of HFixed constant
- (negative sign)Indicates electron is bound to nucleus-
In SI units: Eₙ = -2.18 × 10⁻¹⁸ × Z²/n² J Key rule: n=1 is most negative = most stable. At n=∞, E=0 (electron free = ionised).

Formula 4: Kinetic Energy, Potential Energy, Total Energy Relations

$$KE = -E_n = +\frac{13.6 Z^2}{n^2} \text{ eV (always positive)}$$ $$PE = 2E_n = -\frac{27.2 Z^2}{n^2} \text{ eV (always negative)}$$ $$TE = KE + PE = E_n$$
Three critical relationships:
  • KE = -TE
  • PE = 2 × TE
  • PE = -2 × KE
Example (n=1, H): KE = +13.6 eV, PE = -27.2 eV, TE = -13.6 eV. Check: 13.6 - 27.2 = -13.6 ✓

Formula 5: Ionisation Energy

$$IE = +13.6 \times \frac{Z^2}{n^2} \text{ eV}$$
SymbolMeaningUnit
IEEnergy to remove electron from orbit n to infinityeV or kJ/mol
nOrbit from which electron is removedDimensionless
Derived as: IE = 0 - Eₙ = +13.6 Z²/n² Values: He⁺ (Z=2, n=1): 54.4 eV. Li²⁺ (Z=3, n=1): 122.4 eV.

Formula 6: Energy of Emitted/Absorbed Photon

$$\Delta E = 13.6 Z^2 \left(\frac{1}{n_1^2} - \frac{1}{n_2^2}\right) \text{ eV}$$
SymbolMeaning
n₁LOWER orbit - where electron LANDS after emission (n₁ < n₂)
n₂UPPER orbit - where electron STARTS (n₂ > n₁)
ΔEEnergy of photon emitted or absorbed (always positive)
Emission: electron falls from n₂ to n₁, releases photon. Absorption: electron goes from n₁ to n₂, absorbs photon.

Formula 7: Rydberg Equation (Spectral Lines)

$$\frac{1}{\lambda} = R_H \times Z^2 \times \left(\frac{1}{n_1^2} - \frac{1}{n_2^2}\right)$$
SymbolMeaningUnit
1/λWave number (ν̃)m⁻¹ or cm⁻¹
RₕRydberg constant = 1.097 × 10⁷ m⁻¹ = 109677 cm⁻¹m⁻¹
n₁Lower orbit: 1=Lyman, 2=Balmer, 3=Paschen, 4=Brackett, 5=Pfund, 6=Humphreys-
n₂Upper orbit: integer > n₁-
Series limit (shortest λ, highest frequency): set n₂ = ∞, so 1/λ = Rₕ × Z²/n₁² First line of each series (longest λ): n₂ = n₁ + 1

⚠️ SPECTRAL SERIES TABLE (MISSING FROM YOUR PDF)

Seriesn₁n₂RegionFirst line λ
Lyman12, 3, 4...UV121.6 nm
Balmer23, 4, 5...Visible656.3 nm (red, Hα)
Paschen34, 5, 6...Near IR1875 nm
Brackett45, 6, 7...IR4051 nm
Pfund56, 7, 8...Far IR7459 nm
Humphreys67, 8, 9...Far IR-

Formula 8: Time Period and Frequency of Revolution

$$T_n \propto \frac{n^3}{Z^2}, \quad \nu_n \propto \frac{Z^2}{n^3}$$
Exact form: Tₙ = 2π × rₙ/vₙ, νₙ = vₙ/(2π × rₙ) Trap: If n doubles, T becomes 2³ = 8 times longer. ν decreases as n³.

Formula 9: Number of Spectral Lines

$$\text{Lines from } n_2 \to n_1 = \frac{(n_2 - n_1)(n_2 - n_1 + 1)}{2}$$
Special case (electron falls to ground state, n₁=1): $$\text{Lines} = \frac{n(n-1)}{2}$$
Example: n=5 to n=1: 5 × 4/2 = 10 lines. n=5 to n=3: (2)(3)/2 = 3 lines.

GROUP 2 - ELECTROMAGNETIC RADIATION & PLANCK'S QUANTUM THEORY

⚠️ EM SPECTRUM TABLE (MISSING FROM YOUR PDF)

RegionWavelength RangeFrequency Range
Gamma rays< 0.01 nm> 3 × 10¹⁹ Hz
X-rays0.01 - 10 nm3×10¹⁷ - 3×10¹⁹ Hz
UV10 - 400 nm7.5×10¹⁴ - 3×10¹⁷ Hz
Visible400 - 700 nm4.3×10¹⁴ - 7.5×10¹⁴ Hz
IR700 nm - 1 mm3×10¹¹ - 4.3×10¹⁴ Hz
Microwaves1 mm - 10 cm3×10⁹ - 3×10¹¹ Hz
Radio waves> 10 cm< 3×10⁹ Hz
Visible light VIBGYOR: Violet (400 nm) → Red (700 nm)

Formula 10: Wave Equation

$$c = \nu \times \lambda$$
SymbolMeaningUnit
cSpeed of light in vacuum = 3 × 10⁸ m/sm/s
νFrequencyHz (= s⁻¹)
λWavelengthm (or nm, Å)
Conversions: 1 nm = 10⁻⁹ m. 1 Å = 10⁻¹⁰ m. Key rule: ν and λ are inversely proportional.

Formula 11: Energy of One Photon

$$E = h\nu = \frac{hc}{\lambda} = hc\tilde{\nu}$$
SymbolMeaningUnit
EEnergy of ONE photonJ or eV
hPlanck's constant = 6.626 × 10⁻³⁴J·s
νFrequencyHz
λWavelengthm
ν̃Wave number = 1/λm⁻¹ or cm⁻¹
For n photons: E_total = n·h·ν

Formula 12: Energy-Wavelength Shortcut (NEET favourite)

$$E(\text{eV}) = \frac{1240}{\lambda(\text{nm})}$$
SymbolMeaning
1240= hc in eV·nm units
λ(nm)Wavelength MUST be in nanometres
For Angstroms: E(eV) = 12400/λ(Å)

Formula 13: Wave Number

$$\tilde{\nu} = \frac{1}{\lambda}$$
Units: m⁻¹ or cm⁻¹. Used mainly in IR spectroscopy. Correct definition: ν̃ = 1/λ (NOT ν/c - common wrong answer).

GROUP 3 - PHOTOELECTRIC EFFECT FORMULAS

⚠️ BASICS (MISSING FROM YOUR PDF)

  • Photoelectric effect: When light of sufficient frequency hits a metal surface, electrons are ejected.
  • Discovered by Hertz, explained by Einstein (1905) - won him the Nobel Prize.
  • Effect is INSTANTANEOUS - no time delay.
  • Wave theory fails to explain: No emission below threshold frequency regardless of intensity.

Formula 14: Einstein's Photoelectric Equation

$$h\nu = \phi + KE_{max}$$
SymbolMeaningUnit
Energy of incident photonJ or eV
φ (phi)Work function = minimum energy to eject electron = hν₀J or eV
KE_maxMaximum KE of ejected photoelectron = ½mₑv²_maxJ or eV
Key rules:
  • KE_max = 0 when ν = ν₀ (threshold)
  • If hν < φ: NO emission at all (even at high intensity)
  • More intensity → more electrons ejected, SAME KE_max
  • Higher frequency → same number of electrons, HIGHER KE_max

Formula 15: Work Function

$$\phi = h\nu_0$$
SymbolMeaningUnit
φWork function - fixed for each metalJ or eV
ν₀Threshold frequency - minimum frequency for emissionHz
Work function values (approx):
Metalφ (eV)responds to
Cs2.0Visible
K2.2Visible
Na2.3Visible
Al4.3UV
Zn4.3UV
Pt5.6UV

Formula 16: Stopping Potential

$$eV_0 = KE_{max} = h\nu - \phi$$
SymbolMeaningUnit
eCharge of electron = 1.602 × 10⁻¹⁹ CC
V₀Stopping potential = minimum reverse voltage to stop ALL photoelectronsVolts
KE_maxMaximum KE of emitted electronsJ
Key insight: If KE_max = 2.5 eV, then V₀ = 2.5 V (numerically equal in eV). V₀ vs ν graph: Slope = h/e = 4.136 × 10⁻¹⁵ V·s (used to measure h experimentally). X-intercept = ν₀. Y-intercept = -φ/e.

Formula 17: Threshold Wavelength

$$\lambda_0 = \frac{hc}{\phi} = \frac{c}{\nu_0}$$
Shortcut: λ₀(nm) = 1240/φ(eV) Key rule: Light with λ < λ₀ (higher frequency) WILL cause emission. Light with λ > λ₀ WILL NOT.

Formula 18: Number of Photons (Intensity)

$$n_{photons} = \frac{I}{h\nu} = \frac{I\lambda}{hc}$$
SymbolMeaningUnit
n_photonsNumber of photons per unit area per seconds⁻¹·m⁻²
IIntensity = energy per unit area per secondW/m²
Why intensity affects photocurrent but NOT KE_max: Each photon has fixed energy (hν). More photons = more electrons, but each electron still gets only one photon's energy.

GROUP 4 - de BROGLIE & HEISENBERG FORMULAS

⚠️ de BROGLIE BACKGROUND (MISSING FROM YOUR PDF)

  • Louis de Broglie (1924) proposed matter has wave nature.
  • Wave-particle duality: all matter has both wave and particle properties.
  • Only observable for microscopic particles (electrons, protons, neutrons).
  • Experimental confirmation: Davisson-Germer experiment (1927) - electron diffraction by nickel crystal.

Formula 19: de Broglie Wavelength (General)

$$\lambda = \frac{h}{mv} = \frac{h}{p}$$
SymbolMeaningUnit
λde Broglie (matter) wavelengthm
hPlanck's constant = 6.626 × 10⁻³⁴J·s
mMass of particlekg
vVelocitym/s
pMomentum = mvkg·m/s
Cricket ball (0.1 kg, 30 m/s): λ ~ 2 × 10⁻³⁴ m (undetectable). Electron (9.1 × 10⁻³¹ kg, 10⁶ m/s): λ ~ 0.7 nm (detectable!).

Formula 20: de Broglie Wavelength from Kinetic Energy

$$\lambda = \frac{h}{\sqrt{2m \cdot KE}}$$
Important: KE MUST be in Joules. Convert: KE(J) = KE(eV) × 1.602 × 10⁻¹⁹

Formula 21: de Broglie Wavelength for Accelerated Electron

$$\lambda = \frac{12.27}{\sqrt{V}} \text{ Å}$$
SymbolMeaningUnit
12.27Constant for ELECTRONS onlyÅ·V^(1/2)
VAccelerating potentialVolts
For proton: λ = 0.286/√V Å Example: V=100 V → λ = 12.27/10 = 1.227 Å. V=10000 V → λ = 0.1227 Å.

Formula 22: Heisenberg's Uncertainty Principle

$$\Delta x \cdot \Delta p \geq \frac{h}{4\pi}$$
$$\Delta x \cdot m \cdot \Delta v \geq \frac{h}{4\pi}$$
SymbolMeaningUnit
ΔxUncertainty in positionm
ΔpUncertainty in momentumkg·m/s
ΔvUncertainty in velocitym/s
h/4πMinimum allowed productJ·s
Equivalently: Δx · Δp ≥ ℏ/2 where ℏ = h/2π = 1.055 × 10⁻³⁴ J·s Energy-time version: ΔE · Δt ≥ h/4π If Δx = 0 (exact position): Δp = ∞ (momentum completely unknown). Why it disproves Bohr: Definite orbits would mean exact position AND velocity - which HUP forbids.
Finding minimum Δv: $$\Delta v_{min} = \frac{h}{4\pi \cdot m \cdot \Delta x}$$

GROUP 5 - QUANTUM NUMBER FORMULAS

⚠️ QUANTUM NUMBER OVERVIEW (MISSING FROM YOUR PDF)

Quantum NumberSymbolValuesDescribes
Principaln1, 2, 3, 4...Shell, energy, size
Azimuthall0 to (n-1)Subshell, shape, angular momentum
Magneticmₗ-l to +lOrbital orientation in space
Spinmₛ+1/2 or -1/2 onlyElectron spin direction
Subshell notation:
l value0123
Namespdf
Number of orbitals in a subshell = 2l + 1

Formula 23: Orbital Angular Momentum

$$L = \sqrt{l(l+1)} \cdot \hbar$$
SymbolMeaningUnit
LMagnitude of orbital angular momentumJ·s
lAzimuthal quantum numberDimensionless
h/2π = 1.055 × 10⁻³⁴J·s
Values: s (l=0): L=0. p (l=1): L=√2·ℏ. d (l=2): L=√6·ℏ. f (l=3): L=2√3·ℏ TRAP: Bohr gave L = nℏ (WRONG). Quantum mechanics gives L = √[l(l+1)]·ℏ. Key fact: s orbitals have ZERO angular momentum.

Formula 24: z-Component of Angular Momentum

$$L_z = m_l \cdot \hbar$$
For p (l=1): mₗ = -1, 0, +1 → Lz = -ℏ, 0, +ℏ (3 orientations) For d (l=2): mₗ = -2,-1,0,+1,+2 → Lz = -2ℏ to +2ℏ (5 orientations) A magnetic field splits these orientations (Zeeman effect).

Formula 25: Spin Angular Momentum

$$S = \sqrt{s(s+1)} \cdot \hbar = \frac{\sqrt{3}}{2} \cdot \hbar$$
SymbolMeaning
SSpin angular momentum (SAME for ALL electrons)
sSpin quantum number = 1/2 (always, cannot change)
mₛ+1/2 (spin up ↑) or -1/2 (spin down ↓)
Pauli exclusion: Two electrons in same orbital MUST have opposite mₛ values.

Formula 26: Orbitals and Electrons per Shell

$$\text{Orbitals in } n^{th} \text{ shell} = n^2$$ $$\text{Max electrons in } n^{th} \text{ shell} = 2n^2$$
nOrbitals (n²)Max electrons (2n²)
112
248
3918
41632

GROUP 6 - NODE FORMULAS

Formula 27: Radial Nodes

$$\text{Radial nodes} = n - l - 1$$
Radial nodes = spherical surfaces INSIDE the orbital where electron density = 0.
OrbitalnlRadial nodes
1s100
2s201
2p210
3s302
3p311
3d320 (TRAP: students say 1)
4s403

Formula 28: Angular Nodes

$$\text{Angular nodes} = l$$
Angular nodes = flat planes (or cones) through the NUCLEUS where electron density = 0.
  • s (l=0): 0 angular nodes → spherical
  • p (l=1): 1 angular node → one nodal plane
  • d (l=2): 2 angular nodes → four-leaf clover
  • f (l=3): 3 angular nodes

Formula 29: Total Nodes

$$\text{Total nodes} = n - 1$$
Proof: Radial (n-l-1) + Angular (l) = n-l-1+l = n-1 ✓
TRAP: 2s and 2p BOTH have 1 total node, but:
  • 2s: 1 RADIAL node (spherical shell)
  • 2p: 1 ANGULAR node (flat plane)

GROUP 7 - ELECTRONIC CONFIGURATION

⚠️ THREE RULES (MISSING FROM YOUR PDF)

1. Aufbau Principle: Fill orbitals in order of increasing energy (n+l rule): 1s → 2s → 2p → 3s → 3p → 4s → 3d → 4p → 5s → 4d → 5p → 6s → 4f → 5d → 6p...
Order by (n+l), then by n:
Orbitaln+lFilled
1s11st
2s22nd
2p33rd
3s34th
3p45th
4s46th
3d57th
2. Hund's Rule of Maximum Multiplicity: In degenerate orbitals, electrons fill singly first (all same spin) before pairing.
3. Pauli's Exclusion Principle: No two electrons in an atom can have the same set of all four quantum numbers. Maximum 2 electrons per orbital with opposite spins.

⚠️ EXCEPTIONS IN ELECTRONIC CONFIGURATION (MISSING FROM YOUR PDF)

Cr (Z=24): Expected [Ar]3d⁴4s² → Actual [Ar]3d⁵4s¹ (half-filled d is extra stable) Cu (Z=29): Expected [Ar]3d⁹4s² → Actual [Ar]3d¹⁰4s¹ (fully-filled d is extra stable)
Reason: Half-filled (d⁵) and fully-filled (d¹⁰) subshells have extra stability due to:
  • Symmetrical distribution of electrons
  • Exchange energy is maximum

Formula 30: Spin Multiplicity

$$\text{Spin multiplicity} = 2S + 1$$
where S = (number of unpaired electrons)/2
ElementUnpaired e⁻SMultiplicityType
H11/22 (doublet)Paramagnetic
C213 (triplet)Paramagnetic
N33/24 (quartet)Paramagnetic
O213 (triplet)Paramagnetic
He/Ne001 (singlet)Diamagnetic
Multiplicity = 1 (singlet): all electrons paired = DIAMAGNETIC. Higher multiplicity = more unpaired electrons = stronger PARAMAGNETISM.

Formula 31: Max Electrons in a Subshell

$$\text{Max electrons in subshell} = 2(2l + 1)$$
lSubshellOrbitals (2l+1)Max electrons
0s12
1p36
2d510
3f714

GROUP 8 - NUCLEAR & ATOMIC STRUCTURE FORMULAS

⚠️ DISCOVERY EXPERIMENTS (MISSING FROM YOUR PDF)

Thomson's Cathode Ray Tube Experiment:
  • Discovered electron (1897)
  • Found e/m = 1.758 × 10¹¹ C/kg (constant for ALL cathode rays)
  • Proposed "Plum Pudding Model" (electrons embedded in positive sphere)
Rutherford's α-Particle Scattering Experiment:
  • Fired α-particles at gold foil
  • Most passed through → atom is mostly empty space
  • Few deflected at large angles → small, dense, positive nucleus
  • Proposed Nuclear Model (1911)
  • Limitations: Could not explain stability of atom (accelerating e⁻ should radiate and spiral in) or line spectra.
Millikan's Oil Drop Experiment:
  • Measured charge of electron = 1.602 × 10⁻¹⁹ C
  • From e and e/m → calculated mass of electron = 9.109 × 10⁻³¹ kg

Formula 32: Nuclear Radius

$$r = r_0 \times A^{1/3}$$
SymbolMeaningUnit
rRadius of nucleusm or fm
r₀Empirical constant = 1.2 × 10⁻¹⁵ m = 1.2 fmm
AMass number (protons + neutrons)Dimensionless
Key facts:
  • Nuclear radius depends on A (NOT Z)
  • Nuclear volume ∝ A
  • Nuclear density = CONSTANT for ALL nuclei (~10¹⁷ kg/m³)
  • 1 cm³ of nuclear matter weighs ~100 million tonnes!

Formula 33: Mass Number

$$A = Z + N$$
SymbolMeaning
AMass number = total nucleons
ZAtomic number = protons = electrons (neutral atom)
NNeutron number = A - Z

⚠️ ISOTOPES, ISOBARS, ISOTONES, ISOELECTRONIC (MISSING FROM YOUR PDF)

TermSameDifferentExample
IsotopesZA (different N)¹²C and ¹⁴C (both Z=6)
IsobarsAZ (different elements)¹⁴C and ¹⁴N
IsotonesNZ and A³H and ⁴He (both N=2)
IsoelectronicTotal electrons-Na⁺ and Mg²⁺ and Ne (all 10 e⁻)

Formula 34: Charge-to-Mass Ratio of Electron

$$\frac{e}{m} = 1.758 \times 10^{11} \text{ C/kg}$$
Key fact: This ratio is CONSTANT for cathode rays in ALL gases (same e⁻ everywhere). For proton: e/m = 9.58 × 10⁷ C/kg (proton is 1836× heavier). Canal rays (positive ions): e/m VARIES with gas used (different masses).

COMPLETE CONSTANTS REFERENCE TABLE

SymbolNameValueUnit
hPlanck's constant6.626 × 10⁻³⁴J·s
ℏ (h-bar)Reduced Planck constant1.055 × 10⁻³⁴J·s
cSpeed of light3 × 10⁸m/s
eElementary charge1.602 × 10⁻¹⁹C
mₑMass of electron9.109 × 10⁻³¹kg
mₚMass of proton1.673 × 10⁻²⁷kg
mₙMass of neutron1.675 × 10⁻²⁷kg
a₀Bohr radius (1st orbit of H)0.529 Å = 5.29 × 10⁻¹¹m
RₕRydberg constant1.097 × 10⁷m⁻¹
r₀Nuclear radius constant1.2 × 10⁻¹⁵m
1 eVIn Joules1.602 × 10⁻¹⁹J
1 ÅIn metres10⁻¹⁰m
1 nmIn metres10⁻⁹m
1 fmIn metres10⁻¹⁵m

QUICK FORMULA SUMMARY CARD

FormulaExpression
Orbital radiusrₙ = 0.529 × n²/Z Å
Orbital velocityvₙ = 2.18×10⁶ × Z/n m/s
Total energyEₙ = -13.6 × Z²/n² eV
KE= -Eₙ = +13.6 Z²/n² eV
PE= 2Eₙ = -27.2 Z²/n² eV
Ionisation energyIE = +13.6 × Z²/n² eV
Energy of photonΔE = 13.6 Z² (1/n₁² - 1/n₂²) eV
Rydberg equation1/λ = Rₕ × Z² × (1/n₁² - 1/n₂²)
Spectral lines(n₂-n₁)(n₂-n₁+1)/2
Wave equationc = νλ
Photon energyE = hν = hc/λ
ShortcutE(eV) = 1240/λ(nm)
Wave numberν̃ = 1/λ
Photoelectric eqhν = φ + KE_max
Work functionφ = hν₀
Stopping potentialeV₀ = KE_max
Threshold λλ₀(nm) = 1240/φ(eV)
Photon countn = I/hν
de Broglieλ = h/mv = h/p
de Broglie (KE)λ = h/√(2m·KE)
de Broglie (V)λ = 12.27/√V Å (electron)
HeisenbergΔx·Δp ≥ h/4π
Orbital ang. mom.L = √[l(l+1)]·ℏ
Lz componentLz = mₗ·ℏ
SpinS = (√3/2)·ℏ
Orbitals/shell
Max e⁻/shell2n²
Radial nodesn - l - 1
Angular nodesl
Total nodesn - 1
Spin multiplicity2S + 1
Max e⁻/subshell2(2l+1)
Nuclear radiusr = r₀ × A^(1/3)
Mass numberA = Z + N
e/m (electron)1.758 × 10¹¹ C/kg

Topics in your PDF: Groups 1-8 formulas with symbol definitions.
Topics ADDED that were missing from your PDF:
  1. Spectral series table (Lyman through Humphreys with wavelength regions)
  2. EM spectrum table with all wavelength/frequency ranges and VIBGYOR
  3. de Broglie background and Davisson-Germer experiment
  4. Heisenberg's minimum Δv formula
  5. Thomson's, Rutherford's, Millikan's discovery experiments
  6. Three rules of electronic configuration (Aufbau, Hund's, Pauli)
  7. Exceptions - Cr and Cu configurations with reasons
  8. Isotopes, isobars, isotones, isoelectronic table
  9. Quantum number overview table with all 4 quantum numbers

Like all complete info like the from starting to ending chapter, all the formulas also.

Here is the complete Atomic Structure chapter — from the very beginning (sub-atomic particle discovery) to the very end (electronic configuration rules and magnetic properties), with every single formula, every concept, and every definition:

⚛️ ATOMIC STRUCTURE

Complete Chapter — Class 11 Chemistry | NEET / JEE


PART 1 — DISCOVERY OF SUB-ATOMIC PARTICLES

1.1 Cathode Ray Experiment (J.J. Thomson, 1897)

Setup: Evacuated glass tube with two metal electrodes. High voltage applied → rays travel from cathode (negative) to anode (positive).
Observations of Cathode Rays:
  • Travel in straight lines
  • Cast sharp shadows
  • Deflect towards positive plate (carry negative charge)
  • Cause fluorescence on ZnS screen
  • Same e/m ratio regardless of cathode material or gas used → electrons are UNIVERSAL
Discovery: Electron

Formula — Charge-to-Mass Ratio of Electron

$$\frac{e}{m_e} = 1.758 \times 10^{11} \text{ C/kg}$$
SymbolValueUnit
e1.602 × 10⁻¹⁹C
mₑ9.109 × 10⁻³¹kg
Key: This ratio is CONSTANT for cathode rays in ALL gases. Canal rays (positive ions) have VARYING e/m (different gases give different ions with different masses).

1.2 Millikan's Oil Drop Experiment (1909)

Purpose: Measure the charge of an electron.
Result: $$e = 1.602 \times 10^{-19} \text{ C}$$
From the known e/m ratio: $$m_e = \frac{e}{e/m} = \frac{1.602 \times 10^{-19}}{1.758 \times 10^{11}} = 9.109 \times 10^{-31} \text{ kg}$$

1.3 Canal Ray Experiment (Goldstein, 1886)

Setup: Cathode with holes (perforations). Rays observed behind the cathode — travelling opposite to cathode rays.
Discovery: Proton (positively charged particle)
PropertyProton
Charge+1.602 × 10⁻¹⁹ C
Mass1.673 × 10⁻²⁷ kg
e/m ratio9.58 × 10⁷ C/kg (varies with gas)
Note: The lightest positive ion obtained from hydrogen gas is the proton (H⁺).

1.4 Discovery of Neutron (Chadwick, 1932)

Bombarded beryllium with α-particles → neutral radiation emitted → identified as neutrons
PropertyNeutron
Charge0 (neutral)
Mass1.675 × 10⁻²⁷ kg (slightly > proton)

1.5 Sub-Atomic Particles Summary

ParticleSymbolChargeMass (kg)Mass (amu)Discoverer
Electrone⁻-1.602×10⁻¹⁹ C9.109×10⁻³¹0.000549Thomson
Protonp⁺+1.602×10⁻¹⁹ C1.673×10⁻²⁷1.00728Goldstein/Rutherford
Neutronn01.675×10⁻²⁷1.00867Chadwick

PART 2 — ATOMIC MODELS

2.1 Thomson's Plum Pudding Model (1904)

  • Atom is a uniform positive sphere with electrons embedded like plums in a pudding
  • Radius ~ 10⁻¹⁰ m
  • FAILED because it could not explain Rutherford's scattering results

2.2 Rutherford's Nuclear Model (1911)

Experiment: α-particles (He²⁺) fired at thin gold foil
Observations:
  1. Most α-particles passed straight through → atom is mostly empty space
  2. Few deflected at small angles → small positive charge inside
  3. Very few (1 in 20,000) bounced back at 180° → small, dense, positive nucleus
Conclusions:
  • Atom has a tiny, dense, positively charged nucleus
  • Electrons revolve around nucleus in circular orbits (like planets around sun)
  • Nucleus contains protons (and neutrons - discovered later)
Nuclear radius formula: $$r = r_0 \times A^{1/3}$$
SymbolMeaningValue/Unit
rNuclear radiusm or fm
r₀Nuclear radius constant1.2 × 10⁻¹⁵ m = 1.2 fm
AMass numberdimensionless
Nuclear density: Constant for ALL nuclei ~ 10¹⁷ kg/m³ (does not depend on A or Z)
Limitations of Rutherford's Model:
  1. Stability problem: Accelerating electron should continuously radiate energy → spiral into nucleus → atom should collapse in ~10⁻⁸ s (but atoms are stable!)
  2. Line spectrum problem: A collapsing spiral would emit continuous spectrum, not discrete line spectra

2.3 Important Nuclear Terms

Formula — Mass Number

$$A = Z + N$$
SymbolMeaning
AMass number = total protons + neutrons (nucleons)
ZAtomic number = number of protons = electrons (neutral atom)
NNeutron number = A - Z

Isotopes, Isobars, Isotones, Isoelectronic

TermSameDifferentExample
IsotopesZ (same element)A, N¹H, ²H (deuterium), ³H (tritium)
IsobarsAZ, N (different elements)¹⁴₆C and ¹⁴₇N
IsotonesNZ, A³₁H and ⁴₂He (N=2 each)
IsoelectronicTotal electrons-Na⁺, Mg²⁺, Ne, F⁻ (all 10 e⁻)
IsodiaphersA-2Z (neutron excess)-²³⁸U and ²³⁴Th

PART 3 — ELECTROMAGNETIC RADIATION

3.1 Nature of Light — Wave Theory

Light is an electromagnetic wave — oscillating electric and magnetic fields perpendicular to each other and to the direction of propagation.
Properties of EM waves:
  • Travel at speed c = 3 × 10⁸ m/s in vacuum (all frequencies)
  • Do NOT require a medium to travel
  • Carry energy

Formula — Wave Equation

$$c = \nu \times \lambda$$
SymbolMeaningUnit
cSpeed of light = 3 × 10⁸m/s
ν (nu)FrequencyHz = s⁻¹
λ (lambda)Wavelengthm
ν and λ are inversely proportional at constant c.

Formula — Wave Number

$$\tilde{\nu} = \frac{1}{\lambda}$$
SymbolMeaningUnit
ν̃Wave number = number of waves per unit lengthm⁻¹ or cm⁻¹
Note: ν̃ = 1/λ (NOT ν/c). Used extensively in IR spectroscopy (unit: cm⁻¹).

3.2 Electromagnetic Spectrum

RegionWavelengthFrequencyExamples
Gamma rays< 0.01 nm> 3×10¹⁹ HzNuclear reactions
X-rays0.01 – 10 nm3×10¹⁷ – 3×10¹⁹ HzMedical imaging
Ultraviolet (UV)10 – 400 nm7.5×10¹⁴ – 3×10¹⁷ HzSterilisation, Lyman series
Visible400 – 700 nm4.3×10¹⁴ – 7.5×10¹⁴ HzBalmer series of H
Infrared (IR)700 nm – 1 mm3×10¹¹ – 4.3×10¹⁴ HzHeat, Paschen/Brackett
Microwaves1 mm – 10 cm3×10⁹ – 3×10¹¹ HzRadar, cooking
Radio waves> 10 cm< 3×10⁹ HzBroadcasting
Visible VIBGYOR (increasing wavelength / decreasing energy):
Colourλ (approx)ν (approx)
Violet400 nm7.5×10¹⁴ Hz
Indigo425 nm7.1×10¹⁴ Hz
Blue450 nm6.7×10¹⁴ Hz
Green520 nm5.8×10¹⁴ Hz
Yellow580 nm5.2×10¹⁴ Hz
Orange620 nm4.8×10¹⁴ Hz
Red700 nm4.3×10¹⁴ Hz

3.3 Planck's Quantum Theory (1900)

Postulates:
  1. Energy is NOT continuous — it is emitted or absorbed in discrete packets called quanta (singular: quantum) or photons.
  2. The energy of each quantum is directly proportional to the frequency.

Formula — Energy of One Photon

$$E = h\nu = \frac{hc}{\lambda} = hc\tilde{\nu}$$
SymbolMeaningValue/Unit
EEnergy of ONE photonJ or eV
hPlanck's constant6.626 × 10⁻³⁴ J·s
νFrequencyHz
λWavelengthm
ν̃Wave number = 1/λm⁻¹
For n photons: E_total = n·h·ν
Conversion: 1 eV = 1.602 × 10⁻¹⁹ J

Formula — Energy-Wavelength Shortcut (fastest for NEET)

$$E(\text{eV}) = \frac{1240}{\lambda(\text{nm})}$$
λ MUST be in nm. Answer is in eV.
For Angstroms: E(eV) = 12400/λ(Å)
Derived from: hc = 6.626×10⁻³⁴ × 3×10⁸ = 1.988×10⁻²⁵ J·m = 1240 eV·nm
Quick reference:
λE (eV)
400 nm (violet)3.1 eV
500 nm (green)2.48 eV
600 nm (orange)2.07 eV
700 nm (red)1.77 eV

Formula — Photon Momentum

$$p = \frac{h}{\lambda} = \frac{E}{c} = \frac{h\nu}{c}$$
Photon has zero rest mass but has energy E = hν and momentum p = h/λ.

PART 4 — ATOMIC SPECTRA

4.1 Line Spectrum of Hydrogen

When hydrogen gas is excited (high voltage), it emits light of only certain wavelengths → line spectrum (not continuous).
Each line corresponds to a specific electron transition between energy levels.

4.2 Spectral Series of Hydrogen

Seriesn₁ (lower level)n₂ (upper levels)RegionColour/Comment
Lyman12, 3, 4...UVAll lines UV
Balmer23, 4, 5...Visible (+UV)Hα=656.3nm (red), Hβ=486.1nm (blue-green)
Paschen34, 5, 6...Near IR
Brackett45, 6, 7...IR
Pfund56, 7, 8...Far IR
Humphreys67, 8, 9...Far IR

Formula — Rydberg Equation

$$\frac{1}{\lambda} = R_H \times Z^2 \times \left(\frac{1}{n_1^2} - \frac{1}{n_2^2}\right)$$
SymbolMeaningValue/Unit
1/λWave number (ν̃)m⁻¹ or cm⁻¹
RₕRydberg constant1.097 × 10⁷ m⁻¹ = 109677 cm⁻¹
ZAtomic number (1 for H, 2 for He⁺, etc.)-
n₁Lower orbit (n₁ determines the series)integer
n₂Upper orbit > n₁integer
First line of each series (longest λ, least energy): n₂ = n₁ + 1
Series limit (shortest λ, most energy): n₂ = ∞ $$\frac{1}{\lambda_{limit}} = R_H \times \frac{Z^2}{n_1^2}$$

Formula — Number of Spectral Lines

$$\text{Lines from } n_2 \to n_1 = \frac{(n_2 - n_1)(n_2 - n_1 + 1)}{2}$$
Special case (electron falls to ground state, n₁ = 1): $$\text{Lines} = \frac{n(n-1)}{2}$$
Examples:
  • n=5 → n=1: (5×4)/2 = 10 lines
  • n=4 → n=2: (2×3)/2 = 3 lines
  • n=5 → n=3: (2×3)/2 = 3 lines

PART 5 — BOHR'S ATOMIC MODEL (1913)

5.1 Postulates of Bohr's Model

  1. Electrons revolve around nucleus in fixed circular orbits called stationary states (no energy radiation while in orbit).
  2. Only those orbits are allowed where angular momentum is a whole number multiple of h/2π:
$$mvr = \frac{nh}{2\pi} = n\hbar$$
  1. Energy is absorbed or emitted only when electron jumps between orbits.
  2. Energy of emitted/absorbed photon = difference in energies of the two orbits: ΔE = hν

5.2 Bohr Model Formulas (for H and H-like ions: He⁺, Li²⁺, Be³⁺...)

Formula 1 — Orbital Radius

$$r_n = 0.529 \times \frac{n^2}{Z} \text{ Å}$$
nZ=1 (H)Z=2 (He⁺)
10.529 Å0.265 Å
22.116 Å1.058 Å
34.761 Å2.381 Å
Rules: rₙ ∝ n² (quadruples when n doubles). rₙ ∝ 1/Z. To convert to metres: multiply by 10⁻¹⁰ (1 Å = 10⁻¹⁰ m)

Formula 2 — Orbital Velocity

$$v_n = 2.18 \times 10^6 \times \frac{Z}{n} \text{ m/s}$$
nZ=1 (H)
12.18 × 10⁶ m/s = c/137
21.09 × 10⁶ m/s
30.727 × 10⁶ m/s
Rules: vₙ ∝ Z/n. Outer electrons move slower (easier to remove).

Formula 3 — Total Energy

$$E_n = -13.6 \times \frac{Z^2}{n^2} \text{ eV}$$
$$E_n = -2.18 \times 10^{-18} \times \frac{Z^2}{n^2} \text{ J}$$
nEnergy (H, Z=1)
1-13.6 eV (ground state)
2-3.4 eV
3-1.51 eV
4-0.85 eV
5-0.544 eV
0 (ionised)
Rules:
  • Always NEGATIVE (bound electron)
  • n=1 = most negative = most stable
  • Energy increases (becomes less negative) as n increases
  • At n=∞, E=0 (electron free)

Formula 4 — Kinetic Energy, Potential Energy

$$KE = -E_n = +\frac{13.6 Z^2}{n^2} \text{ eV (always positive)}$$
$$PE = 2E_n = -\frac{27.2 Z^2}{n^2} \text{ eV (always negative)}$$
$$TE = KE + PE = E_n$$
Three golden relationships (must memorise):
  • KE = -TE
  • PE = 2 × TE
  • PE = -2 × KE
Example (H, n=1):
  • KE = +13.6 eV, PE = -27.2 eV, TE = -13.6 eV
  • Check: 13.6 + (-27.2) = -13.6 ✓

Formula 5 — Ionisation Energy

$$IE = 0 - E_n = +\frac{13.6 Z^2}{n^2} \text{ eV}$$
IonnIE
H113.6 eV = 1312 kJ/mol
He⁺154.4 eV
Li²⁺1122.4 eV
H23.4 eV
IE is ALWAYS positive. Decreases as n increases (easier to remove from outer orbits).

Formula 6 — Energy of Emitted/Absorbed Photon

$$\Delta E = 13.6 Z^2 \left(\frac{1}{n_1^2} - \frac{1}{n_2^2}\right) \text{ eV}$$
where n₁ < n₂ always. ΔE is always positive.
  • Emission: electron falls from n₂ → n₁, photon released
  • Absorption: electron jumps from n₁ → n₂, photon absorbed

Formula 7 — Angular Momentum Quantisation (Bohr's condition)

$$mvr_n = \frac{nh}{2\pi} = n\hbar$$
SymbolMeaningUnit
mmass of electronkg
vorbital velocitym/s
rₙorbital radiusm
nprincipal quantum numberdimensionless
hPlanck's constantJ·s

Formula 8 — Time Period of Revolution

$$T_n = \frac{2\pi r_n}{v_n} \propto \frac{n^3}{Z^2}$$

Formula 9 — Frequency of Revolution

$$\nu_n = \frac{v_n}{2\pi r_n} \propto \frac{Z^2}{n^3}$$
NEET trap: If n doubles, T increases by 2³ = 8 times. ν decreases 8 times.

5.3 Limitations of Bohr's Model

  1. Only works for hydrogen and H-like ions (one electron). Fails for multi-electron atoms.
  2. Cannot explain fine structure of spectral lines (Zeeman and Stark effects).
  3. Cannot explain intensity of spectral lines.
  4. Violates Heisenberg's Uncertainty Principle (assumes definite orbit = definite position AND momentum).
  5. Does not explain chemical bonding or 3D shape of molecules.

PART 6 — PHOTOELECTRIC EFFECT

6.1 Observations

When light of sufficient frequency falls on a metal surface, electrons are emitted. These are photoelectrons.
Observations that wave theory CANNOT explain:
  1. Emission is instantaneous (wave theory predicts time delay for energy buildup)
  2. Below threshold frequency (ν₀), NO emission regardless of intensity
  3. Above ν₀, KE_max depends on frequency, NOT intensity
  4. More intensity → more electrons emitted (not faster electrons)
Einstein explained it (1905) using Planck's quantum theory → Nobel Prize 1921

Formula 10 — Einstein's Photoelectric Equation

$$h\nu = \phi + KE_{max}$$
SymbolMeaningUnit
Energy of incident photonJ or eV
φ (phi)Work function = minimum energy to eject electronJ or eV
KE_maxMaximum KE of ejected electron = ½mₑv²_maxJ or eV
If hν < φ: NO emission (even at high intensity) If hν = φ: Emission just starts, KE_max = 0

Formula 11 — Work Function

$$\phi = h\nu_0$$
SymbolMeaningUnit
φWork function — fixed for each metalJ or eV
ν₀Threshold frequency — minimum frequency for emissionHz
Work functions:
Metalφ (eV)Responds to
Cs2.0Visible (620 nm)
K2.2Visible (564 nm)
Na2.3Visible (539 nm)
Al4.3UV (288 nm)
Zn4.3UV
Pt5.6UV (221 nm)

Formula 12 — Threshold Wavelength

$$\lambda_0 = \frac{hc}{\phi} = \frac{c}{\nu_0}$$
$$\lambda_0(\text{nm}) = \frac{1240}{\phi(\text{eV})}$$
Light with λ < λ₀ → emission occurs. Light with λ > λ₀ → no emission.

Formula 13 — Stopping Potential

$$eV_0 = KE_{max} = h\nu - \phi$$
SymbolMeaningUnit
V₀Stopping potential = minimum reverse voltage to stop all photoelectronsVolts
eCharge of electron = 1.602 × 10⁻¹⁹C
Key: KE_max in eV numerically equals V₀ in Volts.
  • If KE_max = 2.5 eV → V₀ = 2.5 V
V₀ vs ν graph:
  • Slope = h/e = 4.136 × 10⁻¹⁵ V·s (used to measure h)
  • X-intercept = ν₀ (threshold frequency)
  • Y-intercept = -φ/e
Increasing intensity: photocurrent ↑, V₀ unchanged Increasing frequency: V₀ ↑, photocurrent unchanged

Formula 14 — Number of Photons per Unit Area per Second

$$n_{photons} = \frac{I}{h\nu} = \frac{I\lambda}{hc}$$
SymbolMeaningUnit
IIntensity = energy/area/timeW/m²
n_photonsNumber of photons/area/seconds⁻¹·m⁻²
Why intensity affects photocurrent but NOT KE: Each photon gives its energy to ONE electron. More photons → more electrons → more current. But each electron's energy depends only on hν.

PART 7 — DUAL NATURE OF MATTER (de Broglie)

7.1 Background

Louis de Broglie (1924): If light (wave) can behave as particles (photons), then matter (particles) should also show wave properties.
Experimental proof: Davisson-Germer experiment (1927) — electron diffraction by nickel crystal confirmed electron's wave nature.

Formula 15 — de Broglie Wavelength (General)

$$\lambda = \frac{h}{mv} = \frac{h}{p}$$
SymbolMeaningUnit
λde Broglie wavelengthm
hPlanck's constant = 6.626 × 10⁻³⁴J·s
mMass of particlekg
vVelocitym/s
p = mvMomentumkg·m/s
Cricket ball (0.1 kg, 30 m/s): λ ~ 2 × 10⁻³⁴ m — unmeasurably tiny. Electron (9.1×10⁻³¹ kg, 10⁶ m/s): λ ~ 0.7 nm — detectable by X-ray diffraction!

Formula 16 — de Broglie Wavelength from Kinetic Energy

$$\lambda = \frac{h}{\sqrt{2m \cdot KE}}$$
Derived from: λ = h/mv and KE = ½mv² → mv = √(2m·KE)
KE MUST be in Joules. Convert: KE(J) = KE(eV) × 1.602 × 10⁻¹⁹

Formula 17 — de Broglie Wavelength for Accelerated Electron

$$\lambda = \frac{12.27}{\sqrt{V}} \text{ Å}$$
SymbolMeaningUnit
12.27Constant for ELECTRONS only = h/√(2mₑe)Å·V^½
VAccelerating potential differenceVolts
For proton: λ = 0.286/√V Å
V (Volts)λ (electron)
1001.227 Å
10000.388 Å
100000.123 Å

Formula 18 — Relationship: de Broglie and Bohr

Bohr's quantisation (mvr = nh/2π) is equivalent to saying the circumference of the orbit must be a whole number multiple of the de Broglie wavelength: $$2\pi r_n = n\lambda$$
This shows Bohr's model has a wave-mechanical basis.

PART 8 — HEISENBERG'S UNCERTAINTY PRINCIPLE (1927)

Statement: It is impossible to simultaneously determine the exact position AND exact momentum (or velocity) of a microscopic particle.

Formula 19 — Heisenberg's Uncertainty Principle

$$\Delta x \cdot \Delta p \geq \frac{h}{4\pi}$$
$$\Delta x \cdot m\Delta v \geq \frac{h}{4\pi}$$
SymbolMeaningUnit
ΔxUncertainty in positionm
ΔpUncertainty in momentumkg·m/s
ΔvUncertainty in velocitym/s
h/4πMinimum allowed productJ·s
Equivalent form using ℏ: $$\Delta x \cdot \Delta p \geq \frac{\hbar}{2}$$ where ℏ = h/2π = 1.055 × 10⁻³⁴ J·s
Energy-time uncertainty: $$\Delta E \cdot \Delta t \geq \frac{h}{4\pi}$$

Formula 20 — Minimum Uncertainty in Velocity

$$\Delta v_{min} = \frac{h}{4\pi \cdot m \cdot \Delta x}$$
Key implications:
  • If Δx = 0 (exact position) → Δp = ∞ (momentum completely unknown)
  • If Δp = 0 (exact momentum) → Δx = ∞ (position completely unknown)
  • Applies to ALL microscopic particles, NOT due to measurement error
  • Invalidates Bohr's model (definite circular orbits imply both exact position and velocity)

PART 9 — QUANTUM MECHANICAL MODEL

9.1 Orbital vs Orbit

Orbit (Bohr)Orbital (Quantum Mechanics)
Definite circular pathRegion of space where probability of finding electron is high (≥90%)
Violates HUPConsistent with HUP
2D circle3D region
Exact position knownOnly probability known

9.2 Quantum Numbers

Every electron in an atom is completely described by a unique set of 4 quantum numbers.

(a) Principal Quantum Number (n)

$$n = 1, 2, 3, 4, 5, 6, 7...$$
nShell nameMax electrons (2n²)
1K2
2L8
3M18
4N32
  • Determines energy and size of the orbital
  • Higher n = larger orbital = higher energy = farther from nucleus

(b) Azimuthal Quantum Number (l) — Subsidiary / Angular Momentum QN

$$l = 0, 1, 2, ..., (n-1)$$
lSubshellShapeMax electrons [2(2l+1)]
0sSphere2
1pDumbbell6
2dDouble dumbbell / clover10
3fComplex14
  • Determines shape of the orbital and subshell
  • For n=1: l=0 only (1s)
  • For n=2: l=0,1 (2s, 2p)
  • For n=3: l=0,1,2 (3s, 3p, 3d)
  • For n=4: l=0,1,2,3 (4s, 4p, 4d, 4f)

(c) Magnetic Quantum Number (mₗ)

$$m_l = -l, -(l-1), ..., 0, ..., (l-1), +l$$
  • Total values = 2l + 1 (= number of orbitals in subshell)
  • Determines orientation of orbital in space
  • Each mₗ value = one distinct orbital
Subshelllmₗ valuesNumber of orbitals
s001
p1-1, 0, +13 (px, py, pz)
d2-2,-1,0,+1,+25
f3-3,-2,-1,0,+1,+2,+37

(d) Spin Quantum Number (mₛ)

$$m_s = +\frac{1}{2} \text{ (spin up, ↑)} \quad \text{or} \quad -\frac{1}{2} \text{ (spin down, ↓)}$$
Only 2 possible values. Every electron is either spin up or spin down.

9.3 Angular Momentum Formulas

Formula 21 — Orbital Angular Momentum

$$L = \sqrt{l(l+1)} \cdot \hbar$$
lSubshellL
0s0
1p√2·ℏ
2d√6·ℏ
3f2√3·ℏ
TRAP: Bohr gave L = nℏ (WRONG). Correct quantum mechanics: L = √[l(l+1)]·ℏ s orbitals have ZERO orbital angular momentum (not ℏ).

Formula 22 — z-Component of Orbital Angular Momentum

$$L_z = m_l \cdot \hbar$$
Values range from -lℏ to +lℏ. Different mₗ = different spatial orientations of the orbital.

Formula 23 — Spin Angular Momentum

$$S = \sqrt{s(s+1)} \cdot \hbar = \sqrt{\frac{1}{2} \cdot \frac{3}{2}} \cdot \hbar = \frac{\sqrt{3}}{2} \cdot \hbar$$
This value is the same for ALL electrons — cannot change. Only the direction (mₛ = ±½) can differ.

9.4 Shell/Subshell Count Formulas

Formula 24 — Number of Orbitals in nth Shell

$$\text{Orbitals in } n^{th} \text{ shell} = n^2$$

Formula 25 — Maximum Electrons in nth Shell

$$\text{Max electrons in } n^{th} \text{ shell} = 2n^2$$

Formula 26 — Number of Orbitals in a Subshell

$$\text{Orbitals in subshell} = 2l + 1$$

Formula 27 — Maximum Electrons in a Subshell

$$\text{Max electrons in subshell} = 2(2l+1)$$

PART 10 — NODES

Formula 28 — Radial Nodes

$$\text{Radial nodes} = n - l - 1$$
Radial nodes = spherical surfaces INSIDE the orbital where electron density = 0.

Formula 29 — Angular Nodes

$$\text{Angular nodes} = l$$
Angular nodes = flat planes (or cones) through the NUCLEUS where electron probability = 0. These determine the shape of the orbital.

Formula 30 — Total Nodes

$$\text{Total nodes} = n - 1$$
Proof: (n-l-1) + l = n-1 ✓
Complete node table:
OrbitalnlRadialAngularTotal
1s10000
2s20101
2p21011
3s30202
3p31112
3d32022
4s40303
4p41213
4d42123
4f43033
KEY TRAP: 2s and 2p both have 1 total node but DIFFERENT types. 3d has ZERO radial nodes (not 1!).

PART 11 — SHAPES OF ORBITALS

s Orbitals

  • Shape: Spherical (same probability in all directions)
  • Nodes: 0 angular nodes (always spherical)
  • 1s: 1 sphere. 2s: 2 concentric spheres (1 radial node). 3s: 3 concentric spheres.

p Orbitals

  • Shape: Dumbbell (two lobes on opposite sides of nucleus)
  • 3 orientations: px (along x-axis), py (along y-axis), pz (along z-axis)
  • 1 angular node: the plane perpendicular to the axis
    • pz: xy-plane is the node
    • px: yz-plane is the node
    • py: xz-plane is the node
  • Start from n=2 (l=1, so min n = l+1 = 2)

d Orbitals

  • Shape: Double dumbbell / clover leaf (4 lobes) for dxy, dxz, dyz, dx²-y²
  • d(z²): doughnut + dumbbell along z-axis
  • 5 orientations: dxy, dxz, dyz, dx²-y², dz²
  • 2 angular nodes
  • Start from n=3

f Orbitals

  • Shape: Complex multi-lobed
  • 7 orientations
  • 3 angular nodes
  • Start from n=4

PART 12 — ELECTRONIC CONFIGURATION

12.1 Three Fundamental Rules

Rule 1 — Aufbau Principle

Electrons fill orbitals in increasing order of energy (lower energy first).
Filling order (n+l rule): If two orbitals have the same (n+l), the one with lower n is filled first.
$$1s \to 2s \to 2p \to 3s \to 3p \to 4s \to 3d \to 4p \to 5s \to 4d \to 5p \to 6s \to 4f \to 5d \to 6p$$
Orbitalnln+lOrder filled
1s1011st
2s2022nd
2p2133rd
3s3034th
3p3145th
4s4046th
3d3257th
4p4158th
5s5059th
4d42610th

Rule 2 — Pauli's Exclusion Principle

No two electrons in the same atom can have the same set of all four quantum numbers.
Consequence: Maximum 2 electrons per orbital, and they must have opposite spins (one ↑, one ↓).

Rule 3 — Hund's Rule of Maximum Multiplicity

In degenerate orbitals (same energy), electrons fill singly first (one per orbital) with parallel spins (same mₛ) before any pairing occurs.
Example — Nitrogen (Z=7): 1s² 2s² 2p³
2p: ↑  ↑  ↑   (three singly occupied — correct)
NOT: ↑↓  ↑  _  (pairing before all singly occupied — WRONG)

12.2 Electronic Configuration of Elements (Z=1 to 30)

ZElementConfigurationUnpaired e⁻
1H1s¹1
2He1s²0
3Li[He] 2s¹1
4Be[He] 2s²0
5B[He] 2s² 2p¹1
6C[He] 2s² 2p²2
7N[He] 2s² 2p³3
8O[He] 2s² 2p⁴2
9F[He] 2s² 2p⁵1
10Ne[He] 2s² 2p⁶0
11Na[Ne] 3s¹1
12Mg[Ne] 3s²0
13Al[Ne] 3s² 3p¹1
14Si[Ne] 3s² 3p²2
15P[Ne] 3s² 3p³3
16S[Ne] 3s² 3p⁴2
17Cl[Ne] 3s² 3p⁵1
18Ar[Ne] 3s² 3p⁶0
19K[Ar] 4s¹1
20Ca[Ar] 4s²0
21Sc[Ar] 3d¹ 4s²1
22Ti[Ar] 3d² 4s²2
23V[Ar] 3d³ 4s²3
24Cr[Ar] 3d⁵ 4s¹6 (EXCEPTION)
25Mn[Ar] 3d⁵ 4s²5
26Fe[Ar] 3d⁶ 4s²4
27Co[Ar] 3d⁷ 4s²3
28Ni[Ar] 3d⁸ 4s²2
29Cu[Ar] 3d¹⁰ 4s¹1 (EXCEPTION)
30Zn[Ar] 3d¹⁰ 4s²0

12.3 Exceptions — Cr and Cu

Chromium (Z=24):
  • Expected: [Ar] 3d⁴ 4s²
  • Actual: [Ar] 3d⁵ 4s¹
Copper (Z=29):
  • Expected: [Ar] 3d⁹ 4s²
  • Actual: [Ar] 3d¹⁰ 4s¹
Reason: Half-filled (d⁵) and completely filled (d¹⁰) subshells have extra stability due to:
  1. Symmetrical distribution of electrons
  2. Maximum exchange energy (more exchanges → lower energy)
Exchange energy formula (for information): Number of exchanges = n(n-1)/2 where n = number of electrons with parallel spin in same subshell.

12.4 Magnetic Properties

Formula 31 — Spin Multiplicity

$$\text{Spin multiplicity} = 2S + 1$$
where S = (number of unpaired electrons)/2
Unpaired e⁻SMultiplicityType
001 (singlet)Diamagnetic
11/22 (doublet)Paramagnetic
213 (triplet)Paramagnetic
33/24 (quartet)Paramagnetic
425 (quintet)Paramagnetic
Diamagnetic: All electrons paired → repelled by magnetic field Paramagnetic: Unpaired electrons → attracted by magnetic field. More unpaired = stronger paramagnetism.

Formula 32 — Magnetic Moment

$$\mu = \sqrt{n(n+2)} \text{ BM (Bohr Magneton)}$$
SymbolMeaningUnit
μMagnetic momentBM (Bohr Magneton)
nNumber of unpaired electrons-
1 BMeh/4πmₑc9.274 × 10⁻²⁴ J/T
Unpaired e⁻μ (BM)Example
1√3 ≈ 1.73Cu²⁺, Ti³⁺
2√8 ≈ 2.83Ni²⁺, Fe²⁺ (some)
3√15 ≈ 3.87Co²⁺, Cr³⁺
4√24 ≈ 4.90Fe²⁺, Cr²⁺
5√35 ≈ 5.92Mn²⁺, Fe³⁺

PART 13 — COMPLETE CONSTANTS & UNIT REFERENCE

SymbolNameValueUnit
hPlanck's constant6.626 × 10⁻³⁴J·s
Reduced Planck (h/2π)1.055 × 10⁻³⁴J·s
cSpeed of light3 × 10⁸m/s
eElementary charge1.602 × 10⁻¹⁹C
mₑMass of electron9.109 × 10⁻³¹kg
mₚMass of proton1.673 × 10⁻²⁷kg
mₙMass of neutron1.675 × 10⁻²⁷kg
a₀Bohr radius0.529 Å = 5.29 × 10⁻¹¹m
RₕRydberg constant1.097 × 10⁷ m⁻¹m⁻¹ or 109677 cm⁻¹
r₀Nuclear radius constant1.2 × 10⁻¹⁵m (fm)
1 eVIn Joules1.602 × 10⁻¹⁹J
1 amuIn kg1.66 × 10⁻²⁷kg
1 ÅIn metres10⁻¹⁰m
1 nmIn metres10⁻⁹m
1 pmIn metres10⁻¹²m
1 fmIn metres10⁻¹⁵m

MASTER FORMULA LIST — ALL 32 FORMULAS AT A GLANCE

#FormulaExpression
1Orbital radiusrₙ = 0.529 × n²/Z Å
2Orbital velocityvₙ = 2.18×10⁶ × Z/n m/s
3Total energyEₙ = -13.6 × Z²/n² eV
4Kinetic energyKE = -Eₙ = +13.6 Z²/n² eV
5Potential energyPE = 2Eₙ = -27.2 Z²/n² eV
6Ionisation energyIE = +13.6 × Z²/n² eV
7Photon energy (transition)ΔE = 13.6 Z² (1/n₁² - 1/n₂²) eV
8Rydberg equation1/λ = Rₕ Z² (1/n₁² - 1/n₂²)
9Angular momentum (Bohr)mvr = nh/2π
10Time periodTₙ ∝ n³/Z²
11Revolution frequencyνₙ ∝ Z²/n³
12Spectral lines(n₂-n₁)(n₂-n₁+1)/2
13Wave equationc = νλ
14Wave numberν̃ = 1/λ
15Photon energyE = hν = hc/λ
16Energy shortcutE(eV) = 1240/λ(nm)
17Photon momentump = h/λ = E/c
18Photoelectric equationhν = φ + KE_max
19Work functionφ = hν₀
20Threshold wavelengthλ₀(nm) = 1240/φ(eV)
21Stopping potentialeV₀ = KE_max
22Photon countn = I/hν
23de Broglie (general)λ = h/mv = h/p
24de Broglie (from KE)λ = h/√(2m·KE)
25de Broglie (electron, voltage)λ = 12.27/√V Å
26Heisenberg's UPΔx·Δp ≥ h/4π
27Orbital angular momentumL = √[l(l+1)]·ℏ
28L z-componentLz = mₗ·ℏ
29Spin angular momentumS = (√3/2)·ℏ
30Radial nodesn - l - 1
31Angular nodesl
32Total nodesn - 1
33Orbitals per shell
34Max e⁻ per shell2n²
35Orbitals per subshell2l + 1
36Max e⁻ per subshell2(2l+1)
37Spin multiplicity2S + 1
38Magnetic momentμ = √[n(n+2)] BM
39Nuclear radiusr = r₀ × A^(1/3)
40Mass numberA = Z + N
41e/m ratio of electron1.758 × 10¹¹ C/kg

This is the complete Atomic Structure chapter — from the first experiment (cathode ray tube) to the last topic (magnetic moment and electronic configuration). Every formula includes every symbol defined with units, when to use it, and key points. All 41 formulas are covered.

Give me in pdf format without placeholders

Reading File
Running Command

ls -la /tmp/workspace/ 2>/dev/null || mkdir -p /tmp/workspace && ls -la /tmp/workspace/

Running Command

mkdir -p /tmp/workspace/atomic-structure && python3 -c "from reportlab.lib import colors; print('reportlab ok')" && python3 -c "from reportlab.platypus import SimpleDocTemplate; print('platypus ok')"

Writing File

~/atomic-structure/generate_pdf.py

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether, PageBreak
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import Flowable

OUTPUT = "/tmp/workspace/atomic-structure/Atomic_Structure_Complete.pdf"

doc = SimpleDocTemplate(
    OUTPUT,
    pagesize=A4,
    rightMargin=1.8*cm,
    leftMargin=1.8*cm,
    topMargin=2*cm,
    bottomMargin=2*cm,
)

W = A4[0] - 3.6*cm

# ── colour palette ──────────────────────────────────────────────
DARK_BLUE   = colors.HexColor("#1a237e")
MED_BLUE    = colors.HexColor("#1565c0")
LIGHT_BLUE  = colors.HexColor("#e3f2fd")
ACCENT      = colors.HexColor("#ff6f00")
FORMULA_BG  = colors.HexColor("#fff8e1")
FORMULA_BOR = colors.HexColor("#f57f17")
TABLE_HEAD  = colors.HexColor("#283593")
TABLE_ALT   = colors.HexColor("#e8eaf6")
TRAP_BG     = colors.HexColor("#fce4ec")
TRAP_BOR    = colors.HexColor("#c62828")
NOTE_BG     = colors.HexColor("#e8f5e9")
NOTE_BOR    = colors.HexColor("#2e7d32")
WHITE       = colors.white
BLACK       = colors.black
GREY        = colors.HexColor("#546e7a")

# ── styles ───────────────────────────────────────────────────────
styles = getSampleStyleSheet()

def S(name, **kw):
    return ParagraphStyle(name, **kw)

sTitle = S("sTitle",
    fontSize=22, fontName="Helvetica-Bold",
    textColor=WHITE, alignment=TA_CENTER,
    spaceAfter=4, spaceBefore=4)

sSubTitle = S("sSubTitle",
    fontSize=11, fontName="Helvetica",
    textColor=WHITE, alignment=TA_CENTER,
    spaceAfter=2)

sPart = S("sPart",
    fontSize=14, fontName="Helvetica-Bold",
    textColor=WHITE, alignment=TA_LEFT,
    spaceAfter=4, spaceBefore=8,
    leftIndent=6)

sSec = S("sSec",
    fontSize=12, fontName="Helvetica-Bold",
    textColor=MED_BLUE, alignment=TA_LEFT,
    spaceAfter=3, spaceBefore=6)

sSub = S("sSub",
    fontSize=10.5, fontName="Helvetica-Bold",
    textColor=DARK_BLUE, alignment=TA_LEFT,
    spaceAfter=2, spaceBefore=4)

sBody = S("sBody",
    fontSize=9.5, fontName="Helvetica",
    textColor=BLACK, alignment=TA_JUSTIFY,
    spaceAfter=3, leading=14)

sBullet = S("sBullet",
    fontSize=9.5, fontName="Helvetica",
    textColor=BLACK, alignment=TA_LEFT,
    spaceAfter=2, leading=13,
    leftIndent=14, bulletIndent=4)

sFormula = S("sFormula",
    fontSize=10.5, fontName="Helvetica-Bold",
    textColor=colors.HexColor("#e65100"),
    alignment=TA_CENTER,
    spaceAfter=3, spaceBefore=3, leading=16)

sFormulaNote = S("sFormulaNote",
    fontSize=9, fontName="Helvetica",
    textColor=colors.HexColor("#4e342e"),
    alignment=TA_LEFT,
    spaceAfter=2, leading=13)

sTrap = S("sTrap",
    fontSize=9.5, fontName="Helvetica-Bold",
    textColor=colors.HexColor("#b71c1c"),
    alignment=TA_LEFT, spaceAfter=2, leading=13)

sNote = S("sNote",
    fontSize=9.5, fontName="Helvetica",
    textColor=colors.HexColor("#1b5e20"),
    alignment=TA_LEFT, spaceAfter=2, leading=13)

sTableH = S("sTableH",
    fontSize=9, fontName="Helvetica-Bold",
    textColor=WHITE, alignment=TA_CENTER, leading=12)

sTableC = S("sTableC",
    fontSize=8.5, fontName="Helvetica",
    textColor=BLACK, alignment=TA_CENTER, leading=11)

sTableL = S("sTableL",
    fontSize=8.5, fontName="Helvetica",
    textColor=BLACK, alignment=TA_LEFT, leading=11)

# ── helpers ──────────────────────────────────────────────────────

def part_header(text):
    """Dark blue full-width banner for a PART"""
    data = [[Paragraph(text, sPart)]]
    t = Table(data, colWidths=[W])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), DARK_BLUE),
        ("ROWPADDING", (0,0), (-1,-1), 6),
        ("BOX", (0,0), (-1,-1), 0, WHITE),
    ]))
    return t

def section_header(text):
    return Paragraph(text, sSec)

def sub_header(text):
    return Paragraph(text, sSub)

def body(text):
    return Paragraph(text, sBody)

def bullet(text):
    return Paragraph(f"• {text}", sBullet)

def sp(h=4):
    return Spacer(1, h)

def hr():
    return HRFlowable(width="100%", thickness=0.5, color=colors.HexColor("#90a4ae"))

def formula_box(formula_text, notes=None):
    """Yellow bordered formula box"""
    content = [Paragraph(formula_text, sFormula)]
    if notes:
        for n in notes:
            content.append(Paragraph(n, sFormulaNote))
    inner = Table([[c] for c in content], colWidths=[W-20])
    inner.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), FORMULA_BG),
        ("BOX", (0,0), (-1,-1), 1.5, FORMULA_BOR),
        ("ROWPADDING", (0,0), (-1,-1), 5),
    ]))
    return inner

def trap_box(text):
    t = Table([[Paragraph("⚠ TRAP: " + text, sTrap)]], colWidths=[W])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), TRAP_BG),
        ("BOX", (0,0), (-1,-1), 1.2, TRAP_BOR),
        ("ROWPADDING", (0,0), (-1,-1), 5),
    ]))
    return t

def note_box(text):
    t = Table([[Paragraph("★ NOTE: " + text, sNote)]], colWidths=[W])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), NOTE_BG),
        ("BOX", (0,0), (-1,-1), 1.2, NOTE_BOR),
        ("ROWPADDING", (0,0), (-1,-1), 5),
    ]))
    return t

def make_table(headers, rows, col_widths=None, left_cols=None):
    if col_widths is None:
        col_widths = [W / len(headers)] * len(headers)
    left_cols = left_cols or []
    head_row = [Paragraph(h, sTableH) for h in headers]
    data = [head_row]
    for i, row in enumerate(rows):
        styled = []
        for j, cell in enumerate(row):
            st = sTableL if j in left_cols else sTableC
            styled.append(Paragraph(str(cell), st))
        data.append(styled)

    t = Table(data, colWidths=col_widths)
    style = [
        ("BACKGROUND", (0,0), (-1,0), TABLE_HEAD),
        ("ROWPADDING", (0,0), (-1,-1), 4),
        ("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#b0bec5")),
        ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
    ]
    for i in range(1, len(data)):
        if i % 2 == 0:
            style.append(("BACKGROUND", (0,i), (-1,i), TABLE_ALT))
    t.setStyle(TableStyle(style))
    return t

# ── cover ─────────────────────────────────────────────────────────
def cover():
    data = [[
        Paragraph("ATOMIC STRUCTURE", sTitle),
    ]]
    t = Table(data, colWidths=[W])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), DARK_BLUE),
        ("ROWPADDING", (0,0), (-1,-1), 18),
        ("BOX", (0,0), (-1,-1), 0, WHITE),
    ]))
    sub_data = [[Paragraph("Complete Chapter | All Formulas | All Topics | Class 11 Chemistry | NEET / JEE", sSubTitle)]]
    sub_t = Table(sub_data, colWidths=[W])
    sub_t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), MED_BLUE),
        ("ROWPADDING", (0,0), (-1,-1), 8),
    ]))
    return [t, sub_t, sp(10)]

# ═══════════════════════════════════════════════════════════════════
# BUILD STORY
# ═══════════════════════════════════════════════════════════════════
story = []
story += cover()

# ─────────────────────────────────────────────────────────────────
# PART 1 — DISCOVERY OF SUB-ATOMIC PARTICLES
# ─────────────────────────────────────────────────────────────────
story.append(part_header("PART 1 — DISCOVERY OF SUB-ATOMIC PARTICLES"))
story.append(sp(6))

story.append(section_header("1.1  Cathode Ray Experiment  (J.J. Thomson, 1897)"))
story.append(body("A high-voltage discharge tube (evacuated glass tube with two metal electrodes) was used. Rays travel from cathode (–) to anode (+). These are <b>cathode rays</b>."))
story.append(sp(3))
story.append(sub_header("Observations of Cathode Rays:"))
for b in [
    "Travel in straight lines; cast sharp shadows",
    "Deflect towards the positive plate → carry negative charge",
    "Cause fluorescence on a ZnS screen",
    "Same e/m ratio regardless of cathode material or gas used → electrons are UNIVERSAL",
    "Discovery: the electron",
]:
    story.append(bullet(b))
story.append(sp(4))
story.append(formula_box(
    "e / m_e  =  1.758 × 10¹¹  C/kg",
    ["e = charge of electron = 1.602 × 10⁻¹⁹ C",
     "m_e = mass of electron = 9.109 × 10⁻³¹ kg",
     "This ratio is CONSTANT for cathode rays in ALL gases.",
     "Canal rays (positive ions) have VARYING e/m — different gases give different ion masses."]
))
story.append(sp(4))

story.append(section_header("1.2  Millikan's Oil Drop Experiment  (1909)"))
story.append(body("Measured the charge on an electron by balancing gravitational and electric forces on charged oil drops."))
story.append(formula_box(
    "e  =  1.602 × 10⁻¹⁹  C",
    ["Mass of electron  m_e = e ÷ (e/m) = 9.109 × 10⁻³¹ kg"]
))
story.append(sp(4))

story.append(section_header("1.3  Canal Ray Experiment  (Goldstein, 1886)"))
story.append(body("Cathode with holes (perforations). Rays observed BEHIND the cathode → travel opposite to cathode rays. <b>Discovery: Proton</b> (positively charged particle). The lightest positive ion from hydrogen gas is the proton (H⁺)."))
story.append(sp(3))
story.append(make_table(
    ["Particle", "Charge", "Mass (kg)", "Charge-to-mass (C/kg)"],
    [
        ["Proton (H⁺)", "+1.602 × 10⁻¹⁹ C", "1.673 × 10⁻²⁷", "9.58 × 10⁷"],
    ],
    col_widths=[W*0.25, W*0.25, W*0.25, W*0.25]
))
story.append(sp(4))

story.append(section_header("1.4  Discovery of Neutron  (Chadwick, 1932)"))
story.append(body("Bombarded beryllium with alpha-particles → neutral radiation identified as <b>neutrons</b>."))
story.append(formula_box("Neutron charge = 0   |   m_n = 1.675 × 10⁻²⁷ kg"))
story.append(sp(4))

story.append(section_header("1.5  Sub-Atomic Particles — Summary Table"))
story.append(make_table(
    ["Particle", "Symbol", "Charge", "Mass (kg)", "Mass (amu)", "Discoverer"],
    [
        ["Electron", "e⁻", "–1.602×10⁻¹⁹ C", "9.109×10⁻³¹", "0.000549", "Thomson (1897)"],
        ["Proton",   "p⁺", "+1.602×10⁻¹⁹ C", "1.673×10⁻²⁷", "1.00728",  "Goldstein/Rutherford"],
        ["Neutron",  "n",  "0",               "1.675×10⁻²⁷", "1.00867",  "Chadwick (1932)"],
    ],
    col_widths=[W*0.14, W*0.10, W*0.22, W*0.20, W*0.14, W*0.20],
    left_cols=[5]
))
story.append(sp(8))

# ─────────────────────────────────────────────────────────────────
# PART 2 — ATOMIC MODELS
# ─────────────────────────────────────────────────────────────────
story.append(part_header("PART 2 — ATOMIC MODELS"))
story.append(sp(6))

story.append(section_header("2.1  Thomson's Plum Pudding Model (1904)"))
for b in [
    "Atom is a uniform positive sphere with electrons embedded like plums in a pudding",
    "Radius ≈ 10⁻¹⁰ m",
    "FAILED — could not explain Rutherford's alpha-scattering results",
]:
    story.append(bullet(b))
story.append(sp(4))

story.append(section_header("2.2  Rutherford's Nuclear Model (1911)"))
story.append(sub_header("Alpha-scattering Experiment Observations:"))
for b in [
    "Most alpha-particles passed straight through → atom is MOSTLY EMPTY SPACE",
    "Few deflected at small angles → small positive charge inside",
    "Very few (1 in 20,000) bounced back at 180° → small, dense, positive NUCLEUS",
]:
    story.append(bullet(b))
story.append(sp(3))
story.append(sub_header("Conclusions:"))
for b in [
    "Tiny, dense, positively charged nucleus at the centre",
    "Electrons revolve around nucleus in circular orbits (like planets around sun)",
    "Nucleus contains protons (and neutrons)",
]:
    story.append(bullet(b))
story.append(sp(4))
story.append(formula_box(
    "Nuclear Radius:  r  =  r₀ × A^(1/3)",
    ["r₀ = nuclear radius constant = 1.2 × 10⁻¹⁵ m = 1.2 fm",
     "A = mass number (number of protons + neutrons)",
     "Nuclear density ≈ 10¹⁷ kg/m³  — CONSTANT for ALL nuclei (independent of A and Z)",
     "1 cm³ of nuclear matter would weigh ~100 million tonnes!"]
))
story.append(sp(4))
story.append(sub_header("Limitations of Rutherford's Model:"))
for b in [
    "Stability problem: Accelerating electron should continuously radiate energy → spiral into nucleus → atom collapses in ~10⁻⁸ s (but atoms are stable!)",
    "Line spectrum problem: A collapsing spiral would give a continuous spectrum, NOT discrete line spectra",
]:
    story.append(bullet(b))
story.append(sp(4))

story.append(section_header("2.3  Nuclear Terminology"))
story.append(formula_box(
    "Mass Number:  A  =  Z  +  N",
    ["A = total nucleons (protons + neutrons)",
     "Z = atomic number = protons = electrons (neutral atom)",
     "N = neutron number = A − Z"]
))
story.append(sp(4))
story.append(make_table(
    ["Term", "Same", "Different", "Example"],
    [
        ["Isotopes",     "Z (same element)",   "A, N",  "¹H, ²H (D), ³H (T)  — all Z=1"],
        ["Isobars",      "A",                  "Z, N",  "¹⁴C and ¹⁴N"],
        ["Isotones",     "N",                  "Z, A",  "³H and ⁴He (both N=2)"],
        ["Isoelectronic","Total electrons",    "—",     "Na⁺, Mg²⁺, Ne, F⁻ (all 10 e⁻)"],
        ["Isodiaphers",  "A−2Z (neutron excess)","—",   "²³⁸U and ²³⁴Th"],
    ],
    col_widths=[W*0.18, W*0.22, W*0.18, W*0.42],
    left_cols=[0,1,2,3]
))
story.append(sp(8))

# ─────────────────────────────────────────────────────────────────
# PART 3 — ELECTROMAGNETIC RADIATION
# ─────────────────────────────────────────────────────────────────
story.append(part_header("PART 3 — ELECTROMAGNETIC RADIATION & PLANCK'S THEORY"))
story.append(sp(6))

story.append(section_header("3.1  Wave Theory of Light"))
for b in [
    "Light = electromagnetic wave — oscillating electric and magnetic fields perpendicular to each other and to the direction of propagation",
    "All EM waves travel at c = 3 × 10⁸ m/s in vacuum (regardless of frequency)",
    "Do NOT require a medium to propagate",
]:
    story.append(bullet(b))
story.append(sp(4))

story.append(formula_box(
    "Wave Equation:  c  =  ν × λ",
    ["c = speed of light = 3 × 10⁸ m/s",
     "ν (nu) = frequency in Hz (= s⁻¹)",
     "λ (lambda) = wavelength in metres",
     "ν and λ are INVERSELY proportional at constant c",
     "Unit conversions: 1 nm = 10⁻⁹ m  |  1 Å = 10⁻¹⁰ m  |  1 pm = 10⁻¹² m"]
))
story.append(sp(4))

story.append(formula_box(
    "Wave Number:  ν̃  =  1 / λ",
    ["ν̃ = wave number = number of waves per unit length",
     "Units: m⁻¹ or cm⁻¹   (mainly used in IR spectroscopy in cm⁻¹)",
     "CORRECT: ν̃ = 1/λ    WRONG: ν̃ = ν/c  (common mistake)"]
))
story.append(sp(4))

story.append(section_header("3.2  Electromagnetic Spectrum"))
story.append(make_table(
    ["Region", "Wavelength Range", "Frequency Range", "Example / Notes"],
    [
        ["Gamma rays",  "< 0.01 nm",        "> 3×10¹⁹ Hz",           "Nuclear reactions"],
        ["X-rays",      "0.01 – 10 nm",     "3×10¹⁷ – 3×10¹⁹ Hz",   "Medical imaging"],
        ["UV",          "10 – 400 nm",      "7.5×10¹⁴ – 3×10¹⁷ Hz", "Lyman series of H"],
        ["Visible",     "400 – 700 nm",     "4.3×10¹⁴ – 7.5×10¹⁴ Hz","Balmer series of H"],
        ["Infrared (IR)","700 nm – 1 mm",   "3×10¹¹ – 4.3×10¹⁴ Hz", "Paschen/Brackett series"],
        ["Microwaves",  "1 mm – 10 cm",     "3×10⁹ – 3×10¹¹ Hz",    "Radar, cooking"],
        ["Radio waves", "> 10 cm",          "< 3×10⁹ Hz",            "Broadcasting"],
    ],
    col_widths=[W*0.17, W*0.22, W*0.30, W*0.31],
    left_cols=[0,3]
))
story.append(sp(4))

story.append(sub_header("Visible Light — VIBGYOR (increasing λ / decreasing energy):"))
story.append(make_table(
    ["Colour", "λ (approx)", "Energy (eV)"],
    [
        ["Violet", "400 nm", "3.10 eV"],
        ["Indigo", "425 nm", "2.92 eV"],
        ["Blue",   "450 nm", "2.76 eV"],
        ["Green",  "520 nm", "2.38 eV"],
        ["Yellow", "580 nm", "2.14 eV"],
        ["Orange", "620 nm", "2.00 eV"],
        ["Red",    "700 nm", "1.77 eV"],
    ],
    col_widths=[W*0.33, W*0.33, W*0.34]
))
story.append(sp(4))

story.append(section_header("3.3  Planck's Quantum Theory (1900)"))
for b in [
    "Energy is NOT continuous — it is emitted/absorbed in discrete packets called QUANTA (singular: quantum) or PHOTONS",
    "Energy of each quantum is directly proportional to frequency",
]:
    story.append(bullet(b))
story.append(sp(4))
story.append(formula_box(
    "Energy of ONE Photon:  E  =  hν  =  hc/λ  =  hcν̃",
    ["h = Planck's constant = 6.626 × 10⁻³⁴ J·s",
     "ν = frequency (Hz)   |   λ = wavelength (m)   |   ν̃ = wave number (m⁻¹)",
     "E = energy of ONE photon (in Joules or eV)",
     "For n photons:  E_total = n × h × ν",
     "1 eV = 1.602 × 10⁻¹⁹ J"]
))
story.append(sp(4))
story.append(formula_box(
    "Energy–Wavelength Shortcut (fastest for NEET):  E(eV)  =  1240 / λ(nm)",
    ["λ MUST be in nanometres (nm). Answer is in eV.",
     "For Angstroms:  E(eV) = 12400 / λ(Å)",
     "Derived from: hc = 6.626×10⁻³⁴ × 3×10⁸ = 1.988×10⁻²⁵ J·m = 1240 eV·nm"]
))
story.append(sp(4))
story.append(formula_box(
    "Photon Momentum:  p  =  h/λ  =  E/c  =  hν/c",
    ["Photon has ZERO rest mass but has energy E = hν and momentum p = h/λ"]
))
story.append(sp(8))

# ─────────────────────────────────────────────────────────────────
# PART 4 — ATOMIC SPECTRA
# ─────────────────────────────────────────────────────────────────
story.append(part_header("PART 4 — ATOMIC SPECTRA & SPECTRAL SERIES"))
story.append(sp(6))

story.append(section_header("4.1  Line Spectrum of Hydrogen"))
story.append(body("When hydrogen gas is excited (high voltage), it emits light of only certain wavelengths → <b>line spectrum</b> (not continuous). Each line corresponds to a specific electron transition between energy levels."))
story.append(sp(4))

story.append(section_header("4.2  Spectral Series Table"))
story.append(make_table(
    ["Series", "n₁ (lower)", "n₂ (upper)", "Region", "First Line (λ)"],
    [
        ["Lyman",      "1", "2, 3, 4…",  "UV",         "121.6 nm"],
        ["Balmer",     "2", "3, 4, 5…",  "Visible+UV", "656.3 nm (Hα, red)"],
        ["Paschen",    "3", "4, 5, 6…",  "Near IR",    "1875 nm"],
        ["Brackett",   "4", "5, 6, 7…",  "IR",         "4051 nm"],
        ["Pfund",      "5", "6, 7, 8…",  "Far IR",     "7459 nm"],
        ["Humphreys",  "6", "7, 8, 9…",  "Far IR",     "—"],
    ],
    col_widths=[W*0.18, W*0.14, W*0.20, W*0.18, W*0.30]
))
story.append(sp(4))

story.append(formula_box(
    "Rydberg Equation:  1/λ  =  R_H × Z² × (1/n₁²  −  1/n₂²)",
    ["R_H = Rydberg constant = 1.097 × 10⁷ m⁻¹ = 109677 cm⁻¹",
     "Z = atomic number  (Z=1 for H, Z=2 for He⁺, Z=3 for Li²⁺, etc.)",
     "n₁ = lower orbit  (n₁ determines the SERIES)",
     "n₂ = upper orbit  (n₂ > n₁, must be integer)",
     "First line of each series (longest λ):  n₂ = n₁ + 1",
     "Series limit (shortest λ):  n₂ = ∞  →  1/λ_limit = R_H × Z² / n₁²"]
))
story.append(sp(4))

story.append(formula_box(
    "Number of Spectral Lines (n₂ → n₁):  Lines  =  (n₂ − n₁)(n₂ − n₁ + 1) / 2",
    ["Special case — electron falls to ground state (n₁ = 1):  Lines = n(n−1)/2",
     "Example 1:  n=5 → n=1:  (5×4)/2 = 10 lines",
     "Example 2:  n=5 → n=3:  (2×3)/2 = 3 lines",
     "Example 3:  n=4 → n=2:  (2×3)/2 = 3 lines"]
))
story.append(sp(8))

# ─────────────────────────────────────────────────────────────────
# PART 5 — BOHR'S MODEL
# ─────────────────────────────────────────────────────────────────
story.append(part_header("PART 5 — BOHR'S ATOMIC MODEL (1913)"))
story.append(sp(6))

story.append(section_header("5.1  Postulates"))
for b in [
    "Electrons revolve in fixed circular orbits called STATIONARY STATES (no energy radiated while in orbit)",
    "Only orbits where angular momentum = whole-number multiple of h/2π are allowed",
    "Energy absorbed or emitted ONLY when electron jumps between orbits",
    "Energy of emitted/absorbed photon = difference in orbital energies:  ΔE = hν",
]:
    story.append(bullet(b))
story.append(sp(4))

story.append(section_header("5.2  Bohr Formulas  (H and H-like ions: He⁺, Li²⁺, Be³⁺…)"))

story.append(sub_header("Formula 1 — Orbital Radius"))
story.append(formula_box(
    "r_n  =  0.529 × n² / Z   Å",
    ["r_n = radius of nth orbit (distance of electron from nucleus)",
     "0.529 Å = a₀ = Bohr radius = radius of 1st orbit of H (n=1, Z=1)",
     "n = principal quantum number = 1, 2, 3, 4…",
     "Z = atomic number",
     "Rules:  r_n ∝ n² (quadruples when n doubles)  |  r_n ∝ 1/Z",
     "To convert to metres: multiply by 10⁻¹⁰   (1 Å = 10⁻¹⁰ m)"]
))
story.append(sp(3))
story.append(make_table(
    ["n", "H (Z=1)", "He⁺ (Z=2)", "Li²⁺ (Z=3)"],
    [
        ["1", "0.529 Å", "0.265 Å", "0.176 Å"],
        ["2", "2.116 Å", "1.058 Å", "0.705 Å"],
        ["3", "4.761 Å", "2.381 Å", "1.587 Å"],
    ],
    col_widths=[W*0.15, W*0.28, W*0.28, W*0.29]
))
story.append(sp(4))

story.append(sub_header("Formula 2 — Orbital Velocity"))
story.append(formula_box(
    "v_n  =  2.18 × 10⁶ × Z / n   m/s",
    ["2.18 × 10⁶ m/s = velocity in 1st orbit of H = c/137 (much less than light speed)",
     "v_n ∝ Z/n   |   Outer electrons move SLOWER (easier to remove)",
     "H (n=1): 2.18×10⁶ m/s   |   H (n=2): 1.09×10⁶ m/s   |   H (n=3): 0.727×10⁶ m/s"]
))
story.append(sp(4))

story.append(sub_header("Formula 3 — Total Energy"))
story.append(formula_box(
    "E_n  =  −13.6 × Z² / n²   eV   =   −2.18×10⁻¹⁸ × Z² / n²   J",
    ["E_n = total energy of electron in nth orbit — ALWAYS NEGATIVE for a bound electron",
     "−13.6 eV = energy in 1st orbit of H (ground state) = ionisation energy of H",
     "Negative sign: electron is BOUND to nucleus",
     "n=1 is most negative = most stable   |   At n=∞:  E=0 (electron free = atom ionised)"]
))
story.append(sp(3))
story.append(make_table(
    ["n", "Energy of H (Z=1)"],
    [
        ["1 (ground)", "−13.6 eV"],
        ["2",          "−3.4 eV"],
        ["3",          "−1.51 eV"],
        ["4",          "−0.85 eV"],
        ["5",          "−0.544 eV"],
        ["∞",          "0 eV (ionised)"],
    ],
    col_widths=[W*0.35, W*0.65]
))
story.append(sp(4))

story.append(sub_header("Formula 4 — Kinetic Energy and Potential Energy"))
story.append(formula_box(
    "KE  =  −E_n  =  +13.6 Z²/n²  eV  (always positive)\n"
    "PE  =  2E_n  =  −27.2 Z²/n²  eV  (always negative)\n"
    "TE  =  KE + PE  =  E_n",
    ["Three golden relationships:   KE = −TE     |     PE = 2×TE     |     PE = −2×KE",
     "Example H (n=1):  KE=+13.6 eV,  PE=−27.2 eV,  TE=−13.6 eV   ✓ (13.6−27.2=−13.6)"]
))
story.append(sp(4))

story.append(sub_header("Formula 5 — Ionisation Energy"))
story.append(formula_box(
    "IE  =  0 − E_n  =  +13.6 × Z² / n²   eV",
    ["IE = energy to remove electron from orbit n to infinity (n → ∞)",
     "IE is ALWAYS positive   |   Decreases as n increases (easier to remove from outer orbits)",
     "H (n=1): 13.6 eV = 1312 kJ/mol   |   He⁺ (Z=2, n=1): 54.4 eV   |   Li²⁺ (Z=3, n=1): 122.4 eV"]
))
story.append(sp(4))

story.append(sub_header("Formula 6 — Energy of Emitted/Absorbed Photon"))
story.append(formula_box(
    "ΔE  =  13.6 × Z² × (1/n₁²  −  1/n₂²)   eV",
    ["n₁ = lower orbit (where electron LANDS after emission) — n₁ < n₂",
     "n₂ = upper orbit (where electron STARTS) — n₂ > n₁",
     "ΔE is always POSITIVE",
     "EMISSION: electron falls n₂ → n₁, photon released",
     "ABSORPTION: electron jumps n₁ → n₂, photon absorbed"]
))
story.append(sp(4))

story.append(sub_header("Formula 7 — Angular Momentum Quantisation (Bohr's Condition)"))
story.append(formula_box(
    "mvr_n  =  nh / 2π  =  nℏ",
    ["m = mass of electron (kg)   |   v = orbital velocity (m/s)   |   r_n = orbital radius (m)",
     "n = principal quantum number   |   h = Planck's constant = 6.626×10⁻³⁴ J·s",
     "ℏ = h/2π = 1.055×10⁻³⁴ J·s  (reduced Planck constant)"]
))
story.append(sp(4))

story.append(sub_header("Formulas 8 & 9 — Time Period and Frequency of Revolution"))
story.append(formula_box(
    "T_n  =  2π r_n / v_n    ∝   n³ / Z²\n"
    "ν_n  =  v_n / (2π r_n)  ∝   Z² / n³",
    ["T_n increases as n³ — if n doubles, T becomes 2³ = 8× longer",
     "ν_n decreases as n³ — outer electrons orbit LESS frequently",
     "NEET trap: If n doubles, time period increases 8 times"]
))
story.append(sp(4))

story.append(section_header("5.3  Limitations of Bohr's Model"))
for b in [
    "Works ONLY for hydrogen and H-like ions (one electron). Fails for multi-electron atoms.",
    "Cannot explain fine structure of spectral lines (Zeeman and Stark effects).",
    "Cannot explain relative intensities of spectral lines.",
    "Violates Heisenberg's Uncertainty Principle — definite orbits imply exact position AND momentum simultaneously.",
    "Does not explain chemical bonding or 3D shapes of molecules.",
]:
    story.append(bullet(b))
story.append(sp(8))

# ─────────────────────────────────────────────────────────────────
# PART 6 — PHOTOELECTRIC EFFECT
# ─────────────────────────────────────────────────────────────────
story.append(part_header("PART 6 — PHOTOELECTRIC EFFECT"))
story.append(sp(6))

story.append(section_header("6.1  Phenomenon & Observations"))
story.append(body("When light of sufficient frequency falls on a metal surface, electrons are ejected. These ejected electrons are called <b>photoelectrons</b>. Einstein explained this in 1905 using Planck's quantum theory (Nobel Prize 1921)."))
story.append(sp(3))
story.append(sub_header("Observations that Wave Theory CANNOT explain:"))
for b in [
    "Emission is INSTANTANEOUS (wave theory predicts a time delay for energy buildup)",
    "Below threshold frequency ν₀: NO emission regardless of intensity",
    "Above ν₀: KE_max depends on frequency, NOT intensity",
    "More intensity → MORE electrons emitted, NOT faster electrons",
]:
    story.append(bullet(b))
story.append(sp(4))

story.append(sub_header("Formula 10 — Einstein's Photoelectric Equation"))
story.append(formula_box(
    "hν  =  φ  +  KE_max",
    ["hν = energy of incident photon (J or eV)",
     "φ (phi) = work function = minimum energy to eject ONE electron = hν₀  (J or eV)",
     "KE_max = maximum KE of ejected photoelectron = ½m_e v²_max  (J or eV)",
     "If hν < φ:  NO emission at all (even at very high intensity)",
     "If hν = φ:  Emission just starts, KE_max = 0"]
))
story.append(sp(4))

story.append(sub_header("Formula 11 — Work Function"))
story.append(formula_box(
    "φ  =  hν₀",
    ["φ = work function — a fixed property of the metal",
     "ν₀ = threshold frequency — minimum frequency for photoelectric emission"]
))
story.append(sp(3))
story.append(make_table(
    ["Metal", "Work Function φ (eV)", "Threshold λ₀ (nm)", "Responds to"],
    [
        ["Cs",  "2.0 eV", "620 nm", "Visible (red-orange)"],
        ["K",   "2.2 eV", "564 nm", "Visible (yellow)"],
        ["Na",  "2.3 eV", "539 nm", "Visible (green)"],
        ["Al",  "4.3 eV", "288 nm", "UV"],
        ["Zn",  "4.3 eV", "288 nm", "UV"],
        ["Pt",  "5.6 eV", "221 nm", "UV"],
    ],
    col_widths=[W*0.12, W*0.28, W*0.28, W*0.32],
    left_cols=[3]
))
story.append(sp(4))

story.append(sub_header("Formula 12 — Threshold Wavelength"))
story.append(formula_box(
    "λ₀  =  hc / φ  =  c / ν₀     →     λ₀(nm)  =  1240 / φ(eV)",
    ["Light with λ < λ₀ (higher frequency) → emission OCCURS",
     "Light with λ > λ₀ (lower frequency) → NO emission (regardless of intensity)",
     "Example: φ=2.0 eV → λ₀ = 620 nm (red).   φ=4.3 eV → λ₀ = 288 nm (UV)"]
))
story.append(sp(4))

story.append(sub_header("Formula 13 — Stopping Potential"))
story.append(formula_box(
    "eV₀  =  KE_max  =  hν − φ",
    ["e = charge of electron = 1.602×10⁻¹⁹ C",
     "V₀ = stopping potential = minimum REVERSE voltage to stop ALL photoelectrons (Volts)",
     "KE_max in eV numerically equals V₀ in Volts",
     "If KE_max = 2.5 eV  →  V₀ = 2.5 V",
     "More intensity: photocurrent ↑, V₀ UNCHANGED",
     "Higher frequency: V₀ ↑, photocurrent unchanged",
     "V₀ vs ν graph: Slope = h/e = 4.136×10⁻¹⁵ V·s  |  X-intercept = ν₀  |  Y-intercept = −φ/e"]
))
story.append(sp(4))

story.append(sub_header("Formula 14 — Number of Photons per Unit Area per Second"))
story.append(formula_box(
    "n_photons  =  I / hν  =  Iλ / hc",
    ["n_photons = number of photons hitting unit area per second  (s⁻¹·m⁻²)",
     "I = intensity of light = energy per unit area per second  (W/m²)",
     "More intensity = MORE photons = MORE electrons ejected = LARGER photocurrent",
     "Each photon gives its energy to ONE electron — so KE_max is unaffected by intensity"]
))
story.append(sp(8))

# ─────────────────────────────────────────────────────────────────
# PART 7 — de BROGLIE
# ─────────────────────────────────────────────────────────────────
story.append(part_header("PART 7 — DUAL NATURE OF MATTER  (de Broglie, 1924)"))
story.append(sp(6))

story.append(section_header("7.1  Background & Principle"))
for b in [
    "Louis de Broglie (1924): If light (wave) can behave as particles (photons), then matter (particles) should also show wave properties",
    "Wave-particle duality: all matter has both wave and particle properties",
    "Only observable for microscopic particles (electrons, protons, neutrons) — not for macroscopic objects",
    "Experimental proof: Davisson-Germer experiment (1927) — electron diffraction by nickel crystal confirmed electron wave nature",
]:
    story.append(bullet(b))
story.append(sp(4))

story.append(sub_header("Formula 15 — de Broglie Wavelength (General)"))
story.append(formula_box(
    "λ  =  h / mv  =  h / p",
    ["λ = de Broglie (matter) wavelength (m)",
     "h = Planck's constant = 6.626×10⁻³⁴ J·s",
     "m = mass of particle (kg)   |   v = velocity (m/s)   |   p = mv = momentum (kg·m/s)",
     "Cricket ball (0.1 kg, 30 m/s): λ ~ 2×10⁻³⁴ m — unmeasurably tiny",
     "Electron (9.1×10⁻³¹ kg, 10⁶ m/s): λ ~ 0.7 nm — detectable by X-ray diffraction!"]
))
story.append(sp(4))

story.append(sub_header("Formula 16 — de Broglie Wavelength from Kinetic Energy"))
story.append(formula_box(
    "λ  =  h / √(2m·KE)",
    ["Derived from λ = h/mv and KE = ½mv²  →  mv = √(2m·KE)",
     "KE MUST be in Joules.  Convert:  KE(J) = KE(eV) × 1.602×10⁻¹⁹"]
))
story.append(sp(4))

story.append(sub_header("Formula 17 — de Broglie Wavelength for Accelerated Electron"))
story.append(formula_box(
    "λ  =  12.27 / √V   Å      (electrons only)",
    ["12.27 = constant for ELECTRONS only = h/√(2·m_e·e)  expressed in Å·V^½ units",
     "V = accelerating potential difference in VOLTS   |   λ answer in ANGSTROMS",
     "For proton:  λ = 0.286/√V Å",
     "V=100 V → λ=1.227 Å   |   V=1000 V → λ=0.388 Å   |   V=10000 V → λ=0.123 Å",
     "Used in electron microscopy (λ much smaller than visible light → far higher resolution)"]
))
story.append(sp(4))

story.append(sub_header("Formula 18 — de Broglie and Bohr Connection"))
story.append(formula_box(
    "2π r_n  =  n λ   (circumference = whole-number multiples of λ)",
    ["Bohr's quantisation condition (mvr = nh/2π) is equivalent to the electron's de Broglie wave fitting exactly into the orbit",
     "This gives a wave-mechanical basis to Bohr's model"]
))
story.append(sp(8))

# ─────────────────────────────────────────────────────────────────
# PART 8 — HEISENBERG'S UNCERTAINTY PRINCIPLE
# ─────────────────────────────────────────────────────────────────
story.append(part_header("PART 8 — HEISENBERG'S UNCERTAINTY PRINCIPLE (1927)"))
story.append(sp(6))

story.append(section_header("8.1  Statement"))
story.append(body("It is impossible to simultaneously determine the EXACT position AND exact momentum (or velocity) of a microscopic particle. This is a fundamental law of nature — NOT due to measurement limitations."))
story.append(sp(4))

story.append(sub_header("Formula 19 — Heisenberg's Uncertainty Principle"))
story.append(formula_box(
    "Δx · Δp  ≥  h / 4π     OR     Δx · m·Δv  ≥  h / 4π",
    ["Δx = uncertainty in position (m)",
     "Δp = uncertainty in momentum (kg·m/s)",
     "Δv = uncertainty in velocity (m/s)",
     "h/4π = minimum allowed product = 5.27×10⁻³⁵ J·s",
     "Equivalent form:  Δx · Δp ≥ ℏ/2  where ℏ = h/2π = 1.055×10⁻³⁴ J·s",
     "Energy-time version:  ΔE · Δt ≥ h/4π"]
))
story.append(sp(4))

story.append(sub_header("Formula 20 — Minimum Uncertainty in Velocity"))
story.append(formula_box(
    "Δv_min  =  h / (4π × m × Δx)",
    ["Use equality sign (=) to get the MINIMUM possible Δv",
     "If Δx = 0 (exact position):  Δp = ∞ (momentum completely unknown)",
     "If Δp = 0 (exact momentum):  Δx = ∞ (position completely unknown)"]
))
story.append(sp(4))
story.append(trap_box("Heisenberg's UP invalidates Bohr's model — definite circular orbits would mean exact position AND velocity simultaneously, which is forbidden by HUP."))
story.append(sp(8))

# ─────────────────────────────────────────────────────────────────
# PART 9 — QUANTUM MECHANICAL MODEL & QUANTUM NUMBERS
# ─────────────────────────────────────────────────────────────────
story.append(part_header("PART 9 — QUANTUM MECHANICAL MODEL & QUANTUM NUMBERS"))
story.append(sp(6))

story.append(section_header("9.1  Orbital vs Orbit"))
story.append(make_table(
    ["Feature", "Orbit (Bohr)", "Orbital (Quantum Mechanics)"],
    [
        ["Definition",   "Definite circular path", "Region of space where probability of finding electron ≥ 90%"],
        ["Heisenberg",   "Violates HUP",           "Consistent with HUP"],
        ["Dimensions",   "2D circle",              "3D region of space"],
        ["Position",     "Exact position known",   "Only probability density known"],
        ["Stability",    "Cannot explain",         "Fully explains stable atoms"],
    ],
    col_widths=[W*0.18, W*0.30, W*0.52],
    left_cols=[0,1,2]
))
story.append(sp(6))

story.append(section_header("9.2  The Four Quantum Numbers"))
story.append(body("Every electron in an atom is completely described by a UNIQUE set of 4 quantum numbers. No two electrons can have the same set of all 4 (Pauli's Exclusion Principle)."))
story.append(sp(4))

story.append(sub_header("(a) Principal Quantum Number (n)"))
story.append(formula_box(
    "n  =  1, 2, 3, 4, 5, 6, 7…",
    ["Determines ENERGY and SIZE of the orbital",
     "Higher n = larger orbital = higher energy = farther from nucleus",
     "Shell names:  n=1→K,  n=2→L,  n=3→M,  n=4→N"]
))
story.append(sp(3))

story.append(sub_header("(b) Azimuthal Quantum Number (l)  — Subsidiary / Angular Momentum QN"))
story.append(formula_box(
    "l  =  0, 1, 2, …, (n−1)",
    ["Determines SHAPE of orbital and which SUBSHELL",
     "l=0 → s (sphere)   |   l=1 → p (dumbbell)   |   l=2 → d (double-dumbbell)   |   l=3 → f (complex)",
     "For n=1: l=0 only (1s)   |   n=2: l=0,1 (2s, 2p)   |   n=3: l=0,1,2 (3s, 3p, 3d)"]
))
story.append(sp(3))

story.append(sub_header("(c) Magnetic Quantum Number (m_l)"))
story.append(formula_box(
    "m_l  =  −l, −(l−1), …, 0, …, (l−1), +l     →     Total values = 2l+1",
    ["Determines ORIENTATION of orbital in space",
     "s (l=0):  m_l=0 (1 orbital)   |   p (l=1):  m_l=−1,0,+1 (3 orbitals: px, py, pz)",
     "d (l=2):  m_l=−2,−1,0,+1,+2 (5 orbitals)   |   f (l=3):  7 orbitals"]
))
story.append(sp(3))

story.append(sub_header("(d) Spin Quantum Number (m_s)"))
story.append(formula_box(
    "m_s  =  +1/2  (spin up ↑)   or   −1/2  (spin down ↓)     [only these two values]",
    ["Every electron has spin — it is an intrinsic property",
     "Pauli's principle: two electrons in the same orbital MUST have opposite m_s values"]
))
story.append(sp(4))

story.append(make_table(
    ["QN", "Symbol", "Values", "Determines", "Number of values"],
    [
        ["Principal",    "n",   "1, 2, 3, 4…",        "Shell, energy, size",   "Infinite"],
        ["Azimuthal",    "l",   "0 to (n−1)",          "Subshell, shape",       "n values (0 to n−1)"],
        ["Magnetic",     "m_l", "−l to +l",            "Orbital orientation",   "2l+1"],
        ["Spin",         "m_s", "+½ or −½ only",       "Spin direction",        "2"],
    ],
    col_widths=[W*0.16, W*0.12, W*0.20, W*0.28, W*0.24],
    left_cols=[0,3]
))
story.append(sp(6))

story.append(section_header("9.3  Angular Momentum Formulas"))

story.append(sub_header("Formula 21 — Orbital Angular Momentum"))
story.append(formula_box(
    "L  =  √[l(l+1)] · ℏ",
    ["L = magnitude of orbital angular momentum (J·s)",
     "l = azimuthal quantum number   |   ℏ = h/2π = 1.055×10⁻³⁴ J·s",
     "s (l=0): L=0   |   p (l=1): L=√2·ℏ   |   d (l=2): L=√6·ℏ   |   f (l=3): L=2√3·ℏ"]
))
story.append(trap_box("Bohr's model gave L = nℏ (WRONG). Correct QM: L = √[l(l+1)]·ℏ. s orbitals have ZERO orbital angular momentum — NOT ℏ."))
story.append(sp(4))

story.append(sub_header("Formula 22 — z-Component of Angular Momentum"))
story.append(formula_box(
    "L_z  =  m_l · ℏ",
    ["L_z = projection of angular momentum on z-axis",
     "p (l=1):  L_z = −ℏ, 0, +ℏ  (3 orientations)",
     "d (l=2):  L_z = −2ℏ, −ℏ, 0, +ℏ, +2ℏ  (5 orientations)",
     "A magnetic field splits these orientations → Zeeman effect"]
))
story.append(sp(4))

story.append(sub_header("Formula 23 — Spin Angular Momentum"))
story.append(formula_box(
    "S  =  √[s(s+1)] · ℏ  =  (√3/2) · ℏ   [same for ALL electrons]",
    ["s = spin quantum number = ½  (always, for every electron — cannot change)",
     "The MAGNITUDE of spin is fixed for all electrons",
     "Only the DIRECTION (m_s = ±½) can vary"]
))
story.append(sp(6))

story.append(section_header("9.4  Shell/Subshell Count Formulas"))
story.append(formula_box(
    "Orbitals in nth shell  =  n²          Max electrons in nth shell  =  2n²\n"
    "Orbitals in a subshell  =  2l+1       Max electrons in a subshell  =  2(2l+1)",
    ["n=1: 1 orbital, 2 e⁻   |   n=2: 4 orbitals, 8 e⁻   |   n=3: 9 orbitals, 18 e⁻   |   n=4: 16 orbitals, 32 e⁻",
     "s: 1 orbital, 2 e⁻   |   p: 3 orbitals, 6 e⁻   |   d: 5 orbitals, 10 e⁻   |   f: 7 orbitals, 14 e⁻"]
))
story.append(sp(8))

# ─────────────────────────────────────────────────────────────────
# PART 10 — SHAPES OF ORBITALS
# ─────────────────────────────────────────────────────────────────
story.append(part_header("PART 10 — SHAPES OF ORBITALS"))
story.append(sp(6))

story.append(make_table(
    ["Orbital", "Shape", "Angular Nodes", "Nodal Planes/Regions", "Starts at n"],
    [
        ["s (l=0)", "Sphere", "0", "None — spherical symmetry", "n=1"],
        ["p (l=1)", "Dumbbell (2 lobes)", "1", "px: yz-plane  |  py: xz-plane  |  pz: xy-plane", "n=2"],
        ["d (l=2)", "Double-dumbbell / clover (4 lobes); dz² has donut+dumbbell", "2", "Two nodal planes in each orbital", "n=3"],
        ["f (l=3)", "Complex multi-lobed", "3", "Three nodal regions", "n=4"],
    ],
    col_widths=[W*0.12, W*0.26, W*0.14, W*0.32, W*0.16],
    left_cols=[1,3]
))
story.append(sp(4))
for b in [
    "1s: 1 sphere. 2s: 2 concentric spheres (1 radial node inside). 3s: 3 concentric spheres.",
    "Three p orbitals (px, py, pz) are identical in shape but oriented along different axes — they are DEGENERATE (same energy in absence of field).",
    "Five d orbitals: dxy, dxz, dyz, dx²−y² (clover-leaf in planes) and dz² (unique doughnut + dumbbell along z-axis).",
]:
    story.append(bullet(b))
story.append(sp(8))

# ─────────────────────────────────────────────────────────────────
# PART 11 — NODES
# ─────────────────────────────────────────────────────────────────
story.append(part_header("PART 11 — NODES"))
story.append(sp(6))

story.append(formula_box(
    "Radial nodes  =  n − l − 1\n"
    "Angular nodes  =  l\n"
    "Total nodes  =  n − 1",
    ["Radial nodes = spherical surfaces INSIDE the orbital where electron density = 0",
     "Angular nodes = flat planes (or cones) through the NUCLEUS where electron probability = 0",
     "Proof:  (n−l−1) + l  =  n−1  ✓  (always true for any orbital)"]
))
story.append(sp(4))

story.append(make_table(
    ["Orbital", "n", "l", "Radial nodes (n−l−1)", "Angular nodes (l)", "Total (n−1)"],
    [
        ["1s", "1", "0", "0",    "0", "0"],
        ["2s", "2", "0", "1",    "0", "1"],
        ["2p", "2", "1", "0",    "1", "1"],
        ["3s", "3", "0", "2",    "0", "2"],
        ["3p", "3", "1", "1",    "1", "2"],
        ["3d", "3", "2", "0 ★",  "2", "2"],
        ["4s", "4", "0", "3",    "0", "3"],
        ["4p", "4", "1", "2",    "1", "3"],
        ["4d", "4", "2", "1",    "2", "3"],
        ["4f", "4", "3", "0",    "3", "3"],
    ],
    col_widths=[W*0.12, W*0.08, W*0.08, W*0.23, W*0.22, W*0.27]
))
story.append(sp(3))
story.append(trap_box("3d has ZERO radial nodes (students often say 1 — that is WRONG)."))
story.append(trap_box("2s and 2p BOTH have 1 total node but DIFFERENT types: 2s has 1 RADIAL node (spherical shell), 2p has 1 ANGULAR node (flat plane)."))
story.append(sp(8))

# ─────────────────────────────────────────────────────────────────
# PART 12 — ELECTRONIC CONFIGURATION
# ─────────────────────────────────────────────────────────────────
story.append(part_header("PART 12 — ELECTRONIC CONFIGURATION"))
story.append(sp(6))

story.append(section_header("12.1  Three Fundamental Rules"))

story.append(sub_header("Rule 1 — Aufbau Principle"))
story.append(body("Fill orbitals in INCREASING order of energy (n+l rule). If two orbitals have the same (n+l), fill the one with <b>lower n</b> first."))
story.append(sp(3))
story.append(formula_box(
    "Filling Order:  1s → 2s → 2p → 3s → 3p → 4s → 3d → 4p → 5s → 4d → 5p → 6s → 4f → 5d → 6p",
    ["4s (n+l=4) is filled BEFORE 3d (n+l=5)",
     "4f (n+l=7) is filled BEFORE 5d (n+l=7, but 5d has higher n)"]
))
story.append(sp(4))

story.append(sub_header("Rule 2 — Pauli's Exclusion Principle"))
story.append(body("No two electrons in the same atom can have the <b>same set of all four quantum numbers</b>. Consequence: maximum 2 electrons per orbital, and they must have OPPOSITE spins (one ↑, one ↓)."))
story.append(sp(4))

story.append(sub_header("Rule 3 — Hund's Rule of Maximum Multiplicity"))
story.append(body("In degenerate orbitals (same energy), electrons fill <b>singly first</b> (one per orbital) with <b>parallel spins</b> (same m_s) before any pairing occurs."))
story.append(sp(3))
story.append(note_box("Nitrogen (Z=7): 1s² 2s² 2p³ → 2p orbitals: ↑ | ↑ | ↑  (three singly occupied — CORRECT). NOT: ↑↓ | ↑ | _ (pairing before all singly occupied — WRONG)"))
story.append(sp(6))

story.append(section_header("12.2  Electronic Configurations of Z = 1 to 30"))
story.append(make_table(
    ["Z", "Element", "Configuration", "Unpaired e⁻"],
    [
        ["1",  "H",  "1s¹",                  "1"],
        ["2",  "He", "1s²",                  "0"],
        ["3",  "Li", "[He] 2s¹",             "1"],
        ["4",  "Be", "[He] 2s²",             "0"],
        ["5",  "B",  "[He] 2s² 2p¹",         "1"],
        ["6",  "C",  "[He] 2s² 2p²",         "2"],
        ["7",  "N",  "[He] 2s² 2p³",         "3"],
        ["8",  "O",  "[He] 2s² 2p⁴",         "2"],
        ["9",  "F",  "[He] 2s² 2p⁵",         "1"],
        ["10", "Ne", "[He] 2s² 2p⁶",         "0"],
        ["11", "Na", "[Ne] 3s¹",             "1"],
        ["12", "Mg", "[Ne] 3s²",             "0"],
        ["13", "Al", "[Ne] 3s² 3p¹",         "1"],
        ["14", "Si", "[Ne] 3s² 3p²",         "2"],
        ["15", "P",  "[Ne] 3s² 3p³",         "3"],
        ["16", "S",  "[Ne] 3s² 3p⁴",         "2"],
        ["17", "Cl", "[Ne] 3s² 3p⁵",         "1"],
        ["18", "Ar", "[Ne] 3s² 3p⁶",         "0"],
        ["19", "K",  "[Ar] 4s¹",             "1"],
        ["20", "Ca", "[Ar] 4s²",             "0"],
        ["21", "Sc", "[Ar] 3d¹ 4s²",         "1"],
        ["22", "Ti", "[Ar] 3d² 4s²",         "2"],
        ["23", "V",  "[Ar] 3d³ 4s²",         "3"],
        ["24", "Cr ★", "[Ar] 3d⁵ 4s¹  (EXCEPTION)", "6"],
        ["25", "Mn", "[Ar] 3d⁵ 4s²",         "5"],
        ["26", "Fe", "[Ar] 3d⁶ 4s²",         "4"],
        ["27", "Co", "[Ar] 3d⁷ 4s²",         "3"],
        ["28", "Ni", "[Ar] 3d⁸ 4s²",         "2"],
        ["29", "Cu ★", "[Ar] 3d¹⁰ 4s¹  (EXCEPTION)", "1"],
        ["30", "Zn", "[Ar] 3d¹⁰ 4s²",        "0"],
    ],
    col_widths=[W*0.07, W*0.13, W*0.60, W*0.20],
    left_cols=[2]
))
story.append(sp(4))
story.append(trap_box("Cr (Z=24): Expected [Ar]3d⁴4s² — Actual [Ar]3d⁵4s¹ (half-filled d⁵ is extra stable).  Cu (Z=29): Expected [Ar]3d⁹4s² — Actual [Ar]3d¹⁰4s¹ (fully-filled d¹⁰ is extra stable)."))
story.append(note_box("Reason for exceptions: Half-filled (d⁵) and fully-filled (d¹⁰) subshells have extra stability due to: (1) Symmetrical distribution of electrons, and (2) Maximum exchange energy."))
story.append(sp(6))

story.append(section_header("12.3  Magnetic Properties & Spin Multiplicity"))

story.append(sub_header("Formula 31 — Spin Multiplicity"))
story.append(formula_box(
    "Spin multiplicity  =  2S + 1     where  S  =  (number of unpaired electrons) / 2",
    ["Multiplicity = 1 (singlet): all electrons paired → DIAMAGNETIC (repelled by magnetic field)",
     "Multiplicity > 1: unpaired electrons → PARAMAGNETIC (attracted by magnetic field)",
     "Higher multiplicity = more unpaired electrons = stronger paramagnetism"]
))
story.append(sp(3))
story.append(make_table(
    ["Unpaired e⁻", "S", "Multiplicity", "Type", "Example"],
    [
        ["0", "0",   "1 (singlet)",  "Diamagnetic",   "He, Ne, Zn, Be"],
        ["1", "½",   "2 (doublet)",  "Paramagnetic",  "H, F, Na"],
        ["2", "1",   "3 (triplet)",  "Paramagnetic",  "C, O, Ni²⁺"],
        ["3", "3/2", "4 (quartet)",  "Paramagnetic",  "N, Co²⁺, Cr³⁺"],
        ["4", "2",   "5 (quintet)",  "Paramagnetic",  "Fe²⁺, Cr²⁺"],
        ["5", "5/2", "6 (sextet)",   "Paramagnetic",  "Mn²⁺, Fe³⁺"],
    ],
    col_widths=[W*0.14, W*0.10, W*0.20, W*0.18, W*0.38],
    left_cols=[4]
))
story.append(sp(4))

story.append(sub_header("Formula 32 — Magnetic Moment"))
story.append(formula_box(
    "μ  =  √[n(n+2)]   BM   (Bohr Magneton)",
    ["n = number of unpaired electrons",
     "1 BM (Bohr Magneton) = 9.274×10⁻²⁴ J/T",
     "1 unpaired: μ = √3 ≈ 1.73 BM  (Cu²⁺, Ti³⁺)",
     "2 unpaired: μ = √8 ≈ 2.83 BM  (Ni²⁺)",
     "3 unpaired: μ = √15 ≈ 3.87 BM (Co²⁺, Cr³⁺)",
     "4 unpaired: μ = √24 ≈ 4.90 BM (Fe²⁺)",
     "5 unpaired: μ = √35 ≈ 5.92 BM (Mn²⁺, Fe³⁺)"]
))
story.append(sp(8))

# ─────────────────────────────────────────────────────────────────
# PART 13 — CONSTANTS & MASTER FORMULA LIST
# ─────────────────────────────────────────────────────────────────
story.append(PageBreak())
story.append(part_header("PART 13 — COMPLETE CONSTANTS REFERENCE"))
story.append(sp(6))
story.append(make_table(
    ["Symbol", "Name", "Value", "Unit"],
    [
        ["h",    "Planck's constant",            "6.626 × 10⁻³⁴",  "J·s"],
        ["ℏ",    "Reduced Planck (h/2π)",         "1.055 × 10⁻³⁴",  "J·s"],
        ["c",    "Speed of light",                "3 × 10⁸",         "m/s"],
        ["e",    "Elementary charge",             "1.602 × 10⁻¹⁹",  "C"],
        ["m_e",  "Mass of electron",              "9.109 × 10⁻³¹",  "kg"],
        ["m_p",  "Mass of proton",                "1.673 × 10⁻²⁷",  "kg"],
        ["m_n",  "Mass of neutron",               "1.675 × 10⁻²⁷",  "kg"],
        ["a₀",   "Bohr radius (1st orbit of H)",  "0.529 Å = 5.29×10⁻¹¹", "m"],
        ["R_H",  "Rydberg constant",              "1.097 × 10⁷ m⁻¹ = 109677 cm⁻¹", "m⁻¹"],
        ["r₀",   "Nuclear radius constant",        "1.2 × 10⁻¹⁵ = 1.2 fm", "m"],
        ["1 eV", "In Joules",                     "1.602 × 10⁻¹⁹",  "J"],
        ["1 amu","In kg",                         "1.66 × 10⁻²⁷",   "kg"],
        ["1 Å",  "In metres",                     "10⁻¹⁰",           "m"],
        ["1 nm", "In metres",                     "10⁻⁹",            "m"],
        ["1 pm", "In metres",                     "10⁻¹²",           "m"],
        ["1 fm", "In metres",                     "10⁻¹⁵",           "m"],
    ],
    col_widths=[W*0.12, W*0.38, W*0.30, W*0.20],
    left_cols=[1]
))
story.append(sp(8))

story.append(PageBreak())
story.append(part_header("MASTER FORMULA SHEET — ALL 41 FORMULAS AT A GLANCE"))
story.append(sp(6))
story.append(make_table(
    ["#", "Formula Name", "Expression"],
    [
        ["1",  "Orbital radius",             "r_n = 0.529 × n²/Z  Å"],
        ["2",  "Orbital velocity",           "v_n = 2.18×10⁶ × Z/n  m/s"],
        ["3",  "Total energy",               "E_n = −13.6 × Z²/n²  eV"],
        ["4",  "Kinetic energy",             "KE = −E_n = +13.6 Z²/n²  eV"],
        ["5",  "Potential energy",           "PE = 2E_n = −27.2 Z²/n²  eV"],
        ["6",  "Ionisation energy",          "IE = +13.6 × Z²/n²  eV"],
        ["7",  "Photon energy (transition)", "ΔE = 13.6 Z² (1/n₁² − 1/n₂²)  eV"],
        ["8",  "Rydberg equation",           "1/λ = R_H Z² (1/n₁² − 1/n₂²)"],
        ["9",  "Angular momentum (Bohr)",    "mvr_n = nh/2π  (= nℏ)"],
        ["10", "Time period",                "T_n ∝ n³/Z²"],
        ["11", "Revolution frequency",       "ν_n ∝ Z²/n³"],
        ["12", "Spectral lines count",       "(n₂−n₁)(n₂−n₁+1)/2"],
        ["13", "Wave equation",              "c = νλ"],
        ["14", "Wave number",                "ν̃ = 1/λ  (m⁻¹ or cm⁻¹)"],
        ["15", "Photon energy",              "E = hν = hc/λ = hcν̃"],
        ["16", "Energy shortcut",            "E(eV) = 1240/λ(nm)"],
        ["17", "Photon momentum",            "p = h/λ = E/c"],
        ["18", "Photoelectric equation",     "hν = φ + KE_max"],
        ["19", "Work function",              "φ = hν₀"],
        ["20", "Threshold wavelength",       "λ₀(nm) = 1240/φ(eV)"],
        ["21", "Stopping potential",         "eV₀ = KE_max = hν − φ"],
        ["22", "Photon count (intensity)",   "n = I/hν = Iλ/hc"],
        ["23", "de Broglie (general)",       "λ = h/mv = h/p"],
        ["24", "de Broglie (from KE)",       "λ = h/√(2m·KE)"],
        ["25", "de Broglie (electron, V)",   "λ = 12.27/√V  Å"],
        ["26", "Heisenberg's UP",            "Δx·Δp ≥ h/4π"],
        ["27", "Min. velocity uncertainty",  "Δv_min = h/(4π·m·Δx)"],
        ["28", "Orbital angular momentum",   "L = √[l(l+1)]·ℏ"],
        ["29", "L z-component",              "L_z = m_l·ℏ"],
        ["30", "Spin angular momentum",      "S = (√3/2)·ℏ  [ALL electrons]"],
        ["31", "Orbitals per shell",         "n²"],
        ["32", "Max e⁻ per shell",           "2n²"],
        ["33", "Orbitals per subshell",      "2l+1"],
        ["34", "Max e⁻ per subshell",        "2(2l+1)"],
        ["35", "Radial nodes",               "n − l − 1"],
        ["36", "Angular nodes",              "l"],
        ["37", "Total nodes",                "n − 1"],
        ["38", "Spin multiplicity",          "2S+1  where  S=(unpaired e⁻)/2"],
        ["39", "Magnetic moment",            "μ = √[n(n+2)]  BM"],
        ["40", "Nuclear radius",             "r = r₀ × A^(1/3)"],
        ["41", "e/m ratio (electron)",       "1.758 × 10¹¹  C/kg"],
    ],
    col_widths=[W*0.06, W*0.36, W*0.58],
    left_cols=[1,2]
))
story.append(sp(6))

# footer note
story.append(HRFlowable(width="100%", thickness=1, color=DARK_BLUE))
story.append(sp(4))
story.append(Paragraph(
    "Atomic Structure | Class 11 Chemistry | Complete Chapter | All 41 Formulas | NEET / JEE",
    S("footer", fontSize=8, fontName="Helvetica", textColor=GREY, alignment=TA_CENTER)
))

# ─────────────────────────────────────────────────────────────────
doc.build(story)
print(f"PDF created: {OUTPUT}")
Running Command

cd /tmp/workspace/atomic-structure && python3 generate_pdf.py 2>&1

Running Command

ls -lh /tmp/workspace/atomic-structure/Atomic_Structure_Complete.pdf

Your PDF is ready! Here's your download link:

Atomic Structure Complete

PDF Document · PDF

What's inside (13 Parts, 41 Formulas):
PartTopic
1Discovery of Sub-Atomic Particles (Thomson, Millikan, Goldstein, Chadwick)
2Atomic Models — Thomson, Rutherford, Nuclear terminology, Isotopes/Isobars/Isotones
3Electromagnetic Radiation, Wave equation, EM Spectrum table, VIBGYOR, Planck's Theory
4Atomic Spectra — All 6 spectral series table, Rydberg equation, Spectral lines count
5Bohr's Model — All 9 formulas (radius, velocity, energy, KE, PE, IE, angular momentum, time period)
6Photoelectric Effect — All 5 formulas + work function table, stopping potential
7de Broglie Wave-Particle Duality — 4 formulas + Davisson-Germer experiment
8Heisenberg's Uncertainty Principle — 2 formulas + implications
9Quantum Numbers — All 4 QNs, angular momentum formulas, shell/subshell count formulas
10Shapes of Orbitals — s, p, d, f orbitals
11Nodes — Complete table for all orbitals with NEET traps highlighted
12Electronic Configuration — Aufbau, Hund's, Pauli + Z=1–30 table + Cr/Cu exceptions + Magnetic moment formula
13Constants reference + Master formula sheet (all 41 formulas)

I told you no placeholders or unnecessary squares in the pdf,and give the pdf in black and white theme with some bigger font for better printouts

Running Command

python3 -c " from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont import os # Check for available Unicode fonts paths = [ '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', '/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf', '/usr/share/fonts/truetype/freefont/FreeSans.ttf', '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf', ] for p in paths: print(p, os.path.exists(p)) "

Running Command

python3 -c " import glob, os # find all .ttf fonts fonts = glob.glob('/usr/share/fonts/**/*.ttf', recursive=True) for f in fonts[:30]: print(f) print('---total---', len(fonts)) "

Running Command

# Test NotoSans variable font and DroidSans for Unicode coverage python3 -c " from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont # Try NotoSans variable font try: pdfmetrics.registerFont(TTFont('NotoSans', '/usr/share/fonts/google-noto-vf/NotoSans[wght].ttf')) print('NotoSans variable: OK') except Exception as e: print('NotoSans variable error:', e) # Try DroidSans try: pdfmetrics.registerFont(TTFont('DroidSans', '/usr/share/fonts/google-droid-sans-fonts/DroidSans.ttf')) pdfmetrics.registerFont(TTFont('DroidSans-Bold', '/usr/share/fonts/google-droid-sans-fonts/DroidSans-Bold.ttf')) print('DroidSans: OK') except Exception as e: print('DroidSans error:', e) "

Running Command

# Test which special chars render correctly with DroidSans python3 -c " from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont from reportlab.platypus import SimpleDocTemplate, Paragraph from reportlab.lib.styles import ParagraphStyle from reportlab.lib.pagesizes import A4 from reportlab.lib import colors pdfmetrics.registerFont(TTFont('DroidSans', '/usr/share/fonts/google-droid-sans-fonts/DroidSans.ttf')) pdfmetrics.registerFont(TTFont('DroidSans-Bold', '/usr/share/fonts/google-droid-sans-fonts/DroidSans-Bold.ttf')) doc = SimpleDocTemplate('/tmp/workspace/atomic-structure/test_chars.pdf', pagesize=A4) style = ParagraphStyle('t', fontName='DroidSans', fontSize=12, leading=18) # Test all special chars we need test_text = [ 'Superscripts: n\u00b9 n\u00b2 n\u00b3 Z\u2074', 'Subscripts: H\u2080 H\u2082 n\u2081 n\u2082', 'Greek: nu \u03bd lambda \u03bb phi \u03c6 Delta \u0394 pi \u03c0 alpha \u03b1 beta \u03b2', 'Arrows: \u2192 \u2190 \u2191 \u2193 \u2264 \u2265', 'Math: \u221a \u00d7 \u00f7 \u2212 \u00b1 \u221e \u2248 \u2260 \u221d', 'Special: \u212b (Angstrom) \u0127 (hbar) \u03c1', 'Chemistry: e\u207b He\u207a Li\u00b2\u207a n\u2081 n\u2082', ] story = [Paragraph(t, style) for t in test_text] doc.build(story) print('Test PDF created OK') "

Writing File

~/atomic-structure/generate_bw.py

"""
Atomic Structure - Complete Chapter PDF
Black & White theme, Unicode fonts (DroidSans), print-optimised large fonts
No colour fills, no placeholder squares
"""
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.units import cm
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, PageBreak, KeepTogether
)

# ── Register Unicode fonts ─────────────────────────────────────────
pdfmetrics.registerFont(TTFont('DS',  '/usr/share/fonts/google-droid-sans-fonts/DroidSans.ttf'))
pdfmetrics.registerFont(TTFont('DSB', '/usr/share/fonts/google-droid-sans-fonts/DroidSans-Bold.ttf'))

OUTPUT = "/tmp/workspace/atomic-structure/Atomic_Structure_BW.pdf"

doc = SimpleDocTemplate(
    OUTPUT,
    pagesize=A4,
    rightMargin=2.0*cm,
    leftMargin=2.0*cm,
    topMargin=2.2*cm,
    bottomMargin=2.2*cm,
)
W = A4[0] - 4.0*cm   # usable width

# ── Unicode constants (no rendering squares) ───────────────────────
SUP = {
    '0':'\u2070','1':'\u00b9','2':'\u00b2','3':'\u00b3',
    '4':'\u2074','5':'\u2075','6':'\u2076','7':'\u2077',
    '8':'\u2078','9':'\u2079','+':'\u207a','-':'\u207b',
    'n':'\u207f',
}
SUB = {
    '0':'\u2080','1':'\u2081','2':'\u2082','3':'\u2083',
    '4':'\u2084','5':'\u2085','6':'\u2086','7':'\u2087',
    '8':'\u2088','9':'\u2089',
}

NU   = '\u03bd'        # nu
LAM  = '\u03bb'        # lambda
PHI  = '\u03c6'        # phi
DEL  = '\u0394'        # Delta
PI   = '\u03c0'        # pi
SQRT = '\u221a'        # sqrt
INF  = '\u221e'        # infinity
PROP = '\u221d'        # proportional
HBAR = '\u0127'        # hbar (h-bar)
ANG  = '\u212b'        # Angstrom symbol
MU   = '\u03bc'        # mu
TILD = '\u0303'        # combining tilde (for nu-tilde)
GEQ  = '\u2265'        # >=
LEQ  = '\u2264'        # <=
ARR  = '\u2192'        # ->
PM   = '\u00b1'        # plus-minus
TIMES= '\u00d7'        # times
CDOT = '\u00b7'        # middle dot
NEQL = '\u2260'        # not equal
UP   = '\u2191'        # up arrow
DOWN = '\u2193'        # down arrow
STAR = '\u2605'        # black star
WARN = '\u26a0'        # warning sign
CHECK= '\u2713'        # checkmark
BULL = '\u2022'        # bullet

# Shorthand for common chemical notation (pure ASCII-safe Unicode)
def sup(s): return ''.join(SUP.get(c, c) for c in str(s))
def sub(s): return ''.join(SUB.get(c, c) for c in str(s))

# Frequently used fragments
N1 = 'n' + sub('1')
N2 = 'n' + sub('2')
NU_TILDE = NU + '\u0304'   # nu with macron as wave-number symbol

# ── Styles ─────────────────────────────────────────────────────────
BLACK = colors.black
WHITE = colors.white
LT_GREY = colors.HexColor("#d0d0d0")
MID_GREY = colors.HexColor("#808080")
DARK_GREY = colors.HexColor("#303030")

def S(name, **kw):
    return ParagraphStyle(name, **kw)

sTitle = S("sTitle",
    fontName="DSB", fontSize=22, textColor=WHITE,
    alignment=TA_CENTER, spaceAfter=4, spaceBefore=4, leading=28)

sSubtitle = S("sSubtitle",
    fontName="DS", fontSize=11, textColor=WHITE,
    alignment=TA_CENTER, spaceAfter=2, leading=16)

sPart = S("sPart",
    fontName="DSB", fontSize=14, textColor=WHITE,
    alignment=TA_LEFT, spaceAfter=4, spaceBefore=6,
    leftIndent=6, leading=20)

sSec = S("sSec",
    fontName="DSB", fontSize=13, textColor=BLACK,
    alignment=TA_LEFT, spaceAfter=4, spaceBefore=8, leading=18)

sSub = S("sSub",
    fontName="DSB", fontSize=11.5, textColor=BLACK,
    alignment=TA_LEFT, spaceAfter=3, spaceBefore=5, leading=16)

sBody = S("sBody",
    fontName="DS", fontSize=11, textColor=BLACK,
    alignment=TA_JUSTIFY, spaceAfter=4, leading=16)

sBullet = S("sBullet",
    fontName="DS", fontSize=11, textColor=BLACK,
    alignment=TA_LEFT, spaceAfter=3, leading=15,
    leftIndent=16, bulletIndent=4)

sFormLabel = S("sFormLabel",
    fontName="DSB", fontSize=11, textColor=BLACK,
    alignment=TA_LEFT, spaceAfter=2, spaceBefore=2, leading=15)

sFormula = S("sFormula",
    fontName="DSB", fontSize=12.5, textColor=BLACK,
    alignment=TA_CENTER, spaceAfter=3, spaceBefore=3, leading=19)

sFormNote = S("sFormNote",
    fontName="DS", fontSize=10.5, textColor=DARK_GREY,
    alignment=TA_LEFT, spaceAfter=2, leading=15, leftIndent=4)

sTrap = S("sTrap",
    fontName="DSB", fontSize=11, textColor=BLACK,
    alignment=TA_LEFT, spaceAfter=2, leading=15)

sNote = S("sNote",
    fontName="DS", fontSize=11, textColor=BLACK,
    alignment=TA_LEFT, spaceAfter=2, leading=15)

sTableH = S("sTableH",
    fontName="DSB", fontSize=10, textColor=WHITE,
    alignment=TA_CENTER, leading=13)

sTableC = S("sTableC",
    fontName="DS", fontSize=10, textColor=BLACK,
    alignment=TA_CENTER, leading=13)

sTableL = S("sTableL",
    fontName="DS", fontSize=10, textColor=BLACK,
    alignment=TA_LEFT, leading=13)

sFooter = S("sFooter",
    fontName="DS", fontSize=9, textColor=MID_GREY,
    alignment=TA_CENTER, leading=12)

# ── Helper flowables ───────────────────────────────────────────────

def sp(h=5):
    return Spacer(1, h)

def hr(thick=0.8):
    return HRFlowable(width="100%", thickness=thick, color=BLACK)

def thinhr():
    return HRFlowable(width="100%", thickness=0.4, color=MID_GREY)

def body(text):
    return Paragraph(text, sBody)

def blt(text):
    return Paragraph(BULL + "  " + text, sBullet)

def part_header(text):
    data = [[Paragraph(text, sPart)]]
    t = Table(data, colWidths=[W])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), BLACK),
        ("ROWPADDING", (0,0), (-1,-1), 8),
    ]))
    return t

def sec(text):
    return Paragraph(text, sSec)

def sub_h(text):
    return Paragraph(text, sSub)

def formula_box(main_text, notes=None):
    """Box with thick border, no fill (white bg), formula centered, notes left."""
    elems = [Paragraph(main_text, sFormula)]
    if notes:
        elems.append(HRFlowable(width="100%", thickness=0.5, color=LT_GREY))
        for n in notes:
            elems.append(Paragraph(n, sFormNote))
    rows = [[e] for e in elems]
    inner = Table(rows, colWidths=[W - 24])
    inner.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), WHITE),
        ("ROWPADDING", (0,0), (-1,-1), 5),
    ]))
    outer = Table([[inner]], colWidths=[W])
    outer.setStyle(TableStyle([
        ("BOX", (0,0), (-1,-1), 2, BLACK),
        ("ROWPADDING", (0,0), (-1,-1), 6),
        ("BACKGROUND", (0,0), (-1,-1), WHITE),
    ]))
    return outer

def trap_box(text):
    t = Table([[Paragraph(WARN + "  NEET TRAP: " + text, sTrap)]], colWidths=[W])
    t.setStyle(TableStyle([
        ("BOX",        (0,0), (-1,-1), 1.5, BLACK),
        ("ROWPADDING", (0,0), (-1,-1), 6),
        ("BACKGROUND", (0,0), (-1,-1), colors.HexColor("#f0f0f0")),
    ]))
    return t

def note_box(text):
    t = Table([[Paragraph(STAR + "  NOTE: " + text, sNote)]], colWidths=[W])
    t.setStyle(TableStyle([
        ("BOX",        (0,0), (-1,-1), 1, BLACK),
        ("ROWPADDING", (0,0), (-1,-1), 6),
        ("BACKGROUND", (0,0), (-1,-1), WHITE),
    ]))
    return t

def make_table(headers, rows, col_widths=None, left_cols=None):
    if col_widths is None:
        col_widths = [W / len(headers)] * len(headers)
    left_cols = left_cols or []
    head_row = [Paragraph(h, sTableH) for h in headers]
    data = [head_row]
    for row in rows:
        styled = []
        for j, cell in enumerate(row):
            st = sTableL if j in left_cols else sTableC
            styled.append(Paragraph(str(cell), st))
        data.append(styled)
    t = Table(data, colWidths=col_widths)
    style_cmds = [
        ("BACKGROUND",  (0,0), (-1,0), BLACK),
        ("ROWPADDING",  (0,0), (-1,-1), 5),
        ("GRID",        (0,0), (-1,-1), 0.5, MID_GREY),
        ("VALIGN",      (0,0), (-1,-1), "MIDDLE"),
        ("LINEBELOW",   (0,0), (-1,0),  1.0, BLACK),
    ]
    for i in range(1, len(data)):
        if i % 2 == 0:
            style_cmds.append(("BACKGROUND", (0,i), (-1,i), colors.HexColor("#f4f4f4")))
    t.setStyle(TableStyle(style_cmds))
    return t

# ══════════════════════════════════════════════════════════════════
# STORY
# ══════════════════════════════════════════════════════════════════
story = []

# ── COVER ─────────────────────────────────────────────────────────
cover_title = Table([[Paragraph("ATOMIC STRUCTURE", sTitle)]], colWidths=[W])
cover_title.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,-1), BLACK),
    ("ROWPADDING", (0,0), (-1,-1), 20),
]))
cover_sub = Table([[Paragraph(
    "Complete Chapter  |  All 41 Formulas  |  All Topics  |  Class 11 Chemistry  |  NEET / JEE",
    sSubtitle)]], colWidths=[W])
cover_sub.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,-1), DARK_GREY),
    ("ROWPADDING", (0,0), (-1,-1), 10),
]))
story += [cover_title, cover_sub, sp(12)]

# ══════════════════════════════════════════════════════════════════
# PART 1 — DISCOVERY OF SUB-ATOMIC PARTICLES
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 1 — DISCOVERY OF SUB-ATOMIC PARTICLES"), sp(8)]

story += [sec("1.1  Cathode Ray Experiment  (J.J. Thomson, 1897)")]
story += [body("High-voltage discharge tube (evacuated glass tube with two metal electrodes). "
               "Rays travel from cathode (-) to anode (+). These are cathode rays.")]
story += [sp(3), sub_h("Observations:")]
for b in [
    "Travel in straight lines; cast sharp shadows",
    "Deflect towards the positive plate  " + ARR + "  carry NEGATIVE charge",
    "Cause fluorescence on a ZnS screen",
    "Same e/m ratio regardless of cathode material or gas used  " + ARR + "  electrons are universal",
    "Discovery: the ELECTRON",
]:
    story.append(blt(b))
story += [sp(5),
    formula_box(
        "e / m" + sub('e') + "  =  1.758 " + TIMES + " 10" + sup('11') + "  C/kg",
        [
            "e = charge of electron = 1.602 " + TIMES + " 10" + sup('-19') + " C",
            "m" + sub('e') + " = mass of electron = 9.109 " + TIMES + " 10" + sup('-31') + " kg",
            "Ratio is CONSTANT for cathode rays in ALL gases (same electron everywhere in matter).",
            "Canal rays (positive ions) have VARYING e/m  --  different gases give different ion masses.",
        ]
    ), sp(6)]

story += [sec("1.2  Millikan's Oil Drop Experiment  (1909)")]
story += [body("Balanced gravitational and electric forces on charged oil droplets to measure electron charge."),
    formula_box(
        "e  =  1.602 " + TIMES + " 10" + sup('-19') + "  C",
        ["From e and e/m:   m" + sub('e') + " = e / (e/m) = 9.109 " + TIMES + " 10" + sup('-31') + " kg"]
    ), sp(6)]

story += [sec("1.3  Canal Ray Experiment  (Goldstein, 1886)")]
story += [body("Cathode with holes (perforations). Rays travel BEHIND the cathode -- opposite to cathode rays. "
               "Discovery: PROTON (positively charged particle). "
               "The lightest positive ion from hydrogen gas is the proton (H" + sup('+') + ")."), sp(6)]

story += [sec("1.4  Discovery of Neutron  (Chadwick, 1932)")]
story += [body("Bombarded beryllium with " + "\u03b1" + "-particles  " + ARR + "  neutral radiation identified as NEUTRONS."),
    formula_box("Neutron charge = 0     m" + sub('n') + " = 1.675 " + TIMES + " 10" + sup('-27') + " kg"),
    sp(6)]

story += [sec("1.5  Sub-Atomic Particles -- Summary")]
story.append(make_table(
    ["Particle","Symbol","Charge","Mass (kg)","Mass (amu)","Discoverer"],
    [
        ["Electron","e" + sup('-'),"-1.602" + TIMES + "10" + sup('-19') + " C","9.109" + TIMES + "10" + sup('-31'),"0.000549","Thomson (1897)"],
        ["Proton",  "p" + sup('+'),"+1.602" + TIMES + "10" + sup('-19') + " C","1.673" + TIMES + "10" + sup('-27'),"1.00728", "Goldstein / Rutherford"],
        ["Neutron", "n","0","1.675" + TIMES + "10" + sup('-27'),"1.00867","Chadwick (1932)"],
    ],
    col_widths=[W*0.13,W*0.10,W*0.23,W*0.20,W*0.13,W*0.21],
    left_cols=[5]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 2 — ATOMIC MODELS
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 2 — ATOMIC MODELS"), sp(8)]

story += [sec("2.1  Thomson's Plum Pudding Model (1904)")]
for b in [
    "Atom = uniform positive sphere with electrons embedded like plums in a pudding",
    "Radius ~ 10" + sup('-10') + " m",
    "FAILED -- could not explain Rutherford's alpha-scattering results",
]:
    story.append(blt(b))
story.append(sp(6))

story += [sec("2.2  Rutherford's Nuclear Model (1911)")]
story += [sub_h("Alpha-Particle Scattering Observations:")]
for b in [
    "Most " + "\u03b1" + "-particles passed straight through  " + ARR + "  atom is MOSTLY EMPTY SPACE",
    "Few deflected at small angles  " + ARR + "  small positive charge inside",
    "Very few (1 in 20,000) bounced back at 180 degrees  " + ARR + "  small, dense, positive NUCLEUS",
]:
    story.append(blt(b))
story += [sp(4),
    formula_box(
        "Nuclear Radius:   r  =  r" + sub('0') + "  " + TIMES + "  A" + sup('1/3'),
        [
            "r" + sub('0') + " = nuclear radius constant = 1.2 " + TIMES + " 10" + sup('-15') + " m  =  1.2 fm",
            "A = mass number (total protons + neutrons)",
            "Nuclear density " + "\u2248" + " 10" + sup('17') + " kg/m" + sup('3') + "  -- CONSTANT for ALL nuclei (independent of A and Z)",
            "1 cm" + sup('3') + " of nuclear matter would weigh ~100 million tonnes!",
        ]
    ), sp(4)]
story += [sub_h("Limitations:")]
for b in [
    "STABILITY: Accelerating electron should continuously radiate energy " + ARR + " spiral into nucleus " + ARR + " collapse in ~10" + sup('-8') + " s  (but atoms ARE stable!)",
    "LINE SPECTRA: A spiralling electron would emit a CONTINUOUS spectrum, not discrete line spectra",
]:
    story.append(blt(b))
story.append(sp(6))

story += [sec("2.3  Nuclear Terminology")]
story.append(formula_box(
    "Mass Number:   A  =  Z  +  N",
    [
        "A = total nucleons (protons + neutrons)",
        "Z = atomic number = number of protons = electrons in neutral atom",
        "N = neutron number = A - Z",
    ]
))
story.append(sp(4))
story.append(make_table(
    ["Term","Same","Different","Example"],
    [
        ["Isotopes","Z (same element)","A, N","" + sup('1') + "H,  " + sup('2') + "H (D),  " + sup('3') + "H (T)  -- all Z=1"],
        ["Isobars","A","Z, N","" + sup('14') + sub('6') + "C  and  " + sup('14') + sub('7') + "N"],
        ["Isotones","N","Z, A","" + sup('3') + sub('1') + "H  and  " + sup('4') + sub('2') + "He  (both N=2)"],
        ["Isoelectronic","Total electrons","--","Na" + sup('+') + ", Mg" + sup('2+') + ", Ne, F" + sup('-') + "  (all 10 e" + sup('-') + ")"],
    ],
    col_widths=[W*0.18,W*0.22,W*0.18,W*0.42],
    left_cols=[0,1,2,3]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 3 — EM RADIATION
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 3 — ELECTROMAGNETIC RADIATION & PLANCK'S THEORY"), sp(8)]

story += [sec("3.1  Wave Theory of Light")]
for b in [
    "Light = electromagnetic wave  --  oscillating electric and magnetic fields perpendicular to each other and to direction of travel",
    "All EM waves travel at c = 3 " + TIMES + " 10" + sup('8') + " m/s in vacuum (all frequencies, always)",
    "Do NOT require a medium to propagate",
]:
    story.append(blt(b))
story.append(sp(5))
story.append(formula_box(
    "Wave Equation:   c  =  " + NU + "  " + TIMES + "  " + LAM,
    [
        "c = speed of light = 3 " + TIMES + " 10" + sup('8') + " m/s",
        NU + " (nu) = frequency  in Hz (= s" + sup('-1') + ")",
        LAM + " (lambda) = wavelength  in metres",
        NU + " and " + LAM + " are INVERSELY proportional at constant c",
        "Conversions:  1 nm = 10" + sup('-9') + " m  |  1 " + ANG + " = 10" + sup('-10') + " m  |  1 pm = 10" + sup('-12') + " m",
    ]
))
story.append(sp(4))
story.append(formula_box(
    "Wave Number:   " + NU + "\u0303  =  1 / " + LAM,
    [
        "Units: m" + sup('-1') + " or cm" + sup('-1') + "  (mainly used in IR spectroscopy in cm" + sup('-1') + ")",
        "CORRECT: wave number = 1/" + LAM + "   |   WRONG: " + NU + "/c  (common mistake)",
    ]
))
story.append(sp(6))

story += [sec("3.2  Electromagnetic Spectrum")]
story.append(make_table(
    ["Region","Wavelength Range","Frequency Range","Notes"],
    [
        ["Gamma rays","< 0.01 nm","> 3" + TIMES + "10" + sup('19') + " Hz","Nuclear reactions"],
        ["X-rays","0.01 - 10 nm","3" + TIMES + "10" + sup('17') + " - 3" + TIMES + "10" + sup('19') + " Hz","Medical imaging"],
        ["UV","10 - 400 nm","7.5" + TIMES + "10" + sup('14') + " - 3" + TIMES + "10" + sup('17') + " Hz","Lyman series of H"],
        ["Visible","400 - 700 nm","4.3" + TIMES + "10" + sup('14') + " - 7.5" + TIMES + "10" + sup('14') + " Hz","Balmer series of H"],
        ["Infrared","700 nm - 1 mm","3" + TIMES + "10" + sup('11') + " - 4.3" + TIMES + "10" + sup('14') + " Hz","Paschen, Brackett series"],
        ["Microwaves","1 mm - 10 cm","3" + TIMES + "10" + sup('9') + " - 3" + TIMES + "10" + sup('11') + " Hz","Radar, cooking"],
        ["Radio waves","> 10 cm","< 3" + TIMES + "10" + sup('9') + " Hz","Broadcasting"],
    ],
    col_widths=[W*0.16,W*0.24,W*0.33,W*0.27],
    left_cols=[3]
))
story.append(sp(4))
story += [sub_h("Visible Light -- VIBGYOR (increasing wavelength, decreasing energy):")]
story.append(make_table(
    ["Colour","Wavelength","Energy"],
    [
        ["Violet","400 nm","3.10 eV"],
        ["Indigo","425 nm","2.92 eV"],
        ["Blue",  "450 nm","2.76 eV"],
        ["Green", "520 nm","2.38 eV"],
        ["Yellow","580 nm","2.14 eV"],
        ["Orange","620 nm","2.00 eV"],
        ["Red",   "700 nm","1.77 eV"],
    ],
    col_widths=[W*0.33,W*0.33,W*0.34]
))
story.append(sp(6))

story += [sec("3.3  Planck's Quantum Theory (1900)")]
for b in [
    "Energy is NOT continuous -- emitted/absorbed in discrete packets called QUANTA (photons)",
    "Energy of each quantum is directly proportional to frequency",
]:
    story.append(blt(b))
story.append(sp(4))
story.append(formula_box(
    "Energy of ONE Photon:   E  =  h" + NU + "  =  hc/" + LAM + "  =  hc" + NU + "\u0303",
    [
        "h = Planck's constant = 6.626 " + TIMES + " 10" + sup('-34') + " J" + CDOT + "s",
        NU + " = frequency (Hz)   |   " + LAM + " = wavelength (m)   |   " + NU + "\u0303 = wave number (m" + sup('-1') + ")",
        "E = energy of ONE photon  (in Joules or eV)   |   For n photons:  E_total = n " + TIMES + " h" + NU,
        "Conversion:  1 eV = 1.602 " + TIMES + " 10" + sup('-19') + " J",
    ]
))
story.append(sp(4))
story.append(formula_box(
    "Energy-Wavelength Shortcut:   E(eV)  =  1240 / " + LAM + "(nm)",
    [
        LAM + " MUST be in nanometres.  Answer in eV.",
        "For Angstroms:  E(eV) = 12400 / " + LAM + "(" + ANG + ")",
        "Derived from:  hc = 1.988" + TIMES + "10" + sup('-25') + " J" + CDOT + "m = 1240 eV" + CDOT + "nm",
        "Examples:  400 nm " + ARR + " 3.1 eV  |  500 nm " + ARR + " 2.48 eV  |  700 nm " + ARR + " 1.77 eV",
    ]
))
story.append(sp(4))
story.append(formula_box(
    "Photon Momentum:   p  =  h/" + LAM + "  =  E/c",
    ["Photon has ZERO rest mass but has energy E=h" + NU + " and momentum p=h/" + LAM]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 4 — ATOMIC SPECTRA
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 4 — ATOMIC SPECTRA & SPECTRAL SERIES"), sp(8)]

story += [sec("4.1  Line Spectrum of Hydrogen")]
story += [body("When hydrogen gas is excited (high voltage), it emits light of only certain wavelengths -- "
               "a LINE SPECTRUM (not continuous). Each spectral line corresponds to one specific electron transition.")]
story.append(sp(6))

story += [sec("4.2  Spectral Series of Hydrogen")]
story.append(make_table(
    ["Series","n" + sub('1') + " (lower)","n" + sub('2') + " (upper)","Region","First Line (" + LAM + ")"],
    [
        ["Lyman",     "1","2, 3, 4...","UV","121.6 nm"],
        ["Balmer",    "2","3, 4, 5...","Visible + UV","656.3 nm  (H" + "\u03b1" + ",  red)"],
        ["Paschen",   "3","4, 5, 6...","Near IR","1875 nm"],
        ["Brackett",  "4","5, 6, 7...","IR","4051 nm"],
        ["Pfund",     "5","6, 7, 8...","Far IR","7459 nm"],
        ["Humphreys", "6","7, 8, 9...","Far IR","--"],
    ],
    col_widths=[W*0.17,W*0.15,W*0.17,W*0.18,W*0.33]
))
story.append(sp(5))
story.append(formula_box(
    "Rydberg Equation:   1/" + LAM + "  =  R" + sub('H') + "  " + TIMES + "  Z" + sup('2') + "  " + TIMES + "  ( 1/n" + sub('1') + sup('2') + "  -  1/n" + sub('2') + sup('2') + " )",
    [
        "R" + sub('H') + " = Rydberg constant = 1.097 " + TIMES + " 10" + sup('7') + " m" + sup('-1') + " = 109677 cm" + sup('-1'),
        "Z = atomic number  (Z=1 for H,  Z=2 for He" + sup('+') + ",  Z=3 for Li" + sup('2+') + ")",
        "n" + sub('1') + " = lower orbit  (determines the SERIES)   |   n" + sub('2') + " = upper orbit  (n" + sub('2') + " > n" + sub('1') + ")",
        "First line of each series (longest " + LAM + "):   n" + sub('2') + " = n" + sub('1') + " + 1",
        "Series limit (shortest " + LAM + "):   n" + sub('2') + " = " + INF + "   " + ARR + "   1/" + LAM + " = R" + sub('H') + " " + TIMES + " Z" + sup('2') + " / n" + sub('1') + sup('2'),
    ]
))
story.append(sp(4))
story.append(formula_box(
    "Number of Spectral Lines  (n" + sub('2') + " " + ARR + " n" + sub('1') + "):   Lines  =  (n" + sub('2') + " - n" + sub('1') + ") (n" + sub('2') + " - n" + sub('1') + " + 1) / 2",
    [
        "Special case -- electron falls to GROUND STATE (n" + sub('1') + " = 1):   Lines = n(n-1)/2",
        "Example 1:  n=5 " + ARR + " n=1:   5" + TIMES + "4/2 = 10 lines",
        "Example 2:  n=5 " + ARR + " n=3:   (2)(3)/2 = 3 lines",
        "Example 3:  n=4 " + ARR + " n=2:   (2)(3)/2 = 3 lines",
    ]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 5 — BOHR'S MODEL
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 5 — BOHR'S ATOMIC MODEL (1913)"), sp(8)]

story += [sec("5.1  Postulates of Bohr's Model")]
for b in [
    "Electrons revolve in fixed circular orbits called STATIONARY STATES (no energy radiated while in orbit)",
    "Only orbits where angular momentum = whole-number multiple of h/2" + PI + " are allowed",
    "Energy absorbed or emitted ONLY when electron jumps between orbits",
    "Energy of emitted/absorbed photon = difference in orbital energies:   " + DEL + "E = h" + NU,
]:
    story.append(blt(b))
story.append(sp(8))

story += [sec("5.2  Bohr Model Formulas  (H and H-like ions: He" + sup('+') + ", Li" + sup('2+') + ", Be" + sup('3+') + "...)")]

story.append(sub_h("Formula 1 -- Orbital Radius"))
story.append(formula_box(
    "r" + sub('n') + "  =  0.529  " + TIMES + "  n" + sup('2') + " / Z     " + ANG,
    [
        "r" + sub('n') + " = radius of nth orbit  (distance of electron from nucleus)",
        "0.529 " + ANG + " = a" + sub('0') + " = Bohr radius = radius of 1st orbit of H  (n=1, Z=1)  -- fixed constant",
        "n = principal quantum number = 1, 2, 3, 4...   |   Z = atomic number",
        "Rule: r" + sub('n') + " " + PROP + " n" + sup('2') + "  (quadruples when n doubles)   |   r" + sub('n') + " " + PROP + " 1/Z",
        "Convert to metres: multiply by 10" + sup('-10') + "   (1 " + ANG + " = 10" + sup('-10') + " m)",
    ]
))
story.append(sp(4))
story.append(make_table(
    ["n","H  (Z=1)","He" + sup('+') + "  (Z=2)","Li" + sup('2+') + "  (Z=3)"],
    [
        ["1","0.529 " + ANG,"0.265 " + ANG,"0.176 " + ANG],
        ["2","2.116 " + ANG,"1.058 " + ANG,"0.705 " + ANG],
        ["3","4.761 " + ANG,"2.381 " + ANG,"1.587 " + ANG],
    ],
    col_widths=[W*0.15,W*0.28,W*0.28,W*0.29]
))
story.append(sp(6))

story.append(sub_h("Formula 2 -- Orbital Velocity"))
story.append(formula_box(
    "v" + sub('n') + "  =  2.18 " + TIMES + " 10" + sup('6') + "  " + TIMES + "  Z / n     m/s",
    [
        "2.18 " + TIMES + " 10" + sup('6') + " m/s = velocity in 1st orbit of H  =  c/137  (much less than light speed)",
        "v" + sub('n') + " " + PROP + " Z/n   |   Outer electrons move SLOWER  (easier to remove)",
        "H (n=1): 2.18" + TIMES + "10" + sup('6') + " m/s   |   H (n=2): 1.09" + TIMES + "10" + sup('6') + " m/s   |   H (n=3): 0.727" + TIMES + "10" + sup('6') + " m/s",
    ]
))
story.append(sp(6))

story.append(sub_h("Formula 3 -- Total Energy"))
story.append(formula_box(
    "E" + sub('n') + "  =  -13.6  " + TIMES + "  Z" + sup('2') + " / n" + sup('2') + "     eV     =     -2.18" + TIMES + "10" + sup('-18') + "  " + TIMES + "  Z" + sup('2') + "/n" + sup('2') + "     J",
    [
        "E" + sub('n') + " = total energy of electron in nth orbit -- ALWAYS NEGATIVE for a bound electron",
        "-13.6 eV = energy in 1st orbit of H  (ground state)  =  ionisation energy of H",
        "Negative sign: electron is BOUND to nucleus",
        "n=1 is most negative = most stable   |   At n=" + INF + ":  E=0  (electron free = atom ionised)",
    ]
))
story.append(sp(4))
story.append(make_table(
    ["n","Energy of H (Z=1)"],
    [
        ["1  (ground state)","-13.6 eV"],
        ["2","-3.4 eV"],
        ["3","-1.51 eV"],
        ["4","-0.85 eV"],
        ["5","-0.544 eV"],
        ["" + INF + "  (ionised)","0 eV"],
    ],
    col_widths=[W*0.40,W*0.60]
))
story.append(sp(6))

story.append(sub_h("Formula 4 -- Kinetic Energy, Potential Energy, Total Energy"))
story.append(formula_box(
    "KE  =  -E" + sub('n') + "  =  +13.6 Z" + sup('2') + "/n" + sup('2') + "  eV   (always positive)\n"
    "PE  =  2E" + sub('n') + "  =  -27.2 Z" + sup('2') + "/n" + sup('2') + "  eV   (always negative)\n"
    "TE  =  KE + PE  =  E" + sub('n'),
    [
        "Three golden relationships:   KE = -TE    |    PE = 2" + TIMES + "TE    |    PE = -2" + TIMES + "KE",
        "Example H (n=1):  KE=+13.6 eV,  PE=-27.2 eV,  TE=-13.6 eV     " + CHECK + "  (13.6-27.2 = -13.6)",
    ]
))
story.append(sp(6))

story.append(sub_h("Formula 5 -- Ionisation Energy"))
story.append(formula_box(
    "IE  =  0 - E" + sub('n') + "  =  +13.6  " + TIMES + "  Z" + sup('2') + " / n" + sup('2') + "     eV",
    [
        "IE = energy to remove electron from orbit n to infinity (n " + ARR + " " + INF + ")",
        "IE is ALWAYS positive   |   Decreases as n increases (easier to remove from outer orbits)",
        "H (n=1): 13.6 eV = 1312 kJ/mol   |   He" + sup('+') + " (Z=2): 54.4 eV   |   Li" + sup('2+') + " (Z=3): 122.4 eV",
    ]
))
story.append(sp(6))

story.append(sub_h("Formula 6 -- Energy of Emitted/Absorbed Photon"))
story.append(formula_box(
    DEL + "E  =  13.6  " + TIMES + "  Z" + sup('2') + "  " + TIMES + "  ( 1/n" + sub('1') + sup('2') + "  -  1/n" + sub('2') + sup('2') + " )     eV",
    [
        "n" + sub('1') + " = lower orbit  (electron LANDS here after emission)   |   n" + sub('2') + " = upper orbit  (electron starts here)",
        "n" + sub('1') + " < n" + sub('2') + " always   |   " + DEL + "E is always POSITIVE",
        "EMISSION: electron falls n" + sub('2') + " " + ARR + " n" + sub('1') + ",  photon released",
        "ABSORPTION: electron jumps n" + sub('1') + " " + ARR + " n" + sub('2') + ",  photon absorbed",
    ]
))
story.append(sp(6))

story.append(sub_h("Formula 7 -- Angular Momentum Quantisation (Bohr's Condition)"))
story.append(formula_box(
    "mv" + sub('n') + "r" + sub('n') + "  =  nh / 2" + PI + "  =  n" + HBAR,
    [
        "m = mass of electron (kg)   |   v = orbital velocity (m/s)   |   r" + sub('n') + " = orbital radius (m)",
        "n = principal quantum number   |   h = Planck's constant = 6.626" + TIMES + "10" + sup('-34') + " J" + CDOT + "s",
        HBAR + " = h/2" + PI + " = 1.055" + TIMES + "10" + sup('-34') + " J" + CDOT + "s  (reduced Planck constant)",
    ]
))
story.append(sp(6))

story.append(sub_h("Formulas 8 & 9 -- Time Period and Frequency of Revolution"))
story.append(formula_box(
    "T" + sub('n') + "  =  2" + PI + "r" + sub('n') + "/v" + sub('n') + "     " + PROP + "     n" + sup('3') + "/Z" + sup('2') + "\n"
    NU + sub('n') + "  =  v" + sub('n') + " / (2" + PI + "r" + sub('n') + ")     " + PROP + "     Z" + sup('2') + "/n" + sup('3'),
    [
        "T" + sub('n') + " " + PROP + " n" + sup('3') + "  --  if n doubles, T becomes 2" + sup('3') + " = 8 times longer",
        NU + sub('n') + " " + PROP + " 1/n" + sup('3') + "  --  outer electrons orbit LESS frequently",
    ]
))
story.append(sp(4))
story.append(trap_box("If n doubles, time period increases by 2" + sup('3') + " = 8 times."))
story.append(sp(6))

story += [sec("5.3  Limitations of Bohr's Model")]
for b in [
    "Works ONLY for hydrogen and H-like ions (one electron). Fails for multi-electron atoms.",
    "Cannot explain fine structure of spectral lines (Zeeman and Stark effects).",
    "Cannot explain relative intensities of spectral lines.",
    "Violates Heisenberg's Uncertainty Principle -- definite orbits imply exact position AND momentum simultaneously.",
    "Does not explain chemical bonding or 3D shapes of molecules.",
]:
    story.append(blt(b))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 6 — PHOTOELECTRIC EFFECT
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 6 — PHOTOELECTRIC EFFECT"), sp(8)]

story += [sec("6.1  Phenomenon & Why Wave Theory Fails")]
story += [body("When light of sufficient frequency falls on a metal surface, electrons are ejected (photoelectrons). "
               "Einstein explained this in 1905 using Planck's quantum theory  (Nobel Prize 1921).")]
story += [sp(3), sub_h("Observations that Wave Theory CANNOT explain:")]
for b in [
    "Emission is INSTANTANEOUS  (wave theory predicts time delay for energy buildup)",
    "Below threshold frequency " + NU + sub('0') + ":  NO emission regardless of intensity",
    "Above " + NU + sub('0') + ":  KE_max depends on FREQUENCY, NOT intensity",
    "More intensity " + ARR + " MORE electrons emitted  (not faster electrons)",
]:
    story.append(blt(b))
story.append(sp(5))

story.append(sub_h("Formula 10 -- Einstein's Photoelectric Equation"))
story.append(formula_box(
    "h" + NU + "  =  " + PHI + "  +  KE_max",
    [
        "h" + NU + " = energy of incident photon (J or eV)",
        PHI + " (phi) = work function = minimum energy to eject ONE electron = h" + NU + sub('0') + "  (J or eV)",
        "KE_max = maximum KE of ejected photoelectron = (1/2)m" + sub('e') + "v" + sup('2') + "_max  (J or eV)",
        "If h" + NU + " < " + PHI + ":  NO emission at all (even at very high intensity)",
        "If h" + NU + " = " + PHI + ":  Emission just starts,  KE_max = 0",
    ]
))
story.append(sp(5))

story.append(sub_h("Formula 11 -- Work Function"))
story.append(formula_box(
    PHI + "  =  h" + NU + sub('0'),
    [
        PHI + " = work function -- a fixed property of the metal surface",
        NU + sub('0') + " = threshold frequency -- minimum frequency for photoelectric emission",
    ]
))
story.append(sp(4))
story.append(make_table(
    ["Metal",PHI + " (eV)","Threshold " + LAM + sub('0') + " (nm)","Responds to"],
    [
        ["Cs","2.0","620 nm","Visible light (red-orange)"],
        ["K", "2.2","564 nm","Visible light (yellow)"],
        ["Na","2.3","539 nm","Visible light (green)"],
        ["Al","4.3","288 nm","UV only"],
        ["Zn","4.3","288 nm","UV only"],
        ["Pt","5.6","221 nm","UV only"],
    ],
    col_widths=[W*0.12,W*0.18,W*0.26,W*0.44],
    left_cols=[3]
))
story.append(sp(5))

story.append(sub_h("Formula 12 -- Threshold Wavelength"))
story.append(formula_box(
    LAM + sub('0') + "  =  hc / " + PHI + "  =  c / " + NU + sub('0') + "     " + ARR + "     " + LAM + sub('0') + "(nm)  =  1240 / " + PHI + "(eV)",
    [
        "Light with " + LAM + " < " + LAM + sub('0') + "  (higher frequency)  " + ARR + "  emission OCCURS",
        "Light with " + LAM + " > " + LAM + sub('0') + "  (lower frequency)  " + ARR + "  NO emission  (regardless of intensity)",
        "Example:  " + PHI + "=2.0 eV  " + ARR + "  " + LAM + sub('0') + "=620 nm (red)     " + PHI + "=4.3 eV  " + ARR + "  " + LAM + sub('0') + "=288 nm (UV)",
    ]
))
story.append(sp(5))

story.append(sub_h("Formula 13 -- Stopping Potential"))
story.append(formula_box(
    "eV" + sub('0') + "  =  KE_max  =  h" + NU + "  -  " + PHI,
    [
        "e = 1.602" + TIMES + "10" + sup('-19') + " C   |   V" + sub('0') + " = stopping potential  (minimum reverse voltage to stop ALL photoelectrons)  in Volts",
        "KE_max in eV numerically equals V" + sub('0') + " in Volts  --  if KE_max=2.5 eV then V" + sub('0') + "=2.5 V",
        "More INTENSITY:  photocurrent " + UP + ",  V" + sub('0') + " UNCHANGED",
        "Higher FREQUENCY:  V" + sub('0') + " " + UP + ",  photocurrent unchanged",
        "V" + sub('0') + " vs " + NU + " graph:  Slope = h/e = 4.136" + TIMES + "10" + sup('-15') + " V" + CDOT + "s   |   x-intercept = " + NU + sub('0') + "   |   y-intercept = -" + PHI + "/e",
    ]
))
story.append(sp(5))

story.append(sub_h("Formula 14 -- Number of Photons per Unit Area per Second"))
story.append(formula_box(
    "n_photons  =  I / h" + NU + "  =  I" + LAM + " / hc",
    [
        "n_photons = number of photons hitting unit area per second  (s" + sup('-1') + CDOT + "m" + sup('-2') + ")",
        "I = intensity = energy per unit area per second  (W/m" + sup('2') + ")",
        "More intensity = MORE photons = MORE electrons ejected = LARGER photocurrent",
        "Each photon gives energy to ONE electron -- so KE_max is unaffected by intensity",
    ]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 7 — de BROGLIE
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 7 — DUAL NATURE OF MATTER  (de Broglie, 1924)"), sp(8)]

story += [sec("7.1  Background")]
for b in [
    "Louis de Broglie (1924): If light (wave) can behave as particles (photons), matter (particles) should also show wave properties",
    "Wave-particle duality: all matter has both wave and particle properties",
    "Only observable for MICROSCOPIC particles (electrons, protons)  --  not for macroscopic objects",
    "Experimental proof: Davisson-Germer experiment (1927)  --  electron diffraction by nickel crystal confirmed electron wave nature",
]:
    story.append(blt(b))
story.append(sp(5))

story.append(sub_h("Formula 15 -- de Broglie Wavelength (General)"))
story.append(formula_box(
    LAM + "  =  h / mv  =  h / p",
    [
        LAM + " = de Broglie (matter) wavelength (m)",
        "h = Planck's constant = 6.626" + TIMES + "10" + sup('-34') + " J" + CDOT + "s   |   m = mass of particle (kg)   |   v = velocity (m/s)",
        "p = mv = momentum (kg" + CDOT + "m/s)",
        "Cricket ball (0.1 kg, 30 m/s):   " + LAM + " ~ 2" + TIMES + "10" + sup('-34') + " m   --  unmeasurably tiny",
        "Electron (9.1" + TIMES + "10" + sup('-31') + " kg, 10" + sup('6') + " m/s):   " + LAM + " ~ 0.7 nm   --  detectable by X-ray diffraction!",
    ]
))
story.append(sp(5))

story.append(sub_h("Formula 16 -- de Broglie Wavelength from Kinetic Energy"))
story.append(formula_box(
    LAM + "  =  h / " + SQRT + "(2m" + CDOT + "KE)",
    [
        "Derived from " + LAM + " = h/mv and KE = (1/2)mv" + sup('2') + "   " + ARR + "   mv = " + SQRT + "(2m" + CDOT + "KE)",
        "KE MUST be in Joules.  Convert:  KE(J) = KE(eV) " + TIMES + " 1.602" + TIMES + "10" + sup('-19'),
    ]
))
story.append(sp(5))

story.append(sub_h("Formula 17 -- de Broglie Wavelength for Accelerated Electron"))
story.append(formula_box(
    LAM + "  =  12.27 / " + SQRT + "V     " + ANG + "       (electrons only)",
    [
        "12.27 = constant for ELECTRONS only = h/" + SQRT + "(2" + CDOT + "m" + sub('e') + CDOT + "e)  in " + ANG + CDOT + "V" + sup('1/2') + " units",
        "V = accelerating potential difference in VOLTS   |   Answer in ANGSTROMS",
        "For PROTON:   " + LAM + " = 0.286/" + SQRT + "V  " + ANG,
        "V=100 V " + ARR + " " + LAM + "=1.227 " + ANG + "   |   V=1000 V " + ARR + " " + LAM + "=0.388 " + ANG + "   |   V=10000 V " + ARR + " " + LAM + "=0.123 " + ANG,
        "Used in electron microscopy  (" + LAM + " much smaller than visible light " + ARR + " far higher resolution)",
    ]
))
story.append(sp(5))

story.append(sub_h("Formula 18 -- de Broglie and Bohr Connection"))
story.append(formula_box(
    "2" + PI + "r" + sub('n') + "  =  n" + LAM + "   (circumference of orbit = whole-number multiples of " + LAM + ")",
    [
        "Bohr's quantisation condition (mvr = nh/2" + PI + ") is equivalent to the electron's de Broglie wave fitting exactly into the orbit.",
        "This gives a wave-mechanical basis to Bohr's model.",
    ]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 8 — HEISENBERG
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 8 — HEISENBERG'S UNCERTAINTY PRINCIPLE (1927)"), sp(8)]

story += [sec("8.1  Statement")]
story += [body("It is impossible to simultaneously determine the EXACT position AND exact momentum (or velocity) "
               "of a microscopic particle. This is a fundamental law of nature -- NOT due to measurement error.")]
story.append(sp(5))

story.append(sub_h("Formula 19 -- Heisenberg's Uncertainty Principle"))
story.append(formula_box(
    DEL + "x  " + CDOT + "  " + DEL + "p  " + GEQ + "  h / 4" + PI + "         OR         "
    + DEL + "x  " + CDOT + "  m" + DEL + "v  " + GEQ + "  h / 4" + PI,
    [
        DEL + "x = uncertainty in position (m)   |   " + DEL + "p = uncertainty in momentum (kg" + CDOT + "m/s)",
        DEL + "v = uncertainty in velocity (m/s)   |   h/4" + PI + " = 5.27" + TIMES + "10" + sup('-35') + " J" + CDOT + "s",
        "Equivalent form:   " + DEL + "x " + CDOT + " " + DEL + "p  " + GEQ + "  " + HBAR + "/2       where  " + HBAR + " = h/2" + PI + " = 1.055" + TIMES + "10" + sup('-34') + " J" + CDOT + "s",
        "Energy-time version:   " + DEL + "E " + CDOT + " " + DEL + "t  " + GEQ + "  h/4" + PI,
    ]
))
story.append(sp(5))

story.append(sub_h("Formula 20 -- Minimum Uncertainty in Velocity"))
story.append(formula_box(
    DEL + "v_min  =  h / (4" + PI + "  " + TIMES + "  m  " + TIMES + "  " + DEL + "x)",
    [
        "If " + DEL + "x = 0 (exact position):   " + DEL + "p = " + INF + "  (momentum completely unknown)",
        "If " + DEL + "p = 0 (exact momentum):   " + DEL + "x = " + INF + "  (position completely unknown)",
        "Applies to ALL microscopic particles -- not due to experimental limitations",
    ]
))
story.append(sp(4))
story.append(trap_box("Heisenberg's UP INVALIDATES Bohr's model -- definite circular orbits would mean exact position AND velocity simultaneously, which is FORBIDDEN by the UP."))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 9 — QUANTUM NUMBERS
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 9 — QUANTUM MECHANICAL MODEL & QUANTUM NUMBERS"), sp(8)]

story += [sec("9.1  Orbital vs Orbit")]
story.append(make_table(
    ["Feature","Orbit (Bohr)","Orbital (Quantum Mechanics)"],
    [
        ["Definition","Definite circular path","Region of space where probability of finding electron is 90% or more"],
        ["Heisenberg","VIOLATES HUP","Consistent with HUP"],
        ["Dimensions","2D circle","3D region of space"],
        ["Position","Exact position known","Only probability density known"],
    ],
    col_widths=[W*0.18,W*0.28,W*0.54],
    left_cols=[0,1,2]
))
story.append(sp(8))

story += [sec("9.2  The Four Quantum Numbers")]
story += [body("Every electron in an atom is described by a UNIQUE set of 4 quantum numbers. "
               "No two electrons can have the same set of all 4  (Pauli's Exclusion Principle).")]
story.append(sp(5))

story.append(sub_h("(a) Principal Quantum Number  (n)"))
story.append(formula_box(
    "n  =  1,  2,  3,  4,  5,  6,  7  ...",
    [
        "Determines ENERGY and SIZE of the orbital",
        "Higher n = larger orbital = higher energy = farther from nucleus",
        "Shell names:  n=1 " + ARR + " K    n=2 " + ARR + " L    n=3 " + ARR + " M    n=4 " + ARR + " N",
    ]
))
story.append(sp(5))

story.append(sub_h("(b) Azimuthal Quantum Number  (l)  --  Subsidiary / Angular Momentum QN"))
story.append(formula_box(
    "l  =  0,  1,  2,  ...,  (n-1)",
    [
        "Determines SHAPE of orbital and SUBSHELL",
        "l=0 " + ARR + " s (sphere)   |   l=1 " + ARR + " p (dumbbell)   |   l=2 " + ARR + " d (double dumbbell)   |   l=3 " + ARR + " f (complex)",
        "For n=1: l=0 only (1s)   |   n=2: l=0,1 (2s, 2p)   |   n=3: l=0,1,2 (3s, 3p, 3d)",
    ]
))
story.append(sp(5))

story.append(sub_h("(c) Magnetic Quantum Number  (m" + sub('l') + ")"))
story.append(formula_box(
    "m" + sub('l') + "  =  -l,  -(l-1),  ...,  0,  ...,  (l-1),  +l     " + ARR + "     Total values = 2l+1",
    [
        "Determines ORIENTATION of orbital in space",
        "s (l=0):  m" + sub('l') + "=0  (1 orbital)   |   p (l=1):  m" + sub('l') + "=-1,0,+1  (3 orbitals: px, py, pz)",
        "d (l=2):  m" + sub('l') + "=-2,-1,0,+1,+2  (5 orbitals)   |   f (l=3):  7 orbitals",
    ]
))
story.append(sp(5))

story.append(sub_h("(d) Spin Quantum Number  (m" + sub('s') + ")"))
story.append(formula_box(
    "m" + sub('s') + "  =  +1/2  (spin up " + UP + ")     or     -1/2  (spin down " + DOWN + ")",
    [
        "Only two possible values -- cannot change",
        "Pauli's principle: two electrons in the same orbital MUST have opposite m" + sub('s') + " values",
    ]
))
story.append(sp(8))

story += [sec("9.3  Angular Momentum Formulas")]

story.append(sub_h("Formula 21 -- Orbital Angular Momentum"))
story.append(formula_box(
    "L  =  " + SQRT + "[l(l+1)]  " + CDOT + "  " + HBAR,
    [
        "L = magnitude of orbital angular momentum (J" + CDOT + "s)",
        "l = azimuthal quantum number   |   " + HBAR + " = h/2" + PI + " = 1.055" + TIMES + "10" + sup('-34') + " J" + CDOT + "s",
        "s (l=0): L=0   |   p (l=1): L=" + SQRT + "2 " + CDOT + HBAR + "   |   d (l=2): L=" + SQRT + "6 " + CDOT + HBAR + "   |   f (l=3): L=2" + SQRT + "3 " + CDOT + HBAR,
    ]
))
story.append(trap_box("Bohr's model gave L=n" + HBAR + "  (WRONG). Correct QM formula: L=" + SQRT + "[l(l+1)]" + CDOT + HBAR + ". s orbitals have ZERO orbital angular momentum -- NOT " + HBAR + "."))
story.append(sp(5))

story.append(sub_h("Formula 22 -- z-Component of Angular Momentum"))
story.append(formula_box(
    "L" + sub('z') + "  =  m" + sub('l') + "  " + CDOT + "  " + HBAR,
    [
        "L" + sub('z') + " = projection of angular momentum on z-axis",
        "p (l=1):  L" + sub('z') + "=-" + HBAR + ", 0, +" + HBAR + "  (3 orientations)",
        "d (l=2):  L" + sub('z') + "=-2" + HBAR + ", -" + HBAR + ", 0, +" + HBAR + ", +2" + HBAR + "  (5 orientations)",
        "A magnetic field splits these orientations  " + ARR + "  Zeeman effect",
    ]
))
story.append(sp(5))

story.append(sub_h("Formula 23 -- Spin Angular Momentum"))
story.append(formula_box(
    "S  =  " + SQRT + "[s(s+1)]  " + CDOT + "  " + HBAR + "  =  (" + SQRT + "3/2)  " + CDOT + "  " + HBAR + "     [same for ALL electrons]",
    [
        "s = spin quantum number = 1/2 always (intrinsic property of electron -- cannot change)",
        "The MAGNITUDE of spin is fixed for all electrons; only DIRECTION (m" + sub('s') + " = " + PM + "1/2) can vary",
    ]
))
story.append(sp(8))

story += [sec("9.4  Shell / Subshell Count Formulas")]
story.append(formula_box(
    "Orbitals in n" + sup('th') + " shell  =  n" + sup('2') + "                Max electrons in n" + sup('th') + " shell  =  2n" + sup('2') + "\n"
    "Orbitals in a subshell  =  2l+1          Max electrons in a subshell  =  2(2l+1)",
    [
        "n=1: 1 orbital, 2 e" + sup('-') + "   |   n=2: 4 orbitals, 8 e" + sup('-') + "   |   n=3: 9 orbitals, 18 e" + sup('-') + "   |   n=4: 16 orbitals, 32 e" + sup('-'),
        "s: 1 orbital, 2 e" + sup('-') + "   |   p: 3 orbitals, 6 e" + sup('-') + "   |   d: 5 orbitals, 10 e" + sup('-') + "   |   f: 7 orbitals, 14 e" + sup('-'),
    ]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 10 — SHAPES OF ORBITALS
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 10 — SHAPES OF ORBITALS"), sp(8)]
story.append(make_table(
    ["Orbital","Shape","Angular Nodes","Nodal Planes","Starts at n"],
    [
        ["s (l=0)","Sphere","0","None (spherical symmetry)","n=1"],
        ["p (l=1)","Dumbbell (2 lobes)","1","px: yz-plane  |  py: xz-plane  |  pz: xy-plane","n=2"],
        ["d (l=2)","Double dumbbell / clover (4 lobes); dz" + sup('2') + " has donut+dumbbell","2","Two nodal planes per orbital","n=3"],
        ["f (l=3)","Complex multi-lobed","3","Three nodal regions","n=4"],
    ],
    col_widths=[W*0.12,W*0.30,W*0.14,W*0.30,W*0.14],
    left_cols=[1,3]
))
story.append(sp(4))
for b in [
    "1s: 1 sphere.  2s: 2 concentric spheres (1 radial node inside).  3s: 3 concentric spheres.",
    "Three p orbitals (px, py, pz) are identical in shape but oriented along different axes -- DEGENERATE (same energy in absence of a field).",
    "Five d orbitals: dxy, dxz, dyz, dx" + sup('2') + "-y" + sup('2') + " (clover-leaf) and dz" + sup('2') + " (unique doughnut + dumbbell along z-axis).",
]:
    story.append(blt(b))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 11 — NODES
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 11 — NODES"), sp(8)]
story.append(formula_box(
    "Radial nodes  =  n - l - 1\n"
    "Angular nodes  =  l\n"
    "Total nodes  =  n - 1",
    [
        "Radial nodes = spherical surfaces INSIDE the orbital where electron density = 0",
        "Angular nodes = flat planes (or cones) through the NUCLEUS where electron probability = 0",
        "Proof:  (n-l-1) + l  =  n-1     " + CHECK + "  (always true for any orbital)",
    ]
))
story.append(sp(5))
story.append(make_table(
    ["Orbital","n","l","Radial (n-l-1)","Angular (l)","Total (n-1)"],
    [
        ["1s","1","0","0","0","0"],
        ["2s","2","0","1","0","1"],
        ["2p","2","1","0","1","1"],
        ["3s","3","0","2","0","2"],
        ["3p","3","1","1","1","2"],
        ["3d","3","2","0  " + WARN,"2","2"],
        ["4s","4","0","3","0","3"],
        ["4p","4","1","2","1","3"],
        ["4d","4","2","1","2","3"],
        ["4f","4","3","0","3","3"],
    ],
    col_widths=[W*0.12,W*0.08,W*0.08,W*0.24,W*0.22,W*0.26]
))
story.append(sp(3))
story.append(trap_box("3d has ZERO radial nodes  (students often say 1 -- that is WRONG)."))
story.append(trap_box("2s and 2p BOTH have 1 total node but DIFFERENT types:  2s has 1 RADIAL node (spherical shell),  2p has 1 ANGULAR node (flat plane)."))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 12 — ELECTRONIC CONFIGURATION
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 12 — ELECTRONIC CONFIGURATION"), sp(8)]

story += [sec("12.1  Three Fundamental Rules")]

story.append(sub_h("Rule 1 -- Aufbau Principle"))
story += [body("Fill orbitals in INCREASING order of energy (n+l rule). "
               "If two orbitals have the same (n+l), fill the one with LOWER n first.")]
story.append(sp(3))
story.append(formula_box(
    "Filling order:  1s " + ARR + " 2s " + ARR + " 2p " + ARR + " 3s " + ARR + " 3p " + ARR + " 4s " + ARR + " 3d " + ARR + " 4p " + ARR + " 5s " + ARR + " 4d " + ARR + " 5p " + ARR + " 6s " + ARR + " 4f " + ARR + " 5d " + ARR + " 6p",
    [
        "4s (n+l=4) is filled BEFORE 3d (n+l=5)",
        "4f (n+l=7) is filled BEFORE 5d (n+l=7, but 5d has higher n)",
    ]
))
story.append(sp(5))

story.append(sub_h("Rule 2 -- Pauli's Exclusion Principle"))
story += [body("No two electrons in the same atom can have the SAME SET of all four quantum numbers. "
               "Consequence: maximum 2 electrons per orbital with OPPOSITE spins (one " + UP + ", one " + DOWN + ").")]
story.append(sp(5))

story.append(sub_h("Rule 3 -- Hund's Rule of Maximum Multiplicity"))
story += [body("In degenerate orbitals (same energy), electrons fill SINGLY first (one per orbital) "
               "with PARALLEL spins before any pairing occurs.")]
story.append(sp(3))
story.append(note_box("Nitrogen (Z=7):  1s" + sup('2') + " 2s" + sup('2') + " 2p" + sup('3') + "   " + ARR + "   2p orbitals:  " + UP + " | " + UP + " | " + UP + "   (singly occupied -- CORRECT)     NOT:  " + UP + DOWN + " | " + UP + " | _ (pairing before all singly occupied -- WRONG)"))
story.append(sp(8))

story += [sec("12.2  Electronic Configurations  Z = 1 to 30")]
story.append(make_table(
    ["Z","Element","Configuration","Unpaired e" + sup('-')],
    [
        ["1","H","1s" + sup('1'),"1"],
        ["2","He","1s" + sup('2'),"0"],
        ["3","Li","[He] 2s" + sup('1'),"1"],
        ["4","Be","[He] 2s" + sup('2'),"0"],
        ["5","B","[He] 2s" + sup('2') + " 2p" + sup('1'),"1"],
        ["6","C","[He] 2s" + sup('2') + " 2p" + sup('2'),"2"],
        ["7","N","[He] 2s" + sup('2') + " 2p" + sup('3'),"3"],
        ["8","O","[He] 2s" + sup('2') + " 2p" + sup('4'),"2"],
        ["9","F","[He] 2s" + sup('2') + " 2p" + sup('5'),"1"],
        ["10","Ne","[He] 2s" + sup('2') + " 2p" + sup('6'),"0"],
        ["11","Na","[Ne] 3s" + sup('1'),"1"],
        ["12","Mg","[Ne] 3s" + sup('2'),"0"],
        ["13","Al","[Ne] 3s" + sup('2') + " 3p" + sup('1'),"1"],
        ["14","Si","[Ne] 3s" + sup('2') + " 3p" + sup('2'),"2"],
        ["15","P","[Ne] 3s" + sup('2') + " 3p" + sup('3'),"3"],
        ["16","S","[Ne] 3s" + sup('2') + " 3p" + sup('4'),"2"],
        ["17","Cl","[Ne] 3s" + sup('2') + " 3p" + sup('5'),"1"],
        ["18","Ar","[Ne] 3s" + sup('2') + " 3p" + sup('6'),"0"],
        ["19","K","[Ar] 4s" + sup('1'),"1"],
        ["20","Ca","[Ar] 4s" + sup('2'),"0"],
        ["21","Sc","[Ar] 3d" + sup('1') + " 4s" + sup('2'),"1"],
        ["22","Ti","[Ar] 3d" + sup('2') + " 4s" + sup('2'),"2"],
        ["23","V","[Ar] 3d" + sup('3') + " 4s" + sup('2'),"3"],
        ["24 " + WARN,"Cr  (EXCEPTION)","[Ar] 3d" + sup('5') + " 4s" + sup('1') + "   (NOT 3d" + sup('4') + " 4s" + sup('2') + ")","6"],
        ["25","Mn","[Ar] 3d" + sup('5') + " 4s" + sup('2'),"5"],
        ["26","Fe","[Ar] 3d" + sup('6') + " 4s" + sup('2'),"4"],
        ["27","Co","[Ar] 3d" + sup('7') + " 4s" + sup('2'),"3"],
        ["28","Ni","[Ar] 3d" + sup('8') + " 4s" + sup('2'),"2"],
        ["29 " + WARN,"Cu  (EXCEPTION)","[Ar] 3d" + sup('10') + " 4s" + sup('1') + "   (NOT 3d" + sup('9') + " 4s" + sup('2') + ")","1"],
        ["30","Zn","[Ar] 3d" + sup('10') + " 4s" + sup('2'),"0"],
    ],
    col_widths=[W*0.12,W*0.18,W*0.54,W*0.16],
    left_cols=[2]
))
story.append(sp(4))
story.append(note_box(
    "Reason for Cr and Cu exceptions:  Half-filled (d" + sup('5') + ") and fully-filled (d" + sup('10') + ") "
    "subshells have extra stability due to  (1) Symmetrical distribution of electrons,  "
    "(2) Maximum exchange energy."
))
story.append(sp(8))

story += [sec("12.3  Magnetic Properties")]

story.append(sub_h("Formula 31 -- Spin Multiplicity"))
story.append(formula_box(
    "Spin multiplicity  =  2S + 1         where  S  =  (number of unpaired electrons) / 2",
    [
        "Multiplicity = 1 (singlet):  all electrons paired  " + ARR + "  DIAMAGNETIC (repelled by magnetic field)",
        "Multiplicity > 1:  unpaired electrons  " + ARR + "  PARAMAGNETIC (attracted by magnetic field)",
        "Higher multiplicity = more unpaired electrons = stronger paramagnetism",
    ]
))
story.append(sp(4))
story.append(make_table(
    ["Unpaired e" + sup('-'),"S","Multiplicity","Type","Example"],
    [
        ["0","0","1  (singlet)","Diamagnetic","He, Ne, Zn, Be, Mg"],
        ["1","1/2","2  (doublet)","Paramagnetic","H, F, Na, Cu" + sup('2+')],
        ["2","1","3  (triplet)","Paramagnetic","C, O, Ni" + sup('2+')],
        ["3","3/2","4  (quartet)","Paramagnetic","N, Co" + sup('2+') + ", Cr" + sup('3+')],
        ["4","2","5  (quintet)","Paramagnetic","Fe" + sup('2+') + ", Cr" + sup('2+')],
        ["5","5/2","6  (sextet)","Paramagnetic","Mn" + sup('2+') + ", Fe" + sup('3+')],
    ],
    col_widths=[W*0.14,W*0.10,W*0.22,W*0.18,W*0.36],
    left_cols=[4]
))
story.append(sp(5))

story.append(sub_h("Formula 32 -- Magnetic Moment"))
story.append(formula_box(
    MU + "  =  " + SQRT + "[n(n+2)]     BM   (Bohr Magneton)",
    [
        "n = number of unpaired electrons   |   1 BM (Bohr Magneton) = 9.274" + TIMES + "10" + sup('-24') + " J/T",
        "1 unpaired: " + MU + "=" + SQRT + "3 " + "\u2248" + " 1.73 BM  (Cu" + sup('2+') + ", Ti" + sup('3+') + ")",
        "2 unpaired: " + MU + "=" + SQRT + "8 " + "\u2248" + " 2.83 BM  (Ni" + sup('2+') + ")",
        "3 unpaired: " + MU + "=" + SQRT + "15 " + "\u2248" + " 3.87 BM  (Co" + sup('2+') + ", Cr" + sup('3+') + ")",
        "4 unpaired: " + MU + "=" + SQRT + "24 " + "\u2248" + " 4.90 BM  (Fe" + sup('2+') + ")",
        "5 unpaired: " + MU + "=" + SQRT + "35 " + "\u2248" + " 5.92 BM  (Mn" + sup('2+') + ", Fe" + sup('3+') + ")",
    ]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 13 — CONSTANTS
# ══════════════════════════════════════════════════════════════════
story.append(PageBreak())
story += [part_header("PART 13 — COMPLETE CONSTANTS REFERENCE"), sp(8)]
story.append(make_table(
    ["Symbol","Name","Value","Unit"],
    [
        ["h","Planck's constant","6.626 " + TIMES + " 10" + sup('-34'),"J" + CDOT + "s"],
        [HBAR,"Reduced Planck (h/2" + PI + ")","1.055 " + TIMES + " 10" + sup('-34'),"J" + CDOT + "s"],
        ["c","Speed of light","3 " + TIMES + " 10" + sup('8'),"m/s"],
        ["e","Elementary charge","1.602 " + TIMES + " 10" + sup('-19'),"C"],
        ["m" + sub('e'),"Mass of electron","9.109 " + TIMES + " 10" + sup('-31'),"kg"],
        ["m" + sub('p'),"Mass of proton","1.673 " + TIMES + " 10" + sup('-27'),"kg"],
        ["m" + sub('n'),"Mass of neutron","1.675 " + TIMES + " 10" + sup('-27'),"kg"],
        ["a" + sub('0'),"Bohr radius (1st orbit of H)","0.529 " + ANG + " = 5.29" + TIMES + "10" + sup('-11'),"m"],
        ["R" + sub('H'),"Rydberg constant","1.097" + TIMES + "10" + sup('7') + " m" + sup('-1') + " = 109677 cm" + sup('-1'),"m" + sup('-1')],
        ["r" + sub('0'),"Nuclear radius constant","1.2 " + TIMES + " 10" + sup('-15') + " = 1.2 fm","m"],
        ["1 eV","In Joules","1.602 " + TIMES + " 10" + sup('-19'),"J"],
        ["1 amu","In kg","1.66 " + TIMES + " 10" + sup('-27'),"kg"],
        ["1 " + ANG,"In metres","10" + sup('-10'),"m"],
        ["1 nm","In metres","10" + sup('-9'),"m"],
        ["1 pm","In metres","10" + sup('-12'),"m"],
        ["1 fm","In metres","10" + sup('-15'),"m"],
    ],
    col_widths=[W*0.13,W*0.40,W*0.32,W*0.15],
    left_cols=[1]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# MASTER FORMULA SHEET
# ══════════════════════════════════════════════════════════════════
story.append(PageBreak())
story += [part_header("MASTER FORMULA SHEET -- ALL 41 FORMULAS AT A GLANCE"), sp(8)]
story.append(make_table(
    ["#","Formula Name","Expression"],
    [
        ["1","Orbital radius","r" + sub('n') + " = 0.529 " + TIMES + " n" + sup('2') + "/Z  " + ANG],
        ["2","Orbital velocity","v" + sub('n') + " = 2.18" + TIMES + "10" + sup('6') + " " + TIMES + " Z/n  m/s"],
        ["3","Total energy","E" + sub('n') + " = -13.6 " + TIMES + " Z" + sup('2') + "/n" + sup('2') + "  eV"],
        ["4","Kinetic energy","KE = -E" + sub('n') + " = +13.6 Z" + sup('2') + "/n" + sup('2') + "  eV"],
        ["5","Potential energy","PE = 2E" + sub('n') + " = -27.2 Z" + sup('2') + "/n" + sup('2') + "  eV"],
        ["6","Ionisation energy","IE = +13.6 " + TIMES + " Z" + sup('2') + "/n" + sup('2') + "  eV"],
        ["7","Photon energy (transition)",DEL + "E = 13.6 Z" + sup('2') + " (1/n" + sub('1') + sup('2') + " - 1/n" + sub('2') + sup('2') + ")  eV"],
        ["8","Rydberg equation","1/" + LAM + " = R" + sub('H') + " Z" + sup('2') + " (1/n" + sub('1') + sup('2') + " - 1/n" + sub('2') + sup('2') + ")"],
        ["9","Angular momentum (Bohr)","mv" + sub('n') + "r" + sub('n') + " = nh/2" + PI + " = n" + HBAR],
        ["10","Time period","T" + sub('n') + " " + PROP + " n" + sup('3') + "/Z" + sup('2')],
        ["11","Revolution frequency",NU + sub('n') + " " + PROP + " Z" + sup('2') + "/n" + sup('3')],
        ["12","Spectral lines count","(n" + sub('2') + "-n" + sub('1') + ")(n" + sub('2') + "-n" + sub('1') + "+1)/2"],
        ["13","Wave equation","c = " + NU + LAM],
        ["14","Wave number",NU + "\u0303 = 1/" + LAM + "  (m" + sup('-1') + " or cm" + sup('-1') + ")"],
        ["15","Photon energy","E = h" + NU + " = hc/" + LAM + " = hc" + NU + "\u0303"],
        ["16","Energy shortcut","E(eV) = 1240/" + LAM + "(nm)"],
        ["17","Photon momentum","p = h/" + LAM + " = E/c"],
        ["18","Photoelectric equation","h" + NU + " = " + PHI + " + KE_max"],
        ["19","Work function",PHI + " = h" + NU + sub('0')],
        ["20","Threshold wavelength",LAM + sub('0') + "(nm) = 1240/" + PHI + "(eV)"],
        ["21","Stopping potential","eV" + sub('0') + " = KE_max = h" + NU + " - " + PHI],
        ["22","Photon count (intensity)","n = I/h" + NU + " = I" + LAM + "/hc"],
        ["23","de Broglie (general)",LAM + " = h/mv = h/p"],
        ["24","de Broglie (from KE)",LAM + " = h/" + SQRT + "(2m" + CDOT + "KE)"],
        ["25","de Broglie (electron, V)",LAM + " = 12.27/" + SQRT + "V  " + ANG],
        ["26","Heisenberg's UP",DEL + "x" + CDOT + DEL + "p " + GEQ + " h/4" + PI],
        ["27","Min velocity uncertainty",DEL + "v_min = h/(4" + PI + CDOT + "m" + CDOT + DEL + "x)"],
        ["28","Orbital angular momentum","L = " + SQRT + "[l(l+1)]" + CDOT + HBAR],
        ["29","L z-component","L" + sub('z') + " = m" + sub('l') + CDOT + HBAR],
        ["30","Spin angular momentum","S = (" + SQRT + "3/2)" + CDOT + HBAR + "  [all electrons]"],
        ["31","Orbitals per shell","n" + sup('2')],
        ["32","Max e" + sup('-') + " per shell","2n" + sup('2')],
        ["33","Orbitals per subshell","2l+1"],
        ["34","Max e" + sup('-') + " per subshell","2(2l+1)"],
        ["35","Radial nodes","n - l - 1"],
        ["36","Angular nodes","l"],
        ["37","Total nodes","n - 1"],
        ["38","Spin multiplicity","2S+1   where S=(unpaired e" + sup('-') + ")/2"],
        ["39","Magnetic moment",MU + " = " + SQRT + "[n(n+2)]  BM"],
        ["40","Nuclear radius","r = r" + sub('0') + " " + TIMES + " A" + sup('1/3')],
        ["41","e/m ratio (electron)","1.758 " + TIMES + " 10" + sup('11') + "  C/kg"],
    ],
    col_widths=[W*0.06,W*0.36,W*0.58],
    left_cols=[1,2]
))
story.append(sp(8))

# Footer
story.append(thinhr())
story.append(sp(4))
story.append(Paragraph(
    "Atomic Structure  |  Class 11 Chemistry  |  Complete Chapter  |  All 41 Formulas  |  NEET / JEE",
    sFooter
))

# ── Build ──────────────────────────────────────────────────────────
doc.build(story)
print("PDF created:", OUTPUT)
Running Command

cd /tmp/workspace/atomic-structure && python3 generate_bw.py 2>&1

Reading File
Editing File

~/atomic-structure/generate_bw.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/atomic-structure/generate_bw.py'
Running Command

cd /tmp/workspace/atomic-structure && python3 -c " import ast, sys with open('generate_bw.py') as f: src = f.read() try: ast.parse(src) print('Syntax OK') except SyntaxError as e: print(f'SyntaxError at line {e.lineno}: {e.msg}') # Print context lines = src.split('\n') for i in range(max(0,e.lineno-3), min(len(lines), e.lineno+2)): print(f'{i+1}: {lines[i]}') "

Running Command

cd /tmp/workspace/atomic-structure && python3 generate_bw.py 2>&1

Running Command

ls -lh /tmp/workspace/atomic-structure/Atomic_Structure_BW.pdf

Your new PDF is ready:

Atomic Structure BW

PDF Document · PDF

What's fixed vs the previous version:
IssueFix applied
Placeholder squaresUsed DroidSans Unicode font -- every character (n², Z²⁺, λ, ν, φ, Δ, ℏ, Å, →, √, ∝, ∞, ↑↓, etc.) renders correctly
Coloured backgroundsRemoved entirely -- pure black & white throughout
Font sizeIncreased to 11pt body, 12.5pt formulas, 13pt section headings, 14pt part banners
ThemeBlack banners with white text, white formula boxes with thick black borders, light grey alternating table rows only
Print qualityLarger fonts, clean black grid lines, no colour ink needed

The values are not clearly visible like e[] this, except this everything is nice even the matter is wonderful

Running Command

python3 -c " from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont from reportlab.platypus import SimpleDocTemplate, Paragraph from reportlab.lib.styles import ParagraphStyle from reportlab.lib.pagesizes import A4 from reportlab.lib import colors pdfmetrics.registerFont(TTFont('DS', '/usr/share/fonts/google-droid-sans-fonts/DroidSans.ttf')) pdfmetrics.registerFont(TTFont('DSB', '/usr/share/fonts/google-droid-sans-fonts/DroidSans-Bold.ttf')) doc = SimpleDocTemplate('/tmp/workspace/atomic-structure/test_markup.pdf', pagesize=A4) style = ParagraphStyle('t', fontName='DS', fontSize=13, leading=20) bstyle = ParagraphStyle('b', fontName='DSB', fontSize=13, leading=20) tests = [ 'Orbital radius: r<sub>n</sub> = 0.529 x n<super>2</super>/Z Angstroms', 'Energy: E<sub>n</sub> = -13.6 x Z<super>2</super>/n<super>2</super> eV', 'Electron: e<super>-</super> Proton: H<super>+</super> He<super>2+</super>', 'n<sub>1</sub> and n<sub>2</sub> transitions', 'KE = +13.6 Z<super>2</super>/n<super>2</super> eV PE = -27.2 Z<super>2</super>/n<super>2</super> eV', 'h = 6.626 x 10<super>-34</super> J.s c = 3 x 10<super>8</super> m/s', 'Delta-x . Delta-p >= h/4pi', 'L = sqrt[l(l+1)] . hbar m<sub>e</sub> = 9.109 x 10<super>-31</super> kg', 'r<sub>0</sub> = 1.2 x 10<super>-15</super> m e/m = 1.758 x 10<super>11</super> C/kg', ] story = [Paragraph(t, style) for t in tests] story += [Paragraph('<b>BOLD TEST: E<sub>n</sub> = -13.6 x Z<super>2</super>/n<super>2</super> eV</b>', style)] doc.build(story) print('Markup test PDF created OK') "

Writing File

~/atomic-structure/generate_final.py

"""
Atomic Structure - Complete Chapter PDF
Black & White, DroidSans Unicode font, ReportLab <sub>/<super> markup,
NO placeholder squares, larger fonts, print-optimised.
"""
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.units import cm
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, PageBreak
)

pdfmetrics.registerFont(TTFont('DS',  '/usr/share/fonts/google-droid-sans-fonts/DroidSans.ttf'))
pdfmetrics.registerFont(TTFont('DSB', '/usr/share/fonts/google-droid-sans-fonts/DroidSans-Bold.ttf'))

OUTPUT = "/tmp/workspace/atomic-structure/Atomic_Structure_Final.pdf"

doc = SimpleDocTemplate(
    OUTPUT, pagesize=A4,
    rightMargin=2.0*cm, leftMargin=2.0*cm,
    topMargin=2.2*cm,   bottomMargin=2.2*cm,
)
W = A4[0] - 4.0*cm

# ── colours (B&W only) ─────────────────────────────────────────────
BLACK    = colors.black
WHITE    = colors.white
LT_GREY  = colors.HexColor("#e0e0e0")
MID_GREY = colors.HexColor("#808080")
DK_GREY  = colors.HexColor("#303030")

# ── styles ─────────────────────────────────────────────────────────
def S(name, **kw): return ParagraphStyle(name, **kw)

sTitle   = S("sTitle",   fontName="DSB", fontSize=22, textColor=WHITE, alignment=TA_CENTER, spaceAfter=4, leading=30)
sSubtit  = S("sSubtit",  fontName="DS",  fontSize=11, textColor=WHITE, alignment=TA_CENTER, spaceAfter=2, leading=16)
sPart    = S("sPart",    fontName="DSB", fontSize=14, textColor=WHITE, alignment=TA_LEFT,   spaceAfter=4, spaceBefore=6, leftIndent=6, leading=20)
sSec     = S("sSec",     fontName="DSB", fontSize=13, textColor=BLACK, alignment=TA_LEFT,   spaceAfter=4, spaceBefore=8, leading=18)
sSub     = S("sSub",     fontName="DSB", fontSize=11.5, textColor=BLACK, alignment=TA_LEFT, spaceAfter=3, spaceBefore=5, leading=16)
sBody    = S("sBody",    fontName="DS",  fontSize=11, textColor=BLACK, alignment=TA_JUSTIFY, spaceAfter=4, leading=16)
sBullet  = S("sBullet",  fontName="DS",  fontSize=11, textColor=BLACK, alignment=TA_LEFT,   spaceAfter=3, leading=15, leftIndent=16)
sFormula = S("sFormula", fontName="DSB", fontSize=12.5, textColor=BLACK, alignment=TA_CENTER, spaceAfter=3, spaceBefore=3, leading=20)
sFormNote= S("sFormNote",fontName="DS",  fontSize=10.5, textColor=DK_GREY, alignment=TA_LEFT, spaceAfter=2, leading=15, leftIndent=4)
sTrap    = S("sTrap",    fontName="DSB", fontSize=11, textColor=BLACK, alignment=TA_LEFT,   spaceAfter=2, leading=15)
sNote    = S("sNote",    fontName="DS",  fontSize=11, textColor=BLACK, alignment=TA_LEFT,   spaceAfter=2, leading=15)
sTableH  = S("sTableH",  fontName="DSB", fontSize=10, textColor=WHITE, alignment=TA_CENTER, leading=13)
sTableC  = S("sTableC",  fontName="DS",  fontSize=10, textColor=BLACK, alignment=TA_CENTER, leading=13)
sTableL  = S("sTableL",  fontName="DS",  fontSize=10, textColor=BLACK, alignment=TA_LEFT,   leading=13)
sFooter  = S("sFooter",  fontName="DS",  fontSize=9,  textColor=MID_GREY, alignment=TA_CENTER, leading=12)

# ── helpers ────────────────────────────────────────────────────────
def sp(h=5):  return Spacer(1, h)
def thinhr(): return HRFlowable(width="100%", thickness=0.4, color=MID_GREY)
def thickhr():return HRFlowable(width="100%", thickness=1.0, color=BLACK)
def body(t):  return Paragraph(t, sBody)
def blt(t):   return Paragraph("&#8226;  " + t, sBullet)
def sec(t):   return Paragraph(t, sSec)
def sub_h(t): return Paragraph(t, sSub)

def part_header(t):
    tbl = Table([[Paragraph(t, sPart)]], colWidths=[W])
    tbl.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),BLACK),("ROWPADDING",(0,0),(-1,-1),8)]))
    return tbl

def formula_box(main, notes=None):
    elems = [Paragraph(main, sFormula)]
    if notes:
        elems.append(HRFlowable(width="100%", thickness=0.5, color=LT_GREY))
        for n in notes:
            elems.append(Paragraph(n, sFormNote))
    rows = [[e] for e in elems]
    inner = Table(rows, colWidths=[W-24])
    inner.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),WHITE),("ROWPADDING",(0,0),(-1,-1),5)]))
    outer = Table([[inner]], colWidths=[W])
    outer.setStyle(TableStyle([("BOX",(0,0),(-1,-1),2,BLACK),("ROWPADDING",(0,0),(-1,-1),6),("BACKGROUND",(0,0),(-1,-1),WHITE)]))
    return outer

def trap_box(t):
    tbl = Table([[Paragraph("&#9888;  NEET TRAP: " + t, sTrap)]], colWidths=[W])
    tbl.setStyle(TableStyle([("BOX",(0,0),(-1,-1),1.5,BLACK),("ROWPADDING",(0,0),(-1,-1),6),("BACKGROUND",(0,0),(-1,-1),LT_GREY)]))
    return tbl

def note_box(t):
    tbl = Table([[Paragraph("&#9733;  NOTE: " + t, sNote)]], colWidths=[W])
    tbl.setStyle(TableStyle([("BOX",(0,0),(-1,-1),1,BLACK),("ROWPADDING",(0,0),(-1,-1),6),("BACKGROUND",(0,0),(-1,-1),WHITE)]))
    return tbl

def make_table(headers, rows, col_widths=None, left_cols=None):
    if col_widths is None: col_widths = [W/len(headers)]*len(headers)
    left_cols = left_cols or []
    data = [[Paragraph(h, sTableH) for h in headers]]
    for row in rows:
        data.append([Paragraph(str(cell), sTableL if j in left_cols else sTableC) for j,cell in enumerate(row)])
    tbl = Table(data, colWidths=col_widths)
    cmds = [
        ("BACKGROUND",(0,0),(-1,0),BLACK),
        ("ROWPADDING",(0,0),(-1,-1),5),
        ("GRID",(0,0),(-1,-1),0.5,MID_GREY),
        ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
        ("LINEBELOW",(0,0),(-1,0),1.0,BLACK),
    ]
    for i in range(1,len(data)):
        if i%2==0: cmds.append(("BACKGROUND",(0,i),(-1,i),colors.HexColor("#f4f4f4")))
    tbl.setStyle(TableStyle(cmds))
    return tbl

# ══════════════════════════════════════════════════════════════════
story = []

# COVER
cover_t = Table([[Paragraph("ATOMIC STRUCTURE", sTitle)]], colWidths=[W])
cover_t.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),BLACK),("ROWPADDING",(0,0),(-1,-1),20)]))
cover_s = Table([[Paragraph("Complete Chapter  |  All 41 Formulas  |  All Topics  |  Class 11 Chemistry  |  NEET / JEE", sSubtit)]], colWidths=[W])
cover_s.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),DK_GREY),("ROWPADDING",(0,0),(-1,-1),10)]))
story += [cover_t, cover_s, sp(12)]

# ══════════════════════════════════════════════════════════════════
# PART 1 — DISCOVERY OF SUB-ATOMIC PARTICLES
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 1 — DISCOVERY OF SUB-ATOMIC PARTICLES"), sp(8)]

story += [sec("1.1  Cathode Ray Experiment  (J.J. Thomson, 1897)")]
story += [body("High-voltage discharge tube with two metal electrodes (evacuated glass tube). "
               "Rays travel from cathode (&#8722;) to anode (+). These are <b>cathode rays</b>.")]
story += [sp(3), sub_h("Observations:")]
for b in [
    "Travel in straight lines; cast sharp shadows",
    "Deflect towards the positive plate  &#8594;  carry NEGATIVE charge",
    "Cause fluorescence on a ZnS screen",
    "Same e/m ratio regardless of cathode material or gas used  &#8594;  electrons are universal (same in all matter)",
    "Discovery: the <b>ELECTRON</b>",
]:  story.append(blt(b))
story += [sp(5),
    formula_box(
        "e / m<sub>e</sub>  =  1.758 &#215; 10<super>11</super>  C/kg",
        [
            "e = charge of electron = 1.602 &#215; 10<super>&#8722;19</super> C",
            "m<sub>e</sub> = mass of electron = 9.109 &#215; 10<super>&#8722;31</super> kg",
            "This ratio is CONSTANT for cathode rays in ALL gases.",
            "Canal rays (positive ions) have VARYING e/m  &#8212;  different gases give different ion masses.",
        ]
    ), sp(6)]

story += [sec("1.2  Millikan's Oil Drop Experiment  (1909)")]
story += [body("Balanced gravitational and electric forces on charged oil droplets to measure the exact charge on one electron."),
    formula_box(
        "e  =  1.602 &#215; 10<super>&#8722;19</super>  C",
        ["From known e and e/m ratio:   m<sub>e</sub> = 9.109 &#215; 10<super>&#8722;31</super> kg"]
    ), sp(6)]

story += [sec("1.3  Canal Ray Experiment  (Goldstein, 1886)")]
story += [body("Cathode with small holes. Rays appear <i>behind</i> the cathode and travel opposite to cathode rays. "
               "Discovery: <b>PROTON</b> (positively charged particle). "
               "The lightest positive ion from hydrogen gas is the proton (H<super>+</super>)."), sp(6)]

story += [sec("1.4  Discovery of Neutron  (Chadwick, 1932)")]
story += [body("Bombarded beryllium with alpha-particles &#8594; neutral radiation identified as <b>NEUTRONS</b>."),
    formula_box("Neutron charge = 0     |     m<sub>n</sub> = 1.675 &#215; 10<super>&#8722;27</super> kg"), sp(6)]

story += [sec("1.5  Sub-Atomic Particles — Summary")]
story.append(make_table(
    ["Particle","Symbol","Charge","Mass (kg)","Mass (amu)","Discoverer"],
    [
        ["Electron","e<super>&#8722;</super>","&#8722;1.602&#215;10<super>&#8722;19</super> C","9.109&#215;10<super>&#8722;31</super>","0.000549","Thomson (1897)"],
        ["Proton",  "p<super>+</super>",  "+1.602&#215;10<super>&#8722;19</super> C","1.673&#215;10<super>&#8722;27</super>","1.00728", "Goldstein/Rutherford"],
        ["Neutron", "n","0","1.675&#215;10<super>&#8722;27</super>","1.00867","Chadwick (1932)"],
    ],
    col_widths=[W*0.13,W*0.10,W*0.23,W*0.20,W*0.13,W*0.21], left_cols=[5]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 2 — ATOMIC MODELS
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 2 — ATOMIC MODELS"), sp(8)]

story += [sec("2.1  Thomson's Plum Pudding Model (1904)")]
for b in [
    "Atom = uniform positive sphere with electrons embedded like plums in a pudding",
    "Radius ~ 10<super>&#8722;10</super> m",
    "FAILED &#8212; could not explain Rutherford's alpha-scattering results",
]:  story.append(blt(b))
story.append(sp(6))

story += [sec("2.2  Rutherford's Nuclear Model (1911)")]
story += [sub_h("Alpha-Particle Scattering Observations:")]
for b in [
    "Most &#945;-particles passed straight through &#8594; atom is MOSTLY EMPTY SPACE",
    "Few deflected at small angles &#8594; small positive charge inside",
    "Very few (1 in 20,000) bounced back at 180&#176; &#8594; small, dense, positive NUCLEUS",
]:  story.append(blt(b))
story += [sp(4),
    formula_box(
        "Nuclear Radius:   r  =  r<sub>0</sub>  &#215;  A<super>1/3</super>",
        [
            "r<sub>0</sub> = nuclear radius constant = 1.2 &#215; 10<super>&#8722;15</super> m  =  1.2 fm",
            "A = mass number (total protons + neutrons)",
            "Nuclear density &#8776; 10<super>17</super> kg/m<super>3</super>  &#8212;  CONSTANT for ALL nuclei (independent of A and Z)",
            "1 cm<super>3</super> of nuclear matter weighs ~100 million tonnes!",
        ]
    ), sp(4)]
story += [sub_h("Limitations of Rutherford's Model:")]
for b in [
    "STABILITY: Accelerating electron should continuously radiate energy &#8594; spiral into nucleus &#8594; collapse in ~10<super>&#8722;8</super> s  (but atoms ARE stable!)",
    "LINE SPECTRA: A spiralling electron would emit a CONTINUOUS spectrum, NOT discrete line spectra",
]:  story.append(blt(b))
story.append(sp(6))

story += [sec("2.3  Nuclear Terminology")]
story.append(formula_box(
    "Mass Number:   A  =  Z  +  N",
    [
        "A = total nucleons (protons + neutrons)",
        "Z = atomic number = number of protons = electrons in neutral atom",
        "N = neutron number = A &#8722; Z",
    ]
))
story.append(sp(4))
story.append(make_table(
    ["Term","Same","Different","Example"],
    [
        ["Isotopes","Z (same element)","A, N","<super>1</super>H,  <super>2</super>H (D),  <super>3</super>H (T)  &#8212; all Z=1"],
        ["Isobars","A","Z, N","<super>14</super><sub>6</sub>C  and  <super>14</super><sub>7</sub>N"],
        ["Isotones","N","Z, A","<super>3</super><sub>1</sub>H  and  <super>4</super><sub>2</sub>He  (both N=2)"],
        ["Isoelectronic","Total electrons","&#8212;","Na<super>+</super>, Mg<super>2+</super>, Ne, F<super>&#8722;</super>  (all 10 e<super>&#8722;</super>)"],
    ],
    col_widths=[W*0.18,W*0.22,W*0.18,W*0.42], left_cols=[0,1,2,3]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 3 — EM RADIATION
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 3 — ELECTROMAGNETIC RADIATION & PLANCK'S THEORY"), sp(8)]

story += [sec("3.1  Wave Theory of Light")]
for b in [
    "Light = electromagnetic wave  &#8212;  oscillating electric and magnetic fields perpendicular to each other and to the direction of travel",
    "All EM waves travel at c = 3 &#215; 10<super>8</super> m/s in vacuum (ALL frequencies, always)",
    "Do NOT require a medium to propagate",
]:  story.append(blt(b))
story.append(sp(5))
story.append(formula_box(
    "Wave Equation:   c  =  &#957;  &#215;  &#955;",
    [
        "c = speed of light = 3 &#215; 10<super>8</super> m/s  (universal constant)",
        "&#957; (nu) = frequency  in Hz (= s<super>&#8722;1</super>)",
        "&#955; (lambda) = wavelength  in metres",
        "&#957; and &#955; are INVERSELY proportional at constant c",
        "Unit conversions:  1 nm = 10<super>&#8722;9</super> m   |   1 &#8491; = 10<super>&#8722;10</super> m   |   1 pm = 10<super>&#8722;12</super> m",
    ]
))
story.append(sp(4))
story.append(formula_box(
    "Wave Number:   &#957;&#771;  =  1 / &#955;",
    [
        "Units: m<super>&#8722;1</super> or cm<super>&#8722;1</super>  (mainly cm<super>&#8722;1</super> in IR spectroscopy)",
        "CORRECT definition: &#957;&#771; = 1/&#955;     WRONG (common mistake): &#957;&#771; = &#957;/c",
    ]
))
story.append(sp(6))

story += [sec("3.2  Electromagnetic Spectrum")]
story.append(make_table(
    ["Region","Wavelength Range","Frequency Range","Notes"],
    [
        ["Gamma rays","&lt; 0.01 nm","&gt; 3&#215;10<super>19</super> Hz","Nuclear reactions"],
        ["X-rays","0.01 &#8211; 10 nm","3&#215;10<super>17</super> &#8211; 3&#215;10<super>19</super> Hz","Medical imaging"],
        ["UV","10 &#8211; 400 nm","7.5&#215;10<super>14</super> &#8211; 3&#215;10<super>17</super> Hz","Lyman series of H"],
        ["Visible","400 &#8211; 700 nm","4.3&#215;10<super>14</super> &#8211; 7.5&#215;10<super>14</super> Hz","Balmer series of H"],
        ["Infrared","700 nm &#8211; 1 mm","3&#215;10<super>11</super> &#8211; 4.3&#215;10<super>14</super> Hz","Paschen, Brackett series"],
        ["Microwaves","1 mm &#8211; 10 cm","3&#215;10<super>9</super> &#8211; 3&#215;10<super>11</super> Hz","Radar, cooking"],
        ["Radio waves","&gt; 10 cm","&lt; 3&#215;10<super>9</super> Hz","Broadcasting"],
    ],
    col_widths=[W*0.16,W*0.24,W*0.34,W*0.26], left_cols=[3]
))
story.append(sp(4))
story += [sub_h("Visible Light — VIBGYOR (increasing wavelength, decreasing energy):")]
story.append(make_table(
    ["Colour","Wavelength","Energy"],
    [["Violet","400 nm","3.10 eV"],["Indigo","425 nm","2.92 eV"],["Blue","450 nm","2.76 eV"],
     ["Green","520 nm","2.38 eV"],["Yellow","580 nm","2.14 eV"],["Orange","620 nm","2.00 eV"],["Red","700 nm","1.77 eV"]],
    col_widths=[W*0.33,W*0.33,W*0.34]
))
story.append(sp(6))

story += [sec("3.3  Planck's Quantum Theory (1900)")]
for b in [
    "Energy is NOT continuous &#8212; emitted/absorbed in discrete packets called QUANTA (photons)",
    "Energy of each quantum is directly proportional to its frequency",
]:  story.append(blt(b))
story.append(sp(4))
story.append(formula_box(
    "Energy of ONE Photon:   E  =  h&#957;  =  hc/&#955;  =  hc&#957;&#771;",
    [
        "h = Planck's constant = 6.626 &#215; 10<super>&#8722;34</super> J&#183;s",
        "&#957; = frequency (Hz)   |   &#955; = wavelength (m)   |   &#957;&#771; = wave number (m<super>&#8722;1</super>)",
        "E = energy of ONE photon  (in Joules or eV)   |   For n photons:  E<sub>total</sub> = n &#215; h&#957;",
        "Conversion:  1 eV = 1.602 &#215; 10<super>&#8722;19</super> J",
    ]
))
story.append(sp(4))
story.append(formula_box(
    "Energy-Wavelength Shortcut (fastest for NEET):   E(eV)  =  1240 / &#955;(nm)",
    [
        "&#955; MUST be in nanometres (nm).  Answer is in eV.",
        "For Angstroms:  E(eV) = 12400 / &#955;(&#8491;)      |      Derived from hc = 1240 eV&#183;nm",
        "Quick values:  400 nm &#8594; 3.1 eV   |   500 nm &#8594; 2.48 eV   |   700 nm &#8594; 1.77 eV",
    ]
))
story.append(sp(4))
story.append(formula_box(
    "Photon Momentum:   p  =  h/&#955;  =  E/c",
    ["Photon has ZERO rest mass but has energy E=h&#957; and momentum p=h/&#955;"]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 4 — ATOMIC SPECTRA
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 4 — ATOMIC SPECTRA & SPECTRAL SERIES"), sp(8)]

story += [sec("4.1  Line Spectrum of Hydrogen")]
story += [body("When hydrogen gas is excited (high voltage), it emits light of only certain wavelengths &#8212; "
               "a <b>LINE SPECTRUM</b> (not continuous). Each line = one specific electron transition.")]
story.append(sp(6))

story += [sec("4.2  Spectral Series of Hydrogen")]
story.append(make_table(
    ["Series","n<sub>1</sub> (lower)","n<sub>2</sub> (upper)","Region","First Line (&#955;)"],
    [
        ["Lyman",    "1","2, 3, 4...","UV","121.6 nm"],
        ["Balmer",   "2","3, 4, 5...","Visible + UV","656.3 nm  (H&#945;,  red)"],
        ["Paschen",  "3","4, 5, 6...","Near IR","1875 nm"],
        ["Brackett", "4","5, 6, 7...","IR","4051 nm"],
        ["Pfund",    "5","6, 7, 8...","Far IR","7459 nm"],
        ["Humphreys","6","7, 8, 9...","Far IR","&#8212;"],
    ],
    col_widths=[W*0.17,W*0.15,W*0.17,W*0.18,W*0.33]
))
story.append(sp(5))
story.append(formula_box(
    "Rydberg Equation:   1/&#955;  =  R<sub>H</sub>  &#215;  Z<super>2</super>  &#215;  ( 1/n<sub>1</sub><super>2</super>  &#8722;  1/n<sub>2</sub><super>2</super> )",
    [
        "R<sub>H</sub> = Rydberg constant = 1.097 &#215; 10<super>7</super> m<super>&#8722;1</super> = 109677 cm<super>&#8722;1</super>",
        "Z = atomic number  (Z=1 for H,  Z=2 for He<super>+</super>,  Z=3 for Li<super>2+</super>)",
        "n<sub>1</sub> = lower orbit  (determines the SERIES)   |   n<sub>2</sub> = upper orbit  (n<sub>2</sub> &gt; n<sub>1</sub>)",
        "First line of each series (longest &#955;):   n<sub>2</sub> = n<sub>1</sub> + 1",
        "Series limit (shortest &#955;):   n<sub>2</sub> = &#8734;   &#8594;   1/&#955; = R<sub>H</sub> &#215; Z<super>2</super> / n<sub>1</sub><super>2</super>",
    ]
))
story.append(sp(4))
story.append(formula_box(
    "Number of Spectral Lines  (n<sub>2</sub> &#8594; n<sub>1</sub>):   Lines  =  (n<sub>2</sub> &#8722; n<sub>1</sub>) (n<sub>2</sub> &#8722; n<sub>1</sub> + 1) / 2",
    [
        "Special case  &#8212;  electron falls to ground state (n<sub>1</sub> = 1):   Lines = n(n&#8722;1)/2",
        "Example 1:  n=5 &#8594; n=1:   5&#215;4/2 = <b>10 lines</b>",
        "Example 2:  n=5 &#8594; n=3:   (2)(3)/2 = <b>3 lines</b>",
        "Example 3:  n=4 &#8594; n=2:   (2)(3)/2 = <b>3 lines</b>",
    ]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 5 — BOHR'S MODEL
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 5 — BOHR'S ATOMIC MODEL (1913)"), sp(8)]

story += [sec("5.1  Postulates")]
for b in [
    "Electrons revolve in fixed circular orbits called STATIONARY STATES (no energy radiated while in orbit)",
    "Only orbits where angular momentum = whole-number multiple of h/2&#960; are allowed",
    "Energy absorbed or emitted ONLY when electron jumps between orbits",
    "Energy of emitted/absorbed photon = difference in orbital energies:   &#916;E = h&#957;",
]:  story.append(blt(b))
story.append(sp(8))

story += [sec("5.2  Bohr Model Formulas  (H and H-like ions: He<super>+</super>, Li<super>2+</super>, Be<super>3+</super>...)")]

story.append(sub_h("Formula 1 — Orbital Radius"))
story.append(formula_box(
    "r<sub>n</sub>  =  0.529  &#215;  n<super>2</super> / Z     &#8491;",
    [
        "r<sub>n</sub> = radius of n<super>th</super> orbit  (distance of electron from nucleus)",
        "0.529 &#8491; = a<sub>0</sub> = Bohr radius = radius of 1<super>st</super> orbit of H  (n=1, Z=1)",
        "n = principal quantum number = 1, 2, 3, 4...   |   Z = atomic number",
        "r<sub>n</sub> &#8733; n<super>2</super>  (quadruples when n doubles)   |   r<sub>n</sub> &#8733; 1/Z  (smaller for larger Z)",
        "Convert to metres: multiply by 10<super>&#8722;10</super>   (1 &#8491; = 10<super>&#8722;10</super> m)",
    ]
))
story.append(sp(4))
story.append(make_table(
    ["n","H  (Z=1)","He<super>+</super>  (Z=2)","Li<super>2+</super>  (Z=3)"],
    [
        ["1","0.529 &#8491;","0.265 &#8491;","0.176 &#8491;"],
        ["2","2.116 &#8491;","1.058 &#8491;","0.705 &#8491;"],
        ["3","4.761 &#8491;","2.381 &#8491;","1.587 &#8491;"],
    ],
    col_widths=[W*0.15,W*0.28,W*0.28,W*0.29]
))
story.append(sp(6))

story.append(sub_h("Formula 2 — Orbital Velocity"))
story.append(formula_box(
    "v<sub>n</sub>  =  2.18 &#215; 10<super>6</super>  &#215;  Z / n     m/s",
    [
        "2.18 &#215; 10<super>6</super> m/s = velocity in 1<super>st</super> orbit of H  =  c/137  (much less than speed of light)",
        "v<sub>n</sub> &#8733; Z/n   |   Outer electrons move SLOWER  (hence easier to remove)",
        "H (n=1): 2.18&#215;10<super>6</super> m/s   |   H (n=2): 1.09&#215;10<super>6</super> m/s   |   H (n=3): 0.727&#215;10<super>6</super> m/s",
    ]
))
story.append(sp(6))

story.append(sub_h("Formula 3 — Total Energy"))
story.append(formula_box(
    "E<sub>n</sub>  =  &#8722;13.6  &#215;  Z<super>2</super> / n<super>2</super>     eV     =     &#8722;2.18&#215;10<super>&#8722;18</super>  &#215;  Z<super>2</super>/n<super>2</super>     J",
    [
        "E<sub>n</sub> = total energy of electron in n<super>th</super> orbit &#8212; ALWAYS NEGATIVE for a bound electron",
        "&#8722;13.6 eV = energy in 1<super>st</super> orbit of H  =  ionisation energy of H from ground state",
        "Negative sign indicates electron is BOUND to nucleus",
        "n=1 is most negative = most stable   |   At n=&#8734;:  E=0  (electron free = atom ionised)",
    ]
))
story.append(sp(4))
story.append(make_table(
    ["n","Energy of H (Z=1)"],
    [
        ["1  (ground state)","&#8722;13.6 eV"],
        ["2","&#8722;3.4 eV"],
        ["3","&#8722;1.51 eV"],
        ["4","&#8722;0.85 eV"],
        ["5","&#8722;0.544 eV"],
        ["&#8734;  (ionised)","0 eV"],
    ],
    col_widths=[W*0.40,W*0.60]
))
story.append(sp(6))

story.append(sub_h("Formula 4 — Kinetic Energy, Potential Energy, Total Energy"))
story.append(formula_box(
    "KE  =  &#8722;E<sub>n</sub>  =  +13.6 Z<super>2</super>/n<super>2</super>  eV     (always POSITIVE)\n"
    "PE  =  2E<sub>n</sub>  =  &#8722;27.2 Z<super>2</super>/n<super>2</super>  eV     (always NEGATIVE)\n"
    "TE  =  KE + PE  =  E<sub>n</sub>",
    [
        "Three golden relationships:   KE = &#8722;TE     |     PE = 2 &#215; TE     |     PE = &#8722;2 &#215; KE",
        "Verification H (n=1):  KE=+13.6 eV,  PE=&#8722;27.2 eV,  TE=&#8722;13.6 eV     (13.6 &#8722; 27.2 = &#8722;13.6 &#10003;)",
    ]
))
story.append(sp(6))

story.append(sub_h("Formula 5 — Ionisation Energy"))
story.append(formula_box(
    "IE  =  0 &#8722; E<sub>n</sub>  =  +13.6  &#215;  Z<super>2</super> / n<super>2</super>     eV",
    [
        "IE = energy to remove electron from orbit n to infinity  (n &#8594; &#8734;)",
        "IE is ALWAYS positive   |   Decreases as n increases  (easier to remove from outer orbits)",
        "H (n=1): 13.6 eV = 1312 kJ/mol   |   He<super>+</super> (Z=2, n=1): 54.4 eV   |   Li<super>2+</super> (Z=3, n=1): 122.4 eV",
    ]
))
story.append(sp(6))

story.append(sub_h("Formula 6 — Energy of Emitted/Absorbed Photon"))
story.append(formula_box(
    "&#916;E  =  13.6  &#215;  Z<super>2</super>  &#215;  ( 1/n<sub>1</sub><super>2</super>  &#8722;  1/n<sub>2</sub><super>2</super> )     eV",
    [
        "n<sub>1</sub> = lower orbit  (electron LANDS here after emission)   n<sub>1</sub> &lt; n<sub>2</sub>",
        "n<sub>2</sub> = upper orbit  (electron STARTS here)   n<sub>2</sub> &gt; n<sub>1</sub>",
        "&#916;E is always POSITIVE",
        "EMISSION: electron falls n<sub>2</sub> &#8594; n<sub>1</sub>,  photon is released",
        "ABSORPTION: electron jumps n<sub>1</sub> &#8594; n<sub>2</sub>,  photon is absorbed",
    ]
))
story.append(sp(6))

story.append(sub_h("Formula 7 — Angular Momentum Quantisation (Bohr's Condition)"))
story.append(formula_box(
    "mv<sub>n</sub>r<sub>n</sub>  =  nh / 2&#960;  =  n&#295;",
    [
        "m = mass of electron (kg)   |   v = orbital velocity (m/s)   |   r<sub>n</sub> = orbital radius (m)",
        "n = principal quantum number  (1, 2, 3...)",
        "h = Planck's constant = 6.626&#215;10<super>&#8722;34</super> J&#183;s   |   &#295; = h/2&#960; = 1.055&#215;10<super>&#8722;34</super> J&#183;s",
    ]
))
story.append(sp(6))

story.append(sub_h("Formulas 8 & 9 — Time Period and Frequency of Revolution"))
story.append(formula_box(
    "T<sub>n</sub>  =  2&#960; r<sub>n</sub> / v<sub>n</sub>     &#8733;     n<super>3</super> / Z<super>2</super>     |     &#957;<sub>n</sub>  =  v<sub>n</sub> / (2&#960; r<sub>n</sub>)     &#8733;     Z<super>2</super> / n<super>3</super>",
    [
        "T<sub>n</sub> &#8733; n<super>3</super>  &#8212;  if n doubles, T becomes 2<super>3</super> = 8 times longer",
        "&#957;<sub>n</sub> &#8733; 1/n<super>3</super>  &#8212;  outer electrons orbit LESS frequently",
    ]
))
story.append(sp(3))
story.append(trap_box("If n doubles, time period increases by 2<super>3</super> = 8 times.  Frequency decreases 8 times."))
story.append(sp(6))

story += [sec("5.3  Limitations of Bohr's Model")]
for b in [
    "Works ONLY for hydrogen and H-like ions (one electron). Fails for multi-electron atoms.",
    "Cannot explain fine structure of spectral lines (Zeeman and Stark effects).",
    "Cannot explain relative intensities of spectral lines.",
    "Violates Heisenberg's Uncertainty Principle &#8212; definite orbits imply exact position AND momentum simultaneously.",
    "Does not explain chemical bonding or 3D shapes of molecules.",
]:  story.append(blt(b))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 6 — PHOTOELECTRIC EFFECT
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 6 — PHOTOELECTRIC EFFECT"), sp(8)]

story += [sec("6.1  Phenomenon and Why Wave Theory Fails")]
story += [body("When light of sufficient frequency falls on a metal surface, electrons are ejected  "
               "(called <b>photoelectrons</b>). Einstein explained this in 1905 using Planck's quantum theory  "
               "(Nobel Prize 1921).")]
story += [sp(3), sub_h("Observations that Wave Theory CANNOT explain:")]
for b in [
    "Emission is INSTANTANEOUS  (wave theory predicts a time delay for energy buildup)",
    "Below threshold frequency &#957;<sub>0</sub>:  NO emission regardless of how intense the light is",
    "Above &#957;<sub>0</sub>:  KE<sub>max</sub> depends on FREQUENCY, NOT intensity",
    "More intensity &#8594; MORE electrons emitted  (not faster electrons)",
]:  story.append(blt(b))
story.append(sp(5))

story.append(sub_h("Formula 10 — Einstein's Photoelectric Equation"))
story.append(formula_box(
    "h&#957;  =  &#966;  +  KE<sub>max</sub>",
    [
        "h&#957; = energy of incident photon (J or eV)",
        "&#966; (phi) = work function = minimum energy to eject ONE electron = h&#957;<sub>0</sub>  (J or eV)",
        "KE<sub>max</sub> = maximum KE of ejected photoelectron = (1/2) m<sub>e</sub> v<super>2</super><sub>max</sub>  (J or eV)",
        "If h&#957; &lt; &#966;:  NO emission at all (even at very high intensity)",
        "If h&#957; = &#966;:  Emission just starts,  KE<sub>max</sub> = 0",
    ]
))
story.append(sp(5))

story.append(sub_h("Formula 11 — Work Function"))
story.append(formula_box(
    "&#966;  =  h&#957;<sub>0</sub>",
    [
        "&#966; = work function &#8212; a fixed property of each metal surface",
        "&#957;<sub>0</sub> = threshold frequency &#8212; minimum frequency for photoelectric emission",
    ]
))
story.append(sp(4))
story.append(make_table(
    ["Metal","&#966; (eV)","Threshold &#955;<sub>0</sub> (nm)","Responds to"],
    [
        ["Cs","2.0","620 nm","Visible light (red)"],
        ["K", "2.2","564 nm","Visible light (yellow)"],
        ["Na","2.3","539 nm","Visible light (green)"],
        ["Al","4.3","288 nm","UV only"],
        ["Zn","4.3","288 nm","UV only"],
        ["Pt","5.6","221 nm","UV only"],
    ],
    col_widths=[W*0.12,W*0.18,W*0.26,W*0.44], left_cols=[3]
))
story.append(sp(5))

story.append(sub_h("Formula 12 — Threshold Wavelength"))
story.append(formula_box(
    "&#955;<sub>0</sub>  =  hc / &#966;  =  c / &#957;<sub>0</sub>     &#8594;     &#955;<sub>0</sub>(nm)  =  1240 / &#966;(eV)",
    [
        "Light with &#955; &lt; &#955;<sub>0</sub>  (higher frequency)  &#8594;  emission OCCURS",
        "Light with &#955; &gt; &#955;<sub>0</sub>  (lower frequency)  &#8594;  NO emission  (regardless of intensity)",
        "Example:  &#966;=2.0 eV  &#8594;  &#955;<sub>0</sub>=620 nm (red)   |   &#966;=4.3 eV  &#8594;  &#955;<sub>0</sub>=288 nm (UV)",
    ]
))
story.append(sp(5))

story.append(sub_h("Formula 13 — Stopping Potential"))
story.append(formula_box(
    "eV<sub>0</sub>  =  KE<sub>max</sub>  =  h&#957;  &#8722;  &#966;",
    [
        "e = 1.602&#215;10<super>&#8722;19</super> C   |   V<sub>0</sub> = stopping potential  (minimum reverse voltage to stop ALL photoelectrons)  in Volts",
        "KE<sub>max</sub> in eV  numerically equals  V<sub>0</sub> in Volts  &#8212;  if KE<sub>max</sub>=2.5 eV then V<sub>0</sub>=2.5 V",
        "More INTENSITY:  photocurrent increases,  V<sub>0</sub> UNCHANGED",
        "Higher FREQUENCY:  V<sub>0</sub> increases,  photocurrent unchanged",
        "V<sub>0</sub> vs &#957; graph:  Slope = h/e = 4.136&#215;10<super>&#8722;15</super> V&#183;s   |   x-intercept = &#957;<sub>0</sub>   |   y-intercept = &#8722;&#966;/e",
    ]
))
story.append(sp(5))

story.append(sub_h("Formula 14 — Number of Photons per Unit Area per Second"))
story.append(formula_box(
    "n<sub>photons</sub>  =  I / h&#957;  =  I&#955; / hc",
    [
        "n<sub>photons</sub> = number of photons hitting unit area per second  (s<super>&#8722;1</super>&#183;m<super>&#8722;2</super>)",
        "I = intensity = energy per unit area per second  (W/m<super>2</super>)",
        "More intensity = MORE photons = MORE electrons ejected = LARGER photocurrent",
        "Each photon gives its energy to ONE electron &#8212; so KE<sub>max</sub> is unaffected by intensity",
    ]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 7 — de BROGLIE
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 7 — DUAL NATURE OF MATTER  (de Broglie, 1924)"), sp(8)]

story += [sec("7.1  Background")]
for b in [
    "Louis de Broglie (1924): If light (wave) can behave as particles (photons), matter (particles) should also show wave properties",
    "Wave-particle duality: all matter has both wave and particle properties",
    "Only observable for MICROSCOPIC particles (electrons, protons) &#8212; not for macroscopic objects",
    "Experimental proof: Davisson-Germer experiment (1927) &#8212; electron diffraction by nickel crystal confirmed electron wave nature",
]:  story.append(blt(b))
story.append(sp(5))

story.append(sub_h("Formula 15 — de Broglie Wavelength (General)"))
story.append(formula_box(
    "&#955;  =  h / mv  =  h / p",
    [
        "&#955; = de Broglie (matter) wavelength (m)",
        "h = Planck's constant = 6.626&#215;10<super>&#8722;34</super> J&#183;s",
        "m = mass of particle (kg)   |   v = velocity (m/s)   |   p = mv = momentum (kg&#183;m/s)",
        "Cricket ball (0.1 kg, 30 m/s):   &#955; ~ 2&#215;10<super>&#8722;34</super> m  &#8212;  unmeasurably tiny",
        "Electron (9.1&#215;10<super>&#8722;31</super> kg, 10<super>6</super> m/s):   &#955; ~ 0.7 nm  &#8212;  detectable by X-ray diffraction!",
    ]
))
story.append(sp(5))

story.append(sub_h("Formula 16 — de Broglie Wavelength from Kinetic Energy"))
story.append(formula_box(
    "&#955;  =  h / &#8730;(2m &#183; KE)",
    [
        "Derived from &#955; = h/mv and KE = (1/2)mv<super>2</super>   &#8594;   mv = &#8730;(2m&#183;KE)",
        "KE MUST be in Joules.  Convert:  KE(J) = KE(eV) &#215; 1.602&#215;10<super>&#8722;19</super>",
    ]
))
story.append(sp(5))

story.append(sub_h("Formula 17 — de Broglie Wavelength for Accelerated Electron"))
story.append(formula_box(
    "&#955;  =  12.27 / &#8730;V     &#8491;       (electrons only)",
    [
        "12.27 = constant for ELECTRONS only = h/&#8730;(2&#183;m<sub>e</sub>&#183;e)  in &#8491;&#183;V<super>1/2</super> units",
        "V = accelerating potential difference in VOLTS   |   Answer in ANGSTROMS",
        "For PROTON:   &#955; = 0.286/&#8730;V  &#8491;",
        "V=100 V &#8594; &#955;=1.227 &#8491;   |   V=1000 V &#8594; &#955;=0.388 &#8491;   |   V=10000 V &#8594; &#955;=0.123 &#8491;",
        "Used in electron microscopy  (&#955; much smaller than visible light &#8594; far higher resolution)",
    ]
))
story.append(sp(5))

story.append(sub_h("Formula 18 — de Broglie and Bohr Connection"))
story.append(formula_box(
    "2&#960; r<sub>n</sub>  =  n&#955;   (circumference = whole-number multiples of the de Broglie wavelength)",
    [
        "Bohr's quantisation (mvr = nh/2&#960;) is equivalent to the electron's de Broglie wave fitting exactly into the orbit.",
        "This gives a wave-mechanical basis to Bohr's model.",
    ]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 8 — HEISENBERG
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 8 — HEISENBERG'S UNCERTAINTY PRINCIPLE (1927)"), sp(8)]

story += [sec("8.1  Statement")]
story += [body("It is IMPOSSIBLE to simultaneously determine the exact position AND exact momentum (or velocity) "
               "of a microscopic particle. This is a fundamental law of nature &#8212; NOT due to measurement error.")]
story.append(sp(5))

story.append(sub_h("Formula 19 — Heisenberg's Uncertainty Principle"))
story.append(formula_box(
    "&#916;x  &#183;  &#916;p  &#8805;  h / 4&#960;         or         &#916;x  &#183;  m&#916;v  &#8805;  h / 4&#960;",
    [
        "&#916;x = uncertainty in position (m)   |   &#916;p = uncertainty in momentum (kg&#183;m/s)",
        "&#916;v = uncertainty in velocity (m/s)",
        "h/4&#960; = 5.27&#215;10<super>&#8722;35</super> J&#183;s  (minimum allowed product  &#8212;  cannot be smaller than this)",
        "Equivalent form:   &#916;x &#183; &#916;p  &#8805;  &#295;/2       where  &#295; = h/2&#960; = 1.055&#215;10<super>&#8722;34</super> J&#183;s",
        "Energy-time version:   &#916;E &#183; &#916;t  &#8805;  h/4&#960;",
    ]
))
story.append(sp(5))

story.append(sub_h("Formula 20 — Minimum Uncertainty in Velocity"))
story.append(formula_box(
    "&#916;v<sub>min</sub>  =  h / (4&#960;  &#215;  m  &#215;  &#916;x)",
    [
        "If &#916;x = 0 (exact position known):   &#916;p = &#8734;  (momentum completely unknown)",
        "If &#916;p = 0 (exact momentum known):   &#916;x = &#8734;  (position completely unknown)",
        "Applies to ALL microscopic particles &#8212; not due to experimental limitations",
    ]
))
story.append(sp(4))
story.append(trap_box("Heisenberg's UP INVALIDATES Bohr's model &#8212; definite circular orbits would require exact position AND exact velocity simultaneously, which is FORBIDDEN by the Uncertainty Principle."))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 9 — QUANTUM NUMBERS
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 9 — QUANTUM MECHANICAL MODEL & QUANTUM NUMBERS"), sp(8)]

story += [sec("9.1  Orbital vs Orbit")]
story.append(make_table(
    ["Feature","Orbit (Bohr)","Orbital (Quantum Mechanics)"],
    [
        ["Definition","Definite circular path","Region of space where probability of finding electron is 90% or more"],
        ["Heisenberg","VIOLATES HUP","Consistent with HUP"],
        ["Dimensions","2D circle","3D region of space"],
        ["Position","Exact position known","Only probability density known"],
    ],
    col_widths=[W*0.18,W*0.28,W*0.54], left_cols=[0,1,2]
))
story.append(sp(8))

story += [sec("9.2  The Four Quantum Numbers")]
story += [body("Every electron in an atom is described by a UNIQUE set of 4 quantum numbers. "
               "No two electrons can have the same set of all 4  (Pauli's Exclusion Principle).")]
story.append(sp(5))

story.append(sub_h("(a) Principal Quantum Number  (n)"))
story.append(formula_box(
    "n  =  1,  2,  3,  4,  5,  6,  7  ...",
    [
        "Determines ENERGY and SIZE of the orbital",
        "Higher n = larger orbital = higher energy = farther from nucleus",
        "Shell names:  n=1 &#8594; K     n=2 &#8594; L     n=3 &#8594; M     n=4 &#8594; N",
    ]
))
story.append(sp(5))

story.append(sub_h("(b) Azimuthal Quantum Number  (l)  —  Subsidiary / Angular Momentum QN"))
story.append(formula_box(
    "l  =  0,  1,  2,  ...,  (n&#8722;1)",
    [
        "Determines SHAPE of orbital and SUBSHELL",
        "l=0 &#8594; s (sphere)   |   l=1 &#8594; p (dumbbell)   |   l=2 &#8594; d (double dumbbell)   |   l=3 &#8594; f (complex)",
        "For n=1: l=0 only (1s)   |   n=2: l=0,1 (2s, 2p)   |   n=3: l=0,1,2 (3s, 3p, 3d)",
    ]
))
story.append(sp(5))

story.append(sub_h("(c) Magnetic Quantum Number  (m<sub>l</sub>)"))
story.append(formula_box(
    "m<sub>l</sub>  =  &#8722;l,  &#8722;(l&#8722;1),  ...,  0,  ...,  (l&#8722;1),  +l     &#8594;     Total values = 2l+1",
    [
        "Determines ORIENTATION of orbital in space",
        "s (l=0):  m<sub>l</sub>=0  (1 orbital)   |   p (l=1):  m<sub>l</sub>=&#8722;1,0,+1  (3 orbitals: p<sub>x</sub>, p<sub>y</sub>, p<sub>z</sub>)",
        "d (l=2):  m<sub>l</sub>=&#8722;2,&#8722;1,0,+1,+2  (5 orbitals)   |   f (l=3):  7 orbitals",
    ]
))
story.append(sp(5))

story.append(sub_h("(d) Spin Quantum Number  (m<sub>s</sub>)"))
story.append(formula_box(
    "m<sub>s</sub>  =  +1/2  (spin up &#8593;)     or     &#8722;1/2  (spin down &#8595;)     [only two possible values]",
    [
        "Every electron has spin &#8212; an intrinsic property",
        "Pauli's principle: two electrons in the same orbital MUST have opposite m<sub>s</sub> values",
    ]
))
story.append(sp(8))

story += [sec("9.3  Angular Momentum Formulas")]
story.append(sub_h("Formula 21 — Orbital Angular Momentum"))
story.append(formula_box(
    "L  =  &#8730;[l(l+1)]  &#183;  &#295;",
    [
        "L = magnitude of orbital angular momentum (J&#183;s)",
        "l = azimuthal quantum number   |   &#295; = h/2&#960; = 1.055&#215;10<super>&#8722;34</super> J&#183;s",
        "s (l=0): L=0   |   p (l=1): L=&#8730;2 &#183; &#295;   |   d (l=2): L=&#8730;6 &#183; &#295;   |   f (l=3): L=2&#8730;3 &#183; &#295;",
    ]
))
story.append(trap_box("Bohr's model gave L=n&#295;  (WRONG). Correct quantum mechanics: L=&#8730;[l(l+1)]&#183;&#295;. s orbitals have ZERO orbital angular momentum &#8212; NOT &#295;."))
story.append(sp(5))

story.append(sub_h("Formula 22 — z-Component of Angular Momentum"))
story.append(formula_box(
    "L<sub>z</sub>  =  m<sub>l</sub>  &#183;  &#295;",
    [
        "L<sub>z</sub> = projection of angular momentum on the z-axis",
        "p (l=1):  L<sub>z</sub>=&#8722;&#295;, 0, +&#295;  (3 orientations)",
        "d (l=2):  L<sub>z</sub>=&#8722;2&#295;, &#8722;&#295;, 0, +&#295;, +2&#295;  (5 orientations)",
        "A magnetic field splits these orientations  &#8594;  Zeeman effect",
    ]
))
story.append(sp(5))

story.append(sub_h("Formula 23 — Spin Angular Momentum"))
story.append(formula_box(
    "S  =  &#8730;[s(s+1)]  &#183;  &#295;  =  (&#8730;3/2)  &#183;  &#295;     [same for ALL electrons]",
    [
        "s = spin quantum number = 1/2 always (intrinsic property, cannot change)",
        "The MAGNITUDE of spin is fixed for all electrons; only DIRECTION (m<sub>s</sub> = &#177;1/2) can vary",
    ]
))
story.append(sp(8))

story += [sec("9.4  Shell / Subshell Count Formulas")]
story.append(formula_box(
    "Orbitals in n<super>th</super> shell  =  n<super>2</super>                Max electrons in n<super>th</super> shell  =  2n<super>2</super>\n"
    "Orbitals in a subshell  =  2l+1          Max electrons in a subshell  =  2(2l+1)",
    [
        "n=1: 1 orbital, 2 e<super>&#8722;</super>   |   n=2: 4 orbitals, 8 e<super>&#8722;</super>   |   n=3: 9 orbitals, 18 e<super>&#8722;</super>   |   n=4: 16 orbitals, 32 e<super>&#8722;</super>",
        "s: 1 orbital, 2 e<super>&#8722;</super>   |   p: 3 orbitals, 6 e<super>&#8722;</super>   |   d: 5 orbitals, 10 e<super>&#8722;</super>   |   f: 7 orbitals, 14 e<super>&#8722;</super>",
    ]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 10 — SHAPES OF ORBITALS
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 10 — SHAPES OF ORBITALS"), sp(8)]
story.append(make_table(
    ["Orbital","Shape","Angular Nodes","Nodal Planes","Starts at n"],
    [
        ["s (l=0)","Sphere","0","None (spherical symmetry)","n=1"],
        ["p (l=1)","Dumbbell  (2 lobes)","1","p<sub>x</sub>: yz-plane   p<sub>y</sub>: xz-plane   p<sub>z</sub>: xy-plane","n=2"],
        ["d (l=2)","Double dumbbell/clover (4 lobes); d<sub>z</sub><super>2</super> has donut+dumbbell","2","Two nodal planes per orbital","n=3"],
        ["f (l=3)","Complex multi-lobed","3","Three nodal regions","n=4"],
    ],
    col_widths=[W*0.12,W*0.31,W*0.14,W*0.29,W*0.14], left_cols=[1,3]
))
story.append(sp(4))
for b in [
    "1s: 1 sphere.  2s: 2 concentric spheres (1 radial node inside).  3s: 3 concentric spheres.",
    "Three p orbitals (p<sub>x</sub>, p<sub>y</sub>, p<sub>z</sub>) are identical in shape but oriented along different axes &#8212; DEGENERATE (same energy in the absence of a field).",
    "Five d orbitals: d<sub>xy</sub>, d<sub>xz</sub>, d<sub>yz</sub>, d<sub>x&#178;&#8722;y&#178;</sub> (clover-leaf) and d<sub>z&#178;</sub> (doughnut + dumbbell along z-axis).",
]:  story.append(blt(b))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 11 — NODES
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 11 — NODES"), sp(8)]
story.append(formula_box(
    "Radial nodes  =  n &#8722; l &#8722; 1\n"
    "Angular nodes  =  l\n"
    "Total nodes  =  n &#8722; 1",
    [
        "Radial nodes = spherical surfaces INSIDE the orbital where electron density = 0",
        "Angular nodes = flat planes (or cones) through the NUCLEUS where electron probability = 0",
        "Proof:  (n&#8722;l&#8722;1) + l  =  n&#8722;1     &#10003;  (always true for any orbital)",
    ]
))
story.append(sp(5))
story.append(make_table(
    ["Orbital","n","l","Radial  (n&#8722;l&#8722;1)","Angular  (l)","Total  (n&#8722;1)"],
    [
        ["1s","1","0","0","0","0"],
        ["2s","2","0","1","0","1"],
        ["2p","2","1","0","1","1"],
        ["3s","3","0","2","0","2"],
        ["3p","3","1","1","1","2"],
        ["3d","3","2","0  &#9888;","2","2"],
        ["4s","4","0","3","0","3"],
        ["4p","4","1","2","1","3"],
        ["4d","4","2","1","2","3"],
        ["4f","4","3","0","3","3"],
    ],
    col_widths=[W*0.12,W*0.08,W*0.08,W*0.24,W*0.22,W*0.26]
))
story.append(sp(3))
story.append(trap_box("3d has ZERO radial nodes  (students often say 1 &#8212; that is WRONG)."))
story.append(trap_box("2s and 2p BOTH have 1 total node but DIFFERENT types:  2s has 1 RADIAL node (spherical shell),  2p has 1 ANGULAR node (flat plane through nucleus)."))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 12 — ELECTRONIC CONFIGURATION
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 12 — ELECTRONIC CONFIGURATION"), sp(8)]

story += [sec("12.1  Three Fundamental Rules")]
story.append(sub_h("Rule 1 — Aufbau Principle"))
story += [body("Fill orbitals in INCREASING order of energy (n+l rule). "
               "If two orbitals have the same (n+l), fill the one with LOWER n first.")]
story.append(sp(3))
story.append(formula_box(
    "Filling order:  1s &#8594; 2s &#8594; 2p &#8594; 3s &#8594; 3p &#8594; 4s &#8594; 3d &#8594; 4p &#8594; 5s &#8594; 4d &#8594; 5p &#8594; 6s &#8594; 4f &#8594; 5d &#8594; 6p",
    [
        "4s (n+l=4) is filled BEFORE 3d (n+l=5)",
        "4f (n+l=7) is filled BEFORE 5d (n+l=7, but 5d has higher n  &#8594;  filled later)",
    ]
))
story.append(sp(5))

story.append(sub_h("Rule 2 — Pauli's Exclusion Principle"))
story += [body("No two electrons in the same atom can have the SAME SET of all four quantum numbers. "
               "Maximum 2 electrons per orbital with OPPOSITE spins (one &#8593;, one &#8595;).")]
story.append(sp(5))

story.append(sub_h("Rule 3 — Hund's Rule of Maximum Multiplicity"))
story += [body("In degenerate orbitals (same energy), electrons fill SINGLY first (one per orbital) "
               "with PARALLEL spins before any pairing occurs.")]
story.append(sp(3))
story.append(note_box("Nitrogen (Z=7):  1s<super>2</super> 2s<super>2</super> 2p<super>3</super>   &#8594;   2p orbitals:  &#8593; | &#8593; | &#8593;   (singly occupied  &#8212;  CORRECT)     NOT:  &#8593;&#8595; | &#8593; | _  (pairing before all singly occupied  &#8212;  WRONG)"))
story.append(sp(8))

story += [sec("12.2  Electronic Configurations  (Z = 1 to 30)")]
story.append(make_table(
    ["Z","Element","Configuration","Unpaired e<super>&#8722;</super>"],
    [
        ["1","H","1s<super>1</super>","1"],
        ["2","He","1s<super>2</super>","0"],
        ["3","Li","[He] 2s<super>1</super>","1"],
        ["4","Be","[He] 2s<super>2</super>","0"],
        ["5","B","[He] 2s<super>2</super> 2p<super>1</super>","1"],
        ["6","C","[He] 2s<super>2</super> 2p<super>2</super>","2"],
        ["7","N","[He] 2s<super>2</super> 2p<super>3</super>","3"],
        ["8","O","[He] 2s<super>2</super> 2p<super>4</super>","2"],
        ["9","F","[He] 2s<super>2</super> 2p<super>5</super>","1"],
        ["10","Ne","[He] 2s<super>2</super> 2p<super>6</super>","0"],
        ["11","Na","[Ne] 3s<super>1</super>","1"],
        ["12","Mg","[Ne] 3s<super>2</super>","0"],
        ["13","Al","[Ne] 3s<super>2</super> 3p<super>1</super>","1"],
        ["14","Si","[Ne] 3s<super>2</super> 3p<super>2</super>","2"],
        ["15","P","[Ne] 3s<super>2</super> 3p<super>3</super>","3"],
        ["16","S","[Ne] 3s<super>2</super> 3p<super>4</super>","2"],
        ["17","Cl","[Ne] 3s<super>2</super> 3p<super>5</super>","1"],
        ["18","Ar","[Ne] 3s<super>2</super> 3p<super>6</super>","0"],
        ["19","K","[Ar] 4s<super>1</super>","1"],
        ["20","Ca","[Ar] 4s<super>2</super>","0"],
        ["21","Sc","[Ar] 3d<super>1</super> 4s<super>2</super>","1"],
        ["22","Ti","[Ar] 3d<super>2</super> 4s<super>2</super>","2"],
        ["23","V","[Ar] 3d<super>3</super> 4s<super>2</super>","3"],
        ["24  &#9888;","Cr  (EXCEPTION)","[Ar] 3d<super>5</super> 4s<super>1</super>   (NOT 3d<super>4</super> 4s<super>2</super>)","6"],
        ["25","Mn","[Ar] 3d<super>5</super> 4s<super>2</super>","5"],
        ["26","Fe","[Ar] 3d<super>6</super> 4s<super>2</super>","4"],
        ["27","Co","[Ar] 3d<super>7</super> 4s<super>2</super>","3"],
        ["28","Ni","[Ar] 3d<super>8</super> 4s<super>2</super>","2"],
        ["29  &#9888;","Cu  (EXCEPTION)","[Ar] 3d<super>10</super> 4s<super>1</super>   (NOT 3d<super>9</super> 4s<super>2</super>)","1"],
        ["30","Zn","[Ar] 3d<super>10</super> 4s<super>2</super>","0"],
    ],
    col_widths=[W*0.13,W*0.18,W*0.53,W*0.16], left_cols=[2]
))
story.append(sp(4))
story.append(note_box(
    "Reason for Cr and Cu exceptions:  Half-filled (d<super>5</super>) and fully-filled (d<super>10</super>) "
    "subshells have extra stability due to  (1) Symmetrical distribution of electrons,  "
    "(2) Maximum exchange energy."
))
story.append(sp(8))

story += [sec("12.3  Magnetic Properties")]
story.append(sub_h("Formula 31 — Spin Multiplicity"))
story.append(formula_box(
    "Spin multiplicity  =  2S + 1         where  S  =  (number of unpaired electrons) / 2",
    [
        "Multiplicity = 1 (singlet):  all electrons paired  &#8594;  DIAMAGNETIC (repelled by magnetic field)",
        "Multiplicity &gt; 1:  unpaired electrons  &#8594;  PARAMAGNETIC (attracted by magnetic field)",
        "Higher multiplicity = more unpaired electrons = stronger paramagnetism",
    ]
))
story.append(sp(4))
story.append(make_table(
    ["Unpaired e<super>&#8722;</super>","S","Multiplicity","Type","Example"],
    [
        ["0","0","1  (singlet)","Diamagnetic","He, Ne, Zn, Be, Mg"],
        ["1","1/2","2  (doublet)","Paramagnetic","H, F, Na, Cu<super>2+</super>"],
        ["2","1","3  (triplet)","Paramagnetic","C, O, Ni<super>2+</super>"],
        ["3","3/2","4  (quartet)","Paramagnetic","N, Co<super>2+</super>, Cr<super>3+</super>"],
        ["4","2","5  (quintet)","Paramagnetic","Fe<super>2+</super>, Cr<super>2+</super>"],
        ["5","5/2","6  (sextet)","Paramagnetic","Mn<super>2+</super>, Fe<super>3+</super>"],
    ],
    col_widths=[W*0.14,W*0.10,W*0.22,W*0.18,W*0.36], left_cols=[4]
))
story.append(sp(5))

story.append(sub_h("Formula 32 — Magnetic Moment"))
story.append(formula_box(
    "&#956;  =  &#8730;[n(n+2)]     BM   (Bohr Magneton)",
    [
        "n = number of unpaired electrons   |   1 BM (Bohr Magneton) = 9.274&#215;10<super>&#8722;24</super> J/T",
        "1 unpaired: &#956; = &#8730;3 &#8776; 1.73 BM  (Cu<super>2+</super>, Ti<super>3+</super>)   |   2 unpaired: &#956; = &#8730;8 &#8776; 2.83 BM  (Ni<super>2+</super>)",
        "3 unpaired: &#956; = &#8730;15 &#8776; 3.87 BM  (Co<super>2+</super>, Cr<super>3+</super>)   |   4 unpaired: &#956; = &#8730;24 &#8776; 4.90 BM  (Fe<super>2+</super>)",
        "5 unpaired: &#956; = &#8730;35 &#8776; 5.92 BM  (Mn<super>2+</super>, Fe<super>3+</super>)",
    ]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 13 — CONSTANTS
# ══════════════════════════════════════════════════════════════════
story.append(PageBreak())
story += [part_header("PART 13 — COMPLETE CONSTANTS REFERENCE"), sp(8)]
story.append(make_table(
    ["Symbol","Name","Value","Unit"],
    [
        ["h","Planck's constant","6.626 &#215; 10<super>&#8722;34</super>","J&#183;s"],
        ["&#295;","Reduced Planck  (h/2&#960;)","1.055 &#215; 10<super>&#8722;34</super>","J&#183;s"],
        ["c","Speed of light","3 &#215; 10<super>8</super>","m/s"],
        ["e","Elementary charge","1.602 &#215; 10<super>&#8722;19</super>","C"],
        ["m<sub>e</sub>","Mass of electron","9.109 &#215; 10<super>&#8722;31</super>","kg"],
        ["m<sub>p</sub>","Mass of proton","1.673 &#215; 10<super>&#8722;27</super>","kg"],
        ["m<sub>n</sub>","Mass of neutron","1.675 &#215; 10<super>&#8722;27</super>","kg"],
        ["a<sub>0</sub>","Bohr radius  (1<super>st</super> orbit of H)","0.529 &#8491; = 5.29&#215;10<super>&#8722;11</super> m","m"],
        ["R<sub>H</sub>","Rydberg constant","1.097&#215;10<super>7</super> m<super>&#8722;1</super>  =  109677 cm<super>&#8722;1</super>","m<super>&#8722;1</super>"],
        ["r<sub>0</sub>","Nuclear radius constant","1.2 &#215; 10<super>&#8722;15</super> m  =  1.2 fm","m"],
        ["1 eV","In Joules","1.602 &#215; 10<super>&#8722;19</super>","J"],
        ["1 amu","In kg","1.66 &#215; 10<super>&#8722;27</super>","kg"],
        ["1 &#8491;","In metres","10<super>&#8722;10</super>","m"],
        ["1 nm","In metres","10<super>&#8722;9</super>","m"],
        ["1 pm","In metres","10<super>&#8722;12</super>","m"],
        ["1 fm","In metres","10<super>&#8722;15</super>","m"],
    ],
    col_widths=[W*0.14,W*0.40,W*0.32,W*0.14], left_cols=[1]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# MASTER FORMULA SHEET
# ══════════════════════════════════════════════════════════════════
story.append(PageBreak())
story += [part_header("MASTER FORMULA SHEET — ALL 41 FORMULAS AT A GLANCE"), sp(8)]
story.append(make_table(
    ["#","Formula Name","Expression"],
    [
        ["1","Orbital radius","r<sub>n</sub> = 0.529 &#215; n<super>2</super>/Z   &#8491;"],
        ["2","Orbital velocity","v<sub>n</sub> = 2.18&#215;10<super>6</super> &#215; Z/n   m/s"],
        ["3","Total energy","E<sub>n</sub> = &#8722;13.6 &#215; Z<super>2</super>/n<super>2</super>   eV"],
        ["4","Kinetic energy","KE = &#8722;E<sub>n</sub> = +13.6 Z<super>2</super>/n<super>2</super>   eV"],
        ["5","Potential energy","PE = 2E<sub>n</sub> = &#8722;27.2 Z<super>2</super>/n<super>2</super>   eV"],
        ["6","Ionisation energy","IE = +13.6 &#215; Z<super>2</super>/n<super>2</super>   eV"],
        ["7","Photon energy (transition)","&#916;E = 13.6 Z<super>2</super> (1/n<sub>1</sub><super>2</super> &#8722; 1/n<sub>2</sub><super>2</super>)   eV"],
        ["8","Rydberg equation","1/&#955; = R<sub>H</sub> Z<super>2</super> (1/n<sub>1</sub><super>2</super> &#8722; 1/n<sub>2</sub><super>2</super>)"],
        ["9","Angular momentum (Bohr)","mv<sub>n</sub>r<sub>n</sub> = nh/2&#960; = n&#295;"],
        ["10","Time period","T<sub>n</sub> &#8733; n<super>3</super>/Z<super>2</super>"],
        ["11","Revolution frequency","&#957;<sub>n</sub> &#8733; Z<super>2</super>/n<super>3</super>"],
        ["12","Spectral lines count","(n<sub>2</sub>&#8722;n<sub>1</sub>)(n<sub>2</sub>&#8722;n<sub>1</sub>+1)/2"],
        ["13","Wave equation","c = &#957;&#955;"],
        ["14","Wave number","&#957;&#771; = 1/&#955;   (m<super>&#8722;1</super> or cm<super>&#8722;1</super>)"],
        ["15","Photon energy","E = h&#957; = hc/&#955; = hc&#957;&#771;"],
        ["16","Energy shortcut","E(eV) = 1240/&#955;(nm)"],
        ["17","Photon momentum","p = h/&#955; = E/c"],
        ["18","Photoelectric equation","h&#957; = &#966; + KE<sub>max</sub>"],
        ["19","Work function","&#966; = h&#957;<sub>0</sub>"],
        ["20","Threshold wavelength","&#955;<sub>0</sub>(nm) = 1240/&#966;(eV)"],
        ["21","Stopping potential","eV<sub>0</sub> = KE<sub>max</sub> = h&#957; &#8722; &#966;"],
        ["22","Photon count (intensity)","n = I/h&#957; = I&#955;/hc"],
        ["23","de Broglie  (general)","&#955; = h/mv = h/p"],
        ["24","de Broglie  (from KE)","&#955; = h/&#8730;(2m&#183;KE)"],
        ["25","de Broglie  (electron, V)","&#955; = 12.27/&#8730;V   &#8491;"],
        ["26","Heisenberg's UP","&#916;x&#183;&#916;p &#8805; h/4&#960;"],
        ["27","Min velocity uncertainty","&#916;v<sub>min</sub> = h/(4&#960;&#183;m&#183;&#916;x)"],
        ["28","Orbital angular momentum","L = &#8730;[l(l+1)]&#183;&#295;"],
        ["29","L z-component","L<sub>z</sub> = m<sub>l</sub>&#183;&#295;"],
        ["30","Spin angular momentum","S = (&#8730;3/2)&#183;&#295;   [all electrons]"],
        ["31","Orbitals per shell","n<super>2</super>"],
        ["32","Max e<super>&#8722;</super> per shell","2n<super>2</super>"],
        ["33","Orbitals per subshell","2l+1"],
        ["34","Max e<super>&#8722;</super> per subshell","2(2l+1)"],
        ["35","Radial nodes","n &#8722; l &#8722; 1"],
        ["36","Angular nodes","l"],
        ["37","Total nodes","n &#8722; 1"],
        ["38","Spin multiplicity","2S+1   where S=(unpaired e<super>&#8722;</super>)/2"],
        ["39","Magnetic moment","&#956; = &#8730;[n(n+2)]   BM"],
        ["40","Nuclear radius","r = r<sub>0</sub> &#215; A<super>1/3</super>"],
        ["41","e/m ratio  (electron)","1.758 &#215; 10<super>11</super>   C/kg"],
    ],
    col_widths=[W*0.06,W*0.36,W*0.58], left_cols=[1,2]
))
story.append(sp(8))

story.append(thinhr())
story.append(sp(4))
story.append(Paragraph(
    "Atomic Structure  |  Class 11 Chemistry  |  Complete Chapter  |  All 41 Formulas  |  NEET / JEE",
    sFooter
))

doc.build(story)
print("PDF created:", OUTPUT)
Running Command

cd /tmp/workspace/atomic-structure && python3 generate_final.py 2>&1

Running Command

ls -lh /tmp/workspace/atomic-structure/Atomic_Structure_Final.pdf

Here's your fixed PDF:

Atomic Structure Final

PDF Document · PDF

What was fixed:
The root cause was that DroidSans font simply doesn't have glyphs for Unicode combining characters like ₑ, ², ³, ⁺, ⁻ etc. — so they showed as empty boxes [].
The fix: switched entirely to ReportLab's XML markup tags inside paragraph text:
  • <sub>e</sub> for subscripts like eₑ
  • <super>2</super> for superscripts like n²
  • HTML entities for special symbols: &#8722; (−), &#215; (×), &#8491; (Å), &#295; (ℏ), &#957; (ν), &#955; (λ), &#966; (φ), &#916; (Δ), &#8730; (√), etc.
These are rendered by ReportLab itself using the base font glyphs, so every character displays cleanly and clearly in print.

Wow! Do the same for class 11 physics motion in a plane .

Running Command

mkdir -p /tmp/workspace/motion-in-plane && ls /tmp/workspace/

Writing File

~/motion-in-plane/generate_mip.py

"""
Motion in a Plane — Complete Chapter PDF
Class 11 Physics | NEET / JEE
Black & White, DroidSans Unicode font, ReportLab HTML markup,
NO placeholder squares, large fonts, print-optimised.
"""
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.units import cm
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, PageBreak
)

pdfmetrics.registerFont(TTFont('DS',  '/usr/share/fonts/google-droid-sans-fonts/DroidSans.ttf'))
pdfmetrics.registerFont(TTFont('DSB', '/usr/share/fonts/google-droid-sans-fonts/DroidSans-Bold.ttf'))

OUTPUT = "/tmp/workspace/motion-in-plane/Motion_in_a_Plane_Complete.pdf"

doc = SimpleDocTemplate(
    OUTPUT, pagesize=A4,
    rightMargin=2.0*cm, leftMargin=2.0*cm,
    topMargin=2.2*cm,   bottomMargin=2.2*cm,
)
W = A4[0] - 4.0*cm

# ── colours ────────────────────────────────────────────────────────
BLACK    = colors.black
WHITE    = colors.white
LT_GREY  = colors.HexColor("#e0e0e0")
MID_GREY = colors.HexColor("#808080")
DK_GREY  = colors.HexColor("#303030")

# ── styles ─────────────────────────────────────────────────────────
def S(name, **kw): return ParagraphStyle(name, **kw)

sTitle   = S("sTitle",   fontName="DSB", fontSize=22, textColor=WHITE, alignment=TA_CENTER, spaceAfter=4, leading=30)
sSubtit  = S("sSubtit",  fontName="DS",  fontSize=11, textColor=WHITE, alignment=TA_CENTER, spaceAfter=2, leading=16)
sPart    = S("sPart",    fontName="DSB", fontSize=14, textColor=WHITE, alignment=TA_LEFT,   spaceAfter=4, spaceBefore=6, leftIndent=6, leading=20)
sSec     = S("sSec",     fontName="DSB", fontSize=13, textColor=BLACK, alignment=TA_LEFT,   spaceAfter=4, spaceBefore=8, leading=18)
sSub     = S("sSub",     fontName="DSB", fontSize=11.5, textColor=BLACK, alignment=TA_LEFT, spaceAfter=3, spaceBefore=5, leading=16)
sBody    = S("sBody",    fontName="DS",  fontSize=11, textColor=BLACK, alignment=TA_JUSTIFY, spaceAfter=4, leading=16)
sBullet  = S("sBullet",  fontName="DS",  fontSize=11, textColor=BLACK, alignment=TA_LEFT,   spaceAfter=3, leading=15, leftIndent=16)
sFormula = S("sFormula", fontName="DSB", fontSize=12.5, textColor=BLACK, alignment=TA_CENTER, spaceAfter=3, spaceBefore=3, leading=20)
sFormNote= S("sFormNote",fontName="DS",  fontSize=10.5, textColor=DK_GREY, alignment=TA_LEFT, spaceAfter=2, leading=15, leftIndent=4)
sTrap    = S("sTrap",    fontName="DSB", fontSize=11, textColor=BLACK, alignment=TA_LEFT,   spaceAfter=2, leading=15)
sNote    = S("sNote",    fontName="DS",  fontSize=11, textColor=BLACK, alignment=TA_LEFT,   spaceAfter=2, leading=15)
sTableH  = S("sTableH",  fontName="DSB", fontSize=10, textColor=WHITE, alignment=TA_CENTER, leading=13)
sTableC  = S("sTableC",  fontName="DS",  fontSize=10, textColor=BLACK, alignment=TA_CENTER, leading=13)
sTableL  = S("sTableL",  fontName="DS",  fontSize=10, textColor=BLACK, alignment=TA_LEFT,   leading=13)
sFooter  = S("sFooter",  fontName="DS",  fontSize=9,  textColor=MID_GREY, alignment=TA_CENTER, leading=12)

# ── helpers ────────────────────────────────────────────────────────
def sp(h=5):  return Spacer(1, h)
def thinhr(): return HRFlowable(width="100%", thickness=0.4, color=MID_GREY)
def body(t):  return Paragraph(t, sBody)
def blt(t):   return Paragraph("&#8226;  " + t, sBullet)
def sec(t):   return Paragraph(t, sSec)
def sub_h(t): return Paragraph(t, sSub)

def part_header(t):
    tbl = Table([[Paragraph(t, sPart)]], colWidths=[W])
    tbl.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),BLACK),("ROWPADDING",(0,0),(-1,-1),8)]))
    return tbl

def formula_box(main, notes=None):
    elems = [Paragraph(main, sFormula)]
    if notes:
        elems.append(HRFlowable(width="100%", thickness=0.5, color=LT_GREY))
        for n in notes:
            elems.append(Paragraph(n, sFormNote))
    rows = [[e] for e in elems]
    inner = Table(rows, colWidths=[W-24])
    inner.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),WHITE),("ROWPADDING",(0,0),(-1,-1),5)]))
    outer = Table([[inner]], colWidths=[W])
    outer.setStyle(TableStyle([("BOX",(0,0),(-1,-1),2,BLACK),("ROWPADDING",(0,0),(-1,-1),6),("BACKGROUND",(0,0),(-1,-1),WHITE)]))
    return outer

def trap_box(t):
    tbl = Table([[Paragraph("&#9888;  TRAP: " + t, sTrap)]], colWidths=[W])
    tbl.setStyle(TableStyle([("BOX",(0,0),(-1,-1),1.5,BLACK),("ROWPADDING",(0,0),(-1,-1),6),("BACKGROUND",(0,0),(-1,-1),LT_GREY)]))
    return tbl

def note_box(t):
    tbl = Table([[Paragraph("&#9733;  NOTE: " + t, sNote)]], colWidths=[W])
    tbl.setStyle(TableStyle([("BOX",(0,0),(-1,-1),1,BLACK),("ROWPADDING",(0,0),(-1,-1),6),("BACKGROUND",(0,0),(-1,-1),WHITE)]))
    return tbl

def make_table(headers, rows, col_widths=None, left_cols=None):
    if col_widths is None: col_widths = [W/len(headers)]*len(headers)
    left_cols = left_cols or []
    data = [[Paragraph(h, sTableH) for h in headers]]
    for row in rows:
        data.append([Paragraph(str(cell), sTableL if j in left_cols else sTableC) for j,cell in enumerate(row)])
    tbl = Table(data, colWidths=col_widths)
    cmds = [
        ("BACKGROUND",(0,0),(-1,0),BLACK),
        ("ROWPADDING",(0,0),(-1,-1),5),
        ("GRID",(0,0),(-1,-1),0.5,MID_GREY),
        ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
        ("LINEBELOW",(0,0),(-1,0),1.0,BLACK),
    ]
    for i in range(1,len(data)):
        if i%2==0: cmds.append(("BACKGROUND",(0,i),(-1,i),colors.HexColor("#f4f4f4")))
    tbl.setStyle(TableStyle(cmds))
    return tbl

# ══════════════════════════════════════════════════════════════════
story = []

# ── COVER ─────────────────────────────────────────────────────────
cover_t = Table([[Paragraph("MOTION IN A PLANE", sTitle)]], colWidths=[W])
cover_t.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),BLACK),("ROWPADDING",(0,0),(-1,-1),20)]))
cover_s = Table([[Paragraph(
    "Complete Chapter  |  All Formulas  |  All Topics  |  Class 11 Physics  |  NEET / JEE",
    sSubtit)]], colWidths=[W])
cover_s.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),DK_GREY),("ROWPADDING",(0,0),(-1,-1),10)]))
story += [cover_t, cover_s, sp(12)]

# ══════════════════════════════════════════════════════════════════
# PART 1 — SCALARS AND VECTORS
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 1 — SCALARS AND VECTORS"), sp(8)]

story += [sec("1.1  Scalar Quantities")]
story += [body("Quantities that have only <b>magnitude</b> (no direction). Obey ordinary arithmetic.")]
story += [blt("Examples: mass, speed, distance, time, temperature, energy, work, power, charge, density")]
story.append(sp(4))

story += [sec("1.2  Vector Quantities")]
story += [body("Quantities that have both <b>magnitude AND direction</b>. Obey vector algebra (not ordinary arithmetic).")]
story += [blt("Examples: displacement, velocity, acceleration, force, momentum, torque, electric field")]
story.append(sp(4))

story += [sec("1.3  Representation of Vectors")]
for b in [
    "A vector is represented by a bold letter or letter with arrow: <b>A</b> or A&#8407;",
    "Magnitude of A&#8407; is written as |A&#8407;| or simply A  (always positive or zero)",
    "Graphically: an arrow where length = magnitude and arrowhead = direction",
    "A negative vector &#8722;A&#8407; has same magnitude but exactly opposite direction",
    "Equal vectors: same magnitude AND same direction  (can have different positions in space)",
]:  story.append(blt(b))
story.append(sp(6))

story += [sec("1.4  Types of Vectors")]
story.append(make_table(
    ["Type","Definition","Example"],
    [
        ["Zero (Null) vector","Magnitude = 0, direction undefined","Velocity of a stationary object"],
        ["Unit vector","Magnitude = 1, used to indicate direction only","i&#770;, j&#770;, k&#770;  (along x, y, z axes)"],
        ["Equal vectors","Same magnitude and same direction","Parallel arrows of equal length"],
        ["Negative vector","Same magnitude, opposite direction","&#8722;A&#8407;"],
        ["Collinear vectors","Lie along same or parallel lines","Can be parallel or anti-parallel"],
        ["Coplanar vectors","Lie in the same plane","Any two vectors are always coplanar"],
        ["Position vector","Vector from origin O to point P","r&#8407; = xi&#770; + yj&#770; + zk&#770;"],
        ["Displacement vector","Change in position vector","&#916;r&#8407; = r&#8407;<sub>2</sub> &#8722; r&#8407;<sub>1</sub>"],
    ],
    col_widths=[W*0.22,W*0.44,W*0.34], left_cols=[0,1,2]
))
story.append(sp(8))

# ══════════════════════════════════════════════════════════════════
# PART 2 — VECTOR ALGEBRA
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 2 — VECTOR ALGEBRA"), sp(8)]

story += [sec("2.1  Addition of Vectors")]
story += [sub_h("Triangle Law of Vector Addition")]
story += [body("If two vectors A&#8407; and B&#8407; are represented by two sides of a triangle taken in order, "
               "then the third side (closing side) taken in reverse order represents the resultant R&#8407; = A&#8407; + B&#8407;.")]
story += [sp(3), sub_h("Parallelogram Law of Vector Addition")]
story += [body("If two vectors A&#8407; and B&#8407; are represented by two adjacent sides of a parallelogram, "
               "then the diagonal of the parallelogram from the same point represents the resultant R&#8407;.")]
story.append(sp(4))
story.append(formula_box(
    "Magnitude of Resultant:   R  =  &#8730;(A<super>2</super> + B<super>2</super> + 2AB cos&#952;)",
    [
        "A = magnitude of vector A&#8407;   |   B = magnitude of vector B&#8407;",
        "&#952; (theta) = angle between A&#8407; and B&#8407;  (angle between their positive directions)",
        "R = magnitude of resultant R&#8407; = A&#8407; + B&#8407;",
    ]
))
story.append(sp(4))
story.append(formula_box(
    "Direction of Resultant:   tan&#945;  =  B sin&#952; / (A + B cos&#952;)",
    [
        "&#945; (alpha) = angle that resultant R&#8407; makes with vector A&#8407;",
        "B sin&#952; = component of B&#8407; perpendicular to A&#8407;",
        "A + B cos&#952; = component of B&#8407; along A&#8407; added to A",
    ]
))
story.append(sp(4))

story += [sub_h("Special Cases:")]
story.append(make_table(
    ["Angle &#952; between A&#8407; and B&#8407;","Resultant Magnitude","Direction"],
    [
        ["&#952; = 0&#176;  (parallel, same direction)","R = A + B  (maximum)","Along A&#8407; and B&#8407;"],
        ["&#952; = 180&#176;  (anti-parallel)","R = |A &#8722; B|  (minimum)","Along larger vector"],
        ["&#952; = 90&#176;  (perpendicular)","R = &#8730;(A<super>2</super> + B<super>2</super>)","tan&#945; = B/A"],
        ["A = B, &#952; = 60&#176;","R = A = B","&#945; = 30&#176;"],
        ["A = B, &#952; = 120&#176;","R = A = B","&#945; = 60&#176;"],
        ["A = B, any &#952;","R = 2A cos(&#952;/2)","&#945; = &#952;/2"],
    ],
    col_widths=[W*0.38,W*0.34,W*0.28], left_cols=[0,1,2]
))
story.append(sp(6))

story += [sec("2.2  Subtraction of Vectors")]
story.append(formula_box(
    "A&#8407; &#8722; B&#8407;  =  A&#8407; + (&#8722;B&#8407;)",
    [
        "Subtraction = adding the negative of the second vector",
        "|A&#8407; &#8722; B&#8407;| = &#8730;(A<super>2</super> + B<super>2</super> &#8722; 2AB cos&#952;)      (same formula with &#952; replaced by 180&#176; &#8722; &#952;)",
        "Direction: tan&#946; = B sin&#952; / (A &#8722; B cos&#952;)    where &#946; is angle with A&#8407;",
    ]
))
story.append(sp(6))

story += [sec("2.3  Multiplication of a Vector by a Scalar")]
story.append(formula_box(
    "k A&#8407;  =  vector of magnitude |k|A  in direction of A&#8407;  (if k &gt; 0)  or  &#8722;A&#8407;  (if k &lt; 0)",
    [
        "k = any real number (scalar)   |   A&#8407; = vector",
        "Direction is reversed if k is negative",
        "Unit vector:   A&#770; = A&#8407; / |A&#8407;| = A&#8407; / A      (divide vector by its magnitude)",
    ]
))
story.append(sp(6))

story += [sec("2.4  Resolution of Vectors — Components")]
story += [body("Any vector in a plane can be resolved into two perpendicular components along the x and y axes.")]
story.append(sp(3))
story.append(formula_box(
    "A<sub>x</sub>  =  A cos&#952;          A<sub>y</sub>  =  A sin&#952;",
    [
        "A<sub>x</sub> = x-component of A&#8407; = projection along x-axis",
        "A<sub>y</sub> = y-component of A&#8407; = projection along y-axis",
        "&#952; = angle A&#8407; makes with the positive x-axis  (measured anticlockwise)",
        "A&#8407; = A<sub>x</sub>i&#770; + A<sub>y</sub>j&#770;",
    ]
))
story.append(sp(4))
story.append(formula_box(
    "Magnitude:   A  =  &#8730;(A<sub>x</sub><super>2</super> + A<sub>y</sub><super>2</super>)          Direction:   tan&#952;  =  A<sub>y</sub> / A<sub>x</sub>",
    [
        "These two formulas reconstruct the original vector from its components",
        "For 3D:  A = &#8730;(A<sub>x</sub><super>2</super> + A<sub>y</sub><super>2</super> + A<sub>z</sub><super>2</super>)",
    ]
))
story.append(sp(6))

story += [sec("2.5  Unit Vectors")]
story.append(formula_box(
    "i&#770;,  j&#770;,  k&#770;  are unit vectors along x, y, z axes respectively",
    [
        "|i&#770;| = |j&#770;| = |k&#770;| = 1   (magnitude = 1 each)",
        "i&#770; &#183; i&#770; = j&#770; &#183; j&#770; = k&#770; &#183; k&#770; = 1     |     i&#770; &#183; j&#770; = j&#770; &#183; k&#770; = k&#770; &#183; i&#770; = 0",
        "i&#770; &#215; j&#770; = k&#770;     |     j&#770; &#215; k&#770; = i&#770;     |     k&#770; &#215; i&#770; = j&#770;",
        "Any vector:  A&#8407; = A<sub>x</sub>i&#770; + A<sub>y</sub>j&#770; + A<sub>z</sub>k&#770;",
    ]
))
story.append(sp(6))

story += [sec("2.6  Addition of Vectors by Components")]
story.append(formula_box(
    "R&#8407;  =  A&#8407; + B&#8407;  =  (A<sub>x</sub>+B<sub>x</sub>)i&#770; + (A<sub>y</sub>+B<sub>y</sub>)j&#770;",
    [
        "R<sub>x</sub> = A<sub>x</sub> + B<sub>x</sub>     |     R<sub>y</sub> = A<sub>y</sub> + B<sub>y</sub>",
        "R = &#8730;(R<sub>x</sub><super>2</super> + R<sub>y</sub><super>2</super>)     |     &#952; = tan<super>&#8722;1</super>(R<sub>y</sub>/R<sub>x</sub>)",
        "This method works for ANY number of vectors &#8212; just add all x-components and all y-components separately",
    ]
))
story.append(sp(8))

story += [sec("2.7  Dot Product (Scalar Product)")]
story.append(formula_box(
    "A&#8407; &#183; B&#8407;  =  AB cos&#952;  =  A<sub>x</sub>B<sub>x</sub> + A<sub>y</sub>B<sub>y</sub> + A<sub>z</sub>B<sub>z</sub>",
    [
        "Result is a SCALAR  (no direction)",
        "&#952; = angle between A&#8407; and B&#8407;  (0&#176; &#8804; &#952; &#8804; 180&#176;)",
        "A&#8407; &#183; B&#8407; = 0 when &#952;=90&#176;  (vectors are perpendicular)   |   Maximum when &#952;=0&#176;:  A&#8407; &#183; B&#8407; = AB",
        "Commutative:  A&#8407; &#183; B&#8407; = B&#8407; &#183; A&#8407;",
        "i&#770; &#183; i&#770; = j&#770; &#183; j&#770; = k&#770; &#183; k&#770; = 1     |     i&#770; &#183; j&#770; = j&#770; &#183; k&#770; = k&#770; &#183; i&#770; = 0",
        "Work done: W = F&#8407; &#183; s&#8407; = Fs cos&#952;",
    ]
))
story.append(sp(6))

story += [sec("2.8  Cross Product (Vector Product)")]
story.append(formula_box(
    "|A&#8407; &#215; B&#8407;|  =  AB sin&#952;          Direction: Right-Hand Rule",
    [
        "Result is a VECTOR  (perpendicular to the plane containing A&#8407; and B&#8407;)",
        "&#952; = angle between A&#8407; and B&#8407;",
        "|A&#8407; &#215; B&#8407;| = 0 when &#952;=0&#176; or 180&#176;  (parallel or anti-parallel vectors)",
        "Maximum when &#952;=90&#176;:  |A&#8407; &#215; B&#8407;| = AB",
        "Anti-commutative:  A&#8407; &#215; B&#8407; = &#8722;(B&#8407; &#215; A&#8407;)",
        "i&#770; &#215; j&#770; = k&#770;     |     j&#770; &#215; k&#770; = i&#770;     |     k&#770; &#215; i&#770; = j&#770;",
        "i&#770; &#215; i&#770; = j&#770; &#215; j&#770; = k&#770; &#215; k&#770; = 0&#8407;",
        "Torque: &#964;&#8407; = r&#8407; &#215; F&#8407;   |   Angular momentum: L&#8407; = r&#8407; &#215; p&#8407;",
    ]
))
story.append(sp(4))
story.append(formula_box(
    "A&#8407; &#215; B&#8407;  =  (A<sub>y</sub>B<sub>z</sub>&#8722;A<sub>z</sub>B<sub>y</sub>)i&#770; + (A<sub>z</sub>B<sub>x</sub>&#8722;A<sub>x</sub>B<sub>z</sub>)j&#770; + (A<sub>x</sub>B<sub>y</sub>&#8722;A<sub>y</sub>B<sub>x</sub>)k&#770;",
    ["Component form of cross product  (evaluated using determinant of 3&#215;3 matrix)"]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 3 — MOTION IN A PLANE (GENERAL)
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 3 — MOTION IN A PLANE — GENERAL EQUATIONS"), sp(8)]

story += [sec("3.1  Position Vector and Displacement")]
story.append(formula_box(
    "r&#8407;  =  xi&#770; + yj&#770;          &#916;r&#8407;  =  r&#8407;<sub>2</sub> &#8722; r&#8407;<sub>1</sub>  =  &#916;xi&#770; + &#916;yj&#770;",
    [
        "r&#8407; = position vector at any instant",
        "&#916;r&#8407; = displacement vector  (always a straight line from initial to final position)",
        "Distance (scalar) = actual path length &#8805; |&#916;r&#8407;|  (displacement magnitude)",
    ]
))
story.append(sp(6))

story += [sec("3.2  Velocity in a Plane")]
story.append(formula_box(
    "Average velocity:   v&#8407;<sub>avg</sub>  =  &#916;r&#8407; / &#916;t  =  &#916;x/&#916;t  i&#770; + &#916;y/&#916;t  j&#770;",
    [
        "Direction of average velocity = direction of displacement &#916;r&#8407;",
    ]
))
story.append(sp(4))
story.append(formula_box(
    "Instantaneous velocity:   v&#8407;  =  dr&#8407;/dt  =  dx/dt  i&#770; + dy/dt  j&#770;  =  v<sub>x</sub>i&#770; + v<sub>y</sub>j&#770;",
    [
        "v<sub>x</sub> = dx/dt  (x-component of velocity)",
        "v<sub>y</sub> = dy/dt  (y-component of velocity)",
        "Speed:   v = |v&#8407;| = &#8730;(v<sub>x</sub><super>2</super> + v<sub>y</sub><super>2</super>)",
        "Direction:   tan&#952; = v<sub>y</sub> / v<sub>x</sub>",
        "Instantaneous velocity is always TANGENT to the path at that point",
    ]
))
story.append(sp(6))

story += [sec("3.3  Acceleration in a Plane")]
story.append(formula_box(
    "Average acceleration:   a&#8407;<sub>avg</sub>  =  &#916;v&#8407; / &#916;t  =  &#916;v<sub>x</sub>/&#916;t  i&#770; + &#916;v<sub>y</sub>/&#916;t  j&#770;",
))
story.append(sp(4))
story.append(formula_box(
    "Instantaneous acceleration:   a&#8407;  =  dv&#8407;/dt  =  dv<sub>x</sub>/dt  i&#770; + dv<sub>y</sub>/dt  j&#770;  =  a<sub>x</sub>i&#770; + a<sub>y</sub>j&#770;",
    [
        "a<sub>x</sub> = dv<sub>x</sub>/dt   |   a<sub>y</sub> = dv<sub>y</sub>/dt",
        "|a&#8407;| = &#8730;(a<sub>x</sub><super>2</super> + a<sub>y</sub><super>2</super>)",
        "Key: in 2D motion, x and y components are INDEPENDENT of each other",
    ]
))
story.append(sp(6))

story += [sec("3.4  Equations of Motion in a Plane (Uniform Acceleration)")]
story += [body("When acceleration is constant (uniform), each component obeys the same equations as 1D motion:")]
story.append(sp(3))
story.append(formula_box(
    "v<sub>x</sub> = u<sub>x</sub> + a<sub>x</sub>t          v<sub>y</sub> = u<sub>y</sub> + a<sub>y</sub>t\n"
    "x = u<sub>x</sub>t + (1/2)a<sub>x</sub>t<super>2</super>          y = u<sub>y</sub>t + (1/2)a<sub>y</sub>t<super>2</super>\n"
    "v<sub>x</sub><super>2</super> = u<sub>x</sub><super>2</super> + 2a<sub>x</sub>x          v<sub>y</sub><super>2</super> = u<sub>y</sub><super>2</super> + 2a<sub>y</sub>y",
    [
        "u<sub>x</sub>, u<sub>y</sub> = initial velocity components  (at t=0)",
        "a<sub>x</sub>, a<sub>y</sub> = acceleration components  (constant)",
        "x, y = displacement components in time t",
        "v<sub>x</sub>, v<sub>y</sub> = velocity components at time t",
        "The two directions are completely INDEPENDENT &#8212; solve each axis separately",
    ]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 4 — PROJECTILE MOTION
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 4 — PROJECTILE MOTION"), sp(8)]

story += [sec("4.1  Introduction and Assumptions")]
story += [body("A projectile is any object thrown/projected with an initial velocity and then moving under the "
               "influence of gravity alone  (no air resistance).")]
story += [sp(3), sub_h("Assumptions:")]
for b in [
    "No air resistance",
    "g is constant throughout the motion (= 9.8 m/s&#178; &#8773; 10 m/s&#178; downward)",
    "Earth is flat (valid for short ranges)",
    "Motion is in a vertical plane",
]:  story.append(blt(b))
story += [sp(3), sub_h("Key Principle: x and y motions are COMPLETELY INDEPENDENT")]
for b in [
    "Horizontal (x): uniform velocity  (no acceleration, since g acts only vertically)",
    "Vertical (y): uniformly accelerated motion under g  (free fall)",
]:  story.append(blt(b))
story.append(sp(6))

story += [sec("4.2  Setting Up the Equations")]
story += [body("A projectile is launched from the origin with initial speed u at angle &#952; to the horizontal.")]
story.append(sp(3))
story.append(formula_box(
    "Initial velocity components:   u<sub>x</sub> = u cos&#952;          u<sub>y</sub> = u sin&#952;",
    [
        "u = initial speed of projection",
        "&#952; = angle of projection with horizontal  (0&#176; &lt; &#952; &lt; 90&#176;)",
        "Acceleration:  a<sub>x</sub> = 0  (horizontal)   |   a<sub>y</sub> = &#8722;g  (vertical, downward)",
    ]
))
story.append(sp(6))

story += [sec("4.3  Equations of Projectile Motion at Time t")]
story.append(formula_box(
    "Horizontal:   x  =  u cos&#952; &#183; t          (uniform velocity in x)",
    [
        "No acceleration in x &#8594; horizontal velocity remains u cos&#952; throughout",
        "v<sub>x</sub> = u cos&#952; = constant  (at ALL times)",
    ]
))
story.append(sp(4))
story.append(formula_box(
    "Vertical:   y  =  u sin&#952; &#183; t  &#8722;  (1/2)gt<super>2</super>",
    [
        "v<sub>y</sub> = u sin&#952; &#8722; gt   (vertical velocity decreases, becomes zero at peak, then negative)",
        "v<sub>y</sub><super>2</super> = (u sin&#952;)<super>2</super> &#8722; 2gy",
    ]
))
story.append(sp(4))
story.append(formula_box(
    "Velocity at time t:   v  =  &#8730;(v<sub>x</sub><super>2</super> + v<sub>y</sub><super>2</super>)          Direction:   tan&#946; = v<sub>y</sub>/v<sub>x</sub>",
    [
        "&#946; = angle velocity makes with horizontal at time t",
        "Speed changes continuously (magnitude of velocity changes), direction also changes",
    ]
))
story.append(sp(6))

story += [sec("4.4  Equation of Trajectory (Path Shape)")]
story.append(formula_box(
    "y  =  x tan&#952;  &#8722;  gx<super>2</super> / (2u<super>2</super>cos<super>2</super>&#952;)",
    [
        "Derived by eliminating t from x = u cos&#952; &#183; t  and  y = u sin&#952; &#183; t &#8722; (1/2)gt&#178;",
        "This is an equation of a PARABOLA  (y = ax &#8722; bx&#178;  form  &#8594;  downward parabola)",
        "Projectile always follows a PARABOLIC path  (under uniform gravity, no air resistance)",
    ]
))
story.append(sp(6))

story += [sec("4.5  Time of Flight  (T)")]
story.append(formula_box(
    "T  =  2u sin&#952; / g",
    [
        "T = total time from launch to landing  (when y = 0 again)",
        "Derived by setting y = 0:   0 = u sin&#952; &#183; t &#8722; (1/2)gt&#178;   &#8594;   t = 0 (launch) or t = 2u sin&#952;/g",
        "Time of ascent = Time of descent = T/2 = u sin&#952;/g",
        "T depends only on vertical component  (u sin&#952;) and g",
        "For &#952; = 90&#176; (vertical throw):  T = 2u/g  (maximum)",
    ]
))
story.append(sp(6))

story += [sec("4.6  Maximum Height  (H)")]
story.append(formula_box(
    "H  =  u<super>2</super> sin<super>2</super>&#952; / (2g)",
    [
        "H = maximum height reached above the launch point",
        "At maximum height: v<sub>y</sub> = 0  (vertical velocity becomes zero at peak)",
        "Derived from:  v<sub>y</sub><super>2</super> = u<sub>y</sub><super>2</super> &#8722; 2gH  with v<sub>y</sub>=0   &#8594;   H = u<sub>y</sub><super>2</super>/2g = (u sin&#952;)<super>2</super>/2g",
        "At maximum height: horizontal velocity = u cos&#952; (unchanged), vertical velocity = 0",
        "Speed at max height = u cos&#952;  (minimum speed during flight)",
    ]
))
story.append(sp(6))

story += [sec("4.7  Horizontal Range  (R)")]
story.append(formula_box(
    "R  =  u<super>2</super> sin 2&#952; / g",
    [
        "R = horizontal distance from launch to landing  (when projectile returns to same height)",
        "Derived from:  R = u cos&#952; &#215; T = u cos&#952; &#215; (2u sin&#952;/g) = u&#178; &#215; 2sin&#952;cos&#952;/g = u&#178;sin2&#952;/g",
        "Maximum range (R<sub>max</sub>) when sin2&#952; = 1 &#8594; 2&#952; = 90&#176; &#8594; <b>&#952; = 45&#176;</b>",
        "R<sub>max</sub> = u<super>2</super>/g   (at &#952; = 45&#176;)",
        "Complementary angles give equal range:  R(&#952;) = R(90&#176;&#8722;&#952;)   e.g. 30&#176; and 60&#176; give the same R",
    ]
))
story.append(sp(4))
story.append(trap_box("Range is the same for complementary angles (e.g. 30&#176; and 60&#176;), but the TIME OF FLIGHT and MAXIMUM HEIGHT are different."))
story.append(sp(6))

story += [sec("4.8  Projectile from a Height / Horizontal Projection")]
story += [body("A body projected horizontally from height h with speed u:")]
story.append(sp(3))
story.append(formula_box(
    "x = ut          y = (1/2)gt<super>2</super>          (taking downward as positive y)",
    [
        "u<sub>x</sub> = u  (horizontal),   u<sub>y</sub> = 0  (no initial vertical velocity)",
        "Time to hit ground:  t = &#8730;(2h/g)",
        "Horizontal range:  x = u &#215; &#8730;(2h/g)",
        "Vertical velocity at ground:  v<sub>y</sub> = gt = &#8730;(2gh)",
        "Speed at ground:  v = &#8730;(u<super>2</super> + 2gh)",
        "Angle with horizontal at ground:  tan&#946; = v<sub>y</sub>/u = &#8730;(2gh)/u",
    ]
))
story.append(sp(6))

story += [sec("4.9  Projectile Motion Summary Table")]
story.append(make_table(
    ["Quantity","Formula","When it applies"],
    [
        ["Horizontal velocity",     "v<sub>x</sub> = u cos&#952;",                "ALL times  (constant)"],
        ["Vertical velocity",       "v<sub>y</sub> = u sin&#952; &#8722; gt",      "At time t"],
        ["x-displacement",          "x = u cos&#952; &#183; t",                   "At time t"],
        ["y-displacement",          "y = u sin&#952; &#183; t &#8722; (1/2)gt&#178;", "At time t"],
        ["Trajectory (path)",       "y = x tan&#952; &#8722; gx&#178;/(2u&#178;cos&#178;&#952;)", "Shape of path"],
        ["Time of flight",          "T = 2u sin&#952;/g",                         "Total time of flight"],
        ["Time to max height",      "t<sub>H</sub> = u sin&#952;/g",              "Time to reach peak"],
        ["Maximum height",          "H = u&#178;sin&#178;&#952;/2g",              "Height at peak"],
        ["Horizontal range",        "R = u&#178;sin2&#952;/g",                    "Horizontal distance"],
        ["Max range",               "R<sub>max</sub> = u&#178;/g  at &#952;=45&#176;","Maximum possible range"],
        ["Speed at max height",     "v = u cos&#952;",                            "At the peak"],
    ],
    col_widths=[W*0.25,W*0.42,W*0.33], left_cols=[0,1,2]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 5 — UNIFORM CIRCULAR MOTION
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 5 — UNIFORM CIRCULAR MOTION  (UCM)"), sp(8)]

story += [sec("5.1  Definition and Key Characteristics")]
story += [body("A body moves in a circle of fixed radius r with <b>constant speed</b>. "
               "Although speed is constant, velocity changes continuously (direction changes) &#8594; "
               "there IS acceleration.")]
story += [sp(3)]
for b in [
    "Speed = constant   |   Velocity = changing  (direction changes continuously)",
    "Acceleration = constant magnitude, but direction changes continuously",
    "Acceleration is always directed towards the CENTRE of the circle  (centripetal)",
    "No net tangential force  (tangential acceleration = 0)",
]:  story.append(blt(b))
story.append(sp(6))

story += [sec("5.2  Angular Quantities")]
story.append(formula_box(
    "Angular displacement:   &#952;  =  s / r   radians",
    [
        "s = arc length  (distance along the circular path)",
        "r = radius of circle",
        "1 revolution = 2&#960; radians = 360&#176;",
        "Relationship:  s = r&#952;",
    ]
))
story.append(sp(4))
story.append(formula_box(
    "Angular velocity:   &#969;  =  &#916;&#952; / &#916;t  =  d&#952;/dt     rad/s",
    [
        "&#969; (omega) = angular velocity = angle swept per unit time",
        "For uniform circular motion: &#969; = constant",
        "Relationship with time period:  &#969; = 2&#960;/T = 2&#960;&#957;",
        "T = time period (time for one complete revolution)",
        "&#957; (nu) = frequency (revolutions per second)",
    ]
))
story.append(sp(4))
story.append(formula_box(
    "Relationship between linear and angular quantities:   v  =  r&#969;",
    [
        "v = linear speed of the particle",
        "r = radius of circular path",
        "&#969; = angular velocity",
        "Also:  s = r&#952;   |   a<sub>t</sub> = r&#945;  (for non-uniform circular motion)",
    ]
))
story.append(sp(6))

story += [sec("5.3  Centripetal Acceleration")]
story.append(formula_box(
    "a<sub>c</sub>  =  v<super>2</super>/r  =  &#969;<super>2</super>r  =  v&#969;",
    [
        "a<sub>c</sub> = centripetal acceleration  (always directed toward the CENTRE)",
        "v = speed of particle   |   r = radius of circle   |   &#969; = angular velocity",
        "Derived from: direction of velocity changes by d&#952; in time dt  &#8594;  |dv&#8407;| = v&#183;d&#952;  &#8594;  a = v&#183;d&#952;/dt = v&#969; = v&#178;/r",
        "Magnitude is constant in UCM  (v and r are constant)",
        "Direction rotates with the particle  (always inward, radially toward centre)",
    ]
))
story.append(sp(4))
story.append(trap_box("Centripetal acceleration does NOT change the speed &#8212; it only changes the DIRECTION of velocity. Speed remains constant in UCM."))
story.append(sp(6))

story += [sec("5.4  Centripetal Force")]
story.append(formula_box(
    "F<sub>c</sub>  =  ma<sub>c</sub>  =  mv<super>2</super>/r  =  m&#969;<super>2</super>r",
    [
        "F<sub>c</sub> = centripetal force  (always directed toward the CENTRE)",
        "m = mass of particle   |   v = speed   |   r = radius   |   &#969; = angular velocity",
        "This is NOT a new kind of force &#8212; it is the NET inward force provided by existing forces",
        "Examples of what provides centripetal force:",
        "  &#8594; Tension in string for circular motion of a ball on string",
        "  &#8594; Gravity for satellite orbiting Earth",
        "  &#8594; Normal force (banked roads) and friction (flat roads) for vehicles",
        "  &#8594; Coulomb force for electron orbiting nucleus  (Bohr model)",
    ]
))
story.append(sp(4))
story.append(note_box("Centrifugal force is a PSEUDO force (fictitious force) that appears only in the rotating (non-inertial) reference frame. It is NOT a real force and does NOT exist in the ground (inertial) frame."))
story.append(sp(6))

story += [sec("5.5  Time Period and Frequency")]
story.append(formula_box(
    "T  =  2&#960;r / v  =  2&#960; / &#969;          &#957;  =  1/T  =  &#969; / 2&#960;",
    [
        "T = time period = time for one complete revolution  (seconds)",
        "&#957; = frequency = number of revolutions per second  (Hz or s<super>&#8722;1</super>)",
        "&#969; = 2&#960;&#957; = 2&#960;/T   (angular frequency in rad/s)",
        "Angular velocity and time period are inversely related:  &#969;T = 2&#960;",
    ]
))
story.append(sp(6))

story += [sec("5.6  Complete Formula Summary for UCM")]
story.append(make_table(
    ["Quantity","Symbol","Formula","SI Unit"],
    [
        ["Arc length",          "s",             "s = r&#952;",                         "m"],
        ["Angular velocity",    "&#969;",         "&#969; = 2&#960;/T = 2&#960;&#957; = v/r",  "rad/s"],
        ["Linear speed",        "v",             "v = r&#969; = 2&#960;r/T",            "m/s"],
        ["Time period",         "T",             "T = 2&#960;/&#969; = 2&#960;r/v",     "s"],
        ["Frequency",           "&#957;",         "&#957; = 1/T = &#969;/2&#960;",       "Hz"],
        ["Centripetal acc.",    "a<sub>c</sub>",  "a<sub>c</sub> = v&#178;/r = &#969;&#178;r",  "m/s&#178;"],
        ["Centripetal force",   "F<sub>c</sub>",  "F<sub>c</sub> = mv&#178;/r = m&#969;&#178;r","N"],
    ],
    col_widths=[W*0.25,W*0.14,W*0.38,W*0.23]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 6 — RELATIVE VELOCITY IN 2D
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 6 — RELATIVE VELOCITY IN TWO DIMENSIONS"), sp(8)]

story += [sec("6.1  Concept")]
story += [body("The velocity of object A as observed from object B  (which is itself moving)  is the "
               "<b>relative velocity of A with respect to B</b>.")]
story.append(sp(5))
story.append(formula_box(
    "v&#8407;<sub>AB</sub>  =  v&#8407;<sub>A</sub>  &#8722;  v&#8407;<sub>B</sub>",
    [
        "v&#8407;<sub>AB</sub> = velocity of A relative to B  (as seen from B)",
        "v&#8407;<sub>BA</sub> = velocity of B relative to A = &#8722;v&#8407;<sub>AB</sub>",
        "v&#8407;<sub>A</sub> = absolute velocity of A  (relative to ground)",
        "v&#8407;<sub>B</sub> = absolute velocity of B  (relative to ground)",
        "Magnitude:  |v&#8407;<sub>AB</sub>| = &#8730;(v<sub>A</sub><super>2</super> + v<sub>B</sub><super>2</super> &#8722; 2v<sub>A</sub>v<sub>B</sub>cos&#952;)",
        "where &#952; = angle between v&#8407;<sub>A</sub> and v&#8407;<sub>B</sub>",
    ]
))
story.append(sp(6))

story += [sec("6.2  Important Cases")]
story.append(make_table(
    ["Case","Condition","Relative velocity magnitude"],
    [
        ["Same direction","&#952; = 0&#176;","v<sub>AB</sub> = |v<sub>A</sub> &#8722; v<sub>B</sub>|   (difference)"],
        ["Opposite directions","&#952; = 180&#176;","v<sub>AB</sub> = v<sub>A</sub> + v<sub>B</sub>   (sum)"],
        ["Perpendicular","&#952; = 90&#176;","v<sub>AB</sub> = &#8730;(v<sub>A</sub><super>2</super> + v<sub>B</sub><super>2</super>)"],
    ],
    col_widths=[W*0.26,W*0.26,W*0.48], left_cols=[0,2]
))
story.append(sp(6))

story += [sec("6.3  River-Boat Problems")]
story += [body("A boat crosses a river of width d. River flows with speed v<sub>r</sub>. "
               "Boat speed in still water = v<sub>b</sub>. Boat steers at angle &#952; to the direction perpendicular to the river bank.")]
story.append(sp(3))
story.append(formula_box(
    "Resultant velocity:   v<sub>res</sub>  =  &#8730;(v<sub>b</sub><super>2</super> + v<sub>r</sub><super>2</super>)      when &#952; = 90&#176; to bank",
    [
        "Time to cross river  (when boat aims perpendicular to bank):  t = d / v<sub>b</sub>",
        "Drift downstream:  x = v<sub>r</sub> &#215; t = v<sub>r</sub>d / v<sub>b</sub>",
        "To go STRAIGHT across (zero drift):  boat must aim upstream at angle &#945; where sin&#945; = v<sub>r</sub>/v<sub>b</sub>   (only possible if v<sub>b</sub> &gt; v<sub>r</sub>)",
        "Minimum time to cross: aim perpendicular to bank  &#8594;  t<sub>min</sub> = d/v<sub>b</sub>",
        "Minimum drift: aim at angle &#945; = sin<super>&#8722;1</super>(v<sub>r</sub>/v<sub>b</sub>) upstream",
    ]
))
story.append(sp(6))

story += [sec("6.4  Rain Problems")]
story += [body("A person walks with velocity v&#8407;<sub>p</sub>. Rain falls with velocity v&#8407;<sub>r</sub>. "
               "To avoid getting wet, the person should hold the umbrella in the direction of "
               "relative velocity of rain with respect to the person.")]
story.append(sp(3))
story.append(formula_box(
    "v&#8407;<sub>rain w.r.t. person</sub>  =  v&#8407;<sub>rain</sub>  &#8722;  v&#8407;<sub>person</sub>",
    [
        "Umbrella should be tilted in the direction of  v&#8407;<sub>rain</sub> &#8722; v&#8407;<sub>person</sub>",
        "If rain falls vertically at speed v<sub>r</sub> and person walks at speed v<sub>p</sub>:",
        "  Angle of umbrella from vertical = tan<super>&#8722;1</super>(v<sub>p</sub>/v<sub>r</sub>)",
    ]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 7 — NON-UNIFORM CIRCULAR MOTION
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 7 — NON-UNIFORM CIRCULAR MOTION"), sp(8)]

story += [sec("7.1  Definition")]
story += [body("Circular motion where the <b>speed changes</b> with time. Both tangential acceleration "
               "(changing speed) and centripetal acceleration (changing direction) are present.")]
story.append(sp(5))

story += [sec("7.2  Tangential Acceleration")]
story.append(formula_box(
    "a<sub>t</sub>  =  dv/dt  =  r&#945;",
    [
        "a<sub>t</sub> = tangential acceleration = rate of change of speed  (scalar: can be + or &#8722;)",
        "&#945; (alpha) = angular acceleration = d&#969;/dt  (in rad/s&#178;)",
        "Direction: tangent to the circle  (same or opposite to velocity)",
    ]
))
story.append(sp(5))

story += [sec("7.3  Total Acceleration")]
story.append(formula_box(
    "a<sub>total</sub>  =  &#8730;(a<sub>c</sub><super>2</super> + a<sub>t</sub><super>2</super>)",
    [
        "a<sub>c</sub> = centripetal acceleration = v&#178;/r  (directed inward, toward centre)",
        "a<sub>t</sub> = tangential acceleration  (directed along tangent)",
        "a<sub>c</sub> and a<sub>t</sub> are perpendicular to each other",
        "Angle of total acceleration with radius:  tan&#966; = a<sub>t</sub>/a<sub>c</sub>",
    ]
))
story.append(sp(5))

story += [sec("7.4  Angular Equations of Motion (Analogous to Linear)")]
story.append(make_table(
    ["Linear  (1D)","Angular  (circular)","Condition"],
    [
        ["v = u + at",           "&#969; = &#969;<sub>0</sub> + &#945;t",                  "Uniform angular acceleration"],
        ["s = ut + (1/2)at&#178;","&#952; = &#969;<sub>0</sub>t + (1/2)&#945;t&#178;",      "Uniform angular acceleration"],
        ["v&#178; = u&#178; + 2as","&#969;&#178; = &#969;<sub>0</sub>&#178; + 2&#945;&#952;","Uniform angular acceleration"],
    ],
    col_widths=[W*0.34,W*0.42,W*0.24], left_cols=[0,1,2]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 8 — IMPORTANT DERIVED RESULTS & SPECIAL CASES
# ══════════════════════════════════════════════════════════════════
story += [part_header("PART 8 — IMPORTANT RESULTS & SPECIAL CASES"), sp(8)]

story += [sec("8.1  Projectile on Inclined Plane")]
story += [body("Projectile launched at angle &#945; to an incline of angle &#946; to horizontal:")]
story.append(formula_box(
    "Range on incline:   R  =  2u<super>2</super>sin(&#945;&#8722;&#946;)cos&#945; / g cos<super>2</super>&#946;",
    [
        "Maximum range on incline when &#945; = 45&#176; + &#946;/2",
        "For projectile thrown down the incline, &#946; is negative",
    ]
))
story.append(sp(6))

story += [sec("8.2  Velocity at Any Point on Projectile Path")]
story.append(formula_box(
    "v  =  &#8730;(u<super>2</super> &#8722; 2gy)          at height y above launch",
    [
        "Derived from energy conservation  (or from v&#178; = u&#178; &#8722; 2gy  in vertical direction + v<sub>x</sub>=u cos&#952; constant)",
        "At y=0 (launch and landing): v = u  (same speed at same height)",
        "At maximum height (y=H): v = u cos&#952;  (minimum speed)",
    ]
))
story.append(sp(6))

story += [sec("8.3  Angle Between Velocity and Acceleration in Projectile")]
story.append(formula_box(
    "cos&#966;  =  &#8722;v<sub>y</sub> / v  =  &#8722;(u sin&#952; &#8722; gt) / v",
    [
        "At launch: &#966; = 90&#176; &#8722; &#952;  (angle between v and g)",
        "At maximum height: &#966; = 90&#176;  (velocity horizontal, g vertical  &#8594;  perpendicular)",
        "At landing: velocity makes angle &#952; below horizontal",
    ]
))
story.append(sp(6))

story += [sec("8.4  Key NEET/JEE Results to Memorise")]
story.append(make_table(
    ["Result","Expression","Condition"],
    [
        ["Same range for complementary angles",  "R(&#952;) = R(90&#176;&#8722;&#952;)",        "Same u, same launch height"],
        ["Max range",                            "R<sub>max</sub> = u&#178;/g",                "&#952; = 45&#176;"],
        ["Relation H and R",                     "R = 4H/tan&#952;",                           "Same u, same &#952;"],
        ["Max height at &#952;=90&#176;",         "H<sub>max</sub> = u&#178;/2g",               "Vertical throw"],
        ["Speed at top = u cos&#952;",           "v<sub>top</sub> = u cos&#952;",              "At max height"],
        ["Velocity at landing",                  "Same speed as launch = u",                   "Same height as launch"],
        ["Total time &#8733; sin&#952;",          "T &#8733; sin&#952;",                        "For fixed u"],
        ["Max height &#8733; sin&#178;&#952;",   "H &#8733; sin&#178;&#952;",                  "For fixed u"],
        ["Range &#8733; sin 2&#952;",            "R &#8733; sin 2&#952;",                       "For fixed u"],
        ["Centripetal acc. &#8733; 1/r",         "a<sub>c</sub> = v&#178;/r  &#8733; 1/r",     "Fixed speed v"],
    ],
    col_widths=[W*0.35,W*0.32,W*0.33], left_cols=[0,1,2]
))
story.append(sp(10))

# ══════════════════════════════════════════════════════════════════
# PART 9 — COMPLETE FORMULA SHEET
# ══════════════════════════════════════════════════════════════════
story.append(PageBreak())
story += [part_header("MASTER FORMULA SHEET — MOTION IN A PLANE"), sp(8)]
story.append(make_table(
    ["#","Topic","Formula"],
    [
        # VECTORS
        ["1","Resultant (Parallelogram law)","R = &#8730;(A&#178; + B&#178; + 2AB cos&#952;)"],
        ["2","Direction of resultant","tan&#945; = B sin&#952; / (A + B cos&#952;)"],
        ["3","Subtraction magnitude","|A&#8407;&#8722;B&#8407;| = &#8730;(A&#178; + B&#178; &#8722; 2AB cos&#952;)"],
        ["4","x-component","A<sub>x</sub> = A cos&#952;"],
        ["5","y-component","A<sub>y</sub> = A sin&#952;"],
        ["6","Magnitude from components","A = &#8730;(A<sub>x</sub>&#178; + A<sub>y</sub>&#178;)"],
        ["7","Unit vector","A&#770; = A&#8407;/|A&#8407;|"],
        ["8","Dot (scalar) product","A&#8407;&#183;B&#8407; = AB cos&#952; = A<sub>x</sub>B<sub>x</sub>+A<sub>y</sub>B<sub>y</sub>+A<sub>z</sub>B<sub>z</sub>"],
        ["9","Cross (vector) product","|A&#8407;&#215;B&#8407;| = AB sin&#952;"],
        # GENERAL 2D MOTION
        ["10","Displacement","&#916;r&#8407; = r&#8407;<sub>2</sub> &#8722; r&#8407;<sub>1</sub>"],
        ["11","Velocity","v&#8407; = dr&#8407;/dt = v<sub>x</sub>i&#770; + v<sub>y</sub>j&#770;"],
        ["12","Speed","v = &#8730;(v<sub>x</sub>&#178;+v<sub>y</sub>&#178;)"],
        ["13","Acceleration","a&#8407; = dv&#8407;/dt = a<sub>x</sub>i&#770; + a<sub>y</sub>j&#770;"],
        # PROJECTILE
        ["14","Initial components","u<sub>x</sub>=u cos&#952;   |   u<sub>y</sub>=u sin&#952;"],
        ["15","x at time t","x = u cos&#952; &#183; t"],
        ["16","y at time t","y = u sin&#952; &#183; t &#8722; (1/2)gt&#178;"],
        ["17","v<sub>x</sub> at time t","v<sub>x</sub> = u cos&#952; = constant"],
        ["18","v<sub>y</sub> at time t","v<sub>y</sub> = u sin&#952; &#8722; gt"],
        ["19","Speed at time t","v = &#8730;(v<sub>x</sub>&#178;+v<sub>y</sub>&#178;)"],
        ["20","Trajectory (parabola)","y = x tan&#952; &#8722; gx&#178;/(2u&#178;cos&#178;&#952;)"],
        ["21","Time of flight","T = 2u sin&#952;/g"],
        ["22","Maximum height","H = u&#178;sin&#178;&#952;/(2g)"],
        ["23","Horizontal range","R = u&#178;sin2&#952;/g"],
        ["24","Maximum range","R<sub>max</sub> = u&#178;/g   at  &#952;=45&#176;"],
        ["25","Speed at max height","v<sub>top</sub> = u cos&#952;"],
        ["26","Horizontal projection: time to ground","t = &#8730;(2h/g)"],
        ["27","Horizontal projection: range","x = u&#8730;(2h/g)"],
        ["28","Horizontal projection: speed at ground","v = &#8730;(u&#178;+2gh)"],
        # CIRCULAR
        ["29","Arc length","s = r&#952;"],
        ["30","Angular velocity","&#969; = d&#952;/dt = v/r = 2&#960;/T = 2&#960;&#957;"],
        ["31","Linear speed","v = r&#969;"],
        ["32","Time period","T = 2&#960;/&#969; = 2&#960;r/v"],
        ["33","Frequency","&#957; = 1/T = &#969;/2&#960;"],
        ["34","Centripetal acceleration","a<sub>c</sub> = v&#178;/r = &#969;&#178;r"],
        ["35","Centripetal force","F<sub>c</sub> = mv&#178;/r = m&#969;&#178;r"],
        # RELATIVE VELOCITY
        ["36","Relative velocity","v&#8407;<sub>AB</sub> = v&#8407;<sub>A</sub> &#8722; v&#8407;<sub>B</sub>"],
        ["37","Relative velocity magnitude","v<sub>AB</sub> = &#8730;(v<sub>A</sub>&#178;+v<sub>B</sub>&#178;&#8722;2v<sub>A</sub>v<sub>B</sub>cos&#952;)"],
        # NON-UNIFORM CIRCULAR
        ["38","Tangential acceleration","a<sub>t</sub> = dv/dt = r&#945;"],
        ["39","Total acceleration","a = &#8730;(a<sub>c</sub>&#178;+a<sub>t</sub>&#178;)"],
        ["40","Angular equations (3 eqs)","&#969;=&#969;<sub>0</sub>+&#945;t   |   &#952;=&#969;<sub>0</sub>t+(1/2)&#945;t&#178;   |   &#969;&#178;=&#969;<sub>0</sub>&#178;+2&#945;&#952;"],
    ],
    col_widths=[W*0.06,W*0.34,W*0.60], left_cols=[1,2]
))
story.append(sp(8))

# ── CONSTANTS USED ────────────────────────────────────────────────
story += [part_header("CONSTANTS & STANDARD VALUES"), sp(8)]
story.append(make_table(
    ["Symbol","Name","Standard Value","Unit"],
    [
        ["g","Acceleration due to gravity","9.8 m/s&#178;  (use 10 m/s&#178; in NEET)","m/s&#178;"],
        ["&#960;","Pi","3.14159...","dimensionless"],
        ["1 rev","In radians","2&#960; rad  =  6.283 rad","rad"],
        ["1&#176;","In radians","&#960;/180  &#8773;  0.01745 rad","rad"],
        ["1 rad","In degrees","180&#176;/&#960;  &#8773;  57.3&#176;","degrees"],
    ],
    col_widths=[W*0.12,W*0.36,W*0.38,W*0.14], left_cols=[1,2]
))
story.append(sp(8))

story.append(thinhr())
story.append(sp(4))
story.append(Paragraph(
    "Motion in a Plane  |  Class 11 Physics  |  Complete Chapter  |  All 40 Formulas  |  NEET / JEE",
    sFooter
))

doc.build(story)
print("PDF created:", OUTPUT)
Running Command

cd /tmp/workspace/motion-in-plane && python3 generate_mip.py 2>&1

Running Command

ls -lh /tmp/workspace/motion-in-plane/Motion_in_a_Plane_Complete.pdf

Your PDF is ready — same format as the Atomic Structure one:

Motion in a Plane Complete

PDF Document · PDF

What's covered (9 Parts, 40 Formulas):
PartTopic
1Scalars and Vectors — definition, types, representation, zero/unit/equal/negative vectors
2Vector Algebra — Triangle law, Parallelogram law, resultant magnitude & direction, subtraction, resolution into components, unit vectors i j k, dot product, cross product
3Motion in a Plane (General) — position vector, displacement, velocity, acceleration, equations of motion in 2D
4Projectile Motion — assumptions, initial components, x/y equations, trajectory (parabola), time of flight, max height, horizontal range, max range, horizontal projection from height
5Uniform Circular Motion — angular displacement, angular velocity, v=rω, centripetal acceleration, centripetal force, time period, frequency — full summary table
6Relative Velocity in 2D — concept, formula, special cases table, river-boat problems, rain problems
7Non-Uniform Circular Motion — tangential acceleration, total acceleration, angular equations of motion
8Important Results & Special Cases — complementary angles, projectile on incline, velocity at any height, key NEET/JEE results table
9Master formula sheet (all 40 formulas) + constants reference

The values are not clearly visible like j[] this, except this everything is nice even the matter is wonderful.

Running Command

python3 -c " from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont from reportlab.platypus import SimpleDocTemplate, Paragraph from reportlab.lib.styles import ParagraphStyle from reportlab.lib.pagesizes import A4 pdfmetrics.registerFont(TTFont('DS', '/usr/share/fonts/google-droid-sans-fonts/DroidSans.ttf')) pdfmetrics.registerFont(TTFont('DSB', '/usr/share/fonts/google-droid-sans-fonts/DroidSans-Bold.ttf')) doc = SimpleDocTemplate('/tmp/workspace/motion-in-plane/test_chars2.pdf', pagesize=A4) style = ParagraphStyle('t', fontName='DS', fontSize=13, leading=20) tests = [ # hat characters 'Unit vectors: i&#770; j&#770; k&#770;', # &#770; = combining circumflex 'Unit vectors: &#238; &#309; k&#x0302;', # i with circumflex, j with circumflex 'Unit vectors: &#xEF; &#xF0;', # arrow on top 'Vector arrow: A&#8407; B&#8407;', # &#8407; = combining right arrow above # omega 'omega: &#969; alpha: &#945; phi: &#966; theta: &#952; beta: &#946; nu: &#957;', # proportional, infinity 'proportional: &#8733; infinity: &#8734; approx: &#8776;', # hat via text 'Unit vectors: i-hat j-hat k-hat', # superscript minus 'v<super>2</super> 10<super>-34</super> m/s<super>2</super>', # subscript 'v<sub>x</sub> a<sub>c</sub> R<sub>H</sub>', # degree symbol '90&#176; 180&#176; 360&#176;', # tilde '&#957;&#771; nu-tilde', ] story = [Paragraph(t, style) for t in tests] doc.build(story) print('test2 done') "

analyse_media
Running Command

python3 -c " # Check which specific glyphs DroidSans actually has from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont font = TTFont('DS', '/usr/share/fonts/google-droid-sans-fonts/DroidSans.ttf') font.face.filename = '/usr/share/fonts/google-droid-sans-fonts/DroidSans.ttf' font.face.charWidths = {} # Test specific codepoints import struct # Parse cmap from the font file from fontTools.ttLib import TTFont as FTFont tt = FTFont('/usr/share/fonts/google-droid-sans-fonts/DroidSans.ttf') cmap = tt.getBestCmap() # Check specific chars we care about chars_to_check = { 'i-hat (î)': 0x00EE, # î 'j-hat (ĵ)': 0x0135, # ĵ 'k-hat (k̂)': 0x006B, # k (base) 'combining circumflex': 0x0302, 'arrow above (&#8407;)': 0x20D7, 'omega (&#969;)': 0x03C9, 'proportional (&#8733;)': 0x221D, 'infinity (&#8734;)': 0x221E, 'nu tilde combo': 0x0303, 'degree (&#176;)': 0x00B0, 'hat i (&#238;)': 0x00EE, 'j circumflex (&#309;)': 0x0135, 'vector arrow &#8407;': 0x20D7, } for name, cp in chars_to_check.items(): has = cp in cmap print(f'{name} U+{cp:04X} : {\"HAS\" if has else \"MISSING\"}' ) "

Your credits are exhausted. You can purchase a top-up or subscribe to a plan to continue using Orris. If you're already subscribed, your daily credits will reset tomorrow.
This is a shared conversation. Sign in to Orris to start your own chat.