Do you do astrology

Yes! I can help with astrology. Here are some things I can do:
Birth Chart & Natal Astrology
  • Interpret your natal chart (sun, moon, rising sign, planetary placements)
  • Explain what your big three signs mean (sun, moon, ascendant)
  • Discuss house placements and aspects
Compatibility
  • Synastry (comparing two charts)
  • General sign compatibility
Transits & Timing
  • Current planetary transits and what they mean
  • Mercury retrograde, eclipses, and other notable events
General Education
  • Explain planets, signs, houses, and aspects
  • Discuss different systems (Western, Vedic/Jyotish, Chinese)
  • History and mythology behind astrological symbolism
What I'll be upfront about: Astrology is not scientifically validated as a predictive system. I can engage with it seriously as a symbolic, psychological, and cultural framework - many people find it useful for self-reflection - but I won't pretend it has empirical predictive power.
To get started, just tell me what you're curious about. If you want a natal chart reading, share your:
  • Date of birth
  • Time of birth (as accurate as possible)
  • Place of birth
What would you like to explore?

DOB: 19-08-1996 TOB: 11:55 AM POB: ROHTAK Q: Is my career growth will be outside of india or in india ? If its abroad which country it would be ? Q: is health care profession is lead my future career ? Q: when will i get married ? Is my from abroad or india ?

Searching the Web

Rohtak India latitude longitude coordinates

Running Command

pip install --user pyswisseph ephem 2>/dev/null | tail -1; python3 -c "import swisseph; print('swisseph ok')" 2>/dev/null || echo "no swisseph"

Running Command

python3 << 'EOF' import swisseph as swe from datetime import datetime, timezone import math # Birth data # 19 Aug 1996, 11:55 AM IST = 06:25 UTC # Rohtak: Lat 28.8955, Lon 76.6066 lat = 28.8955 lon = 76.6066 # Julian Day for 1996-08-19 06:25:00 UTC jd = swe.julday(1996, 8, 19, 6 + 25/60.0) print(f"Julian Day: {jd}") # Set sidereal mode (Lahiri ayanamsha - standard for Vedic) swe.set_sid_mode(swe.SIDM_LAHIRI) # Ayanamsha ayan = swe.get_ayanamsa(jd) print(f"Lahiri Ayanamsha: {ayan:.4f} degrees") # Calculate Ascendant (Lagna) using houses houses, ascmc = swe.houses(jd, lat, lon, b'P') asc_trop = ascmc[0] # tropical ascendant asc_sid = (asc_trop - ayan) % 360 print(f"\nTropical Ascendant: {asc_trop:.4f}") print(f"Sidereal Ascendant (Vedic Lagna): {asc_sid:.4f}") # Planet IDs planets = { 'Sun': swe.SUN, 'Moon': swe.MOON, 'Mercury': swe.MERCURY, 'Venus': swe.VENUS, 'Mars': swe.MARS, 'Jupiter': swe.JUPITER, 'Saturn': swe.SATURN, 'Rahu': swe.MEAN_NODE, } # Signs signs = ['Aries','Taurus','Gemini','Cancer','Leo','Virgo', 'Libra','Scorpio','Sagittarius','Capricorn','Aquarius','Pisces'] def get_sign(deg): return signs[int(deg/30)] def get_nakshatra(deg): nakshatras = [ 'Ashwini','Bharani','Krittika','Rohini','Mrigashira','Ardra', 'Punarvasu','Pushya','Ashlesha','Magha','Purva Phalguni','Uttara Phalguni', 'Hasta','Chitra','Swati','Vishakha','Anuradha','Jyeshtha', 'Mula','Purva Ashadha','Uttara Ashadha','Shravana','Dhanishtha','Shatabhisha', 'Purva Bhadrapada','Uttara Bhadrapada','Revati' ] nak_lords = ['Ketu','Venus','Sun','Moon','Mars','Rahu','Jupiter','Saturn','Mercury'] * 3 idx = int(deg * 27 / 360) pada = int((deg * 108 / 360) % 4) + 1 return nakshatras[idx], nak_lords[idx], pada print("\n--- PLANETARY POSITIONS (Vedic/Sidereal) ---") planet_data = {} for name, pid in planets.items(): result, _ = swe.calc_ut(jd, pid, swe.FLG_SIDEREAL) deg = result[0] % 360 sign = get_sign(deg) deg_in_sign = deg % 30 nak, nak_lord, pada = get_nakshatra(deg) planet_data[name] = {'deg': deg, 'sign': sign, 'deg_in_sign': deg_in_sign, 'nak': nak, 'nak_lord': nak_lord, 'pada': pada} print(f"{name:10}: {sign:15} {deg_in_sign:6.2f}Β° | Nakshatra: {nak} (Lord: {nak_lord}) Pada {pada}") # Ketu = Rahu + 180 ketu_deg = (planet_data['Rahu']['deg'] + 180) % 360 ketu_sign = get_sign(ketu_deg) nak, nak_lord, pada = get_nakshatra(ketu_deg) planet_data['Ketu'] = {'deg': ketu_deg, 'sign': ketu_sign, 'deg_in_sign': ketu_deg % 30, 'nak': nak, 'nak_lord': nak_lord, 'pada': pada} print(f"{'Ketu':10}: {ketu_sign:15} {ketu_deg%30:6.2f}Β° | Nakshatra: {nak} (Lord: {nak_lord}) Pada {pada}") # Lagna details lagna_sign = get_sign(asc_sid) lagna_deg_in_sign = asc_sid % 30 nak, nak_lord, pada = get_nakshatra(asc_sid) print(f"\n{'Lagna':10}: {lagna_sign:15} {lagna_deg_in_sign:6.2f}Β° | Nakshatra: {nak} (Lord: {nak_lord}) Pada {pada}") # --- Houses (Bhava) for Whole Sign system (standard Vedic) --- print("\n--- WHOLE SIGN HOUSES ---") lagna_sign_num = int(asc_sid / 30) # 0=Aries, 1=Taurus... for house_num in range(1, 13): sign_idx = (lagna_sign_num + house_num - 1) % 12 planets_in_house = [] # Check lagna if house_num == 1: planets_in_house.append('Lagna') for pname, pdata in planet_data.items(): p_sign_idx = int(pdata['deg'] / 30) if p_sign_idx == sign_idx: planets_in_house.append(pname) print(f"House {house_num:2} ({signs[sign_idx]:15}): {', '.join(planets_in_house) if planets_in_house else '-'}") # --- Vimshottari Dasha --- print("\n--- VIMSHOTTARI DASHA (Moon-based) ---") moon_nak_deg = planet_data['Moon']['deg'] # Balance of first dasha nak_lords_order = ['Ketu','Venus','Sun','Moon','Mars','Rahu','Jupiter','Saturn','Mercury'] dasha_years = {'Ketu':7,'Venus':20,'Sun':6,'Moon':10,'Mars':7,'Rahu':18,'Jupiter':16,'Saturn':19,'Mercury':17} total = 120 moon_nak_idx = int(moon_nak_deg * 27 / 360) moon_nak_lord_idx = moon_nak_idx % 9 start_lord = nak_lords_order[moon_nak_lord_idx] # Position within nakshatra nak_span = 360/27 pos_in_nak = (moon_nak_deg % nak_span) / nak_span balance = (1 - pos_in_nak) * dasha_years[start_lord] birth_year = 1996 + (8-1 + 19/31)/12 # approx # More precisely: Aug 19.5 1996 birth_jd = jd from datetime import date birth_date = date(1996, 8, 19) print(f"Moon Nakshatra: {planet_data['Moon']['nak']} | Dasha Lord at birth: {start_lord}") print(f"Balance of {start_lord} dasha at birth: {balance:.2f} years") # Build dasha sequence import datetime current_date = datetime.date(2026, 7, 31) birth_date_d = datetime.date(1996, 8, 19) # Start from birth start_idx = nak_lords_order.index(start_lord) dasha_list = [] current_start = birth_date_d # First dasha: only balance first_end_days = balance * 365.25 first_end = birth_date_d + datetime.timedelta(days=first_end_days) dasha_list.append((start_lord, birth_date_d, first_end, balance)) current_start = first_end for i in range(1, 10): lord = nak_lords_order[(start_idx + i) % 9] yrs = dasha_years[lord] end = current_start + datetime.timedelta(days=yrs*365.25) dasha_list.append((lord, current_start, end, yrs)) current_start = end print("\nMahadasha Timeline:") for lord, start, end, yrs in dasha_list: marker = " <-- CURRENT" if start <= current_date <= end else "" print(f" {lord:10}: {start.strftime('%d-%b-%Y')} to {end.strftime('%d-%b-%Y')} ({yrs:.1f} yrs){marker}") # Find current antardasha print("\nCurrent Antardasha:") for lord, start, end, yrs in dasha_list: if start <= current_date <= end: current_maha = lord maha_start = start maha_end = end maha_yrs = yrs print(f"Mahadasha: {lord} ({start.strftime('%d-%b-%Y')} - {end.strftime('%d-%b-%Y')})") # Antardasha maha_total_days = (maha_end - maha_start).days ant_start = maha_start maha_idx = nak_lords_order.index(lord) for j in range(9): ant_lord = nak_lords_order[(maha_idx + j) % 9] ant_days = (dasha_years[ant_lord] / total) * maha_yrs * 365.25 ant_end = ant_start + datetime.timedelta(days=ant_days) ant_marker = " <-- NOW" if ant_start <= current_date <= ant_end else "" print(f" Antardasha {ant_lord:10}: {ant_start.strftime('%d-%b-%Y')} to {ant_end.strftime('%d-%b-%Y')}{ant_marker}") ant_start = ant_end break EOF

I now have a complete, calculated Vedic birth chart. Here is your full reading:

πŸͺ Your Vedic Birth Chart (Jyotish)

DOB: 19 August 1996 | TOB: 11:55 AM IST | POB: Rohtak, Haryana

Chart Summary

PlanetSignDegreeNakshatraHouse
Lagna (Asc)Libra20.25Β°Vishakha (Jupiter)1st
SunLeo2.74Β°Magha (Ketu)11th
MoonVirgo28.07Β°Chitra (Mars)12th
MercuryLeo29.97Β°Uttara Phalguni (Sun)11th
VenusGemini17.00Β°Ardra (Rahu)9th
MarsGemini22.41Β°Punarvasu (Jupiter)9th
JupiterSagittarius14.38Β°Purva Ashadha (Venus)3rd
SaturnPisces12.78Β°Uttara Bhadrapada (Saturn)6th
RahuVirgo16.38Β°Hasta (Moon)12th
KetuPisces16.38Β°Uttara Bhadrapada (Saturn)6th

Q1: Career Growth - India or Abroad? Which Country?

Strong indicators of foreign/abroad career:
  • Venus + Mars in the 9th House (Gemini): The 9th house is the house of long-distance travel, foreign lands, and higher fortune. Having two planets here - and Venus is your Lagna lord (ruler of Libra) - this is a very prominent yoga for working and growing abroad. Venus in the 9th is classically one of the strongest indicators of foreign settlement or career.
  • Moon + Rahu in the 12th House (Virgo): The 12th house is the house of foreign lands, isolation, and overseas settlement. Rahu amplifies all 12th house themes powerfully. Rahu in the 12th is one of the clearest Vedic indicators of foreign connection in both career and residence.
  • Lagna Lord (Venus) in 9th: Your chart ruler sitting in the 9th makes foreign opportunities central to your life path, not peripheral.
  • Jupiter Mahadasha (2019-2035) - CURRENTLY ACTIVE: Jupiter is in your 3rd house in Sagittarius and aspects the 7th, 9th, and 11th houses. Jupiter as a natural benefic aspecting the 9th (foreign lands) during this 16-year period is activating these themes right now.
Which country?
In Jyotish, certain planets and signs are associated with different regions:
  • Venus (Lagna lord in 9th) rules Taurus/Libra - associated with Western Europe, particularly UK, France, Scandinavian countries
  • Rahu in Virgo (12th house) - associated with Western countries, particularly English-speaking nations (USA, Canada, Australia, UK)
  • Mars in Gemini (9th) - associated with North America and Europe
  • Jupiter in Sagittarius (3rd) - associated with USA, Australia, and Southeast Asia
Most indicated regions: USA, Canada, UK, or Australia. Western/English-speaking countries are strongly pointed to by the Rahu-Venus combination. If healthcare is the field (see Q2), countries with high demand for healthcare professionals like USA, Canada, UK, and Australia align perfectly.

Q2: Is Healthcare the Right Career Path?

Yes - this chart has several healthcare pointers:
  • Saturn in the 6th House (Pisces): The 6th house in Vedic astrology directly rules medicine, disease, healing, and service professions. Saturn in the 6th is a classic yoga for a career in healthcare, medical service, or healing. Saturn gives discipline, patience, and long-term commitment - all needed in medical fields.
  • Ketu also in the 6th House: Ketu in the 6th gives sharp analytical and diagnostic ability. Combined with Saturn, this strongly favors medicine, diagnostics, or healing professions.
  • Jupiter in Sagittarius (3rd) aspecting 9th: Jupiter rules wisdom, higher learning, and medicine (Vaidya yoga). Jupiter's aspect on the 9th (higher education) supports advanced medical degrees or specializations.
  • Virgo (12th house) ruled by Mercury: Virgo is the natural 6th sign and is associated with health, hygiene, and healing. Moon + Rahu here deepens your connection to healthcare emotionally and karmically.
  • Moon in Chitra nakshatra (Mars-ruled): Chitra natives are often found in surgical, design, or precision fields. Surgery or a field requiring manual precision (dentistry, surgery, radiology) may suit you.
Most aligned healthcare paths for you: Medicine/Surgery, Nursing, Pharmacy, Medical diagnostics, Allied health sciences, or Healthcare management. The Saturn-Ketu in 6th combination suggests you would do very well in a clinical or technical healthcare role - possibly involving chronic disease management, research, or large hospital settings.

Q3: When Will You Marry? Partner from India or Abroad?

Current Dasha: Jupiter Mahadasha - Ketu Antardasha (27 Jan 2026 to 02 Jan 2027) - right now. Next Antardasha: Jupiter - Venus (02 Jan 2027 to 02 Sep 2029)
Marriage timing indicators:
  • 7th house (Aries) is empty, but its lord is Mars, which sits in the 9th house (Gemini). Mars in the 9th delays but ultimately gives a partner connected to foreign lands or a different background.
  • Venus is the Lagna lord and the natural karaka (significator) of marriage. Venus in the 9th house strongly suggests a partner met through travels, higher education, work abroad, or someone from a different region/culture.
  • Jupiter Mahadasha is the current period (until 2035). Jupiter aspects the 7th house in your chart, making this mahadasha highly favorable for marriage.
  • Jupiter-Venus Antardasha (Jan 2027 to Sep 2029) is the strongest window for marriage. Venus is both the Lagna lord AND the natural significator of marriage, and it activates in this antardasha. This period (2027-2029) is the most likely time for marriage.
  • The Jupiter-Sun antardasha (Sep 2029 - Jun 2030) is also a secondary possibility since Sun sits in the 11th house (gains/fulfillment).
Partner from India or abroad?
  • Mars (7th lord) in the 9th, Venus (Lagna lord) in the 9th, and Rahu in the 12th all point toward a partner either from abroad or someone you meet while abroad. There is also a possibility of an inter-regional/intercultural match within India, but the foreign connection is stronger.
  • If you move abroad for work (which the chart strongly suggests), meeting your partner there is very likely.

Dasha Timeline Summary

MahadashaPeriodKey Theme
Jupiter2019 - 2035Expansion, education, foreign growth
↳ Ketu ADNow - Jan 2027Transitions, spiritual shifts, loose ends
↳ Venus ADJan 2027 - Sep 2029Best window for marriage & career leap abroad
Saturn2035 - 2054Long-term stability, deep professional establishment

Summary

QuestionAnswer
Career - India or Abroad?Abroad is strongly indicated (Rahu in 12th, Venus in 9th)
Which country?USA, Canada, UK, or Australia most likely
Healthcare career?Yes - strong yoga (Saturn+Ketu in 6th house)
Marriage timing?2027-2029 (Jupiter-Venus antardasha)
Partner - India or abroad?Abroad or foreign-connected (7th lord Mars in 9th)

Disclaimer: This reading is based on traditional Vedic (Jyotish) astrological principles calculated from your birth data. Astrology is a symbolic and interpretive system - it shows tendencies and themes, not fixed certainties. Your own choices, effort, and circumstances shape your actual path. Treat this as a reflective framework, not a guarantee.

Follow ups Q: i did my mbbs from china , so yoga is out of option. Currently i am preparing for abroad ! Which one is best for me UK or Australia? Q: my future wife will be foreigner or from indian origin ? Will i meet her in india or abroad uk or Australia?

Great context - MBBS from China confirms the foreign education yoga already activated. Now let me go deeper into UK vs Australia and the marriage question using your chart.

πŸͺ Follow-Up Reading - UK vs Australia & Marriage


Q1: UK or Australia - Which is Better?

Let me break this down through key chart factors:

Planet-Country Associations (Vedic Jyotish)

PlanetCountry AssociationYour Chart Position
Venus (Lagna lord)UK, Europe, luxury/arts countries9th house - very powerful
RahuWestern world, foreign lands broadly12th house (foreign settlement)
Mars (7th lord, co-occupies 9th)Australia, military/active nations, USA9th house
Jupiter (current Mahadasha lord)UK, USA, spiritual/educational nations3rd house in Sagittarius
Saturn (6th lord - career/service)UK, cold/northern countries, service nations6th house

Directional Astrology (Dig Bala / Dik)

  • Venus rules the East and Southeast in some traditions, but in geographic Jyotish, Venus-ruled nations tend to be western and northwestern - pointing toward UK and Europe.
  • Saturn in 6th (Pisces) - Pisces is associated with far seas, isolation, island nations. UK is an island nation. This is a subtle but real pointer.
  • Mars in Gemini (9th) - Mars rules the south and southeast in geographic Jyotish. Australia is in the southern hemisphere. This gives Mars a geographic pull toward Australia.

The Deciding Factor - Your 10th House

  • Your 10th house is Cancer, ruled by Moon.
  • Moon sits in your 12th house (Virgo) with Rahu.
  • Moon-Rahu combination in 12th = career that operates in foreign, institutional, or hospital settings (very fitting for a doctor abroad).
  • Moon rules Cancer = UK's traditional astrological ruler. UK is classically ruled by Cancer/Moon in mundane astrology.
  • This is significant - your career house (10th) lord sitting in the 12th (foreign lands) with Rahu, and that lord being Moon (Cancer = UK) is a strong pointer toward UK.

Jupiter Mahadasha Factor

  • Jupiter is currently running (until 2035) and Jupiter is strongly associated with UK (education, law, established institutions, NHS).
  • The NHS (National Health Service) in the UK has a structured pathway for MBBS-from-abroad doctors through PLAB exams - which aligns with Jupiter's energy of structured, institution-based growth.
  • Australia's AMC pathway is also valid, but Jupiter's natural affinity is more aligned with UK-style structured systems.

Verdict: UK edges ahead of Australia for you

FactorUKAustralia
Venus (Lagna lord) countryβœ… StrongModerate
Moon (10th lord) = Cancer/UKβœ… Direct linkβœ—
Saturn in 6th (island/northern)βœ…Partial
Mars in 9th (southern hemisphere)Partialβœ…
Jupiter Mahadasha alignmentβœ… StrongModerate
OverallStrongerGood backup
Australia is not bad at all - Mars in the 9th gives it real relevance - but UK has more planetary lords pointing to it in your specific chart. If UK doesn't work out or feels limiting, Australia is your strong second option and Mars in 9th makes it genuinely viable.

Q2: Will Your Future Wife Be a Foreigner or Indian Origin? Where Will You Meet?

Key Planets for Marriage

In Vedic astrology, marriage is judged by:
  • 7th house - the house of spouse (Aries in your chart)
  • 7th lord - Mars (sits in 9th house, Gemini)
  • Venus - natural significator of wife (sits in 9th house, Gemini)
  • Darakaraka - the planet with the lowest degree among personal planets
Your Darakaraka (lowest degree planet, significator of wife's nature):
  • Sun: 2.74Β° - this is the lowest among the 7 personal planets
  • Sun is your Darakaraka - the planet that describes your spouse

What Sun as Darakaraka Says About Your Wife

  • Sun in Leo (11th house) - she will be proud, confident, warm, leadership-oriented, possibly in a high-status profession
  • Sun in Magha nakshatra - Magha is ruled by Ketu and is associated with royalty, ancestors, and established lineage. This suggests she comes from a respected family background
  • Sun in the 11th house - you meet her through your social network, professional circle, friend group, or community - not through a traditional arranged introduction

Indian Origin or Foreigner?

  • Mars (7th lord) in Gemini (9th house): The 7th lord being in the 9th strongly indicates a partner connected to foreign lands. This could mean:
    • A foreigner (non-Indian)
    • An Indian living abroad (NRI)
    • Someone you meet in a foreign country
  • Venus (natural wife significator) in Ardra nakshatra (Rahu-ruled): Ardra is a stormy, transformative nakshatra ruled by Rahu. Rahu represents foreign elements, mixing of cultures, unconventional connections. This adds a cross-cultural flavor to your marriage.
  • Sun (Darakaraka) in Magha nakshatra: Magha has a connection to tradition and family lineage - this slightly pulls toward Indian origin.
The most likely profile: An Indian-origin woman living abroad (NRI) - someone who has roots in India but is settled or working in the UK or Australia. This satisfies both indicators - the Rahu/9th house foreign connection AND the Magha/Sun traditional family background. A fully foreign (non-Indian) wife is also possible but slightly less indicated than the NRI profile.

Where Will You Meet Her?

  • Sun in 11th house (gains, networks, social circles): Through your professional or social network abroad - colleagues, a hospital/medical circle, a social event, or mutual friends.
  • Jupiter-Venus Antardasha (Jan 2027 - Sep 2029): This is your strongest marriage window. If you are in the UK or Australia by 2027 (which your chart strongly suggests), you will very likely meet her there - in the country you settle in.
  • The 11th house Sun also points to online connections, community groups, or diaspora events - all very common ways Indian doctors abroad meet their partners.

Summary

QuestionAnswer
UK or Australia?UK is better (Moon-Cancer link, Venus, Saturn, Jupiter alignment)
Australia?Strong backup - Mars in 9th makes it viable if UK doesn't work
Wife - foreigner or Indian?Indian-origin NRI (most likely), foreigner also possible
Where will you meet her?Abroad - in the country you settle in (UK most likely)
When?Jan 2027 - Sep 2029 (Jupiter-Venus antardasha)

Practical note: Your chart is exceptionally well-suited for a medical career abroad. The MBBS from China + preparing for PLAB (UK) or AMC (Australia) is exactly the path your 9th house planets and 12th house Moon-Rahu are pointing toward. The Jupiter Mahadasha until 2035 gives you a long, expansive window to establish yourself. This is your time.

When is my best window to move to UK or Australia?

Running Command

python3 << 'EOF' import swisseph as swe import datetime # Birth data jd_birth = swe.julday(1996, 8, 19, 6 + 25/60.0) swe.set_sid_mode(swe.SIDM_LAHIRI) # Known chart data (from previous calculation) # Lagna: Libra 20.25Β° (Vishakha, Jupiter) # 12th house: Virgo (Moon + Rahu) - foreign lands # 9th house: Gemini (Venus + Mars) - long distance travel # Current: Jupiter MD, Ketu AD (now - Jan 2027) # Next: Jupiter MD, Venus AD (Jan 2027 - Sep 2029) # Dasha lords and dates dasha_periods = [ ('Jupiter', 'Ketu', datetime.date(2026, 1, 27), datetime.date(2027, 1, 2)), ('Jupiter', 'Venus', datetime.date(2027, 1, 2), datetime.date(2029, 9, 2)), ('Jupiter', 'Sun', datetime.date(2029, 9, 2), datetime.date(2030, 6, 21)), ('Jupiter', 'Moon', datetime.date(2030, 6, 21), datetime.date(2031, 10, 21)), ('Jupiter', 'Mars', datetime.date(2031, 10, 21),datetime.date(2032, 9, 25)), ('Jupiter', 'Rahu', datetime.date(2032, 9, 25), datetime.date(2035, 2, 18)), ] # Pratyantardasha within Jupiter-Venus (Jan 2027 - Sep 2029) # Total Venus AD = ~32.9 months # Pratyantardasha order starting from Venus prat_lords = ['Venus','Sun','Moon','Mars','Rahu','Jupiter','Saturn','Mercury','Ketu'] prat_years = {'Ketu':7,'Venus':20,'Sun':6,'Moon':10,'Mars':7,'Rahu':18,'Jupiter':16,'Saturn':19,'Mercury':17} venus_ad_start = datetime.date(2027, 1, 2) venus_ad_end = datetime.date(2029, 9, 2) venus_ad_days = (venus_ad_end - venus_ad_start).days # ~972 days jupiter_md_years = 16 print("=== PRATYANTARDASHA within Jupiter-Venus AD ===") print(f"Venus AD: {venus_ad_start} to {venus_ad_end} ({venus_ad_days} days)") prat_start = venus_ad_start for lord in prat_lords: # Pratyantardasha days = (prat_lord_years / 120) * venus_ad_days_in_years * 365.25 venus_ad_years = venus_ad_days / 365.25 prat_days = (prat_years[lord] / 120) * venus_ad_years * 365.25 prat_end = prat_start + datetime.timedelta(days=prat_days) print(f" {lord:10}: {prat_start.strftime('%d-%b-%Y')} to {prat_end.strftime('%d-%b-%Y')}") prat_start = prat_end # Now check Saturn transits over key houses print("\n=== SATURN TRANSIT (2026-2032) ===") print("Saturn transits are most important for foreign relocation") # Saturn currently in Pisces sidereal (as of birth chart era) # Let's check Saturn's current position today_jd = swe.julday(2026, 7, 31, 0) signs = ['Aries','Taurus','Gemini','Cancer','Leo','Virgo','Libra','Scorpio','Sagittarius','Capricorn','Aquarius','Pisces'] for year in range(2026, 2032): for month in [1, 4, 7, 10]: jd = swe.julday(year, month, 1, 0) result, _ = swe.calc_ut(jd, swe.SATURN, swe.FLG_SIDEREAL) deg = result[0] % 360 sign_idx = int(deg / 30) print(f" {year}-{month:02d}: Saturn at {deg:.1f}Β° = {signs[sign_idx]} {deg%30:.1f}Β°") print("\n=== JUPITER TRANSIT (2026-2031) ===") for year in range(2026, 2031): for month in [1, 6]: jd = swe.julday(year, month, 1, 0) result, _ = swe.calc_ut(jd, swe.JUPITER, swe.FLG_SIDEREAL) deg = result[0] % 360 sign_idx = int(deg / 30) print(f" {year}-{month:02d}: Jupiter at {deg:.1f}Β° = {signs[sign_idx]} {deg%30:.1f}Β°") print("\n=== RAHU/KETU TRANSIT (2026-2030) ===") for year in range(2026, 2030): for month in [1, 7]: jd = swe.julday(year, month, 1, 0) result, _ = swe.calc_ut(jd, swe.MEAN_NODE, swe.FLG_SIDEREAL) deg = result[0] % 360 sign_idx = int(deg / 30) ketu_deg = (deg + 180) % 360 ketu_sign_idx = int(ketu_deg / 30) print(f" {year}-{month:02d}: Rahu={signs[sign_idx]} {deg%30:.1f}Β° | Ketu={signs[ketu_sign_idx]} {ketu_deg%30:.1f}Β°") # Key natal positions for reference print("\n=== KEY NATAL POSITIONS FOR TRANSIT REFERENCE ===") print("Lagna (Ascendant): Libra 20.25Β°") print("12th house cusp: Virgo (Moon+Rahu natal)") print("9th house: Gemini (Venus+Mars natal)") print("10th house: Cancer") print("Natal Venus: Gemini 17Β°") print("Natal Moon: Virgo 28Β°") print("Natal Rahu: Virgo 16Β°") print("7th house: Aries") EOF

Running Command

python3 -c "import sys; sys.path.insert(0, '/root/.local/lib/python3.11/site-packages'); import swisseph; print('ok')" 2>&1

Running Command

pip install --user pyswisseph 2>&1 | tail -3 && python3 -c "import sys; sys.path.insert(0, '/root/.local/lib/python3.11/site-packages'); import swisseph as swe; print('ok', swe.__version__)"

Running Command

python3 << 'EOF' import sys sys.path.insert(0, '/root/.local/lib/python3.11/site-packages') import swisseph as swe import datetime jd_birth = swe.julday(1996, 8, 19, 6 + 25/60.0) swe.set_sid_mode(swe.SIDM_LAHIRI) signs = ['Aries','Taurus','Gemini','Cancer','Leo','Virgo','Libra','Scorpio','Sagittarius','Capricorn','Aquarius','Pisces'] # Pratyantardasha within Jupiter-Venus AD prat_lords_order = ['Venus','Sun','Moon','Mars','Rahu','Jupiter','Saturn','Mercury','Ketu'] prat_years = {'Ketu':7,'Venus':20,'Sun':6,'Moon':10,'Mars':7,'Rahu':18,'Jupiter':16,'Saturn':19,'Mercury':17} venus_ad_start = datetime.date(2027, 1, 2) venus_ad_end = datetime.date(2029, 9, 2) venus_ad_days = (venus_ad_end - venus_ad_start).days print("=== PRATYANTARDASHA within Jupiter-Venus AD ===") venus_ad_years = venus_ad_days / 365.25 prat_start = venus_ad_start for lord in prat_lords_order: prat_days = (prat_years[lord] / 120) * venus_ad_years * 365.25 prat_end = prat_start + datetime.timedelta(days=prat_days) print(f" {lord:10}: {prat_start.strftime('%d-%b-%Y')} to {prat_end.strftime('%d-%b-%Y')}") prat_start = prat_end print("\n=== SATURN TRANSIT (2026-2031) ===") for year in range(2026, 2032): for month in [1, 4, 7, 10]: jd = swe.julday(year, month, 1, 0) result, _ = swe.calc_ut(jd, swe.SATURN, swe.FLG_SIDEREAL) deg = result[0] % 360 sign_idx = int(deg / 30) print(f" {year}-{month:02d}: Saturn at {signs[sign_idx]:15} {deg%30:.1f}Β°") print("\n=== JUPITER TRANSIT (2026-2031) ===") for year in range(2026, 2031): for month in [1, 5, 9]: jd = swe.julday(year, month, 1, 0) result, _ = swe.calc_ut(jd, swe.JUPITER, swe.FLG_SIDEREAL) deg = result[0] % 360 sign_idx = int(deg / 30) print(f" {year}-{month:02d}: Jupiter at {signs[sign_idx]:15} {deg%30:.1f}Β°") print("\n=== RAHU/KETU TRANSIT (2026-2030) ===") for year in range(2026, 2030): for month in [1, 7]: jd = swe.julday(year, month, 1, 0) result, _ = swe.calc_ut(jd, swe.MEAN_NODE, swe.FLG_SIDEREAL) deg = result[0] % 360 sign_idx = int(deg / 30) ketu_deg = (deg + 180) % 360 ketu_sign_idx = int(ketu_deg / 30) print(f" {year}-{month:02d}: Rahu={signs[sign_idx]:12} {deg%30:.1f}Β° | Ketu={signs[ketu_sign_idx]:12} {ketu_deg%30:.1f}Β°") EOF

Excellent - now I have full transit data to overlay on your chart. Here is the complete timing analysis:

πŸͺ Best Window to Move Abroad - Timing Analysis

Recap of your key natal positions:
  • Lagna: Libra | 9th house: Gemini (Venus + Mars) | 12th house: Virgo (Moon + Rahu)
  • 7th house: Aries | 10th house: Cancer

The Three Timing Layers (Dasha + Major Transits + Minor Transits)

Layer 1 - Dasha (Inner Clock)

PeriodDatesScore for Relocation
Jupiter - Ketu ADNow - 02 Jan 2027⚠️ Unstable, transitional
Jupiter - Venus AD02 Jan 2027 - 02 Sep 2029βœ…βœ… Prime window
↳ Venus-Venus Pratyantar02 Jan 2027 - 13 Jun 2027βœ…βœ…βœ… Best sub-period
↳ Venus-Sun Pratyantar13 Jun 2027 - 31 Jul 2027βœ…βœ… Strong
↳ Venus-Moon Pratyantar31 Jul 2027 - 20 Oct 2027βœ…βœ… Good
↳ Venus-Rahu Pratyantar15 Dec 2027 - 09 May 2028βœ…βœ… Foreign push
Jupiter - Sun AD02 Sep 2029 - 21 Jun 2030βœ… Good, secondary
Why Venus-Venus Pratyantar (Jan-Jun 2027) is peak:
  • Venus is your Lagna lord (self/identity)
  • Venus is in the 9th house natally (foreign lands)
  • Venus is the natural significator of comfort, settlement, and new beginnings
  • All three layers align: Jupiter MD + Venus AD + Venus Pratyantar = triple Venus activation

Layer 2 - Saturn Transit (The Gate-Opener)

Saturn is the planet of long-term decisions, hard work, and life structure. Its transit over key houses opens or closes major life gates.
PeriodSaturn PositionImpact on Your Chart
Now - mid 2027Pisces (6th house)Saturn transiting your 6th (service/work) - building foundation, exams, preparation phase
Mid 2027 onwardsAries (7th house)Saturn enters your 7th house - activates partnership, foreign settlement, and serious commitments
Saturn entering Aries (your 7th house) from mid-2027 is a major gateway. The 7th house rules long-term agreements, partnerships, and established relationships with foreign lands. Saturn's entry here signals the start of a serious, long-term foreign chapter. This happens to coincide almost exactly with Jupiter-Venus antardasha beginning January 2027.

Layer 3 - Jupiter Transit (The Expander)

PeriodJupiter PositionImpact
Now - Sep 2026Gemini (9th house)βœ… Jupiter over natal Venus+Mars - activating your 9th house right now
Sep 2026 - 2027Cancer (10th house)βœ…βœ… Jupiter over your career house - professional expansion, new opportunities
2027-2028Leo/Virgo (11/12th house)βœ…βœ… Jupiter over 11th (gains/network) then 12th (foreign lands)
2028-2029Virgo (12th house)βœ…βœ…βœ… Jupiter directly over natal Moon+Rahu in 12th - maximum foreign activation
Jupiter transiting your 12th house (Virgo) in 2028 directly activates your natal Moon-Rahu conjunction there. In Vedic astrology, Jupiter transiting the 12th house is called a "foreign lands opening" transit - it literally means doors to overseas opportunities swing open.

Layer 4 - Rahu/Ketu Transit

PeriodRahu PositionImpact
Now - late 2026Aquarius (5th house)Neutral
Late 2026 - mid 2028Capricorn (4th house)Mixed - disrupts home/roots (push to leave India)
Mid 2028 - 2029Sagittarius (3rd house)Ketu enters Gemini (your 9th house)
When Rahu transits the 4th house (late 2026 onwards): Classic indicator of leaving home/motherland. This is a traditional Vedic timing signal for emigration.

The Convergence Windows - Ranked

πŸ₯‡ WINDOW 1: January 2027 - June 2027

(Best single window)
What aligns:
  • Jupiter-Venus AD starts January 2027 (Venus Pratyantar active)
  • Saturn enters Aries (7th house) in mid-2027 - serious foreign commitment begins
  • Rahu in Capricorn (4th house) - push away from homeland
  • Jupiter transiting Cancer (10th house) - career opening abroad
  • All four layers point the same direction simultaneously
Action: This is the ideal window to have your PLAB/AMC cleared and be making the actual move or receiving your visa/registration clearance.

πŸ₯ˆ WINDOW 2: December 2027 - May 2028

(Venus-Rahu Pratyantar within Jupiter-Venus AD)
What aligns:
  • Rahu Pratyantar activates foreign themes most strongly within the Venus AD
  • Saturn firmly in Aries (7th house) - foreign partnerships cementing
  • Jupiter moving into 12th house (Virgo) territory by 2028
  • If Window 1 has delays (exam retakes, visa processing), this is your strong backup

πŸ₯‰ WINDOW 3: Mid 2028 - Early 2029

(Jupiter transiting 12th house + Saturn in 7th)
What aligns:
  • Jupiter directly over your natal Moon-Rahu in Virgo (12th house) - the most powerful foreign transit possible for your chart
  • Saturn settled in Aries (7th house) - structure and stability abroad
  • If you arrive abroad in 2027, this window marks deep settlement and establishment

What to Do Right Now (Jul 2026 - Jan 2027)

Current period: Jupiter-Ketu Antardasha
Ketu antardasha is a period of:
  • Letting go of old patterns, tying up loose ends
  • Focused, isolated preparation (very good for exam study)
  • Not ideal for big launches or new beginnings, but excellent for groundwork
This current period is actually ideal for passing PLAB Part 1 and Part 2, completing paperwork, and building your application - Ketu sharpens focus and detachment, which helps in concentrated exam preparation.
Target: Clear your qualifying exams (PLAB/AMC) before January 2027 so you are ready to activate when Venus antardasha opens.

Master Timeline

DateMilestone (Astrological Timing)
Now - Jan 2027Exam preparation, applications, paperwork (Ketu AD - focused grind)
Jan 2027Venus AD opens - visa application, registration submission
Jan - Jun 2027Ideal departure window - move to UK/Australia
Dec 2027 - May 2028Secondary window if delays occur
2028Deep establishment abroad, career foothold
Jan 2027 - Sep 2029Full Venus AD - career + marriage both get activated

Bottom Line

January to June 2027 is your single best window to make the move. Everything - dasha, Saturn transit, Jupiter transit, and Rahu transit - converges at that point. Start preparing your PLAB (for UK) or AMC (for Australia) exams aggressively right now during this Ketu AD focused period, and target a mid-to-early 2027 departure.
You are in a genuinely strong chart period. Jupiter Mahadasha until 2035 gives you a long runway to build your medical career abroad - 2027 is the launchpad.

Find recent PLAB/AMC exam success rates and visa timelines

Searching the Web

PLAB exam pass rates 2024 2025 international medical graduates statistics

Searching the Web

AMC exam pass rate 2024 2025 international medical graduates Australia

Searching the Web

UK Skilled Worker visa doctor timeline 2025 processing time NHS

Here is a full breakdown of the latest data across both pathways:

PLAB (UK) vs AMC (Australia) - 2025/2026 Reality Check


πŸ‡¬πŸ‡§ PLAB Route - UK

Pass Rates (Latest Data)

ExamPass RateNotes
PLAB 1~65-75%180 MCQs in 3 hrs, Angoff pass mark (~110-118/180)
PLAB 2~65.8% (2024 data)OSCE format, max 4 attempts
  • PLAB 1 can be taken in India (no need to fly to UK first)
  • PLAB 2 must be taken in the UK only - so you need to budget a UK trip for Part 2
  • Seats are in extreme demand, especially from India/Pakistan - book as early as possible, slots vanish within minutes of release
  • GMC earned Β£25 million from PLAB fees in 2024 - demand is massive

Fees (2025/2026)

ItemCost
PLAB 1Β£268 (β‚Ή29,000)
PLAB 2Β£998 (β‚Ή1,08,000) per attempt
IELTS/OET (if needed)Additional
Travel + accommodation for PLAB 2Budget Β£1,500-2,500 extra

UK Visa Timeline (Health & Care Visa - doctors qualify for this faster route)

Doctors getting an NHS job offer get the Health and Care Visa, which is cheaper and faster than the standard Skilled Worker visa:
StageTypical Timeline
Pass PLAB 1 + 26-12 months of prep
GMC Registration after PLAB 22-4 weeks
Job hunting (NHS Trust offer)1-6 months
Certificate of Sponsorship (CoS) issued by employer1-4 weeks
Health & Care Visa application (from India)~3 weeks standard
Priority service visa5 working days
Super-priority visaNext working day
Total from starting PLAB prep to landing in UK~12-18 months
Important 2025 update: From January 2026, B2 English language evidence is now required with the visa application. Factor in an extra 2-3 weeks for English test booking if you don't have a recent IELTS/OET score.
Key concern flagged by the BMJ (2025): By 2024, nearly 80% of IMGs who passed PLAB 2 were not connected to an NHS designated body (i.e., not in a training post). The NHS job market is currently competitive - passing PLAB does not guarantee immediate NHS employment. Plan for a gap and consider locum/trust grade posts as a bridge.

πŸ‡¦πŸ‡Ί AMC Route - Australia

Pass Rates (Latest Data)

ExamPass RateNotes
AMC Part 1 (MCQ)~47-52%150 questions, 3.5 hrs, Pearson VUE centres worldwide
AMC Part 2 (Clinical)~21-24% historically; may rise slightly to ~25-30% post 2024 rule changeOSCE, Melbourne only, 14 scored stations, need 9/14 to pass (changed from 10/14 in 2024)
The AMC Part 2 clinical exam is significantly harder than PLAB 2 in pass rate terms. The 2024 rule change (lowering threshold from 10 to 9 stations) may improve this slightly.
New from September 2025: EPIC verification replaced by MyIntealth - all credential verification must now go through this new portal. Factor extra time if you haven't started primary source verification yet.

Fees (2025)

ItemCost
AMC Part 1 (MCQ)AUD $1,200 (β‚Ή65,000)
AMC Part 2 (Clinical)AUD $4,500-5,000 (β‚Ή2,40,000)
MyIntealth verification~AUD $400
Travel + accommodation for Clinical (Melbourne)AUD $2,000-4,000

Australia Visa Timeline

StageTypical Timeline
AMC Part 1 + Part 212-24 months of prep (harder exams)
AHPRA Registration after AMC certificate4-8 weeks
Job offer (Area of Need or standard)1-6 months
Skilled Worker (subclass 482) visa from India2-4 months standard
Total from starting AMC prep to landing in Australia~18-30 months
Australia's visa processing is slower than UK's Health & Care Visa. The AMC clinical exam also has a much lower pass rate and costs significantly more.

Head-to-Head Comparison: Your Decision Matrix

FactorUK (PLAB)Australia (AMC)
Part 1 pass rate~70%~50%
Part 2 pass rate~66%~22-30%
Part 1 can be taken in Indiaβœ… Yesβœ… Yes (Pearson VUE)
Part 2 locationUK onlyMelbourne only
Total exam cost~Β£1,300 (β‚Ή1.4L)~AUD $6,000+ (β‚Ή3.2L+)
Visa processing~3 weeks (Health & Care)2-4 months
Time to land~12-18 months~18-30 months
Job market (2026)Competitive but largeCompetitive, geographically spread
Post-qualification pathwayNHS Trust Grade β†’ trainingSupervised practice β†’ general registration

Your Practical Roadmap (Aligned with Your Jan 2027 Astrological Window)

Given your current date is July 2026 and your best window is Jan-Jun 2027:
MonthAction
Aug-Sep 2026Register with GMC, complete IELTS/OET (need 7.5 overall for PLAB), start PLAB 1 prep
Oct-Nov 2026Book and sit PLAB 1 (book immediately - seats go fast)
Nov 2026 - Jan 2027Book PLAB 2 in UK (requires a UK trip)
Jan-Feb 2027Sit PLAB 2 in UK during Venus Antardasha opening
Feb-Apr 2027GMC registration + job hunt for NHS post
Apr-Jun 2027Receive CoS from NHS employer, apply for Health & Care Visa (~3 weeks processing)
Mid 2027Land in UK - right in the heart of your best astrological window
This timeline is tight but very achievable if you start now. PLAB 1 in India around Oct/Nov 2026 is the critical first step. Start your GMC eligibility check and IELTS booking this week.

Bottom line: UK is faster, cheaper, has higher pass rates, and aligns better with your birth chart. Start PLAB prep immediately - your 6-month window right now (Ketu Antardasha) is perfect for the focused, disciplined study needed to clear PLAB 1 before your Venus Antardasha opens in January 2027.

Create a detailed month-by-month prep schedule for PLAB exams

Reading File
Running Command

mkdir -p /tmp/workspace/plab-schedule && ls /tmp/workspace/plab-schedule/

Writing File

~/plab-schedule/build_schedule.py

from docx import Document
from docx.shared import Pt, Inches, RGBColor, Cm
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT, WD_ALIGN_VERTICAL
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
import copy

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

def set_cell_bg(cell, hex_color):
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()
    shd = OxmlElement('w:shd')
    shd.set(qn('w:val'), 'clear')
    shd.set(qn('w:color'), 'auto')
    shd.set(qn('w:fill'), hex_color)
    tcPr.append(shd)

def set_cell_borders(cell, color="CCCCCC"):
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()
    tcBorders = OxmlElement('w:tcBorders')
    for side in ['top', 'left', 'bottom', 'right']:
        border = OxmlElement(f'w:{side}')
        border.set(qn('w:val'), 'single')
        border.set(qn('w:sz'), '4')
        border.set(qn('w:space'), '0')
        border.set(qn('w:color'), color)
        tcBorders.append(border)
    tcPr.append(tcBorders)

def add_cell_text(cell, text, bold=False, size=10, color=None, align=WD_ALIGN_PARAGRAPH.LEFT):
    cell.paragraphs[0].clear()
    run = cell.paragraphs[0].add_run(text)
    run.bold = bold
    run.font.size = Pt(size)
    if color:
        run.font.color.rgb = RGBColor.from_string(color)
    cell.paragraphs[0].alignment = align

def add_heading(doc, text, level=1, color="1B3A6B"):
    p = doc.add_heading(text, level=level)
    p.alignment = WD_ALIGN_PARAGRAPH.LEFT
    for run in p.runs:
        run.font.color.rgb = RGBColor.from_string(color)
    return p

def add_colored_heading(doc, text, bg_color, text_color="FFFFFF", size=13):
    """Full-width colored heading paragraph"""
    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.LEFT
    pPr = p._p.get_or_add_pPr()
    shd = OxmlElement('w:shd')
    shd.set(qn('w:val'), 'clear')
    shd.set(qn('w:color'), 'auto')
    shd.set(qn('w:fill'), bg_color)
    pPr.append(shd)
    run = p.add_run(f'  {text}  ')
    run.bold = True
    run.font.size = Pt(size)
    run.font.color.rgb = RGBColor.from_string(text_color)
    pPr2 = p._p.get_or_add_pPr()
    spacing = OxmlElement('w:spacing')
    spacing.set(qn('w:before'), '120')
    spacing.set(qn('w:after'), '60')
    pPr2.append(spacing)
    return p

def add_info_box(doc, lines, bg="EBF5FB", border_color="2E86C1"):
    """Add a shaded info box as a 1-cell table"""
    tbl = doc.add_table(rows=1, cols=1)
    tbl.alignment = WD_TABLE_ALIGNMENT.LEFT
    cell = tbl.rows[0].cells[0]
    set_cell_bg(cell, bg)
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()
    tcBorders = OxmlElement('w:tcBorders')
    for side in ['top', 'left', 'bottom', 'right']:
        border = OxmlElement(f'w:{side}')
        border.set(qn('w:val'), 'single')
        border.set(qn('w:sz'), '12')
        border.set(qn('w:space'), '0')
        border.set(qn('w:color'), border_color)
        tcBorders.append(border)
    tcPr.append(tcBorders)
    cell.paragraphs[0].clear()
    for line in lines:
        p = cell.add_paragraph(line)
        p.runs[0].font.size = Pt(10) if p.runs else None
        if p.runs:
            p.runs[0].font.size = Pt(10)
    cell.paragraphs[0]._element.getparent().remove(cell.paragraphs[0]._element)
    doc.add_paragraph()

# ── document setup ────────────────────────────────────────────────────────────
doc = Document()

# Page margins
from docx.oxml import OxmlElement
section = doc.sections[0]
section.top_margin    = Cm(2)
section.bottom_margin = Cm(2)
section.left_margin   = Cm(2.2)
section.right_margin  = Cm(2.2)

# Default style
normal = doc.styles['Normal']
normal.font.name = 'Calibri'
normal.font.size = Pt(10.5)

# ── TITLE PAGE ────────────────────────────────────────────────────────────────
doc.add_paragraph()
title = doc.add_paragraph()
title.alignment = WD_ALIGN_PARAGRAPH.CENTER
r = title.add_run('PLAB EXAM PREPARATION SCHEDULE')
r.bold = True
r.font.size = Pt(22)
r.font.color.rgb = RGBColor.from_string('1B3A6B')

sub = doc.add_paragraph()
sub.alignment = WD_ALIGN_PARAGRAPH.CENTER
r2 = sub.add_run('Complete Month-by-Month Roadmap: PLAB 1 + PLAB 2')
r2.bold = True
r2.font.size = Pt(13)
r2.font.color.rgb = RGBColor.from_string('2E86C1')

doc.add_paragraph()

meta = doc.add_paragraph()
meta.alignment = WD_ALIGN_PARAGRAPH.CENTER
r3 = meta.add_run('Personalised for: MBBS (China)  |  Target: UK Medical Practice (NHS)\nPrepared: August 2026  |  Target PLAB 1: November 2026  |  Target PLAB 2: February 2027')
r3.font.size = Pt(10)
r3.font.color.rgb = RGBColor.from_string('555555')

doc.add_paragraph()

# Overview box
tbl = doc.add_table(rows=3, cols=4)
tbl.alignment = WD_TABLE_ALIGNMENT.CENTER
headers = ['Phase', 'Period', 'Exam', 'Goal']
bg_hdr = '1B3A6B'
for i, h in enumerate(headers):
    cell = tbl.rows[0].cells[i]
    set_cell_bg(cell, bg_hdr)
    add_cell_text(cell, h, bold=True, size=10, color='FFFFFF', align=WD_ALIGN_PARAGRAPH.CENTER)

rows_data = [
    ['Phase 1', 'Aug - Oct 2026', 'PLAB 1 Prep', '3 months focused MCQ prep'],
    ['Phase 2', 'Nov 2026',       'PLAB 1 EXAM', 'Sit & Pass PLAB 1 in India'],
    ['Phase 3', 'Nov 26 - Jan 27','PLAB 2 Prep', 'OSCE skills + UK trip planning'],
    ['Phase 4', 'Feb 2027',       'PLAB 2 EXAM', 'Sit & Pass PLAB 2 in UK'],
    ['Phase 5', 'Mar - Jun 2027', 'GMC + Jobs',  'Registration, NHS job, visa'],
]
row_colors = ['EBF5FB', 'D5F5E3', 'FEF9E7', 'FDEDEC', 'F4ECF7']
for i, (phase, period, exam, goal) in enumerate(rows_data):
    row = tbl.add_row()
    data = [phase, period, exam, goal]
    for j, d in enumerate(data):
        cell = row.cells[j]
        set_cell_bg(cell, row_colors[i % len(row_colors)])
        set_cell_borders(cell)
        add_cell_text(cell, d, bold=(j == 0), size=10)

doc.add_paragraph()

# ── SECTION: BEFORE YOU START ────────────────────────────────────────────────
doc.add_page_break()
add_colored_heading(doc, '  PRE-REQUISITES & SETUP  (Do This First)', '1B3A6B', 'FFFFFF', 14)
doc.add_paragraph()

prereqs = [
    ('GMC Eligibility Check',
     'Go to gmc-uk.org and check your China MBBS is on the World Directory of Medical Schools (WDOMS). '
     'Most Chinese medical universities are listed. If yours is not, you must contact GMC directly.'),
    ('English Language Test',
     'PLAB requires IELTS Academic 7.5 overall (min 7.0 in each band) OR OET Grade B in all 4 sections. '
     'Book immediately if you do not have a valid score (scores valid 2 years). IELTS is more common and '
     'widely available in India. Book for September 2026 sitting.'),
    ('GMC Identity Verification',
     'Register on MyGMC portal. Upload your MBBS certificate, transcripts, internship completion certificate, '
     'and ID proof. GMC takes 4-8 weeks to verify. Start this in August 2026.'),
    ('PLAB 1 Booking',
     'PLAB 1 is available in India (Delhi, Mumbai, Chennai). Seats open ~3 months in advance and fill '
     'within MINUTES. Set a calendar alert for when the November 2026 booking window opens (typically August). '
     'Book the moment slots go live.'),
    ('Study Materials - PLAB 1',
     'Primary: PLABABLE (question bank, most popular) or Passmedicine UK. '
     'Books: Oxford Handbook of Clinical Medicine (OHCM), BNF (British National Formulary - free online). '
     'NICE guidelines app (free). Focus on UK-specific management, not MBBS-style theory.'),
    ('Study Materials - PLAB 2',
     'PLAB 2 Ninja or Dr Najeeb OSCE videos. Join a PLAB 2 course in India first (Marrow/DAMS PLAB batch), '
     'then a UK-based course before the exam (highly recommended). PLAB 2 is about communication style, '
     'not just clinical knowledge.'),
    ('Budget Planning',
     'PLAB 1 fee: Β£268. PLAB 2 fee: Β£998. UK trip (PLAB 2): ~Β£1,500-2,500 for flights + accommodation + course. '
     'IELTS: ~β‚Ή16,000. GMC registration: Β£405. Total estimate: ~β‚Ή3.5-4.5 lakhs all-in.'),
]

for title_text, body_text in prereqs:
    p = doc.add_paragraph()
    r_title = p.add_run(f'{title_text}: ')
    r_title.bold = True
    r_title.font.color.rgb = RGBColor.from_string('1B3A6B')
    r_title.font.size = Pt(10.5)
    r_body = p.add_run(body_text)
    r_body.font.size = Pt(10.5)

# ── PHASE 1: AUGUST 2026 ─────────────────────────────────────────────────────
doc.add_page_break()
add_colored_heading(doc, '  PHASE 1 - PLAB 1 PREPARATION  (Aug - Oct 2026)', '154360', 'FFFFFF', 14)

months_p1 = [
    {
        'month': 'MONTH 1 - AUGUST 2026',
        'subtitle': 'Foundation & Orientation',
        'color': 'D5F5E3',
        'hdr_color': '1E8449',
        'daily': '6-8 hours/day',
        'target': 'Complete 600 practice MCQs | Finish Cardiology + Respiratory',
        'topics': [
            ('Week 1', 'Orientation + Cardiology',
             'Download PLABABLE or Passmedicine. Do NOT start random questions yet. '
             'Read OHCM Cardiology chapter. Learn UK-specific: ACS management (NICE NG185), '
             'Heart Failure (NICE NG106), AF management. Do 150 cardiology MCQs.'),
            ('Week 2', 'Respiratory + GI',
             'Asthma (BTS/SIGN stepwise ladder), COPD (NICE NG115 - know the inhalers), '
             'pneumonia (CURB-65 scoring), PE (Wells score + CTPA vs V/Q). '
             'GI: GORD, peptic ulcers (H.pylori eradication - triple therapy), IBD basics. '
             '200 MCQs this week.'),
            ('Week 3', 'Neurology + Psychiatry',
             'Stroke (FAST, thrombolysis window 4.5 hrs, thrombectomy 6 hrs), TIA (ABCD2 score), '
             'seizures (first-line: sodium valproate/levetiracetam), headaches (red flags). '
             'Psychiatry: Mental Health Act basics, depression (NICE - SSRIs first line), '
             'schizophrenia (antipsychotics). 200 MCQs.'),
            ('Week 4', 'Revision + Mock Test 1',
             'Revise all topics covered. Do your FIRST full 180-question timed mock. '
             'Score it, identify weak areas. Do not be discouraged by the score - '
             'this is a baseline. Review every wrong answer with explanation. '
             'Register on GMC portal this week.'),
        ],
        'resources': 'PLABABLE / Passmedicine | OHCM Chapters 1-4 | NICE Guidelines App | BNF online',
        'milestones': ['GMC portal registration started', 'IELTS booked (Sep sitting)', 'PLAB 1 booking alert set', '600+ MCQs completed'],
    },
    {
        'month': 'MONTH 2 - SEPTEMBER 2026',
        'subtitle': 'Core Systems Deep Dive',
        'color': 'EBF5FB',
        'hdr_color': '2E86C1',
        'daily': '6-8 hours/day',
        'target': 'Complete 1,000 more MCQs | Cover Endocrine, Renal, Haematology, Rheumatology',
        'topics': [
            ('Week 1', 'Endocrinology',
             'Diabetes (NICE NG28 - HbA1c targets, metformin first line, SGLT2 inhibitors, insulin types), '
             'thyroid (hypo: levothyroxine, hyper: carbimazole, Graves), adrenal (Addisons vs Cushings - '
             'know the tests: short Synacthen, 24hr urine cortisol, dexamethasone suppression), '
             'pituitary. 200 MCQs.'),
            ('Week 2', 'Renal + Urology',
             'AKI (KDIGO staging, causes - pre/intra/post, management), CKD (eGFR staging), '
             'nephrotic vs nephritic syndrome, UTI management (UK: nitrofurantoin first line), '
             'renal stones (ESWL vs ureteroscopy), prostate (PSA, BPH - alpha blockers). 200 MCQs.'),
            ('Week 3', 'Haematology + Oncology basics',
             'Anaemia workup (iron deficiency vs B12/folate vs haemolytic), anticoagulation '
             '(heparin, warfarin, DOACs - know reversals), blood transfusion reactions, '
             'common cancers screening (breast, cervical, bowel - UK NHS screening programmes), '
             'palliative care basics. 200 MCQs.'),
            ('Week 4', 'Rheumatology + Dermatology + Mock Test 2',
             'RA (DMARDS - methotrexate first line), SLE (ANA, anti-dsDNA), gout (allopurinol for prevention, '
             'colchicine/NSAIDs acute), osteoporosis (DEXA, bisphosphonates). '
             'Dermatology: common rashes, skin cancer (BCC vs SCC vs melanoma - ABCDE). '
             'MOCK TEST 2 - aim for >55% (passing threshold ~61-65%). IELTS exam this week.'),
        ],
        'resources': 'PLABABLE | OHCM Chapters 5-11 | NICE guidelines NG28, NG185, NG106 | BNF sections 6,9',
        'milestones': ['IELTS sat (need 7.5 overall)', 'GMC documents submitted', '1,600+ total MCQs done', 'Mock score improving'],
    },
    {
        'month': 'MONTH 3 - OCTOBER 2026',
        'subtitle': 'High-Yield Specialties + Final Revision',
        'color': 'FEF9E7',
        'hdr_color': 'B7950B',
        'daily': '8-10 hours/day (exam is in November - push hard)',
        'target': '2,500+ total MCQs | All topics covered | 3 full mocks | Score >65% consistently',
        'topics': [
            ('Week 1', 'Obstetrics & Gynaecology',
             'Antenatal care (UK schedule), pre-eclampsia (labetalol/nifedipine, MgSO4 seizure prophylaxis), '
             'PPH management, ectopic pregnancy (methotrexate vs surgery), contraception (UK-specific: COCP '
             'contraindications, copper IUD, Mirena), cervical cancer (HPV, colposcopy). 200 MCQs.'),
            ('Week 2', 'Paediatrics + Neonatology',
             'Development milestones (know all key ages), febrile convulsions, meningitis (benzylpenicillin '
             'IM before hospital if suspected), croup vs epiglottitis, bronchiolitis (RSV, supportive), '
             'safeguarding red flags, vaccination schedule (UK routine immunisation programme). 200 MCQs.'),
            ('Week 3', 'Surgery + Emergency Medicine + Ethics',
             'Surgical emergencies: appendicitis, bowel obstruction, AAA rupture. Pre-op assessment. '
             'Emergency: ABCDE approach, anaphylaxis (adrenaline 0.5mg IM), sepsis (NEWS2 scoring, '
             'Sepsis 6). Ethics: Gillick competence, capacity assessment (Mental Capacity Act 2005), '
             'consent, confidentiality, duty of candour. These are high-yield PLAB topics. 200 MCQs.'),
            ('Week 4', 'Final Revision Sprint',
             'STOP new topics. Only revision. '
             'Day 1-2: Cardiology + Respiratory. Day 3-4: Neuro + Psych. Day 5-6: Endo + Renal. '
             'Day 7: Rest. Day 8-9: O&G + Paeds. Day 10-11: Surgery + Emergency + Ethics. '
             'Day 12-14: 2 FULL mock exams (180 Qs each, timed). Review every wrong answer. '
             'Target score: >65%. Know your weak spots and do targeted MCQs only.'),
        ],
        'resources': 'PLABABLE timed mocks | Passmedicine subject tests | OHCM | NICE Guidance summaries',
        'milestones': ['All 12 systems covered', '2,500+ MCQs completed', '3+ full mocks done', 'Consistent score >65%', 'PLAB 1 slot confirmed'],
    },
]

for m in months_p1:
    add_colored_heading(doc, f"  {m['month']}: {m['subtitle']}", m['hdr_color'], 'FFFFFF', 12)

    # Daily hours + target row
    info_tbl = doc.add_table(rows=1, cols=2)
    info_tbl.alignment = WD_TABLE_ALIGNMENT.LEFT
    c1 = info_tbl.rows[0].cells[0]
    c2 = info_tbl.rows[0].cells[1]
    set_cell_bg(c1, 'F2F3F4')
    set_cell_bg(c2, 'F2F3F4')
    add_cell_text(c1, f'Daily Study: {m["daily"]}', bold=True, size=10)
    add_cell_text(c2, f'Monthly Target: {m["target"]}', bold=False, size=10)
    doc.add_paragraph()

    # Weekly breakdown table
    tbl = doc.add_table(rows=1, cols=3)
    tbl.alignment = WD_TABLE_ALIGNMENT.LEFT
    # Header
    hdr_row = tbl.rows[0]
    for ci, htxt in enumerate(['Week', 'Topic Focus', 'What to Study & Do']):
        cell = hdr_row.cells[ci]
        set_cell_bg(cell, m['hdr_color'])
        add_cell_text(cell, htxt, bold=True, size=10, color='FFFFFF')

    col_widths = [Cm(2.2), Cm(4.0), Cm(12.0)]
    for w_label, w_topic, w_detail in m['topics']:
        row = tbl.add_row()
        data = [w_label, w_topic, w_detail]
        for ci, (cell, txt) in enumerate(zip(row.cells, data)):
            set_cell_bg(cell, m['color'])
            set_cell_borders(cell)
            add_cell_text(cell, txt, bold=(ci < 2), size=10)
        for ci, w in enumerate(col_widths):
            row.cells[ci].width = w

    doc.add_paragraph()

    # Resources + Milestones
    rm_tbl = doc.add_table(rows=1, cols=2)
    r_cell = rm_tbl.rows[0].cells[0]
    m_cell = rm_tbl.rows[0].cells[1]
    set_cell_bg(r_cell, 'EAF2FF')
    set_cell_bg(m_cell, 'EAFAF1')
    set_cell_borders(r_cell, '2E86C1')
    set_cell_borders(m_cell, '1E8449')

    r_cell.paragraphs[0].clear()
    rp = r_cell.add_paragraph()
    rp.add_run('Resources').bold = True
    for res in m['resources'].split(' | '):
        r_cell.add_paragraph(f'β€’ {res}').runs[0].font.size = Pt(9.5)
    r_cell.paragraphs[0]._element.getparent().remove(r_cell.paragraphs[0]._element)

    m_cell.paragraphs[0].clear()
    mp2 = m_cell.add_paragraph()
    mp2.add_run('Monthly Milestones').bold = True
    for ms in m['milestones']:
        m_cell.add_paragraph(f'βœ“ {ms}').runs[0].font.size = Pt(9.5)
    m_cell.paragraphs[0]._element.getparent().remove(m_cell.paragraphs[0]._element)

    doc.add_paragraph()
    doc.add_paragraph()

# ── PHASE 2: NOVEMBER 2026 - EXAM ────────────────────────────────────────────
doc.add_page_break()
add_colored_heading(doc, '  PHASE 2 - NOVEMBER 2026: PLAB 1 EXAM MONTH', 'C0392B', 'FFFFFF', 14)
doc.add_paragraph()

exam_weeks = [
    ('Week 1-2', 'Active Revision Only',
     'No new topics. Rotate through all systems, 100 MCQs/day. Focus on your weakest areas identified '
     'in mocks. Review the GMC PLAB 1 question paper blueprint (available on gmc-uk.org). '
     'Ethics and Law questions: re-read the GMC Good Medical Practice guidance - several questions '
     'come from this document. Know the Mental Capacity Act 2005, Gillick/Fraser competence, '
     'notifiable diseases list.'),
    ('Week 3', 'Final 3 Days Before Exam',
     'Day 1: Light revision, 50 MCQs only. Day 2: Rest, walk, light reading only - do NOT cram. '
     'Day 3 (exam eve): Organize documents (passport, GMC email confirmation). Early sleep. '
     'Exam day: Arrive 30 min early. Read each question carefully - UK context, not textbook. '
     '180 questions in 180 minutes = 1 min per question. Flag uncertain ones, come back.'),
    ('Week 4', 'Post-Exam + PLAB 2 Start',
     'If passed: Immediately begin PLAB 2 preparation. Book PLAB 2 date (first available in UK, '
     'ideally February 2027). Start PLAB 2 Ninja or join a PLAB 2 prep group online. '
     'If failed: Do not panic. Analyse your weak areas. Re-book PLAB 1 for next sitting. '
     'Most candidates pass on 2nd attempt. Adjust study plan accordingly.'),
]

tbl = doc.add_table(rows=1, cols=3)
for ci, htxt in enumerate(['Period', 'Focus', 'Detail']):
    cell = tbl.rows[0].cells[ci]
    set_cell_bg(cell, 'C0392B')
    add_cell_text(cell, htxt, bold=True, size=10, color='FFFFFF')
for w, f, d in exam_weeks:
    row = tbl.add_row()
    for ci, txt in enumerate([w, f, d]):
        cell = row.cells[ci]
        set_cell_bg(cell, 'FDEDEC')
        set_cell_borders(cell)
        add_cell_text(cell, txt, bold=(ci < 2), size=10)

doc.add_paragraph()

# PLAB 1 Key Facts box
kf = doc.add_table(rows=1, cols=1)
kf.alignment = WD_TABLE_ALIGNMENT.LEFT
kf_cell = kf.rows[0].cells[0]
set_cell_bg(kf_cell, 'FDF2F8')
set_cell_borders(kf_cell, 'A93226')
kf_cell.paragraphs[0].clear()
kf_hdr = kf_cell.add_paragraph()
kf_hdr.add_run('PLAB 1 EXAM - KEY FACTS').bold = True
facts = [
    'β€’ 180 single-best-answer MCQs in 3 hours (exactly 1 min per question)',
    'β€’ Pass mark: Angoff method, typically 110-118 correct out of 180 (~61-66%)',
    'β€’ No negative marking - ALWAYS answer every question',
    'β€’ Taken at Pearson VUE centres in India (Delhi, Mumbai, Chennai, Hyderabad)',
    'β€’ Results: Usually within 4 weeks. Certificate issued digitally via GMC portal',
    'β€’ Valid for: No expiry on PLAB 1 pass (you can sit PLAB 2 at any time after)',
    'β€’ UK-specific knowledge is key: NICE guidelines, BNF drug choices, NHS pathways',
    'β€’ Ethics/Law questions: ~10-15% of paper - know Mental Capacity Act, GMC duties',
]
for f in facts:
    p = kf_cell.add_paragraph(f)
    p.runs[0].font.size = Pt(10)
kf_cell.paragraphs[0]._element.getparent().remove(kf_cell.paragraphs[0]._element)
doc.add_paragraph()

# ── PHASE 3: PLAB 2 PREP ─────────────────────────────────────────────────────
doc.add_page_break()
add_colored_heading(doc, '  PHASE 3 - PLAB 2 PREPARATION  (Nov 2026 - Jan 2027)', '6C3483', 'FFFFFF', 14)
doc.add_paragraph()

p2_intro = doc.add_paragraph()
r_intro = p2_intro.add_run(
    'PLAB 2 is fundamentally different from PLAB 1. It is an OSCE (Objective Structured Clinical Examination) '
    'with 18 stations (16 scored, 2 rest). Each station is 8 minutes. You play the role of an FY2 doctor. '
    'The exam is ONLY held in Manchester, UK. You CANNOT pass by reading alone - you must practise '
    'with partners out loud, every single day.')
r_intro.font.size = Pt(10.5)
r_intro.font.color.rgb = RGBColor.from_string('333333')
doc.add_paragraph()

p2_months = [
    {
        'month': 'NOVEMBER 2026 (post-PLAB 1)',
        'color': 'F4ECF7',
        'hdr_color': '6C3483',
        'weeks': [
            ('Week 1-2', 'PLAB 2 Orientation',
             'Watch PLAB 2 Ninja introduction videos or Dr Najeeb PLAB 2 course. '
             'Understand the 6 station types: History taking, Examination, Explanation/counselling, '
             'Communication/ethics, Practical procedures, Data interpretation. '
             'Read the PLAB 2 mark sheet format - understand what examiners are scoring. '
             'Start practising opening lines for each station type.'),
            ('Week 3-4', 'History Taking Stations',
             'The most common station type. Practice SOCRATES for pain, ICE (Ideas, Concerns, '
             'Expectations) framework, systems review, summarising. '
             'Key conditions: chest pain, SOB, headache, abdominal pain, joint pain, collapse. '
             'CRITICAL: The examiner is watching how you talk to the patient, not just what you ask. '
             'Practice with a friend or record yourself. UK communication style = empathetic, '
             'non-directive, patient-centred. Avoid being mechanical.'),
        ],
        'milestones': ['PLAB 2 date booked (Feb 2027)', 'UK trip flights + accommodation booked', 'Study partner found for OSCE practice'],
    },
    {
        'month': 'DECEMBER 2026',
        'color': 'EBF5FB',
        'hdr_color': '2E86C1',
        'weeks': [
            ('Week 1-2', 'Examination Stations + Explanation Stations',
             'Physical examination: Cardiovascular, Respiratory, Abdominal, Neurological (cranial nerves, '
             'peripheral), MSK (hip, knee, shoulder). Learn the exact UK format sequences. '
             'Explanation/counselling: Practice explaining diagnoses (e.g., new diabetes, cancer), '
             'procedures (colonoscopy, bronchoscopy), and medications. Use teach-back method. '
             'Record yourself and watch it back - this is very effective.'),
            ('Week 3-4', 'Communication & Ethics Stations',
             'These are often the hardest for IMGs from India/China due to cultural differences. '
             'Scenarios: breaking bad news (use SPIKES protocol), angry patient, '
             'capacity assessment, confidentiality breach, colleague impairment, consent for surgery. '
             'Key principle: In the UK, patient autonomy is paramount. Do not be paternalistic. '
             'Practice every ethics scenario at least 3 times with a partner. '
             'Christmas week: 2 days off maximum, then continue practice.'),
        ],
        'milestones': ['All 6 station types practiced', 'UK trip itinerary finalised', 'PLAB 2 prep course in India completed (if enrolled)'],
    },
    {
        'month': 'JANUARY 2027',
        'color': 'D5F5E3',
        'hdr_color': '1E8449',
        'weeks': [
            ('Week 1-2', 'Full Mock OSCEs (India)',
             'Do MINIMUM 2 full 18-station mock OSCE circuits every week. '
             'Find a study group - at least 2 other PLAB candidates. '
             'Rotate roles: doctor, patient, examiner. Use PLAB 2 Ninja mock cases or similar. '
             'After each mock: debrief every station. What did you miss? Was your language empathetic? '
             'Did you address the patient\'s concerns? Did you safety-net? '
             'This month should feel like living and breathing OSCE practice.'),
            ('Week 3', 'Travel to UK + UK Prep Course',
             'Fly to Manchester/London. Arrive at least 5-6 days before your exam. '
             'Enrol in a UK-based PLAB 2 course (Options: PLAB 2 Ninja UK, SWOT courses, '
             'or a local tutor group). These courses are valuable because you practice with '
             'other IMGs and get examiner-style feedback. '
             'Visit the exam centre (Manchester Medical Examination Centre) to familiarise yourself. '
             'Acclimatise - jet lag, new environment, cold weather. Look after your health.'),
            ('Week 4', 'Final Polish Week (UK)',
             'Targeted practice on your weakest station types only. '
             'Do NOT try to learn new material. Consolidate. '
             'Focus on: opening each station correctly, time management (8 min is short), '
             'closing each station professionally with safety-netting. '
             'Rest 2 days before exam. Explore Manchester - keep calm and confident.'),
        ],
        'milestones': ['10+ full mock OSCE circuits completed', 'UK-based prep course done', 'Exam venue visited', 'Well-rested before exam'],
    },
]

for m in p2_months:
    add_colored_heading(doc, f"  {m['month']}", m['hdr_color'], 'FFFFFF', 11)

    tbl = doc.add_table(rows=1, cols=3)
    for ci, htxt in enumerate(['Week', 'Focus', 'Detail']):
        cell = tbl.rows[0].cells[ci]
        set_cell_bg(cell, m['hdr_color'])
        add_cell_text(cell, htxt, bold=True, size=10, color='FFFFFF')
    for w, f, d in m['weeks']:
        row = tbl.add_row()
        for ci, txt in enumerate([w, f, d]):
            cell = row.cells[ci]
            set_cell_bg(cell, m['color'])
            set_cell_borders(cell)
            add_cell_text(cell, txt, bold=(ci < 2), size=10)

    doc.add_paragraph()

    ms_tbl = doc.add_table(rows=1, cols=1)
    ms_cell = ms_tbl.rows[0].cells[0]
    set_cell_bg(ms_cell, 'FDFEFE')
    set_cell_borders(ms_cell, m['hdr_color'])
    ms_cell.paragraphs[0].clear()
    mhdr = ms_cell.add_paragraph()
    mhdr.add_run('Month Milestones:').bold = True
    for ms in m['milestones']:
        p = ms_cell.add_paragraph(f'  βœ“  {ms}')
        p.runs[0].font.size = Pt(10)
    ms_cell.paragraphs[0]._element.getparent().remove(ms_cell.paragraphs[0]._element)
    doc.add_paragraph()
    doc.add_paragraph()

# ── PHASE 4: PLAB 2 EXAM ─────────────────────────────────────────────────────
doc.add_page_break()
add_colored_heading(doc, '  PHASE 4 - FEBRUARY 2027: PLAB 2 EXAM MONTH  (UK)', '922B21', 'FFFFFF', 14)
doc.add_paragraph()

p2_facts = [
    'β€’ 18 stations (16 scored, 2 rest), 8 minutes each station + 2 min reading time between',
    'β€’ Location: Manchester Medical Examination Centre (ONLY location in UK)',
    'β€’ Pass mark: Must pass sufficient stations; cumulative score assessed',
    'β€’ Pass rate: ~65-70% overall (higher for well-prepared candidates)',
    'β€’ Maximum 4 attempts allowed',
    'β€’ EXAMINER FOCUS: Communication skills, empathy, patient safety, clinical knowledge',
    'β€’ Common failures: Poor communication, running out of time, not addressing patient concerns (ICE)',
    'β€’ After passing: GMC issues Certificate of Completion - apply for full GMC registration',
]

pf_tbl = doc.add_table(rows=1, cols=1)
pf_cell = pf_tbl.rows[0].cells[0]
set_cell_bg(pf_cell, 'FDEDEC')
set_cell_borders(pf_cell, '922B21')
pf_cell.paragraphs[0].clear()
ph = pf_cell.add_paragraph()
ph.add_run('PLAB 2 EXAM - KEY FACTS').bold = True
for f in p2_facts:
    p = pf_cell.add_paragraph(f)
    p.runs[0].font.size = Pt(10)
pf_cell.paragraphs[0]._element.getparent().remove(pf_cell.paragraphs[0]._element)
doc.add_paragraph()

# Station type quick ref
add_colored_heading(doc, '  PLAB 2 Station Types - Quick Reference', '717D7E', 'FFFFFF', 11)
st_data = [
    ('History Taking', '~5 stations', 'Open questions, ICE, systems review, summarise, safety-net'),
    ('Examination', '~3 stations', 'State what you are doing, expose properly, thank patient, report findings'),
    ('Explanation/Counselling', '~3 stations', 'Simple language, check understanding, leaflets, follow-up plan'),
    ('Communication/Ethics', '~2 stations', 'SPIKES for bad news, CARPA for anger, capacity = MCA 2005'),
    ('Practical Procedures', '~2 stations', 'Consent, sterile technique, talk through steps, safety'),
    ('Data Interpretation', '~1 station', 'ECG, ABG, X-ray, bloods - systematic approach, key findings'),
]
st_tbl = doc.add_table(rows=1, cols=3)
for ci, htxt in enumerate(['Station Type', 'Frequency', 'Key Tips']):
    cell = st_tbl.rows[0].cells[ci]
    set_cell_bg(cell, '717D7E')
    add_cell_text(cell, htxt, bold=True, size=10, color='FFFFFF')
for stype, freq, tips in st_data:
    row = st_tbl.add_row()
    for ci, txt in enumerate([stype, freq, tips]):
        set_cell_bg(row.cells[ci], 'F2F3F4')
        set_cell_borders(row.cells[ci])
        add_cell_text(row.cells[ci], txt, bold=(ci == 0), size=10)
doc.add_paragraph()

# ── PHASE 5: POST-PLAB ───────────────────────────────────────────────────────
doc.add_page_break()
add_colored_heading(doc, '  PHASE 5 - MARCH TO JUNE 2027: GMC Registration + NHS Job + Visa', '1A5276', 'FFFFFF', 14)
doc.add_paragraph()

post_steps = [
    ('March 2027', 'GMC Full Registration',
     'After passing PLAB 2, apply for GMC Full Registration with a licence to practise. '
     'Fee: Β£405. Submit: PLAB 2 pass certificate, MBBS, internship certificate, English test, '
     'identity documents. Processing: 2-4 weeks if all documents in order. '
     'You CANNOT work as a doctor in the UK without this. Priority: get this done immediately.'),
    ('March-April 2027', 'Job Search - NHS Posts',
     'Register on NHS Jobs (jobs.nhs.uk) and Oriel (for specialty training). '
     'Start with Trust Grade / Clinical Fellow posts - these are easier to get than training posts. '
     'Also look at: Locum posts, GP practices, private hospitals, out-of-hours services. '
     'Target hospitals that are known IMG-friendly. '
     'WARNING (2026 reality): The NHS job market is competitive. Apply widely (50+ applications '
     'is not unusual). Consider Area of Need posts in rural or less popular regions - these hire faster. '
     'Network: LinkedIn, BAPIO (British Association of Physicians of Indian Origin), junior doctor forums.'),
    ('April-May 2027', 'Certificate of Sponsorship (CoS)',
     'Once you have a job offer, your NHS employer (as a licensed sponsor) will issue a CoS electronically. '
     'This is your permission to apply for the Health and Care Visa. '
     'The CoS has a reference number - you use it in your visa application. '
     'Apply for your visa WITHIN 3 MONTHS of CoS date. Do not miss this window.'),
    ('May 2027', 'Health and Care Visa Application',
     'Apply online at gov.uk/health-care-visa. Pay: Immigration Health Surcharge (Β£776/year) + '
     'visa fee (Β£284 for Health & Care route, much cheaper than standard Skilled Worker). '
     'Book biometric appointment at VFS Global centre in India. '
     'Processing: ~3 weeks standard (from India). Priority: 5 working days (extra fee). '
     'Documents needed: CoS reference, passport, English test, MBBS, GMC certificate, TB test result.'),
    ('June 2027', 'Arrive in UK',
     'Once visa granted: book flights, find accommodation near your hospital '
     '(NHS often helps with relocation support for new doctors). '
     'Set up: Bank account (Monzo or Starling are easiest as new arrival), '
     'NI number application, BRP collection from local post office. '
     'Join your hospital induction. Your UK medical career begins here. '
     'Astrologically: This lands perfectly in your Jupiter-Venus Antardasha peak window.'),
]

for period, step, detail in post_steps:
    tbl_post = doc.add_table(rows=1, cols=3)
    for ci, (w, txt) in enumerate([(Cm(3.5), period), (Cm(4.5), step), (Cm(11.5), detail)]):
        cell = tbl_post.rows[0].cells[ci]
        set_cell_bg(cell, 'EBF5FB' if ci < 2 else 'FDFEFE')
        set_cell_borders(cell, '2E86C1')
        add_cell_text(cell, txt, bold=(ci < 2), size=10)
        cell.width = w
    doc.add_paragraph()

# ── QUICK REFERENCE: PLAB 1 HIGH-YIELD TOPICS ────────────────────────────────
doc.add_page_break()
add_colored_heading(doc, '  PLAB 1 HIGH-YIELD TOPICS QUICK REFERENCE', '1B3A6B', 'FFFFFF', 14)
doc.add_paragraph()

hy_topics = [
    ('Cardiology', 'ACS (STEMI: PCI within 90 min; NSTEMI: ticagrelor + aspirin), AF (CHADS2-VASc for anticoag), Heart Failure (ACEi + beta-blocker + spironolactone), Hypertension (NICE ladder: ACEI β†’ CCB β†’ thiazide)'),
    ('Respiratory', 'Asthma (BTS stepwise: SABA β†’ ICS β†’ LABA β†’ specialist), COPD (NICE: SABA + SAMA β†’ LABA+LAMA β†’ add ICS), PE (Wells score, CTPA, LMWH then DOAC), Pneumonia (CURB-65 scoring for admission)'),
    ('Neurology', 'Stroke (thrombolysis <4.5h, thrombectomy <6h), Meningitis (IV ceftriaxone, LP after CT if safe), Epilepsy (sodium valproate or levetiracetam 1st line), Parkinson\'s (levodopa + carbidopa)'),
    ('Endocrine', 'DM2 (metformin 1st β†’ add SGLT2i/GLP1 β†’ sulfonylurea β†’ insulin), Hypothyroid (levothyroxine), Hyperthyroid (carbimazole), Addison\'s (short Synacthen test, hydrocortisone replacement)'),
    ('Renal', 'AKI stages (KDIGO: creatinine x1.5/x2/x3 baseline), CKD management (ACEi, BP control, diet), UTI (nitrofurantoin 1st line, trimethoprim if CI), Nephrotic (protein >3.5g/day, oedema, hypoalbuminaemia)'),
    ('Psychiatry', 'Depression (SSRI 1st line, venlafaxine 2nd), Schizophrenia (atypical antipsychotics, clozapine treatment-resistant), Bipolar (lithium, valproate), Section 2 (28 days assessment), Section 3 (6 months treatment)'),
    ('O&G', 'Pre-eclampsia (labetalol/nifedipine, MgSO4 for seizures), Ectopic (methotrexate if stable + criteria met), Contraception (COCP CIs: migraine with aura, DVT), Cervical screening (25-64 yrs, smear every 3-5 yrs)'),
    ('Paediatrics', 'Milestones (smiles 6w, sits 6m, walks 12m, 50 words 18m), Febrile seizure (simple: <15 min, generalised), Meningitis (IM benzylpenicillin before hospital), Croup (dexamethasone, nebulised adrenaline if severe)'),
    ('Ethics/Law', 'Consent: valid if informed, voluntary, capacity. Capacity = MCA 2005 (understand, retain, weigh, communicate). Gillick: under-16 can consent if competent. Notifiable diseases: TB, measles, meningitis, COVID. Duty of candour.'),
    ('Emergency', 'Anaphylaxis (IM adrenaline 0.5mg, then chlorphenamine + hydrocortisone), Sepsis 6 (bloods, cultures, lactate, O2, IV fluids, IV antibiotics within 1 hr), NEWS2 scoring for deterioration trigger'),
]

hy_tbl = doc.add_table(rows=1, cols=2)
for ci, htxt in enumerate(['System', 'High-Yield Knowledge Points']):
    cell = hy_tbl.rows[0].cells[ci]
    set_cell_bg(cell, '1B3A6B')
    add_cell_text(cell, htxt, bold=True, size=10, color='FFFFFF')
alt = ['F2F3F4', 'EBF5FB']
for i, (sys, points) in enumerate(hy_topics):
    row = hy_tbl.add_row()
    set_cell_bg(row.cells[0], alt[i % 2])
    set_cell_bg(row.cells[1], alt[i % 2])
    set_cell_borders(row.cells[0])
    set_cell_borders(row.cells[1])
    add_cell_text(row.cells[0], sys, bold=True, size=10)
    add_cell_text(row.cells[1], points, bold=False, size=9.5)

doc.add_paragraph()

# ── RECOMMENDED RESOURCES ─────────────────────────────────────────────────────
add_colored_heading(doc, '  RECOMMENDED RESOURCES & LINKS', '1B3A6B', 'FFFFFF', 13)
doc.add_paragraph()

resources = [
    ('PLAB 1 Question Banks', [
        'PLABABLE - plabable.com (most popular, UK-specific, subscription)',
        'Passmedicine - passmedicine.com (UK primary care + hospital, excellent)',
        'PasTest PLAB - pastest.co.uk',
    ]),
    ('PLAB 2 Resources', [
        'PLAB 2 Ninja - plab2ninja.com (courses + mock cases)',
        'SWOT Medical - swotmedical.com (UK-based prep courses)',
        'Dr Najeeb PLAB 2 videos (YouTube)',
    ]),
    ('Essential UK Guidelines (FREE)', [
        'NICE Guidelines - nice.org.uk (search by condition)',
        'BNF online - bnf.nice.org.uk (free, UK drug reference)',
        'GMC Good Medical Practice - gmc-uk.org',
        'NHS Clinical Knowledge Summaries - cks.nice.org.uk',
    ]),
    ('Books', [
        'Oxford Handbook of Clinical Medicine (OHCM) - 10th Edition',
        'Oxford Handbook for the PLAB Exam',
        'Get Through PLAB: 1000 EMQs (Achar)',
    ]),
    ('Community & Support', [
        'r/PLAB subreddit - reddit.com/r/PLAB',
        'BAPIO - bapio.co.uk (British Association of Physicians of Indian Origin)',
        'IMG Connect Facebook groups',
        'PLAB WhatsApp groups (search in above communities)',
    ]),
    ('Official GMC Links', [
        'PLAB overview: gmc-uk.org/registration-and-licensing/join-the-register/plab',
        'PLAB 1 booking: gmc-uk.org/plab1',
        'PLAB 2 booking: gmc-uk.org/plab2',
        'GMC fees: gmc-uk.org/fees',
    ]),
]

for section_title, items in resources:
    p = doc.add_paragraph()
    r = p.add_run(f'{section_title}:')
    r.bold = True
    r.font.color.rgb = RGBColor.from_string('1B3A6B')
    r.font.size = Pt(11)
    for item in items:
        p2 = doc.add_paragraph(f'    β€’ {item}', style='List Bullet')
        p2.runs[0].font.size = Pt(10)
    doc.add_paragraph()

# ── FOOTER NOTE ───────────────────────────────────────────────────────────────
doc.add_page_break()
add_colored_heading(doc, '  FINAL NOTES & MOTIVATION', '1B3A6B', 'FFFFFF', 13)
doc.add_paragraph()

final = [
    'This schedule is designed to get you from August 2026 to landing in the UK by mid-2027 - '
    'a 10-12 month journey from today. It is ambitious but completely realistic for a motivated, '
    'disciplined candidate.',
    '',
    'The single most important thing about PLAB 1: consistency over intensity. 6-8 focused hours '
    'daily beats 12-hour cramming sessions followed by burnout. Use spaced repetition - revisit '
    'topics at increasing intervals rather than re-reading the same thing repeatedly.',
    '',
    'The single most important thing about PLAB 2: you cannot prepare alone. Find study partners, '
    'practise out loud every single day, and get used to the UK communication style. Empathy, '
    'patient-centredness, and safety-netting are what examiners are looking for.',
    '',
    'The NHS job market in 2026 is competitive. Having PLAB done is not enough - you also need '
    'a good application, networking, and persistence. Join IMG communities early, build connections, '
    'and apply widely. Do not limit yourself to London or big cities - regional hospitals and '
    'Area of Need posts are often faster routes to your first NHS post.',
    '',
    'You have a strong chart with Venus in the 9th house and Rahu in the 12th - the universe is '
    'pointing you abroad. Jupiter-Venus Antardasha from January 2027 is your activation window. '
    'Use this preparation period (now to January 2027) to be exam-ready and paperwork-ready so '
    'that when that window opens, you can walk through it.',
    '',
    'Best of luck. The UK NHS needs good doctors, and your MBBS from China + this preparation '
    'will get you there.',
]

for line in final:
    p = doc.add_paragraph(line)
    p.runs[0].font.size = Pt(10.5) if p.runs else None
    if not line:
        continue

doc.add_paragraph()
footer_p = doc.add_paragraph()
footer_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
fr = footer_p.add_run('Prepared by Orris AI  |  Based on PLAB 2026 guidelines  |  Always verify current fees and dates at gmc-uk.org')
fr.font.size = Pt(9)
fr.font.color.rgb = RGBColor.from_string('888888')

# ── SAVE ──────────────────────────────────────────────────────────────────────
output_path = '/tmp/workspace/plab-schedule/PLAB_Preparation_Schedule_2026_2027.docx'
doc.save(output_path)
print(f'Saved: {output_path}')
Running Command

cd /tmp/workspace/plab-schedule && python3 build_schedule.py 2>&1

Editing File

~/plab-schedule/build_schedule.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/plab-schedule/build_schedule.py'
Running Command

cd /tmp/workspace/plab-schedule && python3 build_schedule.py 2>&1

Running Command

ls -lh /tmp/workspace/plab-schedule/PLAB_Preparation_Schedule_2026_2027.docx

Your complete PLAB preparation schedule is ready. Download it here:

PLAB Preparation Schedule 2026 2027

Word Document Β· DOCX


Here is a summary of everything inside the document:

What's in the Schedule

5 Phases Covered

PhasePeriodContent
Phase 1Aug - Oct 2026PLAB 1 prep - 3 months, week-by-week breakdown
Phase 2Nov 2026PLAB 1 exam month - final revision + exam day strategy
Phase 3Nov 2026 - Jan 2027PLAB 2 prep - OSCE skills, communication, UK course
Phase 4Feb 2027PLAB 2 exam in UK - station guide, exam day plan
Phase 5Mar - Jun 2027GMC registration, NHS job hunt, Health & Care Visa

Highlights Inside the Document

  • Week-by-week study tables for all 3 months of PLAB 1 prep with exact topics each week
  • System-by-system high-yield cheatsheet (Cardiology, Respiratory, Neuro, Endo, Renal, Psych, O&G, Paediatrics, Ethics, Emergency)
  • PLAB 2 station types quick reference (6 station types with tips for each)
  • Pre-requisites checklist (GMC check, IELTS booking, document verification)
  • Budget breakdown (total ~β‚Ή3.5-4.5 lakhs all-in)
  • All recommended resources with links (PLABABLE, Passmedicine, PLAB 2 Ninja, NICE, BNF)
  • NHS job + Health & Care Visa step-by-step (March to June 2027)
  • Monthly milestones so you always know if you are on track
The timeline is calibrated so you sit PLAB 1 in November 2026, PLAB 2 in February 2027, and land in the UK by mid-2027 - right in your Jupiter-Venus antardasha window. Start your GMC portal registration and IELTS booking this week to stay on track.

Generate a budget tracking sheet for all exam + visa costs

Reading File
Running Command

ls /tmp/workspace/plab-schedule/ && python3 -c "import openpyxl; print('openpyxl', openpyxl.__version__)"

Writing File

~/plab-schedule/build_budget.py

from openpyxl import Workbook
from openpyxl.styles import (
    Font, PatternFill, Alignment, Border, Side, numbers
)
from openpyxl.styles.numbers import FORMAT_NUMBER_COMMA_SEPARATED1
from openpyxl.utils import get_column_letter
from openpyxl.chart import BarChart, Reference, PieChart
from openpyxl.chart.series import DataPoint
from openpyxl.formatting.rule import DataBarRule, ColorScaleRule
from openpyxl.worksheet.datavalidation import DataValidation
import openpyxl

wb = Workbook()

# ── COLOUR PALETTE ────────────────────────────────────────────────────────────
C_NAVY      = "1B3A6B"
C_BLUE      = "2E86C1"
C_LIGHT_BLU = "D6EAF8"
C_GREEN     = "1E8449"
C_LIGHT_GRN = "D5F5E3"
C_AMBER     = "D4AC0D"
C_LIGHT_AMB = "FEF9E7"
C_RED       = "C0392B"
C_LIGHT_RED = "FDEDEC"
C_PURPLE    = "6C3483"
C_LIGHT_PUR = "F4ECF7"
C_GREY_HDR  = "2C3E50"
C_GREY_LITE = "F2F3F4"
C_WHITE     = "FFFFFF"
C_INPUT_BG  = "EBF5FB"
C_PAID_BG   = "EAFAF1"

# ── STYLE HELPERS ─────────────────────────────────────────────────────────────

def fill(hex_color):
    return PatternFill("solid", fgColor=hex_color)

def font(bold=False, size=10, color="000000", italic=False, name="Arial"):
    return Font(bold=bold, size=size, color=color, italic=italic, name=name)

def border(style="thin", color="CCCCCC"):
    s = Side(style=style, color=color)
    return Border(left=s, right=s, top=s, bottom=s)

def thick_border(color="999999"):
    t = Side(style="medium", color=color)
    n = Side(style="thin",   color="CCCCCC")
    return Border(left=t, right=t, top=t, bottom=t)

def align(h="left", v="center", wrap=False):
    return Alignment(horizontal=h, vertical=v, wrap_text=wrap)

INR_FMT  = 'β‚Ή#,##0;(β‚Ή#,##0);"-"'
GBP_FMT  = 'Β£#,##0.00;(Β£#,##0.00);"-"'
PCT_FMT  = '0.0%;-0.0%;"-"'
DATE_FMT = 'MMM YYYY'

def set_col_width(ws, col, width):
    ws.column_dimensions[get_column_letter(col)].width = width

def merge_header(ws, row, col_start, col_end, text,
                 bg=C_NAVY, fg=C_WHITE, size=11, bold=True):
    ws.merge_cells(start_row=row, start_column=col_start,
                   end_row=row,   end_column=col_end)
    cell = ws.cell(row=row, column=col_start, value=text)
    cell.fill = fill(bg)
    cell.font = font(bold=bold, size=size, color=fg)
    cell.alignment = align("center")
    cell.border = border("thin", "AAAAAA")
    return cell

def col_header(ws, row, col, text, bg=C_GREY_HDR, fg=C_WHITE, size=10, wrap=True):
    c = ws.cell(row=row, column=col, value=text)
    c.fill = fill(bg)
    c.font = font(bold=True, size=size, color=fg)
    c.alignment = align("center", "center", wrap)
    c.border = border("thin", "888888")
    return c

def data_cell(ws, row, col, value=None, fmt=None,
              bg=C_WHITE, fg="000000", bold=False,
              h_align="left", border_color="CCCCCC"):
    c = ws.cell(row=row, column=col, value=value)
    c.fill = fill(bg)
    c.font = font(bold=bold, color=fg, size=10)
    c.alignment = align(h_align, "center", wrap_text=(h_align == "left"))
    c.border = border("thin", border_color)
    if fmt:
        c.number_format = fmt
    return c

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 1: OVERVIEW DASHBOARD
# ══════════════════════════════════════════════════════════════════════════════
ws1 = wb.active
ws1.title = "πŸ“Š Dashboard"
ws1.sheet_view.showGridLines = False
ws1.row_dimensions[1].height = 8

# Column widths
widths = [2, 28, 16, 16, 16, 16, 20, 3]
for i, w in enumerate(widths, 1):
    set_col_width(ws1, i, w)

# ── Title block ───────────────────────────────────────────────────────────────
ws1.row_dimensions[2].height = 36
merge_header(ws1, 2, 2, 7,
             "πŸ‡¬πŸ‡§  PLAB EXAM & UK VISA β€” COMPLETE BUDGET TRACKER",
             bg=C_NAVY, fg=C_WHITE, size=16)

ws1.row_dimensions[3].height = 18
merge_header(ws1, 3, 2, 7,
             "MBBS (China) β†’ UK NHS Doctor  |  August 2026 – June 2027  |  All amounts in INR (β‚Ή) & GBP (Β£)",
             bg=C_BLUE, fg=C_WHITE, size=10, bold=False)

ws1.row_dimensions[4].height = 8

# ── Exchange rate assumption (user input) ─────────────────────────────────────
ws1.row_dimensions[5].height = 22
merge_header(ws1, 5, 2, 4, "βš™  KEY ASSUMPTIONS  (blue cells = change these)",
             bg=C_GREY_HDR, fg=C_WHITE, size=10)

labels = ["GBP β†’ INR rate (today)", "Contingency buffer %", "Today's Date"]
vals   = [108,                       15,                      "01-Aug-2026"]
fmts   = ["#,##0",                   "0%",                    "@"]
for i, (lbl, val, fmt) in enumerate(zip(labels, vals, fmts)):
    r = 6 + i
    ws1.row_dimensions[r].height = 20
    c1 = ws1.cell(row=r, column=2, value=lbl)
    c1.fill  = fill(C_GREY_LITE)
    c1.font  = font(bold=True, size=10)
    c1.alignment = align()
    c1.border = border()

    c2 = ws1.cell(row=r, column=3, value=val)
    c2.fill  = fill(C_INPUT_BG)
    c2.font  = font(bold=True, size=11, color="0000FF")   # blue = input
    c2.alignment = align("center")
    c2.border = border("medium", C_BLUE)
    c2.number_format = fmt

# Named references for formula use
# GBP rate = C6, contingency = C7

ws1.row_dimensions[9].height = 8

# ── Summary KPI cards ─────────────────────────────────────────────────────────
kpi_row = 10
ws1.row_dimensions[kpi_row].height = 18
merge_header(ws1, kpi_row, 2, 7, "πŸ’°  COST SUMMARY AT A GLANCE",
             bg=C_GREY_HDR, fg=C_WHITE, size=10)

# Headers
ws1.row_dimensions[kpi_row+1].height = 40
kpi_headers = ["Category", "GBP (Β£)", "INR (β‚Ή)", "% of Total", "Status", "Notes"]
kpi_bg      = [C_GREY_HDR]*6
for j, (h, bg) in enumerate(zip(kpi_headers, kpi_bg)):
    col_header(ws1, kpi_row+1, j+2, h, bg=bg)

# KPI rows - (category, gbp_value, status, note)
kpi_data = [
    ("PLAB 1 Exam Fee",             268,   "Pending",   "GMC fee (as of 2025). Verify at gmc-uk.org"),
    ("PLAB 2 Exam Fee",             998,   "Pending",   "GMC fee per attempt. Manchester only"),
    ("IELTS / OET Exam",            0,     "Pending",   "IELTS ~β‚Ή16,500 | OET ~β‚Ή18,000. Enter in INR"),
    ("GMC Registration (Full)",     405,   "Pending",   "After passing PLAB 2"),
    ("English Language Test (INT)", 0,     "Pending",   "If retake needed"),
    ("PLAB 1 Prep Materials",       0,     "Pending",   "PLABABLE ~Β£90/mo | Passmedicine ~Β£70/mo"),
    ("PLAB 2 Prep Course (India)",  0,     "Pending",   "Marrow/DAMS PLAB batch ~β‚Ή25,000-50,000"),
    ("PLAB 2 Prep Course (UK)",     0,     "Pending",   "PLAB2Ninja UK course ~Β£300-500"),
    ("UK Trip: Flights",            0,     "Pending",   "Return flight India ↔ Manchester ~Β£600-900"),
    ("UK Trip: Accommodation",      0,     "Pending",   "~Β£500-800 for 3-4 week stay"),
    ("UK Trip: Food & Transport",   0,     "Pending",   "~Β£400-600 for trip duration"),
    ("TB Test (mandatory for visa)", 0,    "Pending",   "~β‚Ή4,000 at UKVI-approved clinic"),
    ("Biometric Appointment (VFS)", 0,     "Pending",   "~β‚Ή2,000-3,500 at VFS Global India"),
    ("Health & Care Visa Fee",      284,   "Pending",   "Cheaper than standard Skilled Worker"),
    ("Immigration Health Surcharge","",    "Pending",   "Β£776/yr Γ— visa years (e.g., 3yr = Β£2,328)"),
    ("Document Translation/Notary", 0,     "Pending",   "MBBS, internship cert etc. ~β‚Ή5,000-10,000"),
    ("Relocation (India β†’ UK)",     0,     "Pending",   "Baggage, shipping, misc. ~β‚Ή20,000-50,000"),
]

row_bg_alt = [C_WHITE, C_GREY_LITE]
phase_colors = {
    "PLAB 1": C_LIGHT_BLU,
    "PLAB 2": C_LIGHT_PUR,
    "Visa"  : C_LIGHT_GRN,
    "Travel": C_LIGHT_AMB,
    "Other" : C_WHITE,
}

status_options = '"Pending,Paid,Budgeted,N/A"'

for i, (cat, gbp, status, note) in enumerate(kpi_data):
    r = kpi_row + 2 + i
    ws1.row_dimensions[r].height = 18
    bg = row_bg_alt[i % 2]

    # Category
    data_cell(ws1, r, 2, cat, bg=bg, bold=False, border_color="BBBBBB")

    # GBP
    c_gbp = ws1.cell(row=r, column=3, value=gbp if isinstance(gbp, (int, float)) else None)
    c_gbp.fill = fill(C_INPUT_BG)
    c_gbp.font = font(color="0000FF", size=10)
    c_gbp.alignment = align("right", "center")
    c_gbp.border = border("thin", "BBBBBB")
    if isinstance(gbp, (int, float)):
        c_gbp.number_format = GBP_FMT

    # INR (formula: GBP * rate, or direct input if GBP=0)
    c_inr = ws1.cell(row=r, column=4)
    if isinstance(gbp, (int, float)) and gbp > 0:
        c_inr.value = f"=C{r}*$C$6"
    else:
        c_inr.value = 0  # user fills in INR directly
        c_inr.fill = fill(C_INPUT_BG)
        c_inr.font = font(color="0000FF", size=10)
    c_inr.alignment = align("right", "center")
    c_inr.border = border("thin", "BBBBBB")
    c_inr.number_format = INR_FMT

    # % of total (will be filled after total row is known)
    ws1.cell(row=r, column=5).value = f"=IF(D{kpi_row+2+len(kpi_data)+1}=0,\"-\",D{r}/D{kpi_key_total_row if False else 'PLACEHOLDER'})"

    # Status dropdown
    c_status = ws1.cell(row=r, column=6, value=status)
    c_status.fill = fill(C_LIGHT_GRN if status == "Paid" else C_LIGHT_AMB)
    c_status.font = font(size=10, bold=(status == "Paid"))
    c_status.alignment = align("center", "center")
    c_status.border = border("thin", "BBBBBB")

    # Notes
    data_cell(ws1, r, 7, note, bg=bg, border_color="BBBBBB")

# Total row
total_r = kpi_row + 2 + len(kpi_data)
ws1.row_dimensions[total_r].height = 22
merge_header(ws1, total_r, 2, 2, "TOTAL BASE COST", bg=C_NAVY, fg=C_WHITE, size=10)

c_total_gbp = ws1.cell(row=total_r, column=3, value=f"=SUM(C{kpi_row+2}:C{total_r-1})")
c_total_gbp.fill = fill(C_NAVY)
c_total_gbp.font = font(bold=True, size=11, color=C_WHITE)
c_total_gbp.alignment = align("right", "center")
c_total_gbp.border = thick_border(C_NAVY)
c_total_gbp.number_format = GBP_FMT

c_total_inr = ws1.cell(row=total_r, column=4, value=f"=SUM(D{kpi_row+2}:D{total_r-1})")
c_total_inr.fill = fill(C_NAVY)
c_total_inr.font = font(bold=True, size=11, color=C_WHITE)
c_total_inr.alignment = align("right", "center")
c_total_inr.border = thick_border(C_NAVY)
c_total_inr.number_format = INR_FMT

ws1.cell(row=total_r, column=5, value="-").fill = fill(C_NAVY)
ws1.cell(row=total_r, column=5).font = font(color=C_WHITE)
ws1.cell(row=total_r, column=6, value="β€”").fill = fill(C_NAVY)
ws1.cell(row=total_r, column=6).font = font(color=C_WHITE)
ws1.cell(row=total_r, column=7, value="Excl. contingency").fill = fill(C_NAVY)
ws1.cell(row=total_r, column=7).font = font(color=C_WHITE)

# Now fix % of total formulas
for i in range(len(kpi_data)):
    r = kpi_row + 2 + i
    c_pct = ws1.cell(row=r, column=5)
    c_pct.value = f"=IF(D{total_r}=0,0,D{r}/D{total_r})"
    c_pct.number_format = PCT_FMT
    c_pct.alignment = align("center", "center")
    c_pct.border = border("thin", "BBBBBB")
    c_pct.font = font(size=10, color="000000")
    c_pct.fill = fill(C_GREY_LITE if i % 2 else C_WHITE)

# Contingency row
cont_r = total_r + 1
ws1.row_dimensions[cont_r].height = 20
merge_header(ws1, cont_r, 2, 2, "Contingency (15%)", bg=C_AMBER, fg=C_WHITE, size=10)
c_cont_gbp = ws1.cell(row=cont_r, column=3, value=f"=C{total_r}*$C$7")
c_cont_gbp.fill = fill(C_LIGHT_AMB)
c_cont_gbp.font = font(bold=True, size=10, color="000000")
c_cont_gbp.alignment = align("right")
c_cont_gbp.number_format = GBP_FMT
c_cont_gbp.border = border("medium", C_AMBER)

c_cont_inr = ws1.cell(row=cont_r, column=4, value=f"=D{total_r}*$C$7")
c_cont_inr.fill = fill(C_LIGHT_AMB)
c_cont_inr.font = font(bold=True, size=10, color="000000")
c_cont_inr.alignment = align("right")
c_cont_inr.number_format = INR_FMT
c_cont_inr.border = border("medium", C_AMBER)

# Grand total
gt_r = cont_r + 1
ws1.row_dimensions[gt_r].height = 26
merge_header(ws1, gt_r, 2, 2, "βœ…  GRAND TOTAL (with contingency)", bg=C_GREEN, fg=C_WHITE, size=11)
c_gt_gbp = ws1.cell(row=gt_r, column=3, value=f"=C{total_r}+C{cont_r}")
c_gt_gbp.fill = fill(C_GREEN)
c_gt_gbp.font = font(bold=True, size=12, color=C_WHITE)
c_gt_gbp.alignment = align("right", "center")
c_gt_gbp.number_format = GBP_FMT
c_gt_gbp.border = thick_border(C_GREEN)

c_gt_inr = ws1.cell(row=gt_r, column=4, value=f"=D{total_r}+D{cont_r}")
c_gt_inr.fill = fill(C_GREEN)
c_gt_inr.font = font(bold=True, size=12, color=C_WHITE)
c_gt_inr.alignment = align("right", "center")
c_gt_inr.number_format = INR_FMT
c_gt_inr.border = thick_border(C_GREEN)

ws1.cell(row=gt_r, column=5).fill = fill(C_GREEN)
ws1.cell(row=gt_r, column=6).fill = fill(C_GREEN)
ws1.cell(row=gt_r, column=7, value="← Your total budget needed").fill = fill(C_GREEN)
ws1.cell(row=gt_r, column=7).font = font(bold=True, color=C_WHITE, size=10, italic=True)

# Add dropdown validation for status column
dv = DataValidation(type="list", formula1=status_options, allow_blank=True)
dv.sqref = f"F{kpi_row+2}:F{total_r-1}"
ws1.add_data_validation(dv)

# ── Paid so far tracker ───────────────────────────────────────────────────────
paid_r = gt_r + 2
ws1.row_dimensions[paid_r].height = 18
merge_header(ws1, paid_r, 2, 7, "πŸ“ˆ  PAYMENT PROGRESS TRACKER",
             bg=C_GREY_HDR, fg=C_WHITE, size=10)

ws1.row_dimensions[paid_r+1].height = 20
for j, h in enumerate(["Metric", "GBP (Β£)", "INR (β‚Ή)", "% Complete", "", ""], 1):
    col_header(ws1, paid_r+1, j+1, h, bg=C_BLUE)

tracker_rows = [
    ("Total Budget (incl. contingency)", f"=C{gt_r}", f"=D{gt_r}"),
    ("Amount Paid to Date",              0,            0),
    ("Amount Remaining",                 f"=C{paid_r+2}-C{paid_r+3}", f"=D{paid_r+2}-D{paid_r+3}"),
]
for i, (lbl, gbp_val, inr_val) in enumerate(tracker_rows):
    r = paid_r + 2 + i
    ws1.row_dimensions[r].height = 20
    bg = [C_GREY_LITE, C_LIGHT_BLU, C_LIGHT_GRN][i]

    c_lbl = ws1.cell(row=r, column=2, value=lbl)
    c_lbl.fill = fill(bg)
    c_lbl.font = font(bold=True, size=10)
    c_lbl.alignment = align()
    c_lbl.border = border()

    c_g = ws1.cell(row=r, column=3, value=gbp_val)
    c_g.fill = fill(C_INPUT_BG if i == 1 else bg)
    c_g.font = font(bold=(i != 1), color="0000FF" if i == 1 else "000000", size=10)
    c_g.alignment = align("right")
    c_g.number_format = GBP_FMT
    c_g.border = border()

    c_i = ws1.cell(row=r, column=4, value=inr_val)
    c_i.fill = fill(C_INPUT_BG if i == 1 else bg)
    c_i.font = font(bold=(i != 1), color="0000FF" if i == 1 else "000000", size=10)
    c_i.alignment = align("right")
    c_i.number_format = INR_FMT
    c_i.border = border()

    c_pct = ws1.cell(row=r, column=5)
    if i == 0:
        c_pct.value = "100.0%"
        c_pct.number_format = PCT_FMT
    elif i == 1:
        c_pct.value = f"=IF(C{paid_r+2}=0,0,C{r}/C{paid_r+2})"
        c_pct.number_format = PCT_FMT
    c_pct.fill = fill(bg)
    c_pct.alignment = align("center")
    c_pct.border = border()

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 2: MONTHLY CASHFLOW
# ══════════════════════════════════════════════════════════════════════════════
ws2 = wb.create_sheet("πŸ“… Monthly Cashflow")
ws2.sheet_view.showGridLines = False

# Column setup
months = ["Aug 2026", "Sep 2026", "Oct 2026", "Nov 2026",
          "Dec 2026", "Jan 2027", "Feb 2027", "Mar 2027",
          "Apr 2027", "May 2027", "Jun 2027"]
col_widths2 = [2, 30] + [13]*len(months) + [13, 3]
for i, w in enumerate(col_widths2, 1):
    set_col_width(ws2, i, w)

ws2.row_dimensions[1].height = 8
ws2.row_dimensions[2].height = 36
merge_header(ws2, 2, 2, 2+len(months)+1,
             "πŸ“…  MONTHLY CASHFLOW PLAN  |  When to Spend What",
             bg=C_NAVY, fg=C_WHITE, size=14)

ws2.row_dimensions[3].height = 18
merge_header(ws2, 3, 2, 2+len(months)+1,
             "Enter amounts in INR (β‚Ή). GBP costs auto-converted using rate on Dashboard sheet.",
             bg=C_BLUE, fg=C_WHITE, size=10, bold=False)

ws2.row_dimensions[4].height = 8

# Month headers
ws2.row_dimensions[5].height = 30
col_header(ws2, 5, 2, "Expense Item", bg=C_GREY_HDR)
for j, m in enumerate(months):
    col_header(ws2, 5, 3+j, m, bg=C_GREY_HDR)
col_header(ws2, 5, 3+len(months), "TOTAL", bg=C_NAVY)

# Cashflow rows: (category, label, amounts_by_month_dict)
# months index: 0=Aug26, 1=Sep26, 2=Oct26, 3=Nov26, 4=Dec26, 5=Jan27, 6=Feb27, 7=Mar27, 8=Apr27, 9=May27, 10=Jun27

cashflow_sections = [
    {
        "section": "PLAB 1 COSTS",
        "bg_hdr": C_BLUE,
        "items": [
            ("IELTS/OET Exam Fee",           {1: 16500}),
            ("IELTS Prep Materials",         {0: 3000, 1: 3000}),
            ("PLAB 1 Exam Fee (Β£268)",       {2: 28944}),   # 268 Γ— 108
            ("PLABABLE / Passmedicine Sub.", {0: 9720, 1: 9720, 2: 9720}),
            ("PLAB 1 Study Books",           {0: 4000}),
            ("Mock Test Fees (if any)",      {2: 2000}),
        ]
    },
    {
        "section": "PLAB 2 COSTS",
        "bg_hdr": C_PURPLE,
        "items": [
            ("PLAB 2 Exam Fee (Β£998)",       {6: 107784}),  # 998 Γ— 108
            ("PLAB 2 Prep Course India",     {4: 35000}),
            ("PLAB 2 Ninja / Online Course", {4: 5000, 5: 5000}),
            ("PLAB 2 Prep Books",            {4: 2000}),
        ]
    },
    {
        "section": "UK TRIP (PLAB 2 Exam)",
        "bg_hdr": C_AMBER,
        "items": [
            ("Return Flights India-Manchester", {5: 75000}),
            ("UK Accommodation (3-4 weeks)",    {5: 54000}),  # Β£500 Γ— 108
            ("UK Prep Course (in-person)",       {5: 43200}),  # Β£400 Γ— 108
            ("Food & Local Transport (UK)",      {5: 45000}),  # Β£417 Γ— 108
            ("Travel Insurance",                 {5: 4000}),
            ("SIM Card / Mobile (UK)",           {5: 1500}),
        ]
    },
    {
        "section": "GMC REGISTRATION",
        "bg_hdr": C_GREEN,
        "items": [
            ("GMC Full Registration (Β£405)",    {7: 43740}),  # 405 Γ— 108
            ("Document Translation/Notarisation",{0: 5000, 7: 5000}),
            ("Certificate Verification (MyIntealth)",{0: 3000}),
        ]
    },
    {
        "section": "VISA COSTS",
        "bg_hdr": C_RED,
        "items": [
            ("TB Test (UKVI approved clinic)",  {9: 4000}),
            ("Biometric Appt (VFS Global)",     {9: 3000}),
            ("Health & Care Visa Fee (Β£284)",   {9: 30672}),  # 284 Γ— 108
            ("Immigration Health Surcharge (3yr)", {9: 251856}),  # 776Γ—3Γ—108
            ("Visa Photo / Misc docs",          {9: 1000}),
        ]
    },
    {
        "section": "RELOCATION",
        "bg_hdr": C_GREY_HDR,
        "items": [
            ("One-way Flight to UK (final move)", {10: 55000}),
            ("Initial Accommodation (UK)",         {10: 32400}),  # Β£300 Γ— 108
            ("Household Setup / Misc",             {10: 20000}),
            ("Emergency Fund",                     {10: 50000}),
        ]
    },
]

current_row = 6
section_total_rows = []
all_data_rows = []

for section_info in cashflow_sections:
    ws2.row_dimensions[current_row].height = 20
    merge_header(ws2, current_row, 2, 2+len(months)+1,
                 section_info["section"],
                 bg=section_info["bg_hdr"], fg=C_WHITE, size=10)
    current_row += 1

    section_start = current_row
    for item_label, amounts in section_info["items"]:
        ws2.row_dimensions[current_row].height = 18
        bg_alt = C_WHITE if (current_row % 2 == 0) else C_GREY_LITE

        c_lbl = ws2.cell(row=current_row, column=2, value=item_label)
        c_lbl.fill = fill(bg_alt)
        c_lbl.font = font(size=10)
        c_lbl.alignment = align("left", "center")
        c_lbl.border = border()

        for j in range(len(months)):
            val = amounts.get(j, None)
            c = ws2.cell(row=current_row, column=3+j, value=val)
            c.fill = fill(C_INPUT_BG if val is not None else bg_alt)
            c.font = font(color="0000FF" if val is not None else "000000", size=10)
            c.alignment = align("right", "center")
            c.number_format = INR_FMT
            c.border = border()

        # Row total
        first_col = get_column_letter(3)
        last_col  = get_column_letter(3 + len(months) - 1)
        c_tot = ws2.cell(row=current_row, column=3+len(months),
                         value=f"=SUM({first_col}{current_row}:{last_col}{current_row})")
        c_tot.fill = fill(C_LIGHT_BLU)
        c_tot.font = font(bold=True, size=10, color="000000")
        c_tot.alignment = align("right")
        c_tot.number_format = INR_FMT
        c_tot.border = border("medium", C_BLUE)

        all_data_rows.append(current_row)
        current_row += 1

    # Section subtotal
    ws2.row_dimensions[current_row].height = 20
    c_sub_lbl = ws2.cell(row=current_row, column=2,
                         value=f"  Subtotal – {section_info['section']}")
    c_sub_lbl.fill = fill(section_info["bg_hdr"] + "30" if len(section_info["bg_hdr"]) == 6 else C_GREY_LITE)
    c_sub_lbl.fill = fill("E8E8E8")
    c_sub_lbl.font = font(bold=True, size=10)
    c_sub_lbl.alignment = align()
    c_sub_lbl.border = border("medium", "888888")

    for j in range(len(months)):
        col_letter = get_column_letter(3+j)
        c_sub = ws2.cell(row=current_row, column=3+j,
                         value=f"=SUM({col_letter}{section_start}:{col_letter}{current_row-1})")
        c_sub.fill = fill("E8E8E8")
        c_sub.font = font(bold=True, size=10)
        c_sub.alignment = align("right")
        c_sub.number_format = INR_FMT
        c_sub.border = border("medium", "888888")

    # Subtotal total column
    fc = get_column_letter(3)
    lc = get_column_letter(3+len(months)-1)
    c_sub_t = ws2.cell(row=current_row, column=3+len(months),
                       value=f"=SUM({fc}{current_row}:{lc}{current_row})")
    c_sub_t.fill = fill("CCCCCC")
    c_sub_t.font = font(bold=True, size=10)
    c_sub_t.alignment = align("right")
    c_sub_t.number_format = INR_FMT
    c_sub_t.border = border("medium", "555555")

    section_total_rows.append(current_row)
    current_row += 2  # blank spacer

# GRAND TOTAL ROW
ws2.row_dimensions[current_row].height = 28
merge_header(ws2, current_row, 2, 2, "GRAND TOTAL (INR β‚Ή)", bg=C_NAVY, fg=C_WHITE, size=11)
for j in range(len(months)):
    col_letter = get_column_letter(3+j)
    col_refs = "+".join([f"{col_letter}{r}" for r in section_total_rows])
    c_gt = ws2.cell(row=current_row, column=3+j, value=f"={col_refs}")
    c_gt.fill = fill(C_NAVY)
    c_gt.font = font(bold=True, size=11, color=C_WHITE)
    c_gt.alignment = align("right")
    c_gt.number_format = INR_FMT
    c_gt.border = thick_border(C_NAVY)

fc = get_column_letter(3)
lc = get_column_letter(3+len(months)-1)
c_gt_total = ws2.cell(row=current_row, column=3+len(months),
                      value=f"=SUM({fc}{current_row}:{lc}{current_row})")
c_gt_total.fill = fill(C_GREEN)
c_gt_total.font = font(bold=True, size=13, color=C_WHITE)
c_gt_total.alignment = align("right")
c_gt_total.number_format = INR_FMT
c_gt_total.border = thick_border(C_GREEN)

# RUNNING TOTAL ROW
cr2 = current_row + 1
ws2.row_dimensions[cr2].height = 20
merge_header(ws2, cr2, 2, 2, "Running Cumulative (β‚Ή)", bg=C_BLUE, fg=C_WHITE, size=10)
for j in range(len(months)):
    col_letter = get_column_letter(3+j)
    if j == 0:
        formula = f"={col_letter}{current_row}"
    else:
        prev_col = get_column_letter(3+j-1)
        formula = f"={prev_col}{cr2}+{col_letter}{current_row}"
    c_run = ws2.cell(row=cr2, column=3+j, value=formula)
    c_run.fill = fill(C_LIGHT_BLU)
    c_run.font = font(bold=True, size=10, color="000000")
    c_run.alignment = align("right")
    c_run.number_format = INR_FMT
    c_run.border = border("medium", C_BLUE)

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 3: EXPENSE LOG (actual spending tracker)
# ══════════════════════════════════════════════════════════════════════════════
ws3 = wb.create_sheet("🧾 Expense Log")
ws3.sheet_view.showGridLines = False

log_cols = [2, 16, 16, 16, 16, 20, 28, 3]
lc_widths = [2, 14, 20, 13, 13, 18, 14, 32, 3]
for i, w in enumerate(lc_widths, 1):
    set_col_width(ws3, i, w)

ws3.row_dimensions[1].height = 8
ws3.row_dimensions[2].height = 36
merge_header(ws3, 2, 2, 8, "🧾  ACTUAL EXPENSE LOG  |  Record Every Payment Here",
             bg=C_NAVY, fg=C_WHITE, size=14)
ws3.row_dimensions[3].height = 18
merge_header(ws3, 3, 2, 8,
             "Log every actual payment as you make it. Mark status as PAID. Keep receipts.",
             bg=C_BLUE, fg=C_WHITE, size=10, bold=False)
ws3.row_dimensions[4].height = 8

# Headers
ws3.row_dimensions[5].height = 30
log_headers = ["#", "Date Paid", "Expense Item", "Currency", "Amount (orig.)", "Amount (INR β‚Ή)", "Category", "Notes / Receipt #"]
log_bg = C_GREY_HDR
for j, h in enumerate(log_headers, 1):
    col_header(ws3, 5, j+1, h, bg=log_bg)

# Pre-fill known future payments as reminders
prefill = [
    ("", "Aug 2026", "IELTS Exam Fee",                  "INR", 16500,  "PLAB 1",  "Book at ieltsindia.org"),
    ("", "Aug 2026", "PLABABLE Subscription (Month 1)", "INR", 9720,   "PLAB 1",  "plabable.com"),
    ("", "Aug 2026", "PLAB 1 Study Books",              "INR", 4000,   "PLAB 1",  "OHCM + Oxford PLAB Handbook"),
    ("", "Sep 2026", "PLABABLE Subscription (Month 2)", "INR", 9720,   "PLAB 1",  ""),
    ("", "Oct 2026", "PLAB 1 Exam Fee",                 "GBP", 268,    "PLAB 1",  "gmc-uk.org/plab1 - book slot fast!"),
    ("", "Oct 2026", "PLABABLE Subscription (Month 3)", "INR", 9720,   "PLAB 1",  ""),
    ("", "Dec 2026", "PLAB 2 Prep Course (India)",      "INR", 35000,  "PLAB 2",  "Marrow/DAMS PLAB batch"),
    ("", "Jan 2027", "Return Flights (India-Manchester)","INR", 75000, "UK Trip",  "Book early for better price"),
    ("", "Jan 2027", "UK Accommodation (pre-booked)",   "INR", 54000,  "UK Trip",  "Airbnb or guesthouse near Manchester"),
    ("", "Feb 2027", "PLAB 2 Exam Fee",                 "GBP", 998,    "PLAB 2",  "gmc-uk.org/plab2"),
    ("", "Mar 2027", "GMC Full Registration",            "GBP", 405,    "GMC",     "After passing PLAB 2"),
    ("", "May 2027", "TB Test (UKVI clinic)",            "INR", 4000,   "Visa",    "UKVI-approved clinic only"),
    ("", "May 2027", "Biometric Appointment (VFS)",      "INR", 3000,   "Visa",    "VFS Global India"),
    ("", "May 2027", "Health & Care Visa Fee",           "GBP", 284,    "Visa",    "gov.uk/health-care-visa"),
    ("", "May 2027", "Immigration Health Surcharge 3yr", "GBP", 2328,   "Visa",    "Β£776/yr Γ— 3 = Β£2,328"),
    ("", "Jun 2027", "One-way Flight to UK",             "INR", 55000,  "Relocation","Final move to UK"),
]

for i, (num, date, item, curr, amt, cat, notes) in enumerate(prefill):
    r = 6 + i
    ws3.row_dimensions[r].height = 18
    bg = C_WHITE if i % 2 == 0 else C_GREY_LITE

    ws3.cell(row=r, column=2, value=i+1).alignment = align("center")
    ws3.cell(row=r, column=2).border = border()
    ws3.cell(row=r, column=2).fill = fill(bg)
    ws3.cell(row=r, column=2).font = font(size=9, color="888888")

    c_date = ws3.cell(row=r, column=3, value=date)
    c_date.fill = fill(C_INPUT_BG)
    c_date.font = font(color="0000FF", size=10)
    c_date.alignment = align("center")
    c_date.border = border()

    c_item = ws3.cell(row=r, column=4, value=item)
    c_item.fill = fill(bg)
    c_item.font = font(size=10)
    c_item.alignment = align()
    c_item.border = border()

    c_curr = ws3.cell(row=r, column=5, value=curr)
    c_curr.fill = fill(bg)
    c_curr.font = font(size=10, color="555555")
    c_curr.alignment = align("center")
    c_curr.border = border()

    c_amt = ws3.cell(row=r, column=6, value=amt)
    c_amt.fill = fill(C_INPUT_BG)
    c_amt.font = font(color="0000FF", size=10)
    c_amt.alignment = align("right")
    c_amt.number_format = '#,##0.00'
    c_amt.border = border()

    # INR equivalent
    c_inr = ws3.cell(row=r, column=7)
    if curr == "GBP":
        c_inr.value = f"=F{r}*'πŸ“Š Dashboard'!C6"
        c_inr.font = font(size=10, color="000000")
    else:
        c_inr.value = f"=F{r}"
        c_inr.font = font(size=10, color="000000")
    c_inr.fill = fill(bg)
    c_inr.alignment = align("right")
    c_inr.number_format = INR_FMT
    c_inr.border = border()

    c_cat = ws3.cell(row=r, column=8, value=cat)
    c_cat_colors = {"PLAB 1": C_LIGHT_BLU, "PLAB 2": C_LIGHT_PUR,
                    "UK Trip": C_LIGHT_AMB, "GMC": C_LIGHT_GRN,
                    "Visa": C_LIGHT_RED, "Relocation": C_GREY_LITE}
    c_cat.fill = fill(c_cat_colors.get(cat, C_WHITE))
    c_cat.font = font(size=10)
    c_cat.alignment = align("center")
    c_cat.border = border()

    c_notes = ws3.cell(row=r, column=9, value=notes)
    c_notes.fill = fill(bg)
    c_notes.font = font(size=9, color="555555", italic=True)
    c_notes.alignment = align("left", "center", wrap=True)
    c_notes.border = border()

# Add 20 blank rows for user entries
for i in range(20):
    r = 6 + len(prefill) + i
    ws3.row_dimensions[r].height = 18
    bg = C_WHITE if i % 2 == 0 else C_GREY_LITE
    for col in range(2, 10):
        c = ws3.cell(row=r, column=col)
        c.fill = fill(C_INPUT_BG if col in [3, 6] else bg)
        c.border = border()
        if col == 7:
            last_r = 6 + len(prefill) + i
            c.number_format = INR_FMT

# Running total at bottom
tot_r3 = 6 + len(prefill) + 20 + 1
ws3.row_dimensions[tot_r3].height = 24
merge_header(ws3, tot_r3, 2, 6, "TOTAL SPENT TO DATE (INR β‚Ή)", bg=C_GREEN, fg=C_WHITE, size=11)
c_total3 = ws3.cell(row=tot_r3, column=7,
                    value=f"=SUM(G6:G{tot_r3-1})")
c_total3.fill = fill(C_GREEN)
c_total3.font = font(bold=True, size=13, color=C_WHITE)
c_total3.alignment = align("right")
c_total3.number_format = INR_FMT
c_total3.border = thick_border(C_GREEN)

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 4: NOTES & TIPS
# ══════════════════════════════════════════════════════════════════════════════
ws4 = wb.create_sheet("πŸ“Œ Notes & Tips")
ws4.sheet_view.showGridLines = False
set_col_width(ws4, 1, 2)
set_col_width(ws4, 2, 30)
set_col_width(ws4, 3, 65)
set_col_width(ws4, 4, 3)

ws4.row_dimensions[1].height = 8
ws4.row_dimensions[2].height = 36
merge_header(ws4, 2, 2, 3, "πŸ“Œ  MONEY-SAVING TIPS & IMPORTANT NOTES",
             bg=C_NAVY, fg=C_WHITE, size=14)
ws4.row_dimensions[3].height = 8

tips = [
    ("PLAB 1 EXAM FEE", C_BLUE, C_LIGHT_BLU, [
        "Verify current fee at gmc-uk.org before booking. Fees updated every April.",
        "PLAB 1 can be sat in India - no need to fly to UK for Part 1.",
        "Book the MOMENT slots open - they sell out in minutes. Set phone alerts.",
        "Use the GMC's free PLAB 1 blueprint document to focus your revision.",
    ]),
    ("PLAB 2 EXAM FEE", C_PURPLE, C_LIGHT_PUR, [
        "PLAB 2 is ONLY in Manchester. You must travel to UK.",
        "Book your UK trip and exam slot simultaneously. Accommodation near the exam centre fills fast.",
        "Maximum 4 attempts. Each attempt costs Β£998 - make sure you are ready before booking.",
        "Factor in Β£1,500-2,500 total for the UK trip (flights + accommodation + food + course).",
    ]),
    ("IELTS / ENGLISH TEST", C_GREEN, C_LIGHT_GRN, [
        "IELTS Academic (not General Training) is required. Score valid for 2 years.",
        "Minimum: 7.5 overall, 7.0 in EACH band. All four bands must meet minimum.",
        "OET is an alternative - Grade B in all 4 sections. More expensive than IELTS.",
        "Book your test for September 2026 to have results before applying for PLAB 1.",
    ]),
    ("GMC REGISTRATION", C_AMBER, C_LIGHT_AMB, [
        "Start GMC identity verification NOW - it takes 4-8 weeks.",
        "Ensure your China MBBS institution is listed in the World Directory of Medical Schools (WDOMS).",
        "Keep certified copies of: MBBS certificate, transcripts, internship completion, ID, English test.",
        "GMC Full Registration fee (Β£405) is paid AFTER passing PLAB 2.",
    ]),
    ("HEALTH & CARE VISA", C_RED, C_LIGHT_RED, [
        "Doctors in the NHS qualify for the Health & Care Visa - cheaper and faster than Skilled Worker.",
        "TB test is mandatory for Indian applicants - must use a UKVI-approved clinic. Book early.",
        "Immigration Health Surcharge (IHS): Β£776/year. For 3 years = Β£2,328. Paid upfront with visa.",
        "Visa fee from India: Β£284 (Health & Care route). Processing: ~3 weeks standard.",
        "NEW from Jan 2026: B2 English evidence required with application. Factor extra 2-3 weeks.",
        "Priority service: 5 working days (+fee). Super-priority: next working day (+higher fee).",
        "You must apply within 3 months of your Certificate of Sponsorship (CoS) date.",
    ]),
    ("MONEY-SAVING HACKS", C_GREEN, C_LIGHT_GRN, [
        "Share UK accommodation with other PLAB 2 candidates - split costs to halve the expense.",
        "Book flights 3-4 months in advance for PLAB 2 UK trip (Jan 2027 target = book Sep 2026).",
        "Student/IMG discounts: Ask prep course providers - many offer group or early-bird rates.",
        "PLABABLE + Passmedicine: Consider doing one free trial first to see which you prefer.",
        "NHS relocation support: Some NHS trusts offer relocation packages (Β£1,000-5,000). Ask HR.",
        "Monzo/Wise card: Use for UK spending to avoid international transaction fees.",
    ]),
    ("EXCHANGE RATE RISK", C_GREY_HDR, C_GREY_LITE, [
        "GBP/INR rate fluctuates. This tracker uses a fixed rate (enter your rate on Dashboard).",
        "As of mid-2026: approx Β£1 = β‚Ή107-110. Budget conservatively at β‚Ή112-115.",
        "Pay GBP fees via Wise or HDFC Forex card for best rates.",
        "Avoid airport/hotel money exchange - rates are 5-8% worse.",
    ]),
]

row = 4
for tip_title, hdr_color, bg_color, tip_list in tips:
    ws4.row_dimensions[row].height = 22
    merge_header(ws4, row, 2, 3, tip_title, bg=hdr_color, fg=C_WHITE, size=10)
    row += 1
    for tip in tip_list:
        ws4.row_dimensions[row].height = 18
        c1 = ws4.cell(row=row, column=2, value="β€’")
        c1.fill = fill(bg_color)
        c1.font = font(bold=True, size=12, color=hdr_color)
        c1.alignment = align("center", "top")
        c1.border = border()

        c2 = ws4.cell(row=row, column=3, value=tip)
        c2.fill = fill(bg_color)
        c2.font = font(size=10)
        c2.alignment = align("left", "top", wrap=True)
        c2.border = border()
        row += 1
    row += 1  # spacer

# ── SAVE ──────────────────────────────────────────────────────────────────────
out = "/tmp/workspace/plab-schedule/PLAB_Budget_Tracker_2026_2027.xlsx"
wb.save(out)
print(f"Saved: {out}")
Running Command

cd /tmp/workspace/plab-schedule && python3 build_budget.py 2>&1

Editing File

~/plab-schedule/build_budget.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/plab-schedule/build_budget.py'
Running Command

cd /tmp/workspace/plab-schedule && python3 build_budget.py 2>&1

Running Command

ls -lh /tmp/workspace/plab-schedule/PLAB_Budget_Tracker_2026_2027.xlsx

Your budget tracker is ready. Download it here:

PLAB Budget Tracker 2026 2027

Spreadsheet Β· XLSX


What's Inside - 4 Sheets

πŸ“Š Sheet 1 - Dashboard

The main control panel:
  • GBP β†’ INR rate input (blue cell, change anytime - all conversions auto-update)
  • Contingency buffer % (set to 15% by default, adjustable)
  • Complete cost summary table with all 17 expense items
  • % of total column so you see where most money goes
  • Status dropdown per row (Pending / Paid / Budgeted / N/A) - update as you pay
  • Grand Total with contingency baked in
  • Progress tracker - how much paid vs remaining

πŸ“… Sheet 2 - Monthly Cashflow

When each rupee needs to leave your wallet, month by month from Aug 2026 to Jun 2027:
  • Every expense mapped to the month it falls due
  • Section subtotals (PLAB 1, PLAB 2, UK Trip, GMC, Visa, Relocation)
  • Monthly grand total row - see your heaviest spending months at a glance
  • Running cumulative row - total cash needed by any given month

🧾 Sheet 3 - Expense Log

Your actual payment diary:
  • 16 rows pre-filled with all expected payments, dates, and amounts
  • 20 blank rows to add your own entries
  • GBP amounts auto-convert to INR using the Dashboard rate
  • Category colour-coding (blue = PLAB 1, purple = PLAB 2, red = Visa, etc.)
  • Running total at the bottom

πŸ“Œ Sheet 4 - Notes & Tips

8 categories of money-saving tips covering:
  • When/where to book exams for best price
  • IHS and visa fee breakdown
  • Exchange rate risk management
  • NHS relocation support you can claim
  • How to use Wise/Monzo to save on currency conversion

Budget Snapshot (at Β£1 = β‚Ή108)

CategoryEst. Cost (INR)
PLAB 1 (exam + prep)~β‚Ή70,000
PLAB 2 (exam + prep)~β‚Ή1,60,000
UK Trip (flights + stay + course)~β‚Ή2,20,000
GMC Registration~β‚Ή55,000
Visa (fee + IHS 3yr + TB + biometric)~β‚Ή3,00,000
Relocation to UK~β‚Ή1,60,000
Base Total~β‚Ή9,65,000
+ 15% Contingency~β‚Ή1,45,000
Grand Totalβ‚Ή11,10,000 (Β£10,300)
The biggest single cost is the Immigration Health Surcharge (β‚Ή2.5L+ for 3 years) - plan for that specifically. Everything else is manageable if spread across the 11-month timeline.

I took ielts exam and got 6 band overall, now i am preparing for OET how should I prepare and ace the exam ? I am thinking to taking September oet exam

Searching the Web

OET exam 2026 format scoring grade B requirements doctors preparation tips

Searching the Web

OET September 2026 exam dates registration deadlines

Here is your complete OET preparation plan:

OET Preparation Guide - IELTS 6 β†’ OET Grade B

First: Understand Why OET is Easier for You as a Doctor

OET Medicine is entirely medical in content. Every listening passage, reading text, writing task, and speaking roleplay is clinical. Your existing MBBS knowledge is a massive advantage - you already know the vocabulary, scenarios, and clinical reasoning. You just need to learn how OET tests these skills and practice the format.
Your IELTS 6 tells me:
  • Your English foundation is there but needs polishing in at least 2-3 bands
  • Likely weak areas: Writing structure and either Listening or Speaking
  • OET scoring (350/Grade B in each) is very achievable in 5-6 weeks with focused prep

September 2026 Exam Dates in India

Based on current data, September 2026 OET dates in India are:
  • 5 September 2026
  • 18 September 2026
  • 19 September 2026
Registration deadline: Typically 3-4 weeks before the test date. For the 5 September sitting, the deadline is likely around 8-12 August 2026 - that is THIS WEEK or next week. Register immediately at oet.com.
Recommendation: Target the 18 or 19 September 2026 sitting to give yourself maximum preparation time (7 weeks from today). The registration deadline for those dates would be around 21-25 August 2026.

OET Format - Know This Cold

Sub-testDurationFormatTarget Score
Listening45 min3 parts - consultations, monologues, dialogues350 (Grade B)
Reading60 min3 parts - medical texts, skim/scan/MCQ350 (Grade B)
Writing45 min1 referral/discharge letter from case notes350 (Grade B)
Speaking20 min2 clinical roleplays (doctor-patient)350 (Grade B)
Score scale: 0-500 per sub-test. Grade B = 350-440. All four must hit 350 in a single sitting - no banking of individual scores. Results: 10-16 working days after exam.

Your 6-Week Prep Plan (Aug 1 - Sep 19, 2026)

Week 1 (Aug 1-7) - Format Familiarisation + Diagnostic

Priority: Understand what Grade B actually looks like before studying.
  • Download the free OET sample tests from oet.com (official site) - do one full test untimed first to see where you are
  • Watch the OET official "How to Prepare" videos on YouTube (OET's own channel)
  • Buy or subscribe to OET Practice or E2Language OET - these are the two best prep platforms
  • Study the OET Medicine Writing Criteria sheet (free download from oet.com) - understand what assessors mark
Diagnostic test checklist:
  • Which sub-tests scored well?
  • Did you finish on time?
  • In Writing: Was your letter clear, well-organized, correct in register?
  • In Speaking: Were you empathetic? Did you stay in the doctor role?

Week 2 (Aug 8-14) - Writing Deep Dive

Writing is the sub-test that fails most IMGs. Tackle it first.
What the Writing task is: You are given a case note (patient scenario, history, medications, test results) and must write a referral letter, discharge letter, or transfer letter. 45 minutes total (5 min reading + 40 min writing).
The 5 OET Writing Criteria (memorise these):
  1. Purpose - Is the purpose of the letter clear from the opening?
  2. Content - Have you included all relevant information and excluded irrelevant detail?
  3. Conciseness & Clarity - Is it appropriately concise? No waffle or repetition.
  4. Genre & Style - Formal letter format, appropriate medical register
  5. Organisation & Layout - Logical paragraph structure, clear signposting
Daily Writing Practice (Week 2):
  • Write 1 letter per day from OET practice case notes
  • Do NOT just write and move on - self-assess against the 5 criteria above
  • Get feedback: Post your letters in OET Medicine Facebook groups or use E2Language's writing correction service
  • Key formula for an OET letter:
    1. Opening: "I am writing to refer/request/inform you regarding [Patient Name, Age], who presented with..."
    2. Relevant history + examination findings
    3. Investigations and results
    4. Management so far
    5. Request/reason for referral + what you need from recipient
    6. Sign-off
Common mistakes to avoid:
  • Copying case notes word-for-word (you must paraphrase and organise)
  • Including irrelevant information (test specifically checks if you select appropriately)
  • Using informal language ("the patient is feeling bad") vs medical register ("the patient reports worsening dyspnoea")
  • Forgetting the patient's name, DOB, or key identifiers in the letter

Week 3 (Aug 15-21) - Speaking Mastery

Speaking is your biggest advantage as a doctor - you do clinical consultations every day. You just need to adapt your style.
The 2 Roleplays (5 min each + 3 min prep per card):
  • You are always the doctor/healthcare professional
  • The assessor plays the patient, relative, or carer
  • You get a role card with: setting, patient details, what you need to do
The 9 OET Speaking Criteria:
  1. Intelligibility (can they understand you?)
  2. Fluency
  3. Appropriateness of language
  4. Relationship building (empathy, rapport - this is heavily weighted)
  5. Understanding & incorporating the patient's perspective (ICE - Ideas, Concerns, Expectations)
  6. Providing structure (organised, logical conversation)
  7. Information gathering (asking right questions)
  8. Information giving (explaining clearly in lay terms)
  9. Managing sequencing of topics
The #1 mistake Indian/Chinese doctors make in OET Speaking: Being too task-focused and clinical. OET wants you to slow down, listen, empathise, and respond to the patient as a human. Don't just fire questions.
Golden phrases to use:
  • "That must have been really difficult for you..."
  • "I completely understand your concern about..."
  • "Before I explain the treatment, could you tell me what you already know about...?"
  • "Is there anything specific you were worried about?"
  • "Does that make sense? Do you have any questions?"
Practice method:
  • Find a study partner (another PLAB/OET candidate) and roleplay 2 scenarios per day
  • Record yourself on your phone - play it back and listen critically
  • Time yourself: 5 minutes goes fast - practice managing the clock
  • Join OET Speaking practice groups on Telegram/WhatsApp - search "OET Medicine speaking India 2026"

Week 4 (Aug 22-28) - Listening Strategy

The 3 Listening Parts:
  • Part A: Consultation between doctor and patient - you fill in a form/case notes (most predictable, practice this most)
  • Part B: 6 short workplace recordings (ward rounds, briefings) - short-answer questions
  • Part C: 2 longer talks (lecture, interview) - MCQ and short answer
Key strategies:
  1. Part A is your friend - The recording follows a consultation structure you know. Before it plays, scan the form. You know what will come: presenting complaint β†’ history β†’ examination β†’ plan. Anticipate.
  2. Medical spelling counts - If you write "myocardiall infarction" instead of "myocardial infarction", you lose the mark. Drill medical spelling daily.
  3. Synonyms are tested - The recording might say "the patient has been experiencing chest tightness" but the answer they want is "chest pain" or vice versa. Listen for paraphrasing.
  4. Part C requires predicting argument structure - Talks often follow: problem β†’ cause β†’ solution. Understand the logic, not just the words.
Daily practice: 1 full Listening test per day (45 min). Review every wrong answer - understand WHY you missed it.

Week 5 (Aug 29 - Sep 4) - Reading Speed + Full Mocks

The 3 Reading Parts:
  • Part A: 4 short texts on same topic - fast matching, 15 minutes (very time-pressured)
  • Part B: 6 short workplace texts - MCQ, understand purpose/tone
  • Part C: 2 longer academic texts - detailed comprehension, MCQ
The Reading trap: Part A kills time. You have only 15 minutes for Part A. Do NOT read everything - skim the question first, then scan the text for the answer. This is a skill that requires specific practice.
IELTS 6 β†’ OET Reading: Your IELTS reading skills transfer well here. The main difference is all texts are medical. The vocabulary will be familiar to you as a doctor, which is a big advantage over general test-takers.
Week 5 focus:
  • 1 timed full mock every other day (all 4 sub-tests in a single session)
  • Track your scores in a notebook: are all 4 consistently near 350?
  • Identify your weakest sub-test and dedicate extra time to it
  • Practice Part A under strict 15-minute limit every single day

Week 6 (Sep 5-19) - Final Polish + Exam Week

Days 1-7 (Sep 5-11):
  • 1 full mock every 2 days
  • Fix only your weakest remaining areas
  • Write 1 letter every day - aim for a perfect 450 letter with timing
  • Do 2 speaking roleplays daily with a partner
  • Review your medical spelling list
Days 8-12 (Sep 12-16):
  • Light practice only - 1 sub-test per day
  • Focus on exam-day strategy: timing, pacing, staying calm
  • Re-read the OET Writing criteria one more time
  • Speaking: record your final two roleplays and listen back
Exam days (Sep 18/19):
  • Listening + Reading + Writing typically on the same day (computer or paper)
  • Speaking usually on a different day or the day before/after
  • Arrive 30 min early. Bring passport (only accepted ID).
  • For Speaking: warm up your voice that morning, do 1 quick roleplay with yourself

Sub-test Priority for You (Based on IELTS 6)

Sub-testYour Likely GapPriorityTime Needed
WritingFormat & registerπŸ”΄ Highest2 hrs/day in Week 2
SpeakingEmpathy style🟠 High1 hr/day from Week 3
ListeningMedical vocab in audio🟑 Medium1 hr/day from Week 4
ReadingPart A timing🟒 Lower45 min/day from Week 5

Best Resources (Ranked)

ResourceWhat ForCost
oet.com - Official Sample TestsFree diagnostic + format familiarityFree
E2Language OETAll 4 sub-tests, writing correction~β‚Ή6,000-8,000/month
OETpractice.netFull mock tests, Writing feedback~β‚Ή3,500/month
OET official YouTube channelFree video explanationsFree
"OET Medicine" Facebook groupsWriting feedback, speaking partnersFree
Telegram: OET India 2026 groupsSpeaking practice partnersFree
Swoosh EnglishWriting correction servicePer letter
Do NOT use general IELTS prep materials for OET - the formats are completely different. OET-specific resources only.

Key Differences from IELTS (Critical to Know)

AspectIELTSOET
ContentGeneral English100% medical/clinical
Writing taskEssay or letterClinical referral letter only
SpeakingGeneral conversation topicsClinical roleplays
ListeningNews, education, generalClinical consultations
ReadingAcademic/general articlesMedical journal texts
Your advantageGeneral knowledge neededYour MBBS = massive edge

The One Thing That Will Make or Break Your OET

Writing and Speaking are profession-specific and subjectively marked. They require you to think like a UK clinician, not just demonstrate English. The biggest shift is this:
In IELTS, you demonstrate language ability. In OET, you demonstrate professional communication as a doctor. Empathy, patient-centredness, appropriate register, and clinical organisation are all being assessed simultaneously with your grammar and vocabulary.
Spend the most time on Writing (practice letters daily and get them corrected) and Speaking (roleplay with a partner every day). These two sub-tests are where Indian-trained doctors most commonly drop below 350, and they are also the most improvable with targeted practice.
Register immediately for the September 18/19 sitting at oet.com - the deadline is likely around August 21-25, and you are cutting it close. Do that today.

My ielts band was W: 6.5 L: 6.5 R: 5.5 S: 6 Overall: 6 How to improve for OET ?

This is very useful data. Let me give you a precise diagnosis before anything else.

Reading Your IELTS Scores

Sub-testYour ScoreIELTS 7.5 TargetGapOET Equivalent
Writing6.57.0-0.5Moderate gap
Listening6.57.0-0.5Moderate gap
Reading5.57.0-1.5Biggest gap
Speaking6.07.0-1.0Clear gap
Your Reading (5.5) is the critical problem. That is 1.5 bands below IELTS requirement. Everything else is within striking distance, but Reading needs the most work.
The good news: OET Reading uses 100% medical content. As an MBBS doctor, you already know the vocabulary in every passage - this alone will push your effective reading ability up significantly compared to IELTS general texts.

Exact IELTS β†’ OET Translation for Your Profile

βœ… Writing (IELTS 6.5) - You are close

Your IELTS 6.5 Writing means:
  • You can write in structured English with generally correct grammar
  • You have minor errors in sentence complexity, cohesion, and vocabulary range
  • You lose marks on "lexical resource" and "coherence"
In OET Writing, this translates to: You can likely write a decent letter but may struggle with:
  • Appropriate medical register (formal clinical language)
  • Selecting only relevant information from case notes (conciseness)
  • Letter structure and professional tone
Gap to close: Small. 3-4 weeks of daily practice should get you to 350+. Your IELTS writing base is solid.

βœ… Listening (IELTS 6.5) - You are close

Your IELTS 6.5 Listening means:
  • You follow general English well but miss details in fast/accented speech
  • You likely miss answers when there are distractors ("at first he thought it was X but actually it was Y")
  • You may struggle with unfamiliar vocabulary in general topics
In OET Listening, this translates to: Strong improvement expected because:
  • All audio is medical consultations - you know the vocabulary already
  • The flow is predictable: history β†’ examination β†’ plan (you do this every day)
  • No more "a woman describes her holiday in Norway" style content
Gap to close: Small to none. Medical content will naturally push this to 350+. Focus only on the OET-specific format.

πŸ”΄ Reading (IELTS 5.5) - Priority #1

Your IELTS 5.5 Reading means:
  • You are slow under timed conditions
  • You read every word instead of skimming/scanning
  • You struggle with inference questions and paraphrased answers
  • You likely run out of time, especially in the last passage
This is your most urgent problem. However:
  • OET Reading texts are all medical - journals, patient information, clinical guidelines
  • You already understand the content conceptually
  • The challenge is pure reading speed and technique, not comprehension
Gap to close: Significant, but medical content helps a lot. Technique training over 4-5 weeks can bridge this.

🟠 Speaking (IELTS 6.0) - Needs targeted work

Your IELTS 6.0 Speaking means:
  • You can maintain conversation but with noticeable pauses, hesitation, or limited vocabulary
  • You may struggle to develop ideas at length
  • Pronunciation affects clarity at times
  • You speak in shorter, simpler sentences under pressure
In OET Speaking, this translates to: Your clinical knowledge helps enormously, but:
  • The empathy and patient-centredness scoring is new for you
  • You need to speak more naturally and less mechanically
  • You need to actively manage the 5-minute consultation structure
Gap to close: Moderate. The doctor roleplay format is more natural for you than IELTS topics. With daily practice you can hit 350.

Your Personalised 6-Week Action Plan

Based on your exact scores, here is your priority order:
PRIORITY 1: Reading (5.5) β€” Technique overhaul needed
PRIORITY 2: Speaking (6.0) β€” Clinical empathy + fluency
PRIORITY 3: Writing (6.5) β€” Format + register only
PRIORITY 4: Listening (6.5) β€” Format familiarity only

READING - Specific Fix for 5.5

Why you scored 5.5: Almost certainly time. You are reading too slowly and running out before the last passage.
The 3 techniques you must drill:
1. Skimming (read in 90 seconds):
  • Read the title, first sentence of each paragraph, last sentence of last paragraph
  • Do NOT read every word
  • Goal: understand the overall structure and main idea only
  • Practice: Take any medical journal abstract. Set a 60-second timer. Read it. Close it. Write what it was about in 2 sentences.
2. Scanning (find a specific word/number in 20 seconds):
  • Your eyes move like a search engine - you are looking for ONE thing
  • Numbers, names, specific terms - these are easy to spot visually
  • Practice: Open BNF online. Search for "paracetamol dose in renal failure" by scanning only. Time yourself.
3. OET Part A (15 minutes - the killer):
  • 4 short texts, all on the same medical topic
  • Questions ask which text (A/B/C/D) contains a specific piece of information
  • Strategy: Read all 4 texts in 3 minutes total (skim only). Then match questions to texts. Never re-read a full text - only scan.
  • This is entirely different from IELTS reading and must be practiced as a separate skill.
Daily Reading practice (30-45 min/day):
DayActivityTime
MonOET Part A under 15 min strict20 min
TueRead 1 medical journal abstract β†’ answer 5 questions you write yourself30 min
WedOET Part B practice test20 min
ThuOET Part A again - beat your Monday time20 min
FriFull OET Reading test timed (60 min)65 min
SatReview wrong answers in detail30 min
SunLight: read 2 medical news articles (BBC Health, BMJ news)20 min
Free medical reading materials:

SPEAKING - Specific Fix for 6.0

Why you scored 6.0: Limited fluency, simpler vocabulary, shorter responses. In IELTS you struggled to develop abstract topics. In OET this is largely solved because you ARE an expert in the content.
The 3 things OET Speaking tests that IELTS doesn't:
1. Empathy and rapport building (heavily weighted) The assessor plays a worried patient. You must acknowledge their emotions BEFORE solving their problem.
❌ Wrong (what most doctors do): "So you have chest pain. When did it start? Is it sharp or dull?"
βœ… Right (what OET wants): "I can see you're quite worried about this. That's completely understandable. Before we go through everything, could you tell me a bit about what's been happening?"
Practice this sentence opener every morning: "I can see/hear/understand that [patient emotion]. That must be really [difficult/worrying/frustrating] for you. I want to make sure I fully understand your situation..."
2. Explaining in lay language You must explain medical things in patient-friendly terms.
❌ "You have left ventricular hypertrophy secondary to hypertension" βœ… "Your heart has had to work harder than normal because of your high blood pressure, and over time this has made the heart muscle thicker on one side"
Daily practice: Every day, pick one medical condition and explain it out loud in simple terms as if to a worried patient. Time yourself for 2 minutes.
3. Managing the consultation structure in 5 minutes
  • Minute 1: Open warmly, establish rapport, let patient speak
  • Minutes 2-3: Gather information / give explanation (the clinical content)
  • Minute 4: Address patient's concern directly (ICE - Ideas, Concerns, Expectations)
  • Minute 5: Plan, safety-net, close warmly ("Do you have any questions? Please don't hesitate to come back if you're worried about anything")
Speaking improvement schedule:
  • Record yourself doing 1 roleplay every day (use your phone)
  • Listen back the same evening - note where you hesitated, rushed, or missed empathy
  • Find a study partner (Telegram/WhatsApp OET groups in India) - do 2 roleplays together daily
  • Watch videos of UK GP consultations on YouTube - notice the tone, pace, and language they use

WRITING - Specific Fix for 6.5

You are close here. The fix is mostly about format, not language ability.
The exact OET letter structure to memorise:
[Hospital/Clinic Name]
[Date]

Dear Dr [Name] / To Whom It May Concern,

Re: [Full patient name], [Age], [DOB]

I am writing to refer/inform/request regarding the above-named 
patient who [one-line presenting situation].

[Paragraph 1 - Background & History]
[Relevant history only - no waffle. Past medical history, 
medications, allergies if relevant]

[Paragraph 2 - Current Presentation & Findings]
[What brought them in now. Examination findings. Test results.]

[Paragraph 3 - Management to Date]
[What you have done so far. Medications started. Investigations 
requested.]

[Paragraph 4 - Request / Reason for Referral]
[Specific, clear request. What you need from the recipient.]
[Any urgency flag if needed.]

Yours sincerely,
Dr [Your Name]
[Designation]
The #1 writing mistake that drops you below 350: Copying sentences directly from the case notes. The assessor marks you down heavily for this. You must paraphrase and organise - not transcribe.
Daily writing (Week 2-5):
  • Write 1 letter per day from OET practice case notes
  • Time yourself: complete in 40 minutes
  • Check each letter against the 5 criteria (Purpose, Content, Conciseness, Genre/Style, Organisation)
  • Get at least 5 letters corrected by someone (E2Language correction service, or OET Facebook group)

LISTENING - Specific Fix for 6.5

You are the closest to Grade B here. The fix is only format familiarity.
The OET Listening advantage you have: Your IELTS 6.5 was on completely general content. In OET you will hear:
  • Doctors discussing patient cases (you understand this perfectly)
  • Clinical consultations with familiar vocabulary
  • Medical lectures and conferences (content you know)
Only 3 things to practice for Listening:
1. Medical spelling under pressure Assessors do penalise spelling errors in written answers. Make a list of 50 commonly misspelled medical terms and drill them: hypertension, haemorrhage, dyspnoea, tachycardia, arrhythmia, pneumonia, diarrhoea, anaesthesia, oedema, haematuria (note: UK spelling, not US)
2. Listening for paraphrase The recording says one thing, the answer on the sheet uses different words. Practice catching these equivalences. Example: Recording says "the patient was becoming breathless climbing stairs" β†’ answer sheet says "exertional dyspnoea"
3. Part A prediction Before the consultation recording plays, you get 30 seconds to read the form. Use this time to predict: What is likely to come up? (Chief complaint β†’ duration β†’ severity β†’ associated symptoms β†’ past history β†’ medications β†’ plan)

5-Week Daily Study Schedule

WeekMain FocusDaily TimeKey Task
Week 1Diagnostic + Format2 hrsTake 1 full OET mock test (untimed), identify exact weak spots
Week 2Writing only2.5 hrsWrite 1 letter/day, get 3 corrected externally
Week 3Speaking + Reading Part A2.5 hrs1 roleplay/day recorded + 15-min Part A timed daily
Week 4Full timed mocks3 hrsFull mock every 2 days, track scores
Week 5Weak spot targeting2 hrsFinal polish on your 1-2 lowest sub-tests, 3 full mocks
Exam weekLight revision1 hrSpeaking warmup, 1 letter, rest

Realistic Score Projection

Based on your IELTS bands and the OET medical content advantage:
Sub-testIELTS EquivalentOET Projection (with prep)Grade B (350)
Listening6.5370-400βœ… Likely
Reading5.5330-370⚠️ Risk area
Writing6.5350-390βœ… Achievable
Speaking6.0350-380βœ… Achievable
Reading is your only genuine risk. If you drill Part A technique specifically and practice timed reading every day, you will cross 350. But if you go in unprepared on technique, this is where you could fail and have to resit the entire exam.
Spend 40% of your study time on Reading.

Action Items for Today

  1. Register for September 18/19 OET exam at oet.com - deadline approaching
  2. Download free OET sample tests from oet.com immediately - do Part A today (15 minutes, timed)
  3. Subscribe to E2Language or OETpractice.net - choose one, start tomorrow
  4. Start a medical spelling list - 10 words per day, UK spelling
  5. Find a speaking partner - search Telegram for "OET Medicine India 2026"
You have everything you need to pass this in September. Reading technique is the only real barrier - attack it daily and you will be fine.

I find problem with listening: mcq Reading: mcq Writing: sometime can able to assemble the idea for writing medical history Speaking: i find ok but need polishing

Good - now I have a very precise picture. Let me tackle each problem exactly.

Problem 1: Listening MCQ (Parts B & C)

MCQ in OET Listening appears in Part B (6 short workplace recordings) and Part C (2 longer talks). This is where most people lose marks because the questions are designed to trap you.

Why You Are Getting MCQ Wrong

There are only 3 reasons people fail OET Listening MCQ:
Reason 1 - The Distractor Trap OET deliberately mentions all 3 answer options in the recording. The wrong answers are mentioned first, the correct answer comes later or is implied.
Example recording: "Initially we considered prescribing metformin, and lisinopril was also discussed, but given her renal function, we ultimately decided on amlodipine."
Question: What medication was prescribed?
  • A) Metformin ← mentioned, wrong
  • B) Lisinopril ← mentioned, wrong
  • C) Amlodipine ← correct
Fix: Never mark an answer the moment you hear it mentioned. Wait until the speaker concludes or uses final language: "ultimately", "in the end", "we decided", "the plan was", "finally".

Reason 2 - Missing the Main Point Part C recordings are medical talks/lectures. The MCQ tests the main argument or conclusion, not specific details. You might catch all the facts but miss the overall point.
Example question: "What is the speaker's main concern about the new protocol?" The correct answer is the conclusion the speaker builds to - not a fact mentioned midway.
Fix: For Part C, listen for signpost language that signals the main point:
  • "The key issue here is..."
  • "What this means for clinical practice is..."
  • "The most important takeaway..."
  • "In conclusion / To summarise..."
  • "What concerns me most about this is..."
Write these down and train yourself to perk up whenever you hear them.

Reason 3 - Paraphrase Mismatch The question says "patient compliance" - the recording says "whether patients follow the treatment plan." You think it's a different topic but it's the same answer.
Fix: Before the recording starts, read each MCQ option and quickly note a synonym in your head:
  • "compliance" = following treatment / adherence / taking medications as prescribed
  • "deterioration" = getting worse / decline / worsening
  • "prognosis" = outlook / likely outcome / what will happen

Listening MCQ - 15-Minute Daily Drill

Do this every day for 3 weeks:
Step 1 (2 min): Before playing any audio, read the question and all 3 options. Underline the KEY WORD in each option (usually a noun or verb). Write one paraphrase for each option in the margin.
Step 2 (during audio): Do NOT write your answer until the speaker finishes or you hear conclusion language. Mark your answer in pencil only.
Step 3 (after each question): Check answer. If wrong, replay that section ONLY and find the exact moment you were misled. Write down what distractor word caught you.
Step 4 (weekly): Count your distractor traps. If the same type keeps catching you (e.g., "first option mentioned"), that is your specific pattern to fix.
Free resources:

Problem 2: Reading MCQ (Parts B & C)

OET Reading MCQ appears in Part B (6 short texts - workplace notices, memos, guidelines) and Part C (2 long academic medical articles).

Why You Are Getting MCQ Wrong

The core problem in Reading MCQ is almost always one of these:

Problem 2A - Reading the whole text then the question (too slow)
Most people read the passage first, then go to the question. This wastes time and causes confusion because you try to remember everything.
Fix - Question First Method:
  1. Read the question stem ONLY (not the options yet)
  2. Identify the key words in the question
  3. Scan the text for those key words or their synonyms
  4. Read ONLY that sentence + 1 sentence before and after it
  5. NOW look at the options and eliminate
This cuts your reading time by 40-50% per question.

Problem 2B - "All of the above" / "Not mentioned" traps
OET Part B often asks: "What is the purpose of this notice?" or "Which statement is TRUE according to the text?"
The trap: one option looks correct but is actually slightly more extreme than what the text says.
Text says: "Studies suggest that early mobilisation may reduce recovery time." Wrong option: "Early mobilisation definitely reduces recovery time." Correct option: "Some evidence supports early mobilisation in reducing recovery time."
Fix: Watch for absolute words in options - "always", "never", "definitely", "all patients", "completely." These are almost always wrong in OET. The correct answer usually has hedging language - "may", "some", "in certain cases", "evidence suggests."

Problem 2C - Part C inference questions
These are the hardest. The question asks what the author implies or what can be inferred - the answer is NOT stated directly.
Example: Author writes: "Despite extensive funding and three decades of research, a reliable biomarker for early Alzheimer's detection remains elusive."
Question: "What does the author suggest about Alzheimer's research?" Answer: Research has not achieved its goal despite significant investment - i.e., progress has been disappointing.
This is nowhere in the text word-for-word. You have to reason.
Fix - The 3-step inference method:
  1. Find the relevant section (scan for key words from the question)
  2. Ask: "What is the author's ATTITUDE here - positive, negative, cautious, critical?"
  3. Match that attitude to the correct option
The correct inference option will:
  • Match the author's tone (critical/optimistic/cautious)
  • Be supported by evidence in the passage
  • Not go further than what the text implies

Reading MCQ - Daily Fix Plan

DayActivityTime
DailyPractice Question First Method on 3 MCQ questions15 min
Every 2 daysFull Part C passage with all questions, timed25 min
WeeklyReview every wrong answer - label it: Distractor? Absolute word? Inference?20 min
Track your error types. After 2 weeks you will see a pattern - if 80% of your wrong answers are inference questions, you now know exactly what to fix.

Problem 3: Writing - Can't Assemble Ideas for Medical History

This is the most common problem for IMGs and it has a very specific cause and fix.

Why This Happens

OET Writing gives you messy, unorganised case notes that look like this:
Pt: James White, 54M
CC: chest pain x 3 days
PMH: T2DM (metformin 500mg BD), HTN (amlodipine 5mg), ex-smoker 10yrs
Presenting: central crushing CP radiating L arm, diaphoresis, 
SOB. No fever. No cough.
Ix: ECG - ST elevation V1-V4. Troponin - elevated (x3 ULN)
Mx: Aspirin 300mg, GTN SL. Referred cardiology. 
Pt anxious re: diagnosis.
Rx: needs urgent PCI assessment
And you have to turn this into a professional, flowing referral letter.
The problem is not your English - it is that you are trying to write and organise at the same time, which causes a mental block.

The 3-Step Assembly Framework

Use this every single time. Never skip steps.

Step 1: SORT the case notes (3 minutes)

Before writing a single word, take your reading time (5 min) and physically categorise every piece of information into one of 5 buckets:
BucketWhat Goes Here
A - WhoPatient name, age, DOB
B - Why referredReason for referral / what you need from recipient
C - HistoryPMH, medications, allergies, social history
D - NowCurrent presentation, examination findings, test results
E - DoneManagement already given, investigations pending
With the example above:
  • A: James White, 54M
  • B: Needs urgent PCI assessment (cardiology referral)
  • C: T2DM on metformin, HTN on amlodipine, ex-smoker
  • D: Central crushing chest pain 3 days, radiation to L arm, diaphoresis, SOB. ECG: ST elevation V1-V4. Troponin elevated.
  • E: Aspirin 300mg given, GTN SL given
Now you have organised material. Writing becomes assembly, not composition.

Step 2: FILTER (1 minute)

Cross out anything NOT relevant to the referral purpose. If referring to cardiology for chest pain, the fact that he's an ex-smoker for 10 years IS relevant. His anxiety is relevant to note. His metformin is relevant (diabetes increases cardiac risk). Nothing is irrelevant here - but in other cases you will have decoy information that the assessor is testing you to exclude.
OET assessors specifically mark you down for including irrelevant information. It shows poor clinical judgment.

Step 3: WRITE using the fixed template (36 minutes)

Dear Dr [Name / Colleague],

Re: Mr James White, 54 years, DOB: [if given]

I am writing to urgently refer the above-named patient, who 
presented today with a 3-day history of chest pain, for urgent 
assessment and consideration of percutaneous coronary intervention.

[Background]
Mr White has a background of type 2 diabetes mellitus, currently 
managed with metformin 500mg twice daily, and hypertension, for 
which he takes amlodipine 5mg daily. He is an ex-smoker, having 
ceased 10 years ago.

[Current Presentation]
He presented with a 3-day history of central crushing chest pain 
radiating to the left arm, associated with diaphoresis and 
shortness of breath. There was no fever or cough. 

Investigations revealed ST elevation in leads V1-V4 on 
electrocardiography, with troponin elevated at three times the 
upper limit of normal, consistent with an anterior ST-elevation 
myocardial infarction.

[Management to Date]
He has been administered aspirin 300mg and sublingual 
glyceryl trinitrate. The patient is understandably anxious 
regarding his diagnosis.

[Request]
I would be grateful for your urgent assessment and 
consideration of primary PCI. Please do not hesitate to 
contact me should you require any further information.

Yours sincerely,
Dr [Name]
Notice what happened: the case notes were messy abbreviations. The letter is clear, professional, and flowing. That transformation is the skill - and it is 100% learnable.

The 3 Language Patterns to Memorise for Writing

These phrases do the heavy lifting. Use them as your skeleton:
For history:
  • "Mr X has a background of..."
  • "His past medical history is notable for..."
  • "He has been managed with [drug] for [condition] since [time]."
For current presentation:
  • "He presented with a [duration] history of [complaint]."
  • "On examination, [finding] was noted."
  • "Investigations revealed..." / "Blood tests demonstrated..."
For management:
  • "He was commenced on / administered..."
  • "A [test] has been requested and is pending."
  • "The patient has been informed of the diagnosis and is [worried / understanding / agreeable]."
For the request:
  • "I would be grateful for your assessment and further management."
  • "I would appreciate your urgent review / your expert opinion regarding..."
  • "Please do not hesitate to contact me should you require further information."

Writing Practice Method

Week 1-2: Use the Sort β†’ Filter β†’ Write method on EVERY practice letter. Time yourself strictly - 5 min notes review, 3 min sorting, 2 min filtering, 35 min writing.
Week 3+: Aim to sort and filter in your head (without writing buckets) - it becomes automatic.
Get external feedback on at least 5 letters. You cannot self-assess Writing effectively because you don't notice your own patterns. Options:
  • E2Language writing correction (~Β£8-10 per letter)
  • OET Medicine Facebook group (post anonymised letters, request feedback)
  • Find a PLAB/OET study group partner who is also doing Writing

Problem 4: Speaking - Needs Polishing

Since you feel relatively OK here, the polishing is about 3 specific things:

Polish Point 1: The Opening 30 Seconds

This is marked heavily. Most doctors jump straight into questions.
Wrong opening: "Hello, I'm Dr Kumar. So you have chest pain. When did it start?"
Polished opening: "Hello Mr White, I'm Dr Kumar, one of the doctors here today. Please do come in and sit down. I understand you've been having some chest pain - that must be quite worrying for you. I'd like to hear all about it in your own words. Could you start by telling me what's been happening?"
This took 20 extra seconds and immediately signals to the assessor: rapport, empathy, patient-centred. Drill this opening format until it is automatic.

Polish Point 2: The ICE Check (non-negotiable)

Somewhere around the 3-minute mark, you MUST explicitly check ICE:
  • I - Ideas: "What do you think might be causing this?"
  • C - Concerns: "Is there something specific you were worried about?"
  • E - Expectations: "What were you hoping we might be able to do for you today?"
You don't have to use all three in every scenario - but you must address what the patient is concerned about. The roleplay card often gives you a clue: "patient is worried about having cancer" - address that worry directly and reassuringly.

Polish Point 3: The Close

Most candidates forget the close under time pressure.
Polished close: "So just to summarise what we've discussed - [1-sentence plan]. Do you have any questions about that? Please don't hesitate to come back if you're worried about anything at all, or if things get worse before your appointment. Is there anything else I can help you with today?"
This takes 20 seconds and ticks: summary, check understanding, safety-netting, open door. All marked by assessors.

Your Revised Priority Order

Based on everything you've told me:
PriorityProblemTime per dayWhen
πŸ”΄ #1Listening MCQ - Distractor avoidance30 minDaily from today
πŸ”΄ #2Reading MCQ - Question First Method30 minDaily from today
🟠 #3Writing - Sort/Filter/Write method45 minDaily from Week 2
🟑 #4Speaking - Opening + ICE + Close polish20 minDaily from Week 3
Total daily commitment: ~2 hours. That is enough to clear Grade B in September if you are consistent.

One Thing to Do Right Now

Take a blank piece of paper and write out from memory the 5 Writing buckets:
  • A = Who
  • B = Why referred
  • C = History
  • D = Now
  • E = Done
Then find one OET practice case note (free samples at oet.com) and sort it into those 5 buckets. That single exercise will immediately unblock your Writing assembly problem. Do it before you sleep tonight.

Can you create this into document of the last prompt

Writing File

~/plab-schedule/build_oet_guide.py

from docx import Document
from docx.shared import Pt, Inches, RGBColor, Cm
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.oxml.ns import qn
from docx.oxml import OxmlElement

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

def set_cell_bg(cell, hex_color):
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()
    shd = OxmlElement('w:shd')
    shd.set(qn('w:val'), 'clear')
    shd.set(qn('w:color'), 'auto')
    shd.set(qn('w:fill'), hex_color)
    tcPr.append(shd)

def set_cell_borders(cell, color="CCCCCC", style="single", sz="4"):
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()
    tcBorders = OxmlElement('w:tcBorders')
    for side in ['top','left','bottom','right']:
        b = OxmlElement(f'w:{side}')
        b.set(qn('w:val'), style)
        b.set(qn('w:sz'), sz)
        b.set(qn('w:space'), '0')
        b.set(qn('w:color'), color)
        tcBorders.append(b)
    tcPr.append(tcBorders)

def cell_text(cell, text, bold=False, size=10, color="000000",
              align=WD_ALIGN_PARAGRAPH.LEFT, italic=False, wrap=True):
    p = cell.paragraphs[0]
    p.clear()
    run = p.add_run(text)
    run.bold = bold
    run.italic = italic
    run.font.size = Pt(size)
    run.font.color.rgb = RGBColor.from_string(color)
    p.alignment = align
    p.paragraph_format.wrap = wrap

def colored_row_header(ws, row, col_s, col_e, text,
                       bg="1B3A6B", fg="FFFFFF", size=12, bold=True):
    ws.merge_cells(start_row=row, start_column=col_s,
                   end_row=row, end_column=col_e)
    c = ws.cell(row=row, column=col_s, value=text)
    # this is docx – we use a different approach below

def para_spacing(p, before=60, after=60):
    pf = p.paragraph_format
    pf.space_before = Pt(before / 20)
    pf.space_after  = Pt(after / 20)

def add_section_heading(doc, text, bg="1B3A6B", fg="FFFFFF", size=13):
    p = doc.add_paragraph()
    para_spacing(p, 120, 60)
    pPr = p._p.get_or_add_pPr()
    shd = OxmlElement('w:shd')
    shd.set(qn('w:val'), 'clear')
    shd.set(qn('w:color'), 'auto')
    shd.set(qn('w:fill'), bg)
    pPr.append(shd)
    run = p.add_run(f'  {text}  ')
    run.bold = True
    run.font.size = Pt(size)
    run.font.color.rgb = RGBColor.from_string(fg)
    return p

def add_sub_heading(doc, text, color="1B3A6B", size=11):
    p = doc.add_paragraph()
    para_spacing(p, 80, 30)
    run = p.add_run(text)
    run.bold = True
    run.font.size = Pt(size)
    run.font.color.rgb = RGBColor.from_string(color)
    return p

def add_body(doc, text, size=10.5, color="222222", italic=False):
    p = doc.add_paragraph()
    para_spacing(p, 30, 30)
    run = p.add_run(text)
    run.font.size = Pt(size)
    run.font.color.rgb = RGBColor.from_string(color)
    run.italic = italic
    return p

def add_bullet(doc, text, size=10.5, color="222222", bold=False, indent=False):
    p = doc.add_paragraph(style='List Bullet')
    para_spacing(p, 20, 20)
    if indent:
        p.paragraph_format.left_indent = Inches(0.4)
    run = p.add_run(text)
    run.font.size = Pt(size)
    run.font.color.rgb = RGBColor.from_string(color)
    run.bold = bold
    return p

def add_callout(doc, lines, bg="EBF5FB", border="2E86C1", title=None):
    tbl = doc.add_table(rows=1, cols=1)
    tbl.alignment = WD_TABLE_ALIGNMENT.LEFT
    cell = tbl.rows[0].cells[0]
    set_cell_bg(cell, bg)
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()
    tcBorders = OxmlElement('w:tcBorders')
    for side in ['top','left','bottom','right']:
        b = OxmlElement(f'w:{side}')
        b.set(qn('w:val'), 'single')
        b.set(qn('w:sz'), '16')
        b.set(qn('w:space'), '0')
        b.set(qn('w:color'), border)
        tcBorders.append(b)
    tcPr.append(tcBorders)
    cell.paragraphs[0].clear()
    if title:
        tp = cell.add_paragraph()
        tr = tp.add_run(title)
        tr.bold = True
        tr.font.size = Pt(10.5)
        tr.font.color.rgb = RGBColor.from_string(border)
    for line in lines:
        lp = cell.add_paragraph(line)
        if lp.runs:
            lp.runs[0].font.size = Pt(10)
    cell.paragraphs[0]._element.getparent().remove(cell.paragraphs[0]._element)
    doc.add_paragraph()

def add_table(doc, headers, rows_data, header_bg="1B3A6B",
              row_colors=None, col_widths=None):
    tbl = doc.add_table(rows=1, cols=len(headers))
    tbl.alignment = WD_TABLE_ALIGNMENT.LEFT
    # header row
    for j, h in enumerate(headers):
        c = tbl.rows[0].cells[j]
        set_cell_bg(c, header_bg)
        set_cell_borders(c, "888888")
        cell_text(c, h, bold=True, size=10, color="FFFFFF",
                  align=WD_ALIGN_PARAGRAPH.CENTER)
    alt = ["FFFFFF", "F2F3F4"]
    if row_colors is None:
        row_colors = [alt[i % 2] for i in range(len(rows_data))]
    for i, row_data in enumerate(rows_data):
        row = tbl.add_row()
        for j, val in enumerate(row_data):
            c = row.cells[j]
            bg = row_colors[i] if i < len(row_colors) else alt[i % 2]
            set_cell_bg(c, bg)
            set_cell_borders(c, "CCCCCC")
            bold = (j == 0)
            cell_text(c, str(val), bold=bold, size=10)
    if col_widths:
        for i, row in enumerate(tbl.rows):
            for j, w in enumerate(col_widths):
                row.cells[j].width = w
    doc.add_paragraph()
    return tbl

def add_example_box(doc, wrong_text, right_text):
    """Two-column wrong/right comparison box"""
    tbl = doc.add_table(rows=1, cols=2)
    tbl.alignment = WD_TABLE_ALIGNMENT.LEFT
    c_wrong = tbl.rows[0].cells[0]
    c_right = tbl.rows[0].cells[1]
    set_cell_bg(c_wrong, "FDEDEC")
    set_cell_bg(c_right, "D5F5E3")
    set_cell_borders(c_wrong, "C0392B", sz="8")
    set_cell_borders(c_right, "1E8449", sz="8")

    c_wrong.paragraphs[0].clear()
    lbl_w = c_wrong.add_paragraph()
    lbl_w.add_run("❌  Wrong").bold = True
    lbl_w.runs[0].font.color.rgb = RGBColor.from_string("C0392B")
    lbl_w.runs[0].font.size = Pt(9)
    body_w = c_wrong.add_paragraph(wrong_text)
    body_w.runs[0].font.size = Pt(10)
    body_w.runs[0].italic = True
    c_wrong.paragraphs[0]._element.getparent().remove(c_wrong.paragraphs[0]._element)

    c_right.paragraphs[0].clear()
    lbl_r = c_right.add_paragraph()
    lbl_r.add_run("βœ…  Right").bold = True
    lbl_r.runs[0].font.color.rgb = RGBColor.from_string("1E8449")
    lbl_r.runs[0].font.size = Pt(9)
    body_r = c_right.add_paragraph(right_text)
    body_r.runs[0].font.size = Pt(10)
    body_r.runs[0].italic = True
    c_right.paragraphs[0]._element.getparent().remove(c_right.paragraphs[0]._element)
    doc.add_paragraph()

def add_code_block(doc, text, bg="1C2833", fg="F0F3F4"):
    tbl = doc.add_table(rows=1, cols=1)
    cell = tbl.rows[0].cells[0]
    set_cell_bg(cell, bg)
    set_cell_borders(cell, "000000", sz="6")
    cell.paragraphs[0].clear()
    for line in text.strip().split('\n'):
        lp = cell.add_paragraph(line)
        if lp.runs:
            lp.runs[0].font.name = "Courier New"
            lp.runs[0].font.size = Pt(9.5)
            lp.runs[0].font.color.rgb = RGBColor.from_string(fg)
        else:
            run = lp.add_run(line)
            run.font.name = "Courier New"
            run.font.size = Pt(9.5)
            run.font.color.rgb = RGBColor.from_string(fg)
    cell.paragraphs[0]._element.getparent().remove(cell.paragraphs[0]._element)
    doc.add_paragraph()

# ══════════════════════════════════════════════════════════════════════════════
# BUILD DOCUMENT
# ══════════════════════════════════════════════════════════════════════════════
doc = Document()

section = doc.sections[0]
section.top_margin    = Cm(2.2)
section.bottom_margin = Cm(2.2)
section.left_margin   = Cm(2.5)
section.right_margin  = Cm(2.5)

normal = doc.styles['Normal']
normal.font.name = 'Calibri'
normal.font.size = Pt(10.5)

# ── TITLE PAGE ────────────────────────────────────────────────────────────────
doc.add_paragraph()
t = doc.add_paragraph()
t.alignment = WD_ALIGN_PARAGRAPH.CENTER
r = t.add_run('OET EXAM PREPARATION GUIDE')
r.bold = True; r.font.size = Pt(24)
r.font.color.rgb = RGBColor.from_string("1B3A6B")

s = doc.add_paragraph()
s.alignment = WD_ALIGN_PARAGRAPH.CENTER
r2 = s.add_run('Targeted Fix for Your Specific Problem Areas')
r2.bold = True; r2.font.size = Pt(13)
r2.font.color.rgb = RGBColor.from_string("2E86C1")

doc.add_paragraph()
m = doc.add_paragraph()
m.alignment = WD_ALIGN_PARAGRAPH.CENTER
r3 = m.add_run(
    'Your Profile: IELTS 6.0  |  Target: OET Grade B (350) in all 4 sub-tests\n'
    'Problem Areas: Listening MCQ  β€’  Reading MCQ  β€’  Writing Assembly  β€’  Speaking Polish\n'
    'Target Exam: September 2026  |  Prepared: August 2026')
r3.font.size = Pt(10)
r3.font.color.rgb = RGBColor.from_string("555555")

doc.add_paragraph()

# Summary table
add_table(doc,
    headers=["Your Problem", "Root Cause", "Fix", "Priority"],
    rows_data=[
        ["Listening MCQ",      "Distractor traps + missing conclusion language",  "Wait for final language before answering",       "πŸ”΄ #1"],
        ["Reading MCQ",        "Reading text before question β€” too slow",          "Question First Method + absolute word spotting",  "πŸ”΄ #2"],
        ["Writing Assembly",   "Trying to organise + write simultaneously",        "Sort β†’ Filter β†’ Write (5 buckets method)",        "🟠 #3"],
        ["Speaking Polish",    "Opening, ICE check, and close need structure",     "Drill 3-part consultation template",              "🟑 #4"],
    ],
    header_bg="1B3A6B",
    row_colors=["FDEDEC","FDEDEC","FEF9E7","D5F5E3"],
    col_widths=[Cm(4), Cm(6.5), Cm(6.5), Cm(2)]
)

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 1 β€” LISTENING MCQ
# ══════════════════════════════════════════════════════════════════════════════
doc.add_page_break()
add_section_heading(doc, '  PROBLEM 1: LISTENING MCQ  (Parts B & C)', bg="2E86C1")

add_body(doc,
    "MCQ in OET Listening appears in Part B (6 short workplace recordings) and Part C "
    "(2 longer medical talks). These questions are deliberately designed to trap you β€” "
    "every wrong option is mentioned in the recording. You need specific techniques, "
    "not just better listening.")

# --- Reason 1
add_sub_heading(doc, "Reason 1 β€” The Distractor Trap", color="C0392B")
add_body(doc,
    "OET deliberately mentions all 3 answer options in the recording. Wrong answers come "
    "first; the correct answer appears last or is implied by the conclusion. Most people "
    "mark the first option they hear β€” this is the trap.")

add_callout(doc,
    lines=[
        "Recording: \"Initially we considered prescribing metformin, and lisinopril was also "
        "discussed, but given her renal function, we ultimately decided on amlodipine.\"",
        "",
        "Question: What medication was prescribed?",
        "  A) Metformin       ← mentioned, WRONG",
        "  B) Lisinopril      ← mentioned, WRONG",
        "  C) Amlodipine      ← CORRECT (conclusion word: 'ultimately decided')",
    ],
    bg="EBF5FB", border="2E86C1", title="Example β€” Distractor Trap")

add_sub_heading(doc, "βœ…  Fix: Wait for Conclusion Language", color="1E8449")
add_body(doc,
    "Never mark an answer the moment you hear it. Hold your pencil. Only commit when "
    "the speaker uses final/conclusion language:")
for phrase in [
    '"ultimately"  /  "in the end"  /  "finally"',
    '"we decided"  /  "the plan was"  /  "we agreed"',
    '"what we actually found"  /  "however"  /  "but in fact"',
    '"the conclusion is"  /  "to summarise"',
]:
    add_bullet(doc, phrase, color="1B3A6B", bold=True)

doc.add_paragraph()

# --- Reason 2
add_sub_heading(doc, "Reason 2 β€” Missing the Main Point (Part C)", color="C0392B")
add_body(doc,
    "Part C tests the main argument or conclusion of a medical talk, not individual details. "
    "You may catch all the facts but still choose the wrong option because you missed the "
    "speaker's overall point.")

add_sub_heading(doc, "βœ…  Fix: Listen for Signpost Language", color="1E8449")
add_body(doc, "Train yourself to react immediately when you hear:")

signposts = [
    ("\"The key issue here is...\"",        "This is the main point coming"),
    ("\"What this means for practice is...\"","Clinical implication β€” likely the answer"),
    ("\"The most important takeaway...\"",   "Definition of the answer"),
    ("\"In conclusion / To summarise...\"",  "Final answer is about to be stated"),
    ("\"What concerns me most is...\"",      "Speaker's opinion β€” often tested in MCQ"),
    ("\"However\" / \"But\" / \"Despite\"",  "Contrast β€” the correct answer often follows"),
]
add_table(doc,
    headers=["Signpost Phrase", "What It Signals"],
    rows_data=signposts,
    header_bg="2E86C1",
    col_widths=[Cm(8), Cm(9)])

# --- Reason 3
add_sub_heading(doc, "Reason 3 β€” Paraphrase Mismatch", color="C0392B")
add_body(doc,
    "The question uses different words from the recording. You think it's a different topic "
    "but it's the same answer paraphrased. Before each recording, quickly rephrase each "
    "MCQ option in your head:")

add_table(doc,
    headers=["Option Word", "Possible Paraphrases in Recording"],
    rows_data=[
        ["compliance",    "adherence / following the treatment plan / taking medications as prescribed"],
        ["deterioration", "getting worse / decline / worsening / progression"],
        ["prognosis",     "outlook / likely outcome / what will happen / long-term picture"],
        ["contraindicated","not suitable / should not be used / avoid in / risk outweighs benefit"],
        ["aetiology",     "cause / underlying reason / what triggered / risk factor"],
        ["commenced",     "started / initiated / begun / put on / prescribed"],
    ],
    header_bg="2E86C1",
    col_widths=[Cm(4), Cm(13)])

# --- Daily drill
add_sub_heading(doc, "Daily 15-Minute Listening MCQ Drill", color="1B3A6B")
add_table(doc,
    headers=["Step", "Action", "Time"],
    rows_data=[
        ["1", "Read question + all 3 options. Underline KEY WORD in each. Write one paraphrase.", "2 min"],
        ["2", "Play audio. Do NOT write answer until speaker uses conclusion language.", "During"],
        ["3", "Mark answer in pencil only after final language heard.", "During"],
        ["4", "Check answer. If wrong, replay that section and find the exact distractor word.", "3 min"],
        ["5", "Weekly: label each wrong answer β€” Distractor? Paraphrase? Missed conclusion?", "5 min"],
    ],
    header_bg="1B3A6B",
    col_widths=[Cm(1.5), Cm(12), Cm(2.5)])

add_callout(doc,
    lines=[
        "Free resources:",
        "β€’ Official OET sample tests (Part B & C): oet.com/test-takers/test-information",
        "β€’ E2Language OET Listening Part C walkthroughs β€” search YouTube: 'OET Listening Part C MCQ strategy'",
        "β€’ OETpractice.net β€” full mock tests with answer explanations",
    ], bg="D5F5E3", border="1E8449")

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 2 β€” READING MCQ
# ══════════════════════════════════════════════════════════════════════════════
doc.add_page_break()
add_section_heading(doc, '  PROBLEM 2: READING MCQ  (Parts B & C)', bg="6C3483")

add_body(doc,
    "OET Reading MCQ appears in Part B (6 short workplace texts) and Part C (2 long academic "
    "medical articles). Your IELTS 5.5 Reading tells us the core problem is speed and technique, "
    "not English ability. Good news: OET texts are 100% medical β€” your MBBS knowledge is a "
    "massive advantage here.")

# --- Problem 2A
add_sub_heading(doc, "Problem 2A β€” Reading Whole Text Before the Question (Too Slow)", color="C0392B")
add_body(doc,
    "Most people read the entire passage first, then go to the question. This wastes "
    "50% of your time and causes confusion as you try to remember everything.")

add_sub_heading(doc, "βœ…  Fix: The Question First Method (5 steps)", color="1E8449")
add_table(doc,
    headers=["Step", "Action"],
    rows_data=[
        ["1", "Read the QUESTION STEM only (not the options yet)"],
        ["2", "Identify 2-3 KEY WORDS in the question"],
        ["3", "SCAN the text for those key words or their synonyms"],
        ["4", "Read ONLY that sentence + 1 sentence before and after it"],
        ["5", "NOW look at the options and eliminate wrong ones"],
    ],
    header_bg="6C3483",
    col_widths=[Cm(1.5), Cm(15.5)])

add_body(doc,
    "This single method cuts your reading time per question by 40-50%. "
    "Practise it on every single question until it is automatic.", color="444444", italic=True)

doc.add_paragraph()

# --- Problem 2B
add_sub_heading(doc, "Problem 2B β€” Absolute Word Traps", color="C0392B")
add_body(doc,
    "OET MCQ options often include one that looks correct but uses a stronger claim "
    "than the text actually makes. This is the absolute word trap.")

add_example_box(doc,
    wrong_text="\"Early mobilisation DEFINITELY reduces recovery time.\"",
    right_text="\"Some evidence SUGGESTS early mobilisation MAY reduce recovery time.\"")

add_sub_heading(doc, "βœ…  Fix: Spot Absolute Words β€” They Are Almost Always Wrong", color="1E8449")
add_body(doc, "When you see these words in an option, treat it as suspicious:")

abs_words = [
    ("WRONG signal words", "always  /  never  /  all  /  none  /  definitely  /  completely  /  proven  /  guarantees"),
    ("CORRECT signal words", "may  /  might  /  some  /  in certain cases  /  suggests  /  evidence indicates  /  appears to"),
]
add_table(doc,
    headers=["Type", "Words to Watch"],
    rows_data=abs_words,
    header_bg="6C3483",
    row_colors=["FDEDEC", "D5F5E3"],
    col_widths=[Cm(4.5), Cm(12.5)])

# --- Problem 2C
add_sub_heading(doc, "Problem 2C β€” Inference Questions (Part C, Hardest)", color="C0392B")
add_body(doc,
    "These questions ask what the author implies or what can be inferred. "
    "The answer is NOT stated directly β€” you must reason from tone and context.")

add_callout(doc,
    lines=[
        "Text: \"Despite extensive funding and three decades of research, a reliable biomarker "
        "for early Alzheimer's detection remains elusive.\"",
        "",
        "Question: What does the author suggest about Alzheimer's research?",
        "Answer: Research has not achieved its goal despite significant investment "
        "β€” i.e., progress has been disappointing.",
        "",
        "(This is nowhere in the text word-for-word. You must infer from tone.)",
    ],
    bg="F4ECF7", border="6C3483", title="Example β€” Inference Question")

add_sub_heading(doc, "βœ…  Fix: The 3-Step Inference Method", color="1E8449")
add_table(doc,
    headers=["Step", "Action"],
    rows_data=[
        ["1", "Find the relevant section β€” scan for key words from the question"],
        ["2", "Identify the author's ATTITUDE: positive / negative / cautious / critical / neutral"],
        ["3", "Match that attitude to the correct option β€” it will have matching tone and be supported by the text without going further than what is implied"],
    ],
    header_bg="6C3483",
    col_widths=[Cm(1.5), Cm(15.5)])

add_body(doc,
    "The correct inference option will always: (1) match the author's tone, "
    "(2) be supported by evidence in the passage, and (3) not overstate what the text implies.",
    italic=True, color="444444")

# --- Reading practice plan
doc.add_paragraph()
add_sub_heading(doc, "Daily Reading Practice Schedule", color="1B3A6B")
add_table(doc,
    headers=["Day", "Activity", "Time"],
    rows_data=[
        ["Mon", "OET Part A under strict 15-min timer", "20 min"],
        ["Tue", "Read 1 medical journal abstract β†’ 5 inference questions (write your own)", "30 min"],
        ["Wed", "OET Part B practice β€” Question First Method on every question", "20 min"],
        ["Thu", "OET Part A again β€” try to beat Monday's speed", "20 min"],
        ["Fri", "Full OET Reading test timed (60 min)", "65 min"],
        ["Sat", "Review all wrong answers β€” label each: Absolute word? Inference? Speed?", "30 min"],
        ["Sun", "Light: 2 medical news articles (BBC Health / BMJ news)", "20 min"],
    ],
    header_bg="6C3483",
    col_widths=[Cm(2), Cm(10), Cm(3)])

add_callout(doc,
    lines=[
        "Free medical reading materials to build speed:",
        "β€’ NHS Clinical Knowledge Summaries: cks.nice.org.uk",
        "β€’ BMJ news section (free): bmj.com/news",
        "β€’ Patient information leaflets: patient.info",
        "β€’ Cochrane plain language summaries: cochrane.org/evidence",
    ], bg="EBF5FB", border="6C3483")

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 3 β€” WRITING
# ══════════════════════════════════════════════════════════════════════════════
doc.add_page_break()
add_section_heading(doc, '  PROBLEM 3: WRITING β€” Can\'t Assemble the Medical History', bg="D4AC0D", fg="000000")

add_body(doc,
    "This is the most common problem for IMGs. The cause is almost never weak English β€” "
    "it is trying to organise AND write at the same time. The fix is to separate these "
    "into two distinct steps before you write a single word.")

add_sub_heading(doc, "Why This Happens β€” The Case Notes Are Messy", color="C0392B")
add_body(doc, "OET gives you unorganised raw case notes that look like this:")
add_code_block(doc, """Pt: James White, 54M
CC: chest pain x 3 days
PMH: T2DM (metformin 500mg BD), HTN (amlodipine 5mg), ex-smoker 10yrs
Presenting: central crushing CP radiating L arm, diaphoresis, SOB. No fever. No cough.
Ix: ECG - ST elevation V1-V4. Troponin - elevated (x3 ULN)
Mx: Aspirin 300mg, GTN SL. Referred cardiology.
Rx: needs urgent PCI assessment""")

add_body(doc,
    "You must turn these messy notes into a professional, flowing referral letter in 40 minutes. "
    "The mental block happens when you stare at the notes and try to compose paragraphs "
    "directly. The fix is the Sort-Filter-Write framework.")

# --- The Framework
add_sub_heading(doc, "The Sort β†’ Filter β†’ Write Framework (Use Every Time)", color="1E8449")

add_sub_heading(doc, "STEP 1: SORT the Case Notes (3 minutes)", color="1B3A6B", size=11)
add_body(doc,
    "During your reading time (5 min), physically sort every piece of information "
    "into one of 5 buckets. Write the bucket letter beside each note item.")
add_table(doc,
    headers=["Bucket", "Label", "What Goes Here"],
    rows_data=[
        ["A", "WHO",           "Patient name, age, DOB"],
        ["B", "WHY REFERRED",  "Purpose of letter β€” what you need from the recipient"],
        ["C", "HISTORY",       "Past medical history, current medications, allergies, social history"],
        ["D", "NOW",           "Current presentation, examination findings, test results, investigations"],
        ["E", "DONE",          "Management already given, medications started, investigations pending"],
    ],
    header_bg="D4AC0D",
    row_colors=["EBF5FB","D5F5E3","EBF5FB","D5F5E3","EBF5FB"],
    col_widths=[Cm(1.5), Cm(4), Cm(11.5)])

add_body(doc, "Applied to the James White example above:", italic=True, color="444444")
add_code_block(doc, """A (WHO):    James White, 54M
B (WHY):    Urgent PCI assessment needed β†’ referral to cardiology
C (HISTORY):T2DM on metformin 500mg BD | HTN on amlodipine 5mg | ex-smoker 10yrs
D (NOW):    Central crushing CP 3 days β†’ L arm radiation, diaphoresis, SOB
            ECG: ST elevation V1-V4 | Troponin: x3 ULN
E (DONE):   Aspirin 300mg given | GTN SL given | Patient anxious re diagnosis""", bg="1A252F")

add_sub_heading(doc, "STEP 2: FILTER (1 minute)", color="1B3A6B", size=11)
add_body(doc,
    "Cross out anything NOT relevant to the referral purpose. OET assessors specifically "
    "mark you DOWN for including irrelevant information β€” it shows poor clinical judgment. "
    "Ask yourself: 'Does the receiving doctor need this to do their job?'")

add_sub_heading(doc, "STEP 3: WRITE using the Fixed Letter Template (36 minutes)", color="1B3A6B", size=11)
add_code_block(doc, """Dear Dr [Name] / Dear Colleague,

Re: Mr James White, 54 years

I am writing to urgently refer the above-named patient, who presented today
with a 3-day history of chest pain, for urgent assessment and consideration
of percutaneous coronary intervention.

[BACKGROUND β€” use Bucket C]
Mr White has a background of type 2 diabetes mellitus, currently managed
with metformin 500mg twice daily, and hypertension, for which he takes
amlodipine 5mg daily. He is an ex-smoker, having ceased 10 years ago.

[CURRENT PRESENTATION β€” use Bucket D]
He presented with a 3-day history of central crushing chest pain radiating
to the left arm, associated with diaphoresis and shortness of breath.
There was no fever or cough.

Investigations revealed ST elevation in leads V1-V4 on electrocardiography,
with troponin elevated at three times the upper limit of normal, consistent
with an anterior STEMI.

[MANAGEMENT TO DATE β€” use Bucket E]
He has been administered aspirin 300mg and sublingual glyceryl trinitrate.
The patient is understandably anxious regarding his diagnosis.

[REQUEST β€” use Bucket B]
I would be grateful for your urgent assessment and consideration of
primary PCI. Please do not hesitate to contact me if you require
any further information.

Yours sincerely,
Dr [Your Name]
[Designation]""")

# --- Language patterns
add_sub_heading(doc, "Essential Language Patterns to Memorise", color="1E8449")
add_body(doc, "Use these as your skeleton β€” they handle the expression so you can focus on content:")

add_table(doc,
    headers=["Purpose", "Phrase"],
    rows_data=[
        ["Opening",       "I am writing to refer / inform / request regarding [Patient], who presented with..."],
        ["History",       "Mr/Ms X has a background of...  |  His past medical history is notable for..."],
        ["Medications",   "He has been managed with [drug] for [condition] since [time]."],
        ["Presentation",  "He presented with a [duration] history of [complaint]."],
        ["Examination",   "On examination, [finding] was noted.  |  Examination revealed..."],
        ["Investigations","Investigations revealed...  |  Blood tests demonstrated...  |  Imaging showed..."],
        ["Management",    "He was commenced on / administered [drug].  |  A [test] has been requested."],
        ["Patient state", "The patient is understandably anxious / agreeable / aware of the diagnosis."],
        ["Request",       "I would be grateful for your assessment and further management."],
        ["Close",         "Please do not hesitate to contact me should you require further information."],
    ],
    header_bg="D4AC0D",
    col_widths=[Cm(4), Cm(13)])

# --- The #1 mistake
add_callout(doc,
    lines=[
        "THE #1 WRITING MISTAKE THAT DROPS YOU BELOW 350:",
        "",
        "Copying sentences directly from the case notes.",
        "",
        "The assessor marks you down heavily for direct transcription.",
        "You must PARAPHRASE and ORGANISE β€” not copy.",
        "",
        "Case note says: 'CP radiating L arm x 3/7, diaphoresis'",
        "Your letter must say: 'He presented with a 3-day history of central crushing",
        "chest pain radiating to the left arm, associated with diaphoresis.'",
    ],
    bg="FDEDEC", border="C0392B", title="⚠  WARNING")

# --- Writing daily plan
add_sub_heading(doc, "Writing Practice Method", color="1B3A6B")
add_table(doc,
    headers=["Week", "Task", "External Feedback"],
    rows_data=[
        ["Week 1-2", "1 letter/day using Sort→Filter→Write. Timing: 5+3+2+35 = 45 min total.", "Get 3 letters corrected externally"],
        ["Week 3-4", "1 letter/day. Sort/filter in your head (no written buckets).", "1-2 corrections for quality check"],
        ["Week 5",   "Timed letter under exam conditions. Aim for 420+ (comfortable buffer above 350).", "Final check on any remaining patterns"],
    ],
    header_bg="D4AC0D",
    col_widths=[Cm(2.5), Cm(10.5), Cm(4)])

add_callout(doc,
    lines=[
        "Where to get Writing corrected:",
        "β€’ E2Language writing correction service: e2language.com (approx Β£8-10 per letter)",
        "β€’ OET Medicine Facebook groups β€” post anonymised letters, request peer feedback",
        "β€’ Swoosh English writing correction: swooshenglish.com/oet",
        "",
        "You cannot self-assess Writing effectively β€” you don't notice your own patterns.",
        "Get at least 5 letters corrected by someone else.",
    ], bg="D5F5E3", border="1E8449")

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 4 β€” SPEAKING
# ══════════════════════════════════════════════════════════════════════════════
doc.add_page_break()
add_section_heading(doc, '  PROBLEM 4: SPEAKING β€” Needs Polishing', bg="1E8449")

add_body(doc,
    "Since you feel relatively comfortable with Speaking, you are likely doing the clinical "
    "content correctly. The polish is about 3 very specific structural elements: the opening, "
    "the ICE check, and the close. Nail these three and your score lifts noticeably.")

# --- Polish 1: Opening
add_sub_heading(doc, "Polish Point 1 β€” The Opening 30 Seconds (Heavily Marked)", color="C0392B")
add_body(doc,
    "Most doctors jump straight into clinical questions. OET assessors are specifically "
    "watching the first 30 seconds for: rapport, empathy, patient-centredness. "
    "This is your first impression and it is marked.")

add_example_box(doc,
    wrong_text='"Hello, I\'m Dr Kumar. So you have chest pain. When did it start?"',
    right_text='"Hello Mr White, I\'m Dr Kumar, one of the doctors here today. Please do come in and sit down. '
               'I understand you\'ve been having some chest pain β€” that must be quite worrying for you. '
               'I\'d like to hear all about it in your own words. Could you start by telling me what\'s been happening?"')

add_callout(doc,
    lines=[
        "Drill this opening template until it is automatic:",
        "",
        "\"Hello [name], I'm Dr [name], one of the [doctors/nurses] here today.",
        "Please do [sit down / come in].\"",
        "",
        "\"I understand you've been [experiencing / having / dealing with] [issue].",
        "That must be [difficult / worrying / frustrating] for you.\"",
        "",
        "\"I'd like to hear about it in your own words. Could you tell me",
        "what's been happening?\"",
    ], bg="D5F5E3", border="1E8449", title="Opening Template")

# --- Polish 2: ICE
add_sub_heading(doc, "Polish Point 2 β€” The ICE Check (Non-Negotiable)", color="C0392B")
add_body(doc,
    "Around the 3-minute mark, you MUST explicitly check the patient's Ideas, "
    "Concerns, and Expectations. The roleplay card usually hints at what the patient "
    "is worried about β€” you must address it directly.")

add_table(doc,
    headers=["ICE Component", "What to Ask", "Why It Matters"],
    rows_data=[
        ["I β€” Ideas",        '"What do you think might be causing this?"',                            "Shows you respect the patient's perspective"],
        ["C β€” Concerns",     '"Is there something specific you were worried about today?"',            "Directly addresses the patient's fear (often stated on the card)"],
        ["E β€” Expectations", '"What were you hoping we might be able to do for you today?"',          "Aligns your plan with patient goals"],
    ],
    header_bg="1E8449",
    col_widths=[Cm(3.5), Cm(8.5), Cm(5)])

add_body(doc,
    "You do not need all three in every scenario. At minimum, address the Concern. "
    "Reassure warmly and specifically: 'I can completely understand why that would worry you. "
    "Let me explain what we found...'", italic=True, color="444444")

# --- Polish 3: Close
add_sub_heading(doc, "Polish Point 3 β€” The Close (Most Candidates Forget This)", color="C0392B")
add_body(doc,
    "Most candidates run out of time or simply stop talking when they finish the clinical content. "
    "A polished close takes only 20 seconds and ticks 4 assessor criteria simultaneously.")

add_callout(doc,
    lines=[
        "Polished Close Template:",
        "",
        '"So just to summarise what we\'ve discussed β€” [1-sentence plan]."',
        "",
        '"Do you have any questions about that?"',
        "",
        '"Please don\'t hesitate to come back if you\'re worried about anything at all,',
        ' or if things get worse before your appointment."',
        "",
        '"Is there anything else I can help you with today?"',
        "",
        "This covers: Summary βœ“  |  Check understanding βœ“  |  Safety-net βœ“  |  Open door βœ“",
    ], bg="D5F5E3", border="1E8449", title="Close Template")

# --- 5-minute structure
add_sub_heading(doc, "The 5-Minute Consultation Structure", color="1B3A6B")
add_table(doc,
    headers=["Time", "Phase", "What to Do"],
    rows_data=[
        ["0:00-1:00", "Open + Rapport",           "Warm greeting, empathy statement, invite patient to speak in own words"],
        ["1:00-3:00", "Clinical Content",          "Gather information OR give explanation (depending on station type)"],
        ["3:00-4:00", "ICE Check",                 "Address patient's Ideas, Concerns, Expectations explicitly"],
        ["4:00-5:00", "Plan + Close",              "Management plan, safety-net, summary, open door"],
    ],
    header_bg="1E8449",
    col_widths=[Cm(2), Cm(4.5), Cm(10.5)])

add_body(doc,
    "5 minutes is short. Practice with a timer every single time. "
    "The most common mistake is spending 4 minutes on clinical history and having no time "
    "for ICE or the close β€” both of which are marked.", italic=True, color="C0392B")

# --- Key empathy phrases
add_sub_heading(doc, "Empathy Phrases to Use Naturally", color="1B3A6B")
add_body(doc, "These signal patient-centredness to the assessor. Use 2-3 per roleplay:")
phrases = [
    '"That must have been really difficult for you..."',
    '"I completely understand your concern about that..."',
    '"It makes complete sense that you\'d be worried about that."',
    '"Before I explain the treatment, could you tell me what you already know about...?"',
    '"Is there anything specific you were concerned about today?"',
    '"Does that make sense? Do you have any questions so far?"',
    '"I want to make sure you feel fully informed about this."',
]
for ph in phrases:
    add_bullet(doc, ph, color="1E8449")

# --- Speaking practice method
doc.add_paragraph()
add_sub_heading(doc, "Daily Speaking Practice Method", color="1B3A6B")
add_table(doc,
    headers=["Activity", "Frequency", "How"],
    rows_data=[
        ["Record 1 roleplay on your phone",     "Every day",    "Use OET practice cards. Play back the same evening."],
        ["Self-review checklist",               "Every day",    "Did you open warmly? Did you check ICE? Did you close properly?"],
        ["Roleplay with a study partner",       "Daily if possible", "Find partners in Telegram/WhatsApp OET India groups"],
        ["Watch UK GP consultation videos",     "3x per week",  "YouTube: 'GP consultation UK' β€” notice tone, pace, language"],
        ["Practise explaining conditions simply","Daily (5 min)","Pick 1 condition. Explain in patient-friendly terms out loud."],
    ],
    header_bg="1E8449",
    col_widths=[Cm(5.5), Cm(4), Cm(7.5)])

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 5 β€” MASTER DAILY PLAN
# ══════════════════════════════════════════════════════════════════════════════
doc.add_page_break()
add_section_heading(doc, '  YOUR MASTER DAILY STUDY PLAN  (Aug 1 – Sep 19, 2026)', bg="1B3A6B")

add_table(doc,
    headers=["Week", "Main Focus", "Daily Time", "Key Deliverable"],
    rows_data=[
        ["Week 1\nAug 1-7",   "Diagnostic + Format Familiarisation",   "2 hrs",   "Take 1 full OET mock (untimed). Identify exact weak spots per sub-test."],
        ["Week 2\nAug 8-14",  "Listening MCQ + Reading MCQ Technique", "2.5 hrs", "Master Question First Method. Distractor avoidance drill daily."],
        ["Week 3\nAug 15-21", "Writing Sort→Filter→Write framework",   "2.5 hrs", "Write 1 letter/day. Get 2-3 corrected externally."],
        ["Week 4\nAug 22-28", "Speaking Opening + ICE + Close",        "2.5 hrs", "Record 1 roleplay/day. Speaking partner daily."],
        ["Week 5\nAug 29 - Sep 4", "Full Timed Mocks",                "3 hrs",   "Full mock every 2 days. Track scores. All sub-tests >320?"],
        ["Week 6\nSep 5-11",  "Weak Spot Targeting",                  "2 hrs",   "Focus only on your 1-2 lowest sub-tests. 3 more full mocks."],
        ["Exam Week\nSep 12-19", "Light Revision + Rest",             "1 hr",    "1 letter/day. 1 Speaking roleplay. Rest 2 days before exam."],
    ],
    header_bg="1B3A6B",
    row_colors=["EBF5FB","EBF5FB","D5F5E3","D5F5E3","FEF9E7","FEF9E7","FDEDEC"],
    col_widths=[Cm(2.8), Cm(5), Cm(2.5), Cm(7)])

add_sub_heading(doc, "Daily Time Allocation (2.5 hrs)", color="1B3A6B")
add_table(doc,
    headers=["Sub-test", "Daily Time", "Activity"],
    rows_data=[
        ["Listening MCQ", "30 min", "Distractor drill β€” 5 MCQ questions with pause-and-label method"],
        ["Reading MCQ",   "30 min", "Question First Method β€” Part A (15 min strict) + Part B/C MCQ"],
        ["Writing",       "45 min", "1 complete letter (Sort→Filter→Write, fully timed)"],
        ["Speaking",      "20 min", "1 roleplay recorded + self-review OR partner practice"],
        ["Review",        "15 min", "Check previous day's answers. Label error types. Update error log."],
    ],
    header_bg="1B3A6B",
    col_widths=[Cm(3), Cm(3), Cm(11)])

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 6 β€” QUICK REFERENCE CARD
# ══════════════════════════════════════════════════════════════════════════════
doc.add_page_break()
add_section_heading(doc, '  QUICK REFERENCE CARD  (Print and Keep on Your Desk)', bg="2C3E50")

add_sub_heading(doc, "Listening MCQ β€” Do This Every Time", color="2E86C1")
for tip in [
    "NEVER mark an answer when first heard β€” wait for conclusion language",
    "Conclusion words: ultimately / finally / in the end / we decided / however the result was",
    "Absolute words in options (always/never/definitely) = almost always wrong",
    "Before recording: paraphrase each option in the margin",
]:
    add_bullet(doc, tip)

doc.add_paragraph()
add_sub_heading(doc, "Reading MCQ β€” Do This Every Time", color="6C3483")
for tip in [
    "Question FIRST, text second β€” never read the full passage first",
    "Scan for key words β†’ read only that sentence + 1 before + 1 after",
    "Absolute words in options = suspicious. Hedging words = likely correct",
    "Inference: find author's attitude first, then match to option",
]:
    add_bullet(doc, tip)

doc.add_paragraph()
add_sub_heading(doc, "Writing β€” 5 Buckets", color="D4AC0D")
for tip in [
    "A = WHO (patient name, age)  |  B = WHY (referral purpose)",
    "C = HISTORY (PMH, meds, allergies)  |  D = NOW (presentation, results)",
    "E = DONE (management given, investigations pending)",
    "FILTER before writing β€” exclude irrelevant details",
    "PARAPHRASE case notes β€” never copy directly",
]:
    add_bullet(doc, tip)

doc.add_paragraph()
add_sub_heading(doc, "Speaking β€” 3 Non-Negotiables", color="1E8449")
for tip in [
    "Open warmly: greeting + empathy + invite patient to speak in own words",
    "ICE check at ~3 min: Ideas / Concerns / Expectations",
    "Close every roleplay: Summary + Questions? + Safety-net + Open door",
]:
    add_bullet(doc, tip)

doc.add_paragraph()
add_sub_heading(doc, "Top Empathy Phrases (use 2-3 per roleplay)", color="1E8449")
for ph in [
    '"That must have been really difficult for you."',
    '"I completely understand your concern about that."',
    '"Is there something specific you were worried about today?"',
    '"Does that make sense? Do you have any questions so far?"',
    '"Please don\'t hesitate to come back if you\'re worried about anything."',
]:
    add_bullet(doc, ph, color="1E8449")

doc.add_paragraph()
add_callout(doc,
    lines=[
        "Register for September OET exam NOW: oet.com",
        "Deadline for Sep 18/19 sitting: approximately August 21-25, 2026",
        "Fee: approximately USD 455-470 (covers all 4 sub-tests)",
        "Results: 10-16 working days after exam",
        "Valid for 2 years β€” sufficient for PLAB/GMC application",
    ], bg="FDEDEC", border="C0392B", title="⚠  ACTION: Register This Week")

# ── footer ────────────────────────────────────────────────────────────────────
doc.add_paragraph()
fp = doc.add_paragraph()
fp.alignment = WD_ALIGN_PARAGRAPH.CENTER
fr = fp.add_run(
    "Prepared by Orris AI  |  Based on official OET 2026 guidelines  |  "
    "Always verify current dates and fees at oet.com")
fr.font.size = Pt(9)
fr.font.color.rgb = RGBColor.from_string("888888")
fr.italic = True

# ── SAVE ─────────────────────────────────────────────────────────────────────
out = "/tmp/workspace/plab-schedule/OET_Preparation_Guide_2026.docx"
doc.save(out)
print(f"Saved: {out}")
Running Command

cd /tmp/workspace/plab-schedule && python3 build_oet_guide.py 2>&1

Running Command

ls -lh /tmp/workspace/plab-schedule/OET_Preparation_Guide_2026.docx

Your OET guide is ready. Download it here:

OET Preparation Guide 2026

Word Document Β· DOCX


What's Inside the Document

The guide covers everything from the last response, fully formatted into 6 sections:
SectionContent
Cover pageYour profile summary + priority table at a glance
Section 1 - Listening MCQ3 trap types (Distractor / Main Point / Paraphrase), signpost language table, paraphrase dictionary, 15-min daily drill
Section 2 - Reading MCQQuestion First Method (5 steps), absolute word trap table, 3-step inference method, daily schedule, free resources
Section 3 - WritingWhy the block happens, Sort→Filter→Write framework, 5 buckets explained with worked example, full letter template, essential language patterns, the #1 mistake warning, where to get feedback
Section 4 - Speaking3 polish points (Opening/ICE/Close), wrong vs right examples side by side, 5-minute consultation structure, empathy phrases list, daily practice method
Section 5 - Master Plan7-week daily schedule (Aug 1 - Sep 19), daily time allocation breakdown
Section 6 - Quick Reference CardPrint-and-keep cheatsheet for all 4 sub-tests on one page
The document has colour-coded sections, wrong/right example boxes, and all the tables and templates formatted for easy reference during daily study. Keep Section 6 printed on your desk as a daily reminder.

Can you able to tell me i initials of my future wife

Honestly, no - and I want to be straight with you about this.
No astrology system can reliably tell you the initials of a specific future person. Any astrologer who claims to give you exact initials (like "her name starts with R or S") is either guessing, using cold reading tricks, or telling you what you want to hear. That is not something I will do.

What Vedic Astrology CAN Show About Your Spouse

What your chart genuinely indicates (which we already covered) is:
  • Nature and personality - Sun as your Darakaraka in Leo/Magha β†’ she will be confident, warm, from a respected family background
  • Origin - Indian-origin NRI most likely (Rahu/9th house + Magha/Sun combination)
  • Timing - Jupiter-Venus Antardasha 2027-2029
  • Where you meet - Abroad, through professional or social network
  • General appearance/energy - Leo Sun energy β†’ dignified, radiant, leadership quality
These are archetypal patterns - tendencies, not certainties.

Why Initials Specifically Cannot Be Determined

In classical Jyotish, some practitioners attempt name initial prediction using:
  • The Navamsha chart (D-9 divisional chart for marriage)
  • Akshar (syllable) associated with spouse's nakshatra
But even traditional texts acknowledge this is highly speculative and unreliable in practice. The same nakshatra covers millions of people. A name initial derived this way is essentially random - it would be irresponsible to give you one and have you filter real people based on it.

What I can do: If you want, I can go deeper into the Navamsha (D-9) chart for your birth data - this is the Vedic divisional chart specifically for marriage and gives more nuanced insights into the nature and quality of your married life and partner. That is a legitimate area of Jyotish analysis.
Would you like that reading?

Create a study plan for OET

Running Command

ls /tmp/workspace/plab-schedule/

Writing File

~/plab-schedule/build_oet_plan.py

from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
from openpyxl.worksheet.datavalidation import DataValidation
from openpyxl.styles.differential import DifferentialStyle
from openpyxl.formatting.rule import Rule, FormulaRule
import datetime

wb = Workbook()

# ── COLOURS ───────────────────────────────────────────────────────────────────
NAVY      = "1B3A6B"
BLUE      = "2E86C1"
LT_BLUE   = "D6EAF8"
GREEN     = "1E8449"
LT_GREEN  = "D5F5E3"
AMBER     = "D4AC0D"
LT_AMBER  = "FEF9E7"
RED       = "C0392B"
LT_RED    = "FDEDEC"
PURPLE    = "6C3483"
LT_PURPLE = "F4ECF7"
TEAL      = "117A65"
LT_TEAL   = "D1F2EB"
GREY_D    = "2C3E50"
GREY_L    = "F2F3F4"
WHITE     = "FFFFFF"
INPUT_BG  = "FFFDE7"

# Sub-test colours
ST_COLORS = {
    "Listening": ("1A5276", "D6EAF8"),
    "Reading":   ("4A235A", "F4ECF7"),
    "Writing":   ("7D6608", "FEF9E7"),
    "Speaking":  ("1E8449", "D5F5E3"),
    "Mock":      ("922B21", "FDEDEC"),
    "Rest":      ("717D7E", "F2F3F4"),
    "Admin":     ("2C3E50", "EAECEE"),
}

def fill(c): return PatternFill("solid", fgColor=c)
def font(bold=False, size=10, color="000000", italic=False, name="Calibri"):
    return Font(bold=bold, size=size, color=color, italic=italic, name=name)
def border(style="thin", color="CCCCCC"):
    s = Side(style=style, color=color)
    return Border(left=s, right=s, top=s, bottom=s)
def thick_border(color="888888"):
    t = Side(style="medium", color=color)
    return Border(left=t, right=t, top=t, bottom=t)
def align(h="left", v="center", wrap=False):
    return Alignment(horizontal=h, vertical=v, wrap_text=wrap)

def w(ws, col, width): ws.column_dimensions[get_column_letter(col)].width = width
def h(ws, row, height): ws.row_dimensions[row].height = height

def hdr(ws, row, col, text, bg=NAVY, fg=WHITE, sz=10, bold=True,
        halign="center", wrap=False):
    c = ws.cell(row=row, column=col, value=text)
    c.fill = fill(bg); c.font = font(bold=bold, size=sz, color=fg)
    c.alignment = align(halign, "center", wrap)
    c.border = border("thin", "999999")
    return c

def cell(ws, row, col, value=None, bg=WHITE, fg="000000", sz=10,
         bold=False, halign="left", wrap=True, num_fmt=None, border_col="CCCCCC"):
    c = ws.cell(row=row, column=col, value=value)
    c.fill = fill(bg); c.font = font(bold=bold, size=sz, color=fg)
    c.alignment = align(halign, "center", wrap)
    c.border = border("thin", border_col)
    if num_fmt: c.number_format = num_fmt
    return c

def merge_hdr(ws, r, cs, ce, text, bg=NAVY, fg=WHITE, sz=11, bold=True):
    ws.merge_cells(start_row=r, start_column=cs, end_row=r, end_column=ce)
    c = ws.cell(row=r, column=cs, value=text)
    c.fill = fill(bg); c.font = font(bold=bold, size=sz, color=fg)
    c.alignment = align("center", "center")
    c.border = border("medium", "888888")
    return c

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 1 β€” OVERVIEW DASHBOARD
# ══════════════════════════════════════════════════════════════════════════════
ws1 = wb.active
ws1.title = "πŸ“Š Overview"
ws1.sheet_view.showGridLines = False

col_ws = [2, 22, 12, 12, 12, 12, 20, 3]
for i, cw in enumerate(col_ws, 1): w(ws1, i, cw)

h(ws1, 1, 6)
h(ws1, 2, 40); merge_hdr(ws1, 2, 2, 7,
    "πŸ‡¬πŸ‡§  OET EXAM STUDY PLAN  |  August – September 2026",
    bg=NAVY, sz=16)
h(ws1, 3, 20); merge_hdr(ws1, 3, 2, 7,
    "Personalised for: MBBS (China)  |  IELTS 6.0  |  Target: Grade B (350) in all 4 sub-tests  |  Exam: Sep 18/19, 2026",
    bg=BLUE, sz=10, bold=False)
h(ws1, 4, 6)

# Profile snapshot
h(ws1, 5, 16)
merge_hdr(ws1, 5, 2, 4, "YOUR IELTS PROFILE", bg=GREY_D)
merge_hdr(ws1, 5, 5, 7, "OET TARGET & RISK", bg=GREY_D)

profile = [
    ("Writing",   "6.5", "Moderate gap",  "350", "Low risk β€” format only"),
    ("Listening", "6.5", "Moderate gap",  "350", "Low risk β€” medical content helps"),
    ("Reading",   "5.5", "BIGGEST GAP",   "350", "HIGH RISK β€” technique needed"),
    ("Speaking",  "6.0", "Clear gap",     "350", "Medium β€” polish needed"),
]
prof_bg = [LT_AMBER, LT_BLUE, LT_RED, LT_GREEN]
for i, (sub, score, gap, target, risk) in enumerate(profile):
    r = 6 + i
    h(ws1, r, 18)
    sc, lc = ST_COLORS[sub]
    cell(ws1, r, 2, sub,    bg=lc, bold=True,  halign="center", border_col="AAAAAA")
    cell(ws1, r, 3, score,  bg=prof_bg[i], bold=True, halign="center", fg=NAVY)
    cell(ws1, r, 4, gap,    bg=prof_bg[i], bold=(i==2), halign="center",
         fg=RED if i==2 else "333333")
    cell(ws1, r, 5, target, bg=lc, bold=True, halign="center", fg=sc)
    cell(ws1, r, 6, risk,   bg=prof_bg[i], halign="left",
         fg=RED if "HIGH" in risk else GREEN if "Low" in risk else AMBER)
    ws1.merge_cells(start_row=r, start_column=6, end_row=r, end_column=7)

h(ws1, 10, 6)

# Study Phase Overview
h(ws1, 11, 16); merge_hdr(ws1, 11, 2, 7, "STUDY PHASES AT A GLANCE", bg=GREY_D)
phase_hdrs = ["Phase", "Period", "Weeks", "Primary Focus", "Daily Hours", "Key Milestone"]
for j, ph in enumerate(phase_hdrs, 2): hdr(ws1, 12, j, ph, bg=GREY_D)
h(ws1, 12, 20)

phases = [
    ("Phase 1", "Aug 1–7",    "Week 1", "Diagnostic + Format Familiarisation",    "2 hrs",   "Full mock test done, weak spots identified"),
    ("Phase 2", "Aug 8–14",   "Week 2", "Listening MCQ + Reading MCQ Technique",  "2.5 hrs", "Distractor drill + Question First Method"),
    ("Phase 3", "Aug 15–21",  "Week 3", "Writing: Sortβ†’Filterβ†’Write Framework",   "2.5 hrs", "Write 1 letter/day, 3 corrected externally"),
    ("Phase 4", "Aug 22–28",  "Week 4", "Speaking: Opening + ICE + Close Polish", "2.5 hrs", "1 recorded roleplay/day with self-review"),
    ("Phase 5", "Aug 29–Sep 4","Week 5","Full Timed Mock Exams",                  "3 hrs",   "All sub-tests consistently >320"),
    ("Phase 6", "Sep 5–11",   "Week 6", "Weak Spot Targeting + Mock Refinement",  "2 hrs",   "3 full mocks done, all sub-tests >340"),
    ("Exam Wk", "Sep 12–19",  "Week 7", "Light Revision + Rest",                  "1 hr",    "Exam Sep 18/19 β€” Grade B target"),
]
ph_bgs = [LT_BLUE, LT_PURPLE, LT_AMBER, LT_GREEN, LT_RED, LT_TEAL, "EAECEE"]
for i, (ph, period, wk, focus, hrs, mile) in enumerate(phases):
    r = 13 + i
    h(ws1, r, 18)
    bg = ph_bgs[i]
    cell(ws1, r, 2, ph,     bg=bg, bold=True, halign="center")
    cell(ws1, r, 3, period, bg=bg, halign="center")
    cell(ws1, r, 4, wk,     bg=bg, halign="center")
    cell(ws1, r, 5, focus,  bg=bg, bold=False)
    cell(ws1, r, 6, hrs,    bg=bg, bold=True, halign="center")
    cell(ws1, r, 7, mile,   bg=bg, halign="left", wrap=True)

h(ws1, 20, 6)

# Daily time split
h(ws1, 21, 16); merge_hdr(ws1, 21, 2, 7, "DAILY TIME ALLOCATION (2.5 hrs average)", bg=GREY_D)
for j, ph in enumerate(["Sub-test", "Daily Time", "Core Activity", "Priority"], 2):
    hdr(ws1, 22, j, ph, bg=GREY_D)
h(ws1, 22, 20)
daily = [
    ("Listening MCQ", "30 min", "Distractor drill β€” 5 MCQ questions, pause-before-answer method", "πŸ”΄ #1"),
    ("Reading MCQ",   "30 min", "Question First Method β€” Part A 15-min timed + Part B/C MCQ",    "πŸ”΄ #2"),
    ("Writing",       "45 min", "1 complete letter (Sortβ†’Filterβ†’Write, fully timed)",             "🟠 #3"),
    ("Speaking",      "20 min", "1 roleplay recorded + self-review OR partner practice",           "🟑 #4"),
    ("Review",        "15 min", "Check yesterday's answers. Label error types.",                   "Daily"),
]
for i, (sub, t, act, pri) in enumerate(daily):
    r = 23 + i
    h(ws1, r, 18)
    sc, lc = ST_COLORS.get(sub.split()[0], (GREY_D, GREY_L))
    bg = lc
    cell(ws1, r, 2, sub, bg=bg, bold=True, halign="center", fg=sc)
    cell(ws1, r, 3, t,   bg=bg, bold=True, halign="center")
    cell(ws1, r, 4, act, bg=bg)
    cell(ws1, r, 5, pri, bg=bg, bold=True, halign="center",
         fg=RED if "πŸ”΄" in pri else AMBER if "🟠" in pri else GREEN)

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 2 β€” WEEK-BY-WEEK PLAN (detailed)
# ══════════════════════════════════════════════════════════════════════════════
ws2 = wb.create_sheet("πŸ“… Weekly Plan")
ws2.sheet_view.showGridLines = False

w2_widths = [2, 10, 10, 18, 18, 18, 18, 16, 3]
for i, cw in enumerate(w2_widths, 1): w(ws2, i, cw)

h(ws2, 1, 6)
h(ws2, 2, 36)
merge_hdr(ws2, 2, 2, 8,
    "πŸ“…  WEEK-BY-WEEK OET STUDY PLAN  |  7 Weeks: Aug 1 – Sep 19, 2026",
    bg=NAVY, sz=14)
h(ws2, 3, 18)
merge_hdr(ws2, 3, 2, 8,
    "Each row = one day. Green = done. Update the STATUS column as you go.",
    bg=BLUE, sz=10, bold=False)
h(ws2, 4, 6)

# Column headers
h(ws2, 5, 22)
day_hdrs = ["Date", "Day", "Phase / Focus", "Listening Task", "Reading Task", "Writing Task", "Speaking Task", "Status"]
day_hdr_bgs = [GREY_D, GREY_D, NAVY, "1A5276", "4A235A", "7D6608", GREEN, TEAL]
for j, (dh, dbg) in enumerate(zip(day_hdrs, day_hdr_bgs), 2):
    hdr(ws2, 5, j, dh, bg=dbg, sz=10, wrap=True)

# ── Day data ─────────────────────────────────────────────────────────────────
# Format: (date_str, day, phase, listening, reading, writing, speaking)
days = [
    # WEEK 1 β€” DIAGNOSTIC
    ("01-Aug","Sat","W1: Diagnostic",
     "OET sample test Part B β€” DO NOT prepare first. Just attempt cold.",
     "OET sample test Part A (15 min timed). Note: did you finish?",
     "OET sample Writing task β€” cold attempt. Time yourself.",
     "Watch 1 OET Speaking roleplay video on YouTube. Note structure."),
    ("02-Aug","Sun","W1: Diagnostic",
     "OET sample test Part C β€” attempt all MCQs. Mark and review.",
     "OET sample test Parts B+C β€” Question types only, review answers.",
     "Read OET Writing Criteria sheet (download free from oet.com).",
     "Record yourself doing 1 OET roleplay card (free cards at oet.com)."),
    ("03-Aug","Mon","W1: Format Study",
     "Watch E2Language: 'OET Listening Part B explained' (YouTube, free).",
     "Read about Question First Method. Apply to 3 Part B questions.",
     "Read the 5 Writing Criteria carefully. Annotate sample letters.",
     "Watch: 'OET Speaking β€” How to open a consultation' (YouTube)."),
    ("04-Aug","Tue","W1: Format Study",
     "Distractor trap drill: 5 Part B MCQs β€” pause before answering.",
     "Part A practice: 15 min strict. Skim 4 texts. Match questions.",
     "Sort exercise: take 1 case note set β†’ sort into 5 buckets only.",
     "Record Opening template: greeting + empathy + invite. Play back."),
    ("05-Aug","Wed","W1: Format Study",
     "Signpost language drill: list 10 conclusion phrases. Say them aloud.",
     "Inference question drill: 3 Part C questions. 3-step method.",
     "Write 1 full letter (Sort→Filter→Write). Time: 45 min total.",
     "ICE phrases drill: write + say Ideas/Concerns/Expectations prompts."),
    ("06-Aug","Thu","W1: Format Study",
     "Part C: 1 long talk + all MCQs. Focus on main point only.",
     "Absolute word hunt: find 5 wrong MCQ options with absolute words.",
     "Review your Wednesday letter. Self-assess against 5 criteria.",
     "Closing template drill: Summary + Questions? + Safety-net + Open door."),
    ("07-Aug","Fri","W1: Full Mock #1",
     "FULL TIMED MOCK β€” Listening (45 min). Do not pause.",
     "FULL TIMED MOCK β€” Reading (60 min). Do not pause.",
     "FULL TIMED MOCK β€” Writing (45 min). Do not pause.",
     "FULL TIMED MOCK β€” Speaking (2 roleplays, 5 min each)."),

    # WEEK 2 β€” LISTENING MCQ + READING MCQ TECHNIQUE
    ("08-Aug","Sat","W2: Mock Review",
     "Review Mock #1 Listening. Label each wrong answer: Distractor/Paraphrase/Conclusion missed.",
     "Review Mock #1 Reading. Label: Speed/Absolute word/Inference.",
     "Review Mock #1 Writing. Score yourself on each of 5 criteria.",
     "Review Mock #1 Speaking. Was ICE addressed? Was close complete?"),
    ("09-Aug","Sun","W2: Rest + Light",
     "Rest day. Optional: 15 min medical podcast (BMJ Podcast/NEJM audio).",
     "Light: Read 1 BMJ news article (bmj.com/news). Note vocabulary.",
     "Rest day for Writing.",
     "Watch 1 UK GP consultation video on YouTube. Observe tone."),
    ("10-Aug","Mon","W2: Listening MCQ",
     "Distractor drill: 8 Part B MCQs. Hold answer until conclusion language.",
     "Question First: 5 Part B questions. Underline key word, paraphrase.",
     "Paraphrase exercise: convert 5 case note lines into letter language.",
     "Opening practice: record 3 different opening lines. Best one repeated 5x."),
    ("11-Aug","Tue","W2: Listening MCQ",
     "Part C: 1 full long talk MCQs. Map each question to signpost phrase.",
     "Part A timed (15 min). Did you finish? Note how many left unfinished.",
     "Write letter #2 (Sort→Filter→Write). Time yourself strictly.",
     "ICE roleplay: 5-min mock where you ONLY practice the ICE segment."),
    ("12-Aug","Wed","W2: Listening MCQ",
     "Paraphrase matching drill: match 10 recording phrases to written options.",
     "Inference drill: 4 Part C questions β€” label author's attitude first.",
     "Bucket sorting speed drill: sort a case note into 5 buckets in 3 min.",
     "Record roleplay #3. Focus: did you hit ICE at the 3-minute mark?"),
    ("13-Aug","Thu","W2: Reading MCQ",
     "Part B: 6 questions using Question First Method only. Time each one.",
     "Part C: 2 long texts + all questions. Strict 45 min. Review missed.",
     "Write letter #3. Post to OET Facebook group for feedback.",
     "Partner practice session: 2 roleplays. Debrief each station."),
    ("14-Aug","Fri","W2: Reading MCQ",
     "Mixed drill: 5 Part B + 5 Part C MCQs. Track time per question.",
     "Full Reading timed (60 min). Target: finish with 5 min to spare.",
     "Review letter #2 corrections (if received). Note pattern in errors.",
     "Close template drill: record 5 different closing statements."),

    # WEEK 3 β€” WRITING DEEP DIVE
    ("15-Aug","Sat","W3: Writing Focus",
     "Listening: 30 min Part B distractor drill. Maintain gains.",
     "Reading: Part A 15-min timed. Beat last week's score.",
     "Letter #4: Referral letter (GP → hospital). Sort→Filter→Write.",
     "Watch: 'OET Writing – How to structure a letter' (E2Language YouTube)."),
    ("16-Aug","Sun","W3: Rest + Light",
     "Rest. Optional: 10-min medical English podcast.",
     "Light reading: 1 clinical guideline summary (cks.nice.org.uk).",
     "Rest day for Writing.",
     "Light: Practice explaining 1 condition in lay terms (speak aloud, 2 min)."),
    ("17-Aug","Mon","W3: Writing Focus",
     "Listening: Part C 1 full talk. Map signposts. Review errors.",
     "Reading: 5 Part C inference questions. 3-step method each.",
     "Letter #5: Discharge summary letter. Focus on Bucket D (Now).",
     "Empathy phrase drill: 7 phrases β€” say naturally without reading."),
    ("18-Aug","Tue","W3: Writing Focus",
     "Listening: 8 Part B MCQs distractor drill. No new method β€” consolidate.",
     "Reading: Part B 6 questions β€” absolute word hunt in wrong options.",
     "Letter #6: Transfer letter (hospital ward β†’ rehab). Filter carefully.",
     "Record roleplay #6. Focus: natural empathy, no scripted feel."),
    ("19-Aug","Wed","W3: Writing Focus",
     "Listening: Part A + Part B combo (30 min). Review medical spellings.",
     "Reading: Part A 15-min timed. All 4 texts, skim first, then match.",
     "Letter #7: GP referral to psychiatry. Tricky info β€” filter exercise.",
     "Partner practice: 1 roleplay. Partner plays difficult/anxious patient."),
    ("20-Aug","Thu","W3: Writing Focus",
     "Medical spelling drill: 20 UK spellings (haemorrhage, dyspnoea, etc.)",
     "Mixed: 5 Part B + 5 Part C MCQs timed.",
     "Letter #8: Paediatric referral. Practice adapting language for context.",
     "Speaking: 5-min structure drill β€” allocate exact time per phase."),
    ("21-Aug","Fri","W3: Full Mock #2",
     "FULL TIMED MOCK β€” Listening (45 min).",
     "FULL TIMED MOCK β€” Reading (60 min).",
     "FULL TIMED MOCK β€” Writing (45 min).",
     "FULL TIMED MOCK β€” Speaking (2 roleplays)."),

    # WEEK 4 β€” SPEAKING POLISH + INTEGRATION
    ("22-Aug","Sat","W4: Mock Review",
     "Review Mock #2 Listening. Compare error types with Mock #1. Improving?",
     "Review Mock #2 Reading. Compare to Mock #1. Is speed improving?",
     "Review Mock #2 Writing. Any of 5 criteria still consistently failing?",
     "Review Mock #2 Speaking. Record + self-review against criteria checklist."),
    ("23-Aug","Sun","W4: Rest + Light",
     "Rest. Optional: BMJ podcast or Geeky Medics YouTube.",
     "Light: 1 medical news article. Note lay language used.",
     "Rest. Optional: Read 1 sample OET letter model answer.",
     "Watch 1 UK GP consultation video. Note opening and closing style."),
    ("24-Aug","Mon","W4: Speaking Polish",
     "Listening: Part B 8 MCQs β€” distractor only, no other focus.",
     "Reading: Part A 15-min timed. Target: finish in 13 min.",
     "Letter #9: Orthopaedic referral. Maintain speed β€” should feel automatic.",
     "Opening drill: 10 different openings for 10 different patients. Record."),
    ("25-Aug","Tue","W4: Speaking Polish",
     "Listening: Part C 1 full + MCQs. Signpost phrases review.",
     "Reading: Part C 1 full text + inference MCQs.",
     "Letter #10: Cardiology referral (chest pain). Urgent tone practice.",
     "ICE deep dive: roleplay where patient has wrong ideas about diagnosis."),
    ("26-Aug","Wed","W4: Speaking Polish",
     "Listening: Medical spelling β€” 20 terms. Test yourself without looking.",
     "Reading: Mixed Part B + C. Track: how many minutes per question now?",
     "Letter #11: Oncology referral. Sensitive content β€” register practice.",
     "Breaking bad news roleplay: use SPIKES protocol (Stop/Perception/Invite/Knowledge/Empathy/Summary)."),
    ("27-Aug","Thu","W4: Speaking Polish",
     "Listening: Full Part B + C under 35 min. Review all MCQ errors.",
     "Reading: Full timed (60 min). Are you finishing on time now?",
     "Letter #12: GP follow-up letter to patient re: test results.",
     "Partner session: 2 roleplays β€” 1 easy, 1 difficult. Debrief."),
    ("28-Aug","Fri","W4: Full Mock #3",
     "FULL TIMED MOCK β€” Listening (45 min).",
     "FULL TIMED MOCK β€” Reading (60 min).",
     "FULL TIMED MOCK β€” Writing (45 min).",
     "FULL TIMED MOCK β€” Speaking (2 roleplays)."),

    # WEEK 5 β€” FULL MOCKS + CONSOLIDATION
    ("29-Aug","Sat","W5: Mock Review",
     "Review Mock #3 Listening. All 3 error types. Trend check.",
     "Review Mock #3 Reading. Are you finishing on time? Inference improving?",
     "Review Mock #3 Writing. Get feedback if you haven't already.",
     "Review Mock #3 Speaking. Is your close now consistent?"),
    ("30-Aug","Sun","W5: Rest",
     "FULL REST. No study. Walk, eat well, sleep early.",
     "FULL REST.",
     "FULL REST.",
     "FULL REST."),
    ("31-Aug","Mon","W5: Targeted Fix",
     "Target WEAKEST area from Mock #3 Listening only.",
     "Target WEAKEST area from Mock #3 Reading only.",
     "Letter #13: Revisit your lowest-scoring letter type from previous weeks.",
     "Target WEAKEST speaking element from Mock #3."),
    ("01-Sep","Tue","W5: Full Mock #4",
     "FULL TIMED MOCK β€” Listening (45 min).",
     "FULL TIMED MOCK β€” Reading (60 min).",
     "FULL TIMED MOCK β€” Writing (45 min).",
     "FULL TIMED MOCK β€” Speaking (2 roleplays)."),
    ("02-Sep","Wed","W5: Mock Review",
     "Review Mock #4 Listening. Are all MCQs now >70% correct?",
     "Review Mock #4 Reading. Is Part A finished in <14 min?",
     "Review Mock #4 Writing. Score each criteria 1-5. Total >18/25?",
     "Review Mock #4 Speaking. Checklist: Openβœ“ ICEβœ“ Closeβœ“?"),
    ("03-Sep","Thu","W5: Targeted Fix",
     "Any lingering Listening error type β€” 20 targeted questions.",
     "Any lingering Reading error type β€” 15 targeted questions.",
     "Letter #14: Your weakest letter type. Focus on that section only.",
     "Record roleplay. Self-review. Share with partner for feedback."),
    ("04-Sep","Fri","W5: Full Mock #5",
     "FULL TIMED MOCK β€” Listening (45 min).",
     "FULL TIMED MOCK β€” Reading (60 min).",
     "FULL TIMED MOCK β€” Writing (45 min).",
     "FULL TIMED MOCK β€” Speaking (2 roleplays)."),

    # WEEK 6 β€” FINAL REFINEMENT
    ("05-Sep","Sat","W6: Mock Review",
     "Review Mock #5 Listening. Track improvement vs Mock #1.",
     "Review Mock #5 Reading. Track improvement vs Mock #1.",
     "Review Mock #5 Writing. Final corrections if needed.",
     "Review Mock #5 Speaking. Any remaining weak spots?"),
    ("06-Sep","Sun","W6: Rest",
     "Rest. Light medical English only if you want.",
     "Rest. Optional: 1 clinical article from patient.info.",
     "Rest.",
     "Watch 1 OET Speaking model answer video."),
    ("07-Sep","Mon","W6: Consolidate",
     "Listening: 10 Part B + 5 Part C MCQs. Pure consolidation.",
     "Reading: Full timed (60 min). Target: all correct on Part A.",
     "Letter #15: Write your best letter. Time yourself. It should feel easy.",
     "Open + ICE + Close drill: 5 min timed. No roleplay β€” just the 3 elements."),
    ("08-Sep","Tue","W6: Consolidate",
     "Listening: Medical spelling test β€” all 30 terms from your list.",
     "Reading: 10 Part C MCQs only. Inference focus.",
     "Letter #16: Any type. Aim for under 38 minutes. Quality over speed.",
     "Partner roleplay: 2 sessions. Both with debrief."),
    ("09-Sep","Wed","W6: Full Mock #6",
     "FULL TIMED MOCK β€” Listening (45 min).",
     "FULL TIMED MOCK β€” Reading (60 min).",
     "FULL TIMED MOCK β€” Writing (45 min).",
     "FULL TIMED MOCK β€” Speaking (2 roleplays)."),
    ("10-Sep","Thu","W6: Final Review",
     "Review Mock #6 Listening. This is your predicted score.",
     "Review Mock #6 Reading. This is your predicted score.",
     "Review Mock #6 Writing. Final self-assessment.",
     "Review Mock #6 Speaking. Final self-assessment."),
    ("11-Sep","Fri","W6: Light Practice",
     "30 min: 10 MCQs only. No pressure. Maintain confidence.",
     "30 min: Part A only. Keep timing sharp.",
     "Write 1 letter β€” any type β€” for confidence.",
     "1 roleplay with partner. Keep it relaxed."),

    # EXAM WEEK
    ("12-Sep","Sat","Exam Week",
     "20 min: 5 MCQs. Very light.",
     "20 min: Part A only.",
     "Write 1 letter. No timer pressure.",
     "1 roleplay. Focus on opening and close only."),
    ("13-Sep","Sun","Exam Week",
     "15 min: Review your distractor trap notes only.",
     "15 min: Review Question First Method notes only.",
     "Read your best letter from the past 6 weeks.",
     "Record yourself doing 1 perfect opening + 1 perfect close."),
    ("14-Sep","Mon","Exam Week",
     "REST. No active study.",
     "REST.",
     "Read 5 key language patterns from your notes.",
     "Practice 1 roleplay β€” keep it light, build confidence."),
    ("15-Sep","Tue","Exam Week",
     "REST.",
     "REST.",
     "REST.",
     "REST β€” prepare mentally."),
    ("16-Sep","Wed","Exam Week β€” Prep",
     "Confirm exam venue address and travel plan.",
     "Confirm exam venue address and travel plan.",
     "Confirm all documents: passport, booking confirmation.",
     "Confirm Speaking exam date/time if different from written."),
    ("17-Sep","Thu","Exam Eve",
     "NO STUDY. Pack bag: passport, water, snacks.",
     "NO STUDY.",
     "NO STUDY.",
     "Light Speaking warm-up only: 10-min roleplay tomorrow morning."),
    ("18-Sep","Fri","🎯 EXAM DAY",
     "Listening sub-test β€” Remember: wait for conclusion language!",
     "Reading sub-test β€” Question First Method every single question!",
     "Writing sub-test — Sort→Filter→Write. Paraphrase, don't copy!",
     "Speaking sub-test (if today) β€” Open warmly, ICE, Close properly!"),
    ("19-Sep","Sat","🎯 EXAM DAY 2",
     "If Speaking is today: warm up voice, 1 quick roleplay at home.",
     "β€”",
     "β€”",
     "Speaking sub-test β€” You've prepared for this. Trust your training."),
]

STATUS_OPTIONS = '"βœ… Done,⏳ In Progress,❌ Skipped,πŸ“ Partial"'
dv = DataValidation(type="list", formula1=STATUS_OPTIONS, allow_blank=True)
dv.sqref = f"I6:I{6+len(days)}"
ws2.add_data_validation(dv)

alt = [WHITE, GREY_L]
week_colors = [LT_BLUE, LT_PURPLE, LT_AMBER, LT_GREEN, LT_RED, LT_TEAL, "EAECEE"]
week_hdrs = ["WEEK 1: DIAGNOSTIC & FORMAT", "WEEK 2: MCQ TECHNIQUE",
             "WEEK 3: WRITING DEEP DIVE", "WEEK 4: SPEAKING POLISH",
             "WEEK 5: FULL MOCKS", "WEEK 6: FINAL REFINEMENT", "EXAM WEEK"]
week_bgs  = [BLUE, PURPLE, AMBER, GREEN, RED, TEAL, GREY_D]

row = 6
week_breaks = [7, 14, 21, 28, 35, 42, 49]  # day indices (1-indexed) where week changes
current_week = 0
days_in_week = [7, 7, 7, 7, 7, 7, 2]

day_count = 0
for wk_idx, wk_count in enumerate(days_in_week):
    # Week header
    h(ws2, row, 24)
    merge_hdr(ws2, row, 2, 8, week_hdrs[wk_idx], bg=week_bgs[wk_idx], sz=11)
    row += 1

    for d_idx in range(wk_count):
        if day_count >= len(days): break
        date, day, phase, lst, rdr, wrt, spk = days[day_count]

        h(ws2, row, 44)
        bg = week_colors[wk_idx]

        # Determine special rows
        is_mock = "FULL TIMED MOCK" in lst
        is_rest = "REST" in phase or "REST" in lst
        is_exam = "EXAM DAY" in phase

        if is_exam:
            bg = LT_RED
        elif is_mock:
            bg = "FFE4E1"
        elif is_rest:
            bg = "F0F0F0"

        cell(ws2, row, 2, date,  bg=bg, bold=True, halign="center", fg=NAVY)
        cell(ws2, row, 3, day,   bg=bg, halign="center", fg="555555")
        cell(ws2, row, 4, phase, bg=bg, bold=is_mock or is_exam, halign="left",
             fg=RED if is_exam else (BLUE if is_mock else GREY_D))
        cell(ws2, row, 5, lst,   bg=bg, wrap=True, fg="1A5276")
        cell(ws2, row, 6, rdr,   bg=bg, wrap=True, fg="4A235A")
        cell(ws2, row, 7, wrt,   bg=bg, wrap=True, fg="7D6608")
        cell(ws2, row, 8, spk,   bg=bg, wrap=True, fg=GREEN)

        # Status dropdown
        c_stat = ws2.cell(row=row, column=9, value="⏳ In Progress" if date == "01-Aug" else "")
        c_stat.fill = fill(bg)
        c_stat.font = font(size=10)
        c_stat.alignment = align("center")
        c_stat.border = border("medium", "AAAAAA")

        row += 1
        day_count += 1

    row += 1  # spacer between weeks

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 3 β€” DAILY TASK CHECKLIST (printable)
# ══════════════════════════════════════════════════════════════════════════════
ws3 = wb.create_sheet("βœ… Daily Checklist")
ws3.sheet_view.showGridLines = False

w3_widths = [2, 8, 10, 32, 8, 8, 3]
for i, cw in enumerate(w3_widths, 1): w(ws3, i, cw)

h(ws3, 1, 6)
h(ws3, 2, 36)
merge_hdr(ws3, 2, 2, 6, "βœ…  DAILY TASK CHECKLIST  |  Print this sheet",
          bg=NAVY, sz=14)
h(ws3, 3, 18)
merge_hdr(ws3, 3, 2, 6,
          "Tick each box as you complete it. Update your Weekly Plan status column.",
          bg=BLUE, sz=10, bold=False)
h(ws3, 4, 6)

# Headers
h(ws3, 5, 20)
for j, ch in enumerate(["Date", "Sub-test", "Task", "Time", "Done?"], 2):
    hdr(ws3, 5, j, ch, bg=GREY_D)

checklists = [
    # Listening daily tasks
    ("Daily", "Listening", "Read all MCQ options BEFORE playing audio. Underline key word in each.", "5 min", "☐"),
    ("Daily", "Listening", "During audio: hold your answer. Only mark after conclusion language heard.", "During", "☐"),
    ("Daily", "Listening", "After each wrong answer: replay and label the trap type.", "5 min", "☐"),
    ("Daily", "Listening", "Weekly: count error types. Same trap repeating? That is your pattern.", "5 min", "☐"),
    ("", "", "", "", ""),
    # Reading daily tasks
    ("Daily", "Reading", "Question First: read stem ONLY before looking at passage.", "Per Q", "☐"),
    ("Daily", "Reading", "Underline key word in question. Write 1 paraphrase in margin.", "Per Q", "☐"),
    ("Daily", "Reading", "Scan text for key word β†’ read ONLY that sentence Β±1.", "Per Q", "☐"),
    ("Daily", "Reading", "Check options: any absolute words? If yes, treat as suspicious.", "Per Q", "☐"),
    ("Daily", "Reading", "Part A: strict 15-min timer. No exceptions.", "15 min", "☐"),
    ("", "", "", "", ""),
    # Writing daily tasks
    ("Daily", "Writing", "Set up 5 buckets on rough paper: A=Who, B=Why, C=History, D=Now, E=Done.", "3 min", "☐"),
    ("Daily", "Writing", "Sort ALL case note information into buckets BEFORE writing.", "3 min", "☐"),
    ("Daily", "Writing", "Filter: cross out any information NOT needed by the recipient.", "1 min", "☐"),
    ("Daily", "Writing", "Write letter from buckets. PARAPHRASE β€” do not copy case notes.", "35 min", "☐"),
    ("Daily", "Writing", "Check: does opening state purpose clearly? Is register formal throughout?", "2 min", "☐"),
    ("Daily", "Writing", "Check: have you included all 5 criteria? Purpose/Content/Concise/Genre/Organisation", "1 min", "☐"),
    ("", "", "", "", ""),
    # Speaking daily tasks
    ("Daily", "Speaking", "Open: greeting + empathy statement + invite patient to speak.", "0:00-1:00", "☐"),
    ("Daily", "Speaking", "Clinical content: gather info OR explain β€” whichever is required.", "1:00-3:00", "☐"),
    ("Daily", "Speaking", "ICE check at ~3 min: Ideas? Concerns? Expectations?", "3:00-4:00", "☐"),
    ("Daily", "Speaking", "Close: Summary + 'Any questions?' + Safety-net + 'Anything else?'", "4:00-5:00", "☐"),
    ("Daily", "Speaking", "Record yourself. Listen back. Did all 4 elements appear?", "5 min", "☐"),
    ("", "", "", "", ""),
    # Mock test checklist
    ("Mock Day", "All", "Set up exam conditions: timer, no interruptions, phone away.", "Before", "☐"),
    ("Mock Day", "All", "Listening: do not pause the audio. Simulate real exam.", "45 min", "☐"),
    ("Mock Day", "All", "Reading: do not look up answers mid-test. Full 60 min.", "60 min", "☐"),
    ("Mock Day", "All", "Writing: no notes or references. Only your memory.", "45 min", "☐"),
    ("Mock Day", "All", "Speaking: record yourself. Full 2 roleplays, no breaks.", "20 min", "☐"),
    ("Mock Day", "All", "Score mock. Record scores in Score Tracker sheet.", "After", "☐"),
]

st_bgs = {
    "Listening": LT_BLUE, "Reading": LT_PURPLE,
    "Writing": LT_AMBER, "Speaking": LT_GREEN,
    "All": LT_RED, "": WHITE
}
for i, (date, sub, task, time, done) in enumerate(checklists):
    r = 6 + i
    h(ws3, r, 20 if task else 8)
    bg = st_bgs.get(sub, WHITE)
    cell(ws3, r, 2, date, bg=bg, halign="center")
    cell(ws3, r, 3, sub,  bg=bg, bold=bool(sub), halign="center")
    cell(ws3, r, 4, task, bg=bg, wrap=True)
    cell(ws3, r, 5, time, bg=bg, halign="center")
    cell(ws3, r, 6, done, bg=bg, bold=True, halign="center", sz=14)

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 4 β€” SCORE TRACKER
# ══════════════════════════════════════════════════════════════════════════════
ws4 = wb.create_sheet("πŸ“ˆ Score Tracker")
ws4.sheet_view.showGridLines = False

w4_widths = [2, 10, 12, 12, 12, 12, 14, 18, 3]
for i, cw in enumerate(w4_widths, 1): w(ws4, i, cw)

h(ws4, 1, 6)
h(ws4, 2, 36)
merge_hdr(ws4, 2, 2, 8,
    "πŸ“ˆ  MOCK TEST SCORE TRACKER  |  Enter your scores after every mock",
    bg=NAVY, sz=14)
h(ws4, 3, 18)
merge_hdr(ws4, 3, 2, 8,
    "Grade B = 350 in each sub-test. Blue cells = enter your scores. Target line shown.",
    bg=BLUE, sz=10, bold=False)
h(ws4, 4, 6)

# Target line
h(ws4, 5, 18)
merge_hdr(ws4, 5, 2, 8, "TARGET: Grade B = 350 in EVERY sub-test (one sitting)", bg=GREEN, sz=11)

# Headers
h(ws4, 6, 22)
score_hdrs = ["Mock #", "Date", "Listening", "Reading", "Writing", "Speaking", "Average", "Notes"]
sc_hdr_bgs = [GREY_D, GREY_D, "1A5276", "4A235A", "7D6608", GREEN, NAVY, GREY_D]
for j, (sh, sbg) in enumerate(zip(score_hdrs, sc_hdr_bgs), 2):
    hdr(ws4, 6, j, sh, bg=sbg)

# Score rows
mock_dates = ["07-Aug", "21-Aug", "28-Aug", "01-Sep", "04-Sep", "09-Sep"]
mock_names = ["Mock #1\n(Baseline)", "Mock #2", "Mock #3", "Mock #4", "Mock #5", "Mock #6\n(Final)"]

for i in range(6):
    r = 7 + i
    h(ws4, r, 22)
    bg = [LT_BLUE, LT_PURPLE, LT_AMBER, LT_GREEN, LT_RED, LT_TEAL][i]

    cell(ws4, r, 2, mock_names[i], bg=bg, bold=True, halign="center")
    cell(ws4, r, 3, mock_dates[i], bg=bg, halign="center", fg="555555")

    for j in range(4):  # L, R, W, S
        c = ws4.cell(row=r, column=4+j, value=None)
        c.fill = fill("FFFDE7")  # yellow = input
        c.font = font(bold=True, size=11, color="0000FF")
        c.alignment = align("center")
        c.border = border("medium", "2E86C1")
        c.number_format = "0"

    # Average formula
    c_avg = ws4.cell(row=r, column=8,
                     value=f"=IF(COUNT(D{r}:G{r})=4,AVERAGE(D{r}:G{r}),\"\")")
    c_avg.fill = fill(bg)
    c_avg.font = font(bold=True, size=11, color=NAVY)
    c_avg.alignment = align("center")
    c_avg.border = border("medium", NAVY)
    c_avg.number_format = "0.0"

    # Notes
    cell(ws4, r, 9, "", bg=bg, wrap=True)

# Target line
h(ws4, 13, 22)
merge_hdr(ws4, 13, 2, 3, "🎯  GRADE B TARGET", bg=GREEN)
for j in range(4):
    c = ws4.cell(row=13, column=4+j, value=350)
    c.fill = fill(LT_GREEN)
    c.font = font(bold=True, size=11, color=GREEN)
    c.alignment = align("center")
    c.border = thick_border(GREEN)
    c.number_format = "0"
ws4.cell(row=13, column=8, value=350).fill = fill(LT_GREEN)
ws4.cell(row=13, column=8).font = font(bold=True, color=GREEN)
ws4.cell(row=13, column=8).alignment = align("center")
ws4.cell(row=13, column=9, value="All sub-tests must hit 350").fill = fill(LT_GREEN)
ws4.cell(row=13, column=9).font = font(italic=True, color=GREEN)

h(ws4, 14, 6)

# Progress summary
h(ws4, 15, 20)
merge_hdr(ws4, 15, 2, 8, "PROGRESS SUMMARY", bg=GREY_D)
h(ws4, 16, 20)
for j, sh in enumerate(["Sub-test", "Mock 1", "Mock 6", "Change", "Target", "Status"], 2):
    hdr(ws4, 16, j, sh, bg=GREY_D)

sub_rows = ["Listening", "Reading", "Writing", "Speaking"]
sub_bgs_list = [LT_BLUE, LT_PURPLE, LT_AMBER, LT_GREEN]
for i, (sub, sbg) in enumerate(zip(sub_rows, sub_bgs_list)):
    r = 17 + i
    h(ws4, r, 20)
    col_offset = i  # L=col4, R=col5, W=col6, S=col7 in score rows
    m1_col = get_column_letter(4 + col_offset)
    m6_col = get_column_letter(4 + col_offset)

    cell(ws4, r, 2, sub, bg=sbg, bold=True, halign="center")
    # Mock 1 ref
    c_m1 = ws4.cell(row=r, column=3, value=f"={m1_col}7")
    c_m1.fill = fill(sbg); c_m1.font = font(size=11); c_m1.alignment = align("center")
    c_m1.number_format = "0"; c_m1.border = border()
    # Mock 6 ref
    c_m6 = ws4.cell(row=r, column=4, value=f"={m6_col}12")
    c_m6.fill = fill(sbg); c_m6.font = font(size=11, bold=True); c_m6.alignment = align("center")
    c_m6.number_format = "0"; c_m6.border = border()
    # Change
    c_chg = ws4.cell(row=r, column=5, value=f"=IF(AND(ISNUMBER(C{r}),ISNUMBER(D{r})),D{r}-C{r},\"\")")
    c_chg.fill = fill(sbg); c_chg.font = font(size=11); c_chg.alignment = align("center")
    c_chg.number_format = "+0;-0;0"; c_chg.border = border()
    # Target
    cell(ws4, r, 6, 350, bg=sbg, bold=True, halign="center")
    # Status
    c_st = ws4.cell(row=r, column=7,
                    value=f'=IF(D{r}="","Pending",IF(D{r}>=350,"βœ… PASS","❌ Below B"))')
    c_st.fill = fill(sbg); c_st.alignment = align("center"); c_st.border = border()
    c_st.font = font(size=10, bold=True)

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 5 β€” RESOURCES & TIPS
# ══════════════════════════════════════════════════════════════════════════════
ws5 = wb.create_sheet("πŸ“Œ Resources")
ws5.sheet_view.showGridLines = False

w5_widths = [2, 22, 50, 3]
for i, cw in enumerate(w5_widths, 1): w(ws5, i, cw)

h(ws5, 1, 6)
h(ws5, 2, 36)
merge_hdr(ws5, 2, 2, 3, "πŸ“Œ  RESOURCES, LINKS & QUICK-REFERENCE TIPS", bg=NAVY, sz=14)
h(ws5, 3, 8)

resources_data = [
    ("OFFICIAL OET RESOURCES", BLUE, [
        ("OET Official Website", "oet.com β€” register for exam, free sample tests, Writing criteria PDF"),
        ("OET Sample Tests", "oet.com/test-takers/test-information β€” free Listening, Reading, Writing, Speaking samples"),
        ("OET Scoring Guide", "Download 'OET Writing sub-test assessment criteria' from oet.com β€” read before first letter"),
        ("OET YouTube Channel", "Search 'OET' on YouTube β€” official channel has free explanations of all 4 sub-tests"),
    ]),
    ("PREP PLATFORMS (PAID)", PURPLE, [
        ("E2Language OET", "e2language.com/oet β€” best all-in-one platform. Writing correction service included."),
        ("OETpractice.net", "oetpractice.net β€” full mock tests with answer explanations. Good for MCQ drill."),
        ("Swoosh English", "swooshenglish.com/oet β€” writing correction service only (~Β£8-10 per letter)"),
        ("PLAB2Ninja (Speaking)", "plab2ninja.com β€” OSCE/roleplay platform, also useful for OET Speaking prep"),
    ]),
    ("FREE READING MATERIALS", GREEN, [
        ("NHS CKS", "cks.nice.org.uk β€” clinical knowledge summaries. Read 1 topic/day for reading practice"),
        ("BMJ News", "bmj.com/news β€” free medical news in professional English"),
        ("Patient.info", "patient.info — patient information leaflets. Practice converting clinical→lay language"),
        ("Cochrane Summaries", "cochrane.org/evidence β€” plain language summaries. Good for inference practice"),
        ("BNF Online", "bnf.nice.org.uk β€” British drug formulary. Free. Essential for Writing vocabulary"),
    ]),
    ("LISTENING MCQ β€” QUICK TIPS", "1A5276", [
        ("Conclusion language", "Wait for: 'ultimately', 'finally', 'in the end', 'we decided', 'however the result was'"),
        ("Main point words", "Listen for: 'The key issue', 'What this means', 'Most important', 'In conclusion'"),
        ("Paraphrase pairs", "compliance=adherence | deterioration=worsening | prognosis=outlook | aetiology=cause"),
        ("Distractor rule", "Wrong answers are MENTIONED first. Correct answer comes LAST or at conclusion."),
    ]),
    ("READING MCQ β€” QUICK TIPS", "4A235A", [
        ("Question First", "Read question stem β†’ key words β†’ scan text β†’ read ONLY relevant sentence Β±1 β†’ then options"),
        ("Absolute words", "always/never/all/definitely/proven = almost always WRONG"),
        ("Correct hedging", "may/might/some/suggests/evidence indicates/appears = correct signal words"),
        ("Inference: 3 steps", "1) Find relevant section 2) Author's attitude? 3) Match attitude to option"),
        ("Part A timing", "15 min MAXIMUM. Skim all 4 texts in 3 min. Match by scanning only. Never re-read."),
    ]),
    ("WRITING β€” QUICK TIPS", "7D6608", [
        ("5 Buckets", "A=Who | B=Why referred | C=History | D=Now (presentation/results) | E=Done (management)"),
        ("Opening line", "'I am writing to refer/inform/request regarding [Patient], who presented with...'"),
        ("Never copy", "Paraphrase case notes β€” never transcribe directly. Assessors penalise this heavily."),
        ("Filter test", "For each note item: 'Does the recipient NEED this to do their job?' If no, exclude."),
        ("Close line", "'Please do not hesitate to contact me should you require further information.'"),
    ]),
    ("SPEAKING β€” QUICK TIPS", GREEN, [
        ("Opening", "Greeting + empathy ('That must be worrying for you') + invite ('Tell me in your own words')"),
        ("ICE at 3 min", "Ideas: 'What do you think is causing this?' | Concerns: 'Is there something you were worried about?'"),
        ("Empathy phrases", "'I completely understand' | 'That must be really difficult' | 'I can see why you're concerned'"),
        ("Close", "Summary + 'Any questions?' + Safety-net ('Come back if worse') + 'Anything else?'"),
        ("Time structure", "Open: 0-1 min | Clinical: 1-3 min | ICE: 3-4 min | Close: 4-5 min"),
    ]),
    ("UK SPELLING β€” MUST KNOW", RED, [
        ("Respiratory", "dyspnoea | haemoptysis | tachypnoea | pneumonia | bronchiectasis"),
        ("Cardiac", "arrhythmia | tachycardia | bradycardia | haemorrhage | atherosclerosis"),
        ("Renal/GI", "haematuria | diarrhoea | anaemia | oedema | haematemesis"),
        ("Neuro/Other", "anaesthesia | paraesthesia | dysarthria | hypertension | hypothyroidism"),
        ("Note", "UK spelling uses -ae- and -oe- where US uses -e- (anaemia vs anemia, oedema vs edema)"),
    ]),
]

row = 4
for section_title, section_color, items in resources_data:
    h(ws5, row, 22)
    ws5.merge_cells(start_row=row, start_column=2, end_row=row, end_column=3)
    c = ws5.cell(row=row, column=2, value=section_title)
    c.fill = fill(section_color); c.font = font(bold=True, size=11, color=WHITE)
    c.alignment = align("left", "center")
    c.border = border("medium", "888888")
    row += 1

    sc_light = {"1A5276": LT_BLUE, "4A235A": LT_PURPLE, "7D6608": LT_AMBER,
                GREEN: LT_GREEN, RED: LT_RED, BLUE: LT_BLUE, PURPLE: LT_PURPLE, TEAL: LT_TEAL}
    bg_light = sc_light.get(section_color, GREY_L)

    for i, (label, detail) in enumerate(items):
        h(ws5, row, 20)
        bg = WHITE if i % 2 == 0 else bg_light
        cell(ws5, row, 2, label,  bg=bg, bold=True, halign="left")
        cell(ws5, row, 3, detail, bg=bg, wrap=True)
        row += 1
    row += 1

# ── SAVE ─────────────────────────────────────────────────────────────────────
out = "/tmp/workspace/plab-schedule/OET_Study_Plan_2026.xlsx"
wb.save(out)
print(f"Saved: {out}")
Running Command

cd /tmp/workspace/plab-schedule && python3 build_oet_plan.py 2>&1

Running Command

ls -lh /tmp/workspace/plab-schedule/OET_Study_Plan_2026.xlsx

Your OET study plan spreadsheet is ready. Download it here:

OET Study Plan 2026

Spreadsheet Β· XLSX


What's Inside β€” 5 Sheets

πŸ“Š Sheet 1 - Overview

  • Your IELTS profile vs OET target at a glance
  • 7-phase study plan summary table
  • Daily time allocation breakdown (2.5 hrs/day)
  • Priority order (Reading = #1 risk, fix first)

πŸ“… Sheet 2 - Weekly Plan (Main Sheet)

49 days mapped out, day by day, from Aug 1 to Sep 19:
  • Every single day has a specific task for all 4 sub-tests
  • Mock test days highlighted in red (6 full mocks scheduled)
  • Rest days built in so you don't burn out
  • Status dropdown on every row β€” mark as βœ… Done / ⏳ In Progress / ❌ Skipped / πŸ“ Partial
  • Exam days (Sep 18/19) marked clearly with exam-day reminders

βœ… Sheet 3 - Daily Checklist (Print this)

  • Sub-test by sub-test checklist of what to check EVERY session
  • Listening: distractor rule, conclusion language trigger
  • Reading: Question First steps, absolute word check
  • Writing: 5-bucket sort, filter, paraphrase rule
  • Speaking: 4-phase timing, ICE, close
  • Mock test checklist

πŸ“ˆ Sheet 4 - Score Tracker

  • Enter your mock scores after each of the 6 mocks
  • Auto-calculates average across all 4 sub-tests
  • Compares Mock 1 vs Mock 6 progress per sub-test
  • Auto-shows βœ… PASS / ❌ Below B status
  • Target Grade B line (350) displayed clearly

πŸ“Œ Sheet 5 - Resources & Tips

  • All official OET links
  • Best prep platforms (free + paid)
  • Free medical reading materials
  • Quick-tip reference for all 4 sub-tests
  • UK spelling list (the ones assessors check)

Start today with Sheet 2, Aug 1 row - it tells you exactly what to do first: take a cold, untimed OET sample test to establish your baseline. Everything else flows from there.

Can you also make workout plan for gym as i am 174 kg, 6.2 ft

Asking for Preferences

I was 155 kg back in university then i trained there and reduced to 98kg but after lock down i put on weight and due to studies I didn’t give attention to obesity. Then i joined gym on 9th july and have been doing 45 min treadmill and random strength workout.

Writing File

~/plab-schedule/build_gym_plan.py

from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
from openpyxl.worksheet.datavalidation import DataValidation

wb = Workbook()

# ── COLOURS ───────────────────────────────────────────────────────────────────
NAVY      = "1B3A6B"
BLUE      = "2E86C1"
LT_BLUE   = "D6EAF8"
GREEN     = "1E8449"
LT_GREEN  = "D5F5E3"
AMBER     = "D4AC0D"
LT_AMBER  = "FEF9E7"
RED       = "C0392B"
LT_RED    = "FDEDEC"
PURPLE    = "6C3483"
LT_PURPLE = "F4ECF7"
TEAL      = "117A65"
LT_TEAL   = "D1F2EB"
ORANGE    = "CA6F1E"
LT_ORANGE = "FDEBD0"
GREY_D    = "2C3E50"
GREY_L    = "F2F3F4"
WHITE     = "FFFFFF"
BLACK     = "000000"

# Day type colours
DAY_COLORS = {
    "Upper Body":   ("1A5276", "D6EAF8"),
    "Lower Body":   ("1E8449", "D5F5E3"),
    "Cardio":       ("7D6608", "FEF9E7"),
    "Full Body":    ("6C3483", "F4ECF7"),
    "Push":         ("922B21", "FDEDEC"),
    "Pull":         ("117A65", "D1F2EB"),
    "Legs":         ("1E8449", "D5F5E3"),
    "REST":         ("717D7E", "F2F3F4"),
    "Active Rest":  ("2E86C1", "EBF5FB"),
}

def fill(c): return PatternFill("solid", fgColor=c)
def font(bold=False, size=10, color=BLACK, italic=False, name="Calibri"):
    return Font(bold=bold, size=size, color=color, italic=italic, name=name)
def border(style="thin", color="CCCCCC"):
    s = Side(style=style, color=color)
    return Border(left=s, right=s, top=s, bottom=s)
def thick_border(color="888888"):
    t = Side(style="medium", color=color)
    return Border(left=t, right=t, top=t, bottom=t)
def align(h="left", v="center", wrap=False):
    return Alignment(horizontal=h, vertical=v, wrap_text=wrap)
def w(ws, col, width): ws.column_dimensions[get_column_letter(col)].width = width
def h(ws, row, height): ws.row_dimensions[row].height = height

def hdr(ws, row, col, text, bg=NAVY, fg=WHITE, sz=10, bold=True,
        halign="center", wrap=True):
    c = ws.cell(row=row, column=col, value=text)
    c.fill = fill(bg); c.font = font(bold=bold, size=sz, color=fg)
    c.alignment = align(halign, "center", wrap)
    c.border = border("thin", "999999")
    return c

def cell(ws, row, col, value=None, bg=WHITE, fg=BLACK, sz=10,
         bold=False, halign="left", wrap=True, border_col="CCCCCC"):
    c = ws.cell(row=row, column=col, value=value)
    c.fill = fill(bg); c.font = font(bold=bold, size=sz, color=fg)
    c.alignment = align(halign, "center", wrap)
    c.border = border("thin", border_col)
    return c

def merge_hdr(ws, r, cs, ce, text, bg=NAVY, fg=WHITE, sz=12, bold=True):
    ws.merge_cells(start_row=r, start_column=cs, end_row=r, end_column=ce)
    c = ws.cell(row=r, column=cs, value=text)
    c.fill = fill(bg); c.font = font(bold=bold, size=sz, color=fg)
    c.alignment = align("center", "center")
    c.border = border("medium", "888888")
    return c

def section_hdr(ws, r, cs, ce, text, bg, fg=WHITE, sz=11):
    ws.merge_cells(start_row=r, start_column=cs, end_row=r, end_column=ce)
    c = ws.cell(row=r, column=cs, value=text)
    c.fill = fill(bg); c.font = font(bold=True, size=sz, color=fg)
    c.alignment = align("left", "center")
    c.border = border("thin", "AAAAAA")
    return c

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 1 β€” OVERVIEW & PROFILE
# ══════════════════════════════════════════════════════════════════════════════
ws1 = wb.active
ws1.title = "πŸ“Š Overview"
ws1.sheet_view.showGridLines = False

for i, cw in enumerate([2,24,14,14,14,14,20,3], 1): w(ws1, i, cw)

h(ws1, 1, 6)
h(ws1, 2, 42)
merge_hdr(ws1, 2, 2, 7, "πŸ’ͺ  GYM WORKOUT PLAN  |  Weight Loss & Strength Programme",
          bg=NAVY, sz=16)
h(ws1, 3, 20)
merge_hdr(ws1, 3, 2, 7,
    "Height: 6'2\" (188cm)  |  Weight: 174kg  |  Previous Low: 98kg  |  Start Date: 09-Jul-2026",
    bg=BLUE, sz=10, bold=False)
h(ws1, 4, 6)

# Profile & Stats
h(ws1, 5, 18)
merge_hdr(ws1, 5, 2, 4, "YOUR STATS", bg=GREY_D)
merge_hdr(ws1, 5, 5, 7, "PROGRAMME OVERVIEW", bg=GREY_D)

stats = [
    ("Current Weight", "174 kg"),
    ("Height", "6'2\" / 188 cm"),
    ("BMI", "49.2 (Class III)"),
    ("Previous Low", "98 kg"),
    ("Weight to Goal (98kg)", "76 kg to lose"),
    ("Currently Active Since", "09 July 2026"),
]
stat_bgs = [LT_RED, LT_BLUE, LT_RED, LT_GREEN, LT_AMBER, LT_TEAL]
for i, (lbl, val) in enumerate(stats):
    r = 6 + i
    h(ws1, r, 20)
    cell(ws1, r, 2, lbl, bg=stat_bgs[i], bold=True)
    cell(ws1, r, 3, val, bg=stat_bgs[i], bold=True, halign="center",
         fg=RED if "BMI" in lbl else GREEN if "Previous" in lbl else NAVY)
    ws1.merge_cells(start_row=r, start_column=3, end_row=r, end_column=4)

prog_info = [
    ("Programme Type",   "Push/Pull/Legs + Daily Cardio"),
    ("Training Days",    "5 days/week + 1 active rest"),
    ("Session Length",   "75–90 minutes total"),
    ("Cardio",           "45 min daily (treadmill/bike/elliptical)"),
    ("Strength Focus",   "Compound lifts, progressive overload"),
    ("Phases",           "Phase 1: Foundation (8 wks) β†’ Phase 2: Strength (8 wks) β†’ Phase 3: Cut"),
]
prog_bgs = [LT_BLUE, LT_GREEN, LT_AMBER, LT_TEAL, LT_PURPLE, LT_ORANGE]
for i, (lbl, val) in enumerate(prog_info):
    r = 6 + i
    h(ws1, r, 20)
    cell(ws1, r, 5, lbl, bg=prog_bgs[i], bold=True)
    cell(ws1, r, 6, val, bg=prog_bgs[i])
    ws1.merge_cells(start_row=r, start_column=6, end_row=r, end_column=7)

h(ws1, 12, 6)

# Weekly schedule
h(ws1, 13, 18)
merge_hdr(ws1, 13, 2, 7, "WEEKLY TRAINING SPLIT", bg=GREY_D)
h(ws1, 14, 22)
for j, dh in enumerate(["Day", "Session Type", "Strength Focus", "Cardio", "Duration", "Intensity"], 2):
    hdr(ws1, 14, j, dh, bg=GREY_D)

weekly = [
    ("Monday",    "Push Day",    "Chest, Shoulders, Triceps",        "20 min HIIT treadmill",      "75 min", "Moderate-High"),
    ("Tuesday",   "Pull Day",    "Back, Biceps, Rear Delts",         "30 min steady treadmill",    "80 min", "Moderate"),
    ("Wednesday", "Legs Day",    "Quads, Hamstrings, Glutes, Calves","45 min treadmill (incline)",  "90 min", "Moderate"),
    ("Thursday",  "Active Rest", "Core + Stretching + Mobility",     "45 min brisk walk/elliptical","60 min", "Low"),
    ("Friday",    "Upper Body",  "Full upper β€” chest/back/shoulders","25 min steady cardio",        "85 min", "Moderate-High"),
    ("Saturday",  "Legs + Core", "Legs (lighter) + Core circuit",    "45 min treadmill (steady)",   "90 min", "Moderate"),
    ("Sunday",    "REST",        "Full rest or 20-min walk only",     "Optional: 20 min walk",       "β€”",     "Rest"),
]
day_bgs_map = {
    "Monday": LT_RED, "Tuesday": LT_TEAL, "Wednesday": LT_GREEN,
    "Thursday": LT_BLUE, "Friday": LT_ORANGE, "Saturday": LT_PURPLE, "Sunday": GREY_L
}
for i, (day, stype, focus, cardio, dur, intensity) in enumerate(weekly):
    r = 15 + i
    h(ws1, r, 22)
    bg = day_bgs_map[day]
    cell(ws1, r, 2, day,       bg=bg, bold=True, halign="center")
    cell(ws1, r, 3, stype,     bg=bg, bold=True, halign="center")
    cell(ws1, r, 4, focus,     bg=bg)
    cell(ws1, r, 5, cardio,    bg=bg)
    cell(ws1, r, 6, dur,       bg=bg, bold=True, halign="center")
    cell(ws1, r, 7, intensity, bg=bg, halign="center")

h(ws1, 22, 6)

# Phase overview
h(ws1, 23, 18)
merge_hdr(ws1, 23, 2, 7, "3-PHASE PROGRAMME ROADMAP", bg=GREY_D)
h(ws1, 24, 22)
for j, ph in enumerate(["Phase", "Duration", "Period", "Focus", "Expected Result", "Key Principle"], 2):
    hdr(ws1, 24, j, ph, bg=GREY_D)

phases_data = [
    ("Phase 1\nFoundation", "8 weeks", "Jul 9 – Sep 2, 2026",
     "Re-establish movement patterns, build work capacity, daily cardio habit",
     "8–12 kg weight loss, improved stamina, joints adapting",
     "High volume, moderate weight, perfect form. Don't ego-lift."),
    ("Phase 2\nStrength", "8 weeks", "Sep 3 – Oct 28, 2026",
     "Progressive overload on compound lifts, increase intensity",
     "Further 8–12 kg loss, noticeable strength gains, body recomposition",
     "Add weight every 1-2 weeks. Track every session."),
    ("Phase 3\nCut", "8 weeks+", "Oct 29, 2026 onwards",
     "Maintain muscle, accelerate fat loss with calorie deficit + cardio increase",
     "Push toward 130 kg range by early 2027",
     "Protein high, calories moderate deficit, cardio 60 min/day"),
]
phase_bgs = [LT_BLUE, LT_GREEN, LT_AMBER]
for i, (ph, dur, period, focus, result, principle) in enumerate(phases_data):
    r = 25 + i
    h(ws1, r, 44)
    bg = phase_bgs[i]
    cell(ws1, r, 2, ph,        bg=bg, bold=True, halign="center")
    cell(ws1, r, 3, dur,       bg=bg, bold=True, halign="center")
    cell(ws1, r, 4, period,    bg=bg, halign="center")
    cell(ws1, r, 5, focus,     bg=bg, wrap=True)
    cell(ws1, r, 6, result,    bg=bg, wrap=True)
    cell(ws1, r, 7, principle, bg=bg, wrap=True, italic=True, fg="444444")

# Important medical note
h(ws1, 28, 6)
h(ws1, 29, 20)
merge_hdr(ws1, 29, 2, 7,
    "⚠  MEDICAL NOTE: At 174kg, always warm up 10 min before lifting. Never hold breath during lifts (Valsalva carefully). "
    "Stop if chest pain, dizziness, or severe joint pain. Ideally get BP checked before starting heavy lifting.",
    bg=LT_RED, fg=RED, sz=10, bold=False)

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 2 β€” PHASE 1 WORKOUTS (detailed)
# ══════════════════════════════════════════════════════════════════════════════
ws2 = wb.create_sheet("πŸ“‹ Phase 1 Workouts")
ws2.sheet_view.showGridLines = False

for i, cw in enumerate([2,14,24,10,10,10,10,22,3], 1): w(ws2, i, cw)

h(ws2, 1, 6)
h(ws2, 2, 36)
merge_hdr(ws2, 2, 2, 8,
    "πŸ“‹  PHASE 1 WORKOUTS  |  Foundation  |  Weeks 1–8  (Jul 9 – Sep 2, 2026)",
    bg=NAVY, sz=14)
h(ws2, 3, 20)
merge_hdr(ws2, 3, 2, 8,
    "Focus: re-establish form, build work capacity, all compound movements. "
    "Rest 90 sec between sets. Increase weight when all reps feel easy.",
    bg=BLUE, sz=10, bold=False)
h(ws2, 4, 6)

# ── WORKOUT DATA ──────────────────────────────────────────────────────────────
workouts = {
    "MONDAY β€” PUSH DAY (Chest / Shoulders / Triceps)": (LT_RED, "922B21", [
        ("Warm-up",       "Treadmill walk",                "10 min",    "β€”",   "β€”",   "β€”",   "Build heart rate to 120 bpm"),
        ("A1",            "Incline Machine Press",          "4 Γ— 10",    "RPE 7","90s", "Wk5+","Safer than free bench at high BW. Full ROM."),
        ("A2",            "Cable Chest Fly",                "3 Γ— 12",    "RPE 6","60s", "β€”",   "Squeeze chest at peak. Control the negative."),
        ("B1",            "Seated Dumbbell Shoulder Press", "4 Γ— 10",    "RPE 7","90s", "Wk4+","Seated = safer for lower back at this weight."),
        ("B2",            "Lateral Raise (cable or DB)",    "3 Γ— 15",    "RPE 6","60s", "β€”",   "Lightweight, strict form, no swinging."),
        ("C1",            "Tricep Rope Pushdown",           "3 Γ— 12",    "RPE 6","60s", "β€”",   "Full extension, elbows fixed at sides."),
        ("C2",            "Overhead Tricep Extension (DB)", "3 Γ— 12",    "RPE 6","60s", "β€”",   "Stretch at top, control the negative."),
        ("Cardio Finisher","Treadmill steady (incline 3%)", "20 min",    "β€”",   "β€”",   "β€”",   "HR 130-145 bpm. Can split: 10 min pre + 10 min post."),
    ]),
    "TUESDAY β€” PULL DAY (Back / Biceps / Rear Delts)": (LT_TEAL, TEAL, [
        ("Warm-up",       "Treadmill / Bike",               "10 min",    "β€”",   "β€”",   "β€”",   "Easy pace, loosen up shoulders and back"),
        ("A1",            "Lat Pulldown (wide grip)",        "4 Γ— 10",    "RPE 7","90s", "Wk5+","Pull to upper chest. Don't lean back excessively."),
        ("A2",            "Seated Cable Row",                "4 Γ— 10",    "RPE 7","90s", "Wk5+","Drive elbows back. Squeeze shoulder blades together."),
        ("B1",            "Single-Arm DB Row",               "3 Γ— 12",    "RPE 7","75s", "Wk4+","Support on bench. Full stretch at bottom."),
        ("B2",            "Face Pull (cable rope)",          "3 Γ— 15",    "RPE 6","60s", "β€”",   "Crucial for shoulder health at this volume. Never skip."),
        ("C1",            "EZ Bar Curl",                     "3 Γ— 12",    "RPE 6","60s", "β€”",   "No swinging. Pause at top."),
        ("C2",            "Hammer Curl (alternating)",       "3 Γ— 12",    "RPE 6","60s", "β€”",   "Works brachialis β€” builds arm thickness."),
        ("Cardio Finisher","Treadmill steady",               "30 min",    "β€”",   "β€”",   "β€”",   "HR 125-140. Longest cardio day β€” this is your fat burner."),
    ]),
    "WEDNESDAY β€” LEGS DAY (Quads / Hamstrings / Glutes / Calves)": (LT_GREEN, GREEN, [
        ("Warm-up",       "Treadmill walk (incline 5%)",     "10 min",    "β€”",   "β€”",   "β€”",   "Hip circles and leg swings after. CRITICAL: never skip leg warm-up."),
        ("A1",            "Leg Press (wide stance)",         "4 Γ— 12",    "RPE 7","120s","Wk5+","Wide stance = less knee stress. Don't lock knees at top."),
        ("A2",            "Leg Extension (machine)",         "3 Γ— 15",    "RPE 6","60s", "β€”",   "Light weight, high reps. Control on the way down."),
        ("B1",            "Romanian Deadlift (RDL)",         "3 Γ— 10",    "RPE 7","90s", "Wk4+","Hinge at hips. Feel hamstring stretch. Back flat always."),
        ("B2",            "Leg Curl (machine, lying)",       "3 Γ— 12",    "RPE 6","75s", "β€”",   "Full range. Squeeze at top of contraction."),
        ("C1",            "Glute Bridge or Hip Thrust",      "3 Γ— 15",    "RPE 6","60s", "β€”",   "Activate glutes β€” protects knees and lower back."),
        ("C2",            "Calf Raise (standing, machine)",  "4 Γ— 15",    "RPE 6","45s", "β€”",   "Full stretch at bottom. Don't bounce."),
        ("Cardio Finisher","Treadmill (incline 4-6%)",       "20 min",    "β€”",   "β€”",   "β€”",   "Walking at incline burns more than flat running at this weight."),
    ]),
    "THURSDAY β€” ACTIVE REST (Core / Mobility / Light Cardio)": (LT_BLUE, BLUE, [
        ("Warm-up",       "5-min easy walk",                 "5 min",     "β€”",   "β€”",   "β€”",   "Just get moving. No rush."),
        ("A1",            "Dead Bug",                        "3 Γ— 10",    "RPE 5","45s", "β€”",   "Core stability. Lower back stays flat on ground at all times."),
        ("A2",            "Plank (modified on knees OK)",    "3 Γ— 30s",   "RPE 5","45s", "β€”",   "Build to full plank over weeks. Quality over duration."),
        ("B1",            "Bird Dog",                        "3 Γ— 10",    "RPE 4","45s", "β€”",   "Opposite arm/leg. Great for lower back stability."),
        ("B2",            "Hip Flexor Stretch",              "3 Γ— 45s",   "β€”",   "β€”",   "β€”",   "Each side. Tight hip flexors from sitting = injury risk."),
        ("C1",            "Foam Roll (quads, IT band, back)","10 min",    "β€”",   "β€”",   "β€”",   "Slow rolls on sore spots. 30 sec each area."),
        ("C2",            "Shoulder Mobility Circuit",       "5 min",     "β€”",   "β€”",   "β€”",   "Arm circles, cross-body stretch, door frame pec stretch."),
        ("Cardio",        "Brisk walk or elliptical",        "45 min",    "β€”",   "β€”",   "β€”",   "HR 110-125. Active recovery burns cals without taxing muscles."),
    ]),
    "FRIDAY β€” UPPER BODY (Full Upper β€” Chest / Back / Shoulders)": (LT_ORANGE, ORANGE, [
        ("Warm-up",       "Bike or treadmill",               "10 min",    "β€”",   "β€”",   "β€”",   "Band pull-aparts x 15 after β€” shoulder warm-up"),
        ("A1",            "Machine Chest Press (flat)",      "4 Γ— 10",    "RPE 7","90s", "Wk5+","Compound push. Full ROM, controlled negative."),
        ("A2",            "Cable Row (seated, narrow grip)", "4 Γ— 10",    "RPE 7","90s", "Wk5+","Compound pull. Superset with A1 if gym allows."),
        ("B1",            "Incline DB Press",                "3 Γ— 10",    "RPE 7","90s", "Wk4+","Upper chest emphasis. Don't flare elbows too wide."),
        ("B2",            "Single-Arm Cable Row",            "3 Γ— 12",    "RPE 7","75s", "β€”",   "Unilateral β€” addresses muscle imbalances."),
        ("C1",            "DB Lateral Raise",                "3 Γ— 15",    "RPE 6","60s", "β€”",   "Shoulder width and definition."),
        ("C2",            "Tricep Pushdown (straight bar)",  "3 Γ— 12",    "RPE 6","60s", "β€”",   "Arms straight at sides. Elbows locked."),
        ("C3",            "DB Hammer Curl",                  "3 Γ— 12",    "RPE 6","60s", "β€”",   "Arms."),
        ("Cardio Finisher","Treadmill steady",               "25 min",    "β€”",   "β€”",   "β€”",   "HR 130-145. Good post-lift fat burning window."),
    ]),
    "SATURDAY β€” LEGS + CORE (Lighter Legs + Core Circuit)": (LT_PURPLE, PURPLE, [
        ("Warm-up",       "Treadmill walk (incline)",        "10 min",    "β€”",   "β€”",   "β€”",   "10 bodyweight squats + hip circles after"),
        ("A1",            "Goblet Squat (DB or KB)",         "4 Γ— 12",    "RPE 6","90s", "Wk4+","Squat depth to ability. Heels flat. Chest up."),
        ("A2",            "Step-Up (low box, bodyweight)",   "3 Γ— 10",    "RPE 5","60s", "β€”",   "Functional movement. Builds glutes and quads safely."),
        ("B1",            "Leg Curl (machine)",              "3 Γ— 12",    "RPE 6","60s", "β€”",   "Hamstring maintenance from Wednesday."),
        ("B2",            "Calf Raise (seated machine)",     "3 Γ— 15",    "RPE 5","45s", "β€”",   "Seated hits soleus (deeper calf). Variety."),
        ("C1",            "Cable Crunch (kneeling)",         "3 Γ— 15",    "RPE 6","60s", "β€”",   "Best ab exercise at this weight. Weighted ab work > crunches."),
        ("C2",            "Pallof Press (cable anti-rotation)","3 Γ— 12",  "RPE 6","60s", "β€”",   "Core stability β€” protects lumbar spine under load."),
        ("C3",            "Reverse Crunch",                  "3 Γ— 12",    "RPE 5","45s", "β€”",   "Lower abs. Slow and controlled."),
        ("Cardio Finisher","Treadmill (steady, incline 4%)", "20 min",    "β€”",   "β€”",   "β€”",   "Keep HR 125-140. Don't push hard β€” recovery day."),
    ]),
}

row = 5
for workout_title, (bg_light, fg_dark, exercises) in workouts.items():
    # Section header
    h(ws2, row, 24)
    ws2.merge_cells(start_row=row, start_column=2, end_row=row, end_column=8)
    c = ws2.cell(row=row, column=2, value=f"  {workout_title}")
    c.fill = fill(fg_dark); c.font = font(bold=True, size=11, color=WHITE)
    c.alignment = align("left", "center")
    c.border = thick_border(fg_dark)
    row += 1

    # Column headers
    h(ws2, row, 22)
    ex_hdrs = ["#", "Exercise", "Sets Γ— Reps", "RPE", "Rest", "Progress", "Notes / Form Cues"]
    ex_hdr_bg = [fg_dark] * 7
    for j, (eh, eb) in enumerate(zip(ex_hdrs, ex_hdr_bg), 2):
        hdr(ws2, row, j, eh, bg=eb)
    row += 1

    # Exercise rows
    alt = [WHITE, bg_light]
    for i, (num, ex, sets, rpe, rest, prog, notes) in enumerate(exercises):
        h(ws2, row, 36)
        bg = alt[i % 2]
        is_cardio = "Cardio" in num or "Warm" in num
        cell(ws2, row, 2, num,   bg=bg, bold=True, halign="center",
             fg=BLUE if is_cardio else fg_dark)
        cell(ws2, row, 3, ex,    bg=bg, bold=not is_cardio)
        cell(ws2, row, 4, sets,  bg=bg, bold=True, halign="center", fg=fg_dark)
        cell(ws2, row, 5, rpe,   bg=bg, halign="center")
        cell(ws2, row, 6, rest,  bg=bg, halign="center")
        cell(ws2, row, 7, prog,  bg=bg if not prog else LT_GREEN,
             halign="center", fg=GREEN if prog else "999999",
             bold=bool(prog))
        cell(ws2, row, 8, notes, bg=bg, wrap=True, fg="333333", sz=9)
        row += 1

    row += 1  # spacer

# Progress note
h(ws2, row, 28)
ws2.merge_cells(start_row=row, start_column=2, end_row=row, end_column=8)
c = ws2.cell(row=row, column=2,
    value="  PROGRESSION RULE: When all reps in all sets feel RPE ≀6, increase weight by 2.5–5kg next session. "
          "Track in the Session Log sheet. Never increase weight if form breaks down.")
c.fill = fill(LT_GREEN); c.font = font(bold=True, size=10, color=GREEN)
c.alignment = align("left", "center")
c.border = thick_border(GREEN)

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 3 β€” CARDIO PLAN
# ══════════════════════════════════════════════════════════════════════════════
ws3 = wb.create_sheet("πŸƒ Cardio Plan")
ws3.sheet_view.showGridLines = False

for i, cw in enumerate([2,14,16,14,14,14,26,3], 1): w(ws3, i, cw)

h(ws3, 1, 6)
h(ws3, 2, 36)
merge_hdr(ws3, 2, 2, 7,
    "πŸƒ  CARDIO PLAN  |  Your most important fat-loss tool at this stage",
    bg=NAVY, sz=14)
h(ws3, 3, 18)
merge_hdr(ws3, 3, 2, 7,
    "At 174kg, 45 min treadmill burns ~550-700 kcal/session. This is your #1 fat loss lever. "
    "Keep it consistent before adding intensity.",
    bg=BLUE, sz=10, bold=False)
h(ws3, 4, 6)

# Cardio types explained
h(ws3, 5, 18)
merge_hdr(ws3, 5, 2, 7, "CARDIO TYPES β€” KNOW THESE", bg=GREY_D)
h(ws3, 6, 22)
for j, ch in enumerate(["Type", "HR Zone", "Effort", "Duration", "Best For", "Notes"], 2):
    hdr(ws3, 6, j, ch, bg=GREY_D)

cardio_types = [
    ("LISS\n(Low Intensity Steady State)", "60-70% max HR\n(~125-140 bpm)", "Can hold a conversation",
     "30-60 min", "Fat burning, recovery, daily calorie burn",
     "Your main cardio right now. Treadmill walk at incline 3-5%. Burns fat without taxing muscles."),
    ("MISS\n(Moderate Intensity)", "70-80% max HR\n(~140-155 bpm)", "Can speak in short sentences",
     "20-40 min", "Increased calorie burn, cardiovascular fitness",
     "Add from Week 5 onwards. Brisk walk + occasional jog intervals on treadmill."),
    ("HIIT\n(High Intensity Intervals)", "80-90% max HR\n(~155-170 bpm)", "Difficult to speak",
     "15-25 min", "Post-exercise calorie burn (EPOC), fitness spike",
     "Add ONLY from Week 8+ and only on Push/Upper days. NOT on leg days. 30s on / 60s off format."),
]
ct_bgs = [LT_GREEN, LT_AMBER, LT_RED]
for i, (ct, hr, effort, dur, best, notes) in enumerate(cardio_types):
    r = 7 + i
    h(ws3, r, 44)
    bg = ct_bgs[i]
    cell(ws3, r, 2, ct,     bg=bg, bold=True, halign="center")
    cell(ws3, r, 3, hr,     bg=bg, halign="center")
    cell(ws3, r, 4, effort, bg=bg, halign="center")
    cell(ws3, r, 5, dur,    bg=bg, bold=True, halign="center")
    cell(ws3, r, 6, best,   bg=bg, wrap=True)
    cell(ws3, r, 7, notes,  bg=bg, wrap=True, fg="333333")

h(ws3, 10, 6)

# Weekly cardio plan
h(ws3, 11, 18)
merge_hdr(ws3, 11, 2, 7, "WEEKLY CARDIO SCHEDULE", bg=GREY_D)
h(ws3, 12, 22)
for j, ch in enumerate(["Day", "Type", "Duration", "Intensity", "HR Target", "Notes"], 2):
    hdr(ws3, 12, j, ch, bg=GREY_D)

cardio_week = [
    ("Monday",    "LISS β†’ MISS (Wk5+)",  "20 min post-strength",   "Low-Moderate", "125-145", "Post-push day. Don't exhaust yourself for Tuesday pull."),
    ("Tuesday",   "LISS",                "30 min post-strength",   "Low",          "120-135", "Longest cardio. Treadmill or bike. Fat burning focus."),
    ("Wednesday", "LISS (incline walk)", "20 min post-strength",   "Moderate",     "130-145", "Incline 4-6%. Burns more calories than flat running at this weight."),
    ("Thursday",  "LISS (Active Rest)",  "45 min walk/elliptical", "Very Low",     "110-130", "Recovery cardio. The elliptical is excellent β€” no impact on joints."),
    ("Friday",    "LISS β†’ HIIT (Wk8+)", "25 min post-strength",   "Moderate",     "130-150", "HIIT only from Wk 8+. 30s sprint / 60s walk on treadmill."),
    ("Saturday",  "LISS",               "20 min post-strength",   "Low",          "120-135", "Light day. Keep HR low. Recovery."),
    ("Sunday",    "Optional walk",       "20 min (optional)",      "Very Low",     "100-120", "Just a walk. Or full rest. Listen to your body."),
]
day_bgs_c = [LT_RED, LT_TEAL, LT_GREEN, LT_BLUE, LT_ORANGE, LT_PURPLE, GREY_L]
for i, (day, ctype, dur, intensity, hr, notes) in enumerate(cardio_week):
    r = 13 + i
    h(ws3, r, 28)
    bg = day_bgs_c[i]
    cell(ws3, r, 2, day,       bg=bg, bold=True, halign="center")
    cell(ws3, r, 3, ctype,     bg=bg, bold=True)
    cell(ws3, r, 4, dur,       bg=bg, bold=True, halign="center")
    cell(ws3, r, 5, intensity, bg=bg, halign="center")
    cell(ws3, r, 6, hr,        bg=bg, bold=True, halign="center", fg=GREEN)
    cell(ws3, r, 7, notes,     bg=bg, wrap=True, fg="333333")

h(ws3, 20, 6)

# 8-week cardio progression
h(ws3, 21, 18)
merge_hdr(ws3, 21, 2, 7, "8-WEEK CARDIO PROGRESSION", bg=GREY_D)
h(ws3, 22, 22)
for j, ch in enumerate(["Weeks", "Mon", "Tue", "Wed", "Thu", "Fri + Sat"], 2):
    hdr(ws3, 22, j, ch, bg=GREY_D)

prog_8wk = [
    ("Wk 1-2", "20 min LISS", "30 min LISS", "20 min incline walk", "45 min walk", "25 + 20 min LISS"),
    ("Wk 3-4", "20 min LISS", "35 min LISS", "25 min incline walk", "45 min walk", "25 + 20 min LISS"),
    ("Wk 5-6", "20 min MISS", "35 min LISS", "30 min incline walk", "45 elliptical","25 min MISS + 20 LISS"),
    ("Wk 7-8", "20 min MISS", "40 min LISS", "30 min incline walk", "45 elliptical","25 MISS + 20 LISS"),
    ("Wk 8+",  "20 min HIIT","40 min LISS", "30 min incline walk", "45 elliptical","25 HIIT + 20 LISS"),
]
for i, row_data in enumerate(prog_8wk):
    r = 23 + i
    h(ws3, r, 24)
    bgs = [LT_GREEN, LT_GREEN, LT_AMBER, LT_AMBER, LT_RED]
    bg = bgs[i]
    for j, val in enumerate(row_data, 2):
        cell(ws3, r, j, val, bg=bg, halign="center" if j == 2 else "left",
             bold=(j == 2), wrap=True)

h(ws3, 28, 6)

# Max HR note
h(ws3, 29, 20)
merge_hdr(ws3, 29, 2, 7,
    "YOUR MAX HR ESTIMATE: 220 - 29 (age) = ~191 bpm.  "
    "Fat burning zone (60-70%): 115-134 bpm.  Aerobic zone (70-80%): 134-153 bpm.  "
    "Get a cheap HR monitor watch β€” this is the single best investment for cardio.",
    bg=LT_AMBER, fg=AMBER, sz=10, bold=False)

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 4 β€” NUTRITION GUIDELINES
# ══════════════════════════════════════════════════════════════════════════════
ws4 = wb.create_sheet("πŸ₯— Nutrition")
ws4.sheet_view.showGridLines = False

for i, cw in enumerate([2,26,52,3], 1): w(ws4, i, cw)

h(ws4, 1, 6)
h(ws4, 2, 36)
merge_hdr(ws4, 2, 2, 3,
    "πŸ₯—  NUTRITION GUIDELINES  |  Exercise without diet = 50% of results",
    bg=NAVY, sz=14)
h(ws4, 3, 18)
merge_hdr(ws4, 3, 2, 3,
    "You don't need a complex diet. These simple rules will accelerate your results.",
    bg=BLUE, sz=10, bold=False)
h(ws4, 4, 6)

nutrition_sections = [
    ("CALORIE TARGETS", GREEN, [
        ("Maintenance calories (est.)", "~3,200-3,500 kcal/day at 174kg, moderately active"),
        ("Target for weight loss",      "2,500-2,800 kcal/day (deficit of ~500-700 kcal)"),
        ("Expected loss at this deficit","~0.5-0.8 kg per week β€” realistic and sustainable"),
        ("Do NOT drop below",           "2,200 kcal β€” muscle loss increases, energy crashes, training suffers"),
        ("Weigh yourself",              "Once per week, same time (morning, after toilet, before food). Log it."),
    ]),
    ("PROTEIN β€” YOUR #1 PRIORITY", RED, [
        ("Daily protein target",        "2.0-2.2g per kg of TARGET bodyweight (use 100kg target = 200-220g/day)"),
        ("Why this matters",            "Preserves muscle while losing fat. Keeps you fuller. Higher thermic effect."),
        ("Best sources",                "Chicken breast, eggs, Greek yoghurt, paneer, dal, fish, whey protein"),
        ("Protein per meal (roughly)",  "Aim for 30-40g protein per main meal Γ— 3 meals + snacks"),
        ("Supplement",                  "1-2 whey protein shakes/day if hitting target from food is difficult"),
    ]),
    ("CARBOHYDRATES", AMBER, [
        ("Carb timing",                 "Eat most carbs around your workout β€” pre and post training"),
        ("Best sources",                "Oats, brown rice, sweet potato, roti (1-2), fruits, vegetables"),
        ("Reduce / avoid",              "White bread, maida products, fried snacks, packaged biscuits"),
        ("Portion strategy",            "Fill half your plate with vegetables, quarter protein, quarter carbs"),
        ("Pre-workout meal",            "1-2 hrs before gym: rice/roti + protein (chicken/eggs). Don't train empty."),
    ]),
    ("FATS", BLUE, [
        ("Daily fat target",            "60-80g/day. Fat is essential β€” don't eliminate it."),
        ("Best sources",                "Almonds, walnuts, olive oil, ghee (small amount), avocado, eggs"),
        ("Avoid",                       "Trans fats, excessive deep-fried food, vanaspati/dalda"),
        ("Omega-3 specifically",        "Fish or fish oil supplement β€” reduces inflammation from heavy training"),
    ]),
    ("HYDRATION β€” CRITICAL AT 174KG", TEAL, [
        ("Daily water target",          "Minimum 4-5 litres/day. More on training days."),
        ("Why it matters",              "At high body weight, kidney load is high. Water also aids fat metabolism."),
        ("During workout",              "500ml-750ml during session. Don't wait to feel thirsty."),
        ("Easy habit",                  "1 large glass of water before every meal and before every gym session"),
        ("Electrolytes",                "Add a pinch of salt + squeeze of lemon to 1-2 water bottles on heavy training days"),
    ]),
    ("MEAL TIMING & STRUCTURE", PURPLE, [
        ("Breakfast (pre-workout or morning)","Oats + milk + banana + 2 boiled eggs  OR  Greek yoghurt + fruit + protein shake"),
        ("Lunch (main meal)",           "Rice/roti + dal/chicken/fish + vegetables + salad"),
        ("Pre-workout snack (if evening gym)","Banana + handful of almonds OR 1 slice brown bread + peanut butter"),
        ("Post-workout",                "Protein shake within 30-45 min + a piece of fruit. Don't delay this."),
        ("Dinner",                      "Lighter than lunch. Protein + vegetables. Reduce carbs if not training next morning."),
        ("Avoid late night eating",     "Try to finish eating 2-3 hrs before sleep. Reduces insulin resistance."),
    ]),
    ("WHAT TO TRACK", GREY_D, [
        ("Track protein daily",         "Even rough tracking (use MyFitnessPal or Cronometer app) helps enormously"),
        ("Track body weight weekly",    "Every Monday morning. Note in Session Log sheet."),
        ("Track measurements monthly", "Waist, hips, chest, thighs. The scale doesn't show muscle gain but measurements do."),
        ("Track energy levels",         "Rate 1-10 daily. If consistently below 5, eat more or rest more."),
        ("Don't track obsessively",     "Rough tracking is better than no tracking. Perfection is the enemy of progress."),
    ]),
]

row = 5
for sec_title, sec_color, items in nutrition_sections:
    h(ws4, row, 22)
    ws4.merge_cells(start_row=row, start_column=2, end_row=row, end_column=3)
    c = ws4.cell(row=row, column=2, value=f"  {sec_title}")
    c.fill = fill(sec_color); c.font = font(bold=True, size=11, color=WHITE)
    c.alignment = align("left", "center")
    c.border = thick_border(sec_color)
    row += 1

    sc_lite = {GREEN: LT_GREEN, RED: LT_RED, AMBER: LT_AMBER,
               BLUE: LT_BLUE, TEAL: LT_TEAL, PURPLE: LT_PURPLE, GREY_D: GREY_L}
    bg_l = sc_lite.get(sec_color, GREY_L)
    for i, (lbl, detail) in enumerate(items):
        h(ws4, row, 28)
        bg = WHITE if i % 2 == 0 else bg_l
        cell(ws4, r=row, col=2, value=lbl,    bg=bg, bold=True)
        cell(ws4, r=row, col=3, value=detail, bg=bg, wrap=True)
        row += 1
    row += 1

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 5 β€” SESSION LOG (tracker)
# ══════════════════════════════════════════════════════════════════════════════
ws5 = wb.create_sheet("πŸ“ Session Log")
ws5.sheet_view.showGridLines = False

for i, cw in enumerate([2,10,8,14,10,10,10,10,14,14,3], 1): w(ws5, i, cw)

h(ws5, 1, 6)
h(ws5, 2, 36)
merge_hdr(ws5, 2, 2, 10,
    "πŸ“  SESSION LOG  |  Track every workout. Progress only happens when you measure it.",
    bg=NAVY, sz=14)
h(ws5, 3, 18)
merge_hdr(ws5, 3, 2, 10,
    "Fill in after every session. Blue cells = enter data. The pattern over weeks tells you everything.",
    bg=BLUE, sz=10, bold=False)
h(ws5, 4, 6)

# Weekly weight box
h(ws5, 5, 18)
merge_hdr(ws5, 5, 2, 5, "WEEKLY WEIGHT (Monday morning, before food)", bg=GREEN)
merge_hdr(ws5, 5, 6, 10, "Starting: 174 kg  |  Target: 98 kg  |  To lose: 76 kg", bg=LT_GREEN, fg=GREEN, sz=10)

h(ws5, 6, 20)
for j, wh in enumerate(["Week #", "Date", "Weight (kg)", "Change (kg)", "Running Total Lost"], 2):
    hdr(ws5, 6, j, wh, bg=GREY_D)

for i in range(24):  # 24 weeks of tracking
    r = 7 + i
    h(ws5, r, 20)
    bg = LT_GREEN if i == 0 else (WHITE if i % 2 == 0 else GREY_L)

    cell(ws5, r, 2, f"Week {i+1}", bg=bg, bold=True, halign="center")
    # Date (empty for user)
    c_date = ws5.cell(row=r, column=3, value=None)
    c_date.fill = fill("FFFDE7"); c_date.font = font(color="0000FF", size=10)
    c_date.alignment = align("center"); c_date.border = border()
    # Weight (empty for user)
    c_wt = ws5.cell(row=r, column=4, value=174 if i == 0 else None)
    c_wt.fill = fill("FFFDE7"); c_wt.font = font(color="0000FF", size=11, bold=True)
    c_wt.alignment = align("center"); c_wt.border = border("medium", BLUE)
    c_wt.number_format = "0.0"
    # Change
    if i > 0:
        c_chg = ws5.cell(row=r, column=5, value=f"=IF(D{r}=\"\",\"\",D{r}-D{r-1})")
        c_chg.fill = fill(bg); c_chg.font = font(size=10)
        c_chg.alignment = align("center"); c_chg.border = border()
        c_chg.number_format = "+0.0;-0.0;0.0"
    else:
        cell(ws5, r, 5, "β€”", bg=bg, halign="center")
    # Running total
    c_tot = ws5.cell(row=r, column=6, value=f"=IF(D{r}=\"\",\"\",174-D{r})")
    c_tot.fill = fill(bg); c_tot.font = font(bold=True, color=GREEN, size=10)
    c_tot.alignment = align("center"); c_tot.border = border()
    c_tot.number_format = "0.0"
    ws5.merge_cells(start_row=r, start_column=6, end_row=r, end_column=10)

h(ws5, 31, 6)

# Session log headers
h(ws5, 32, 18)
merge_hdr(ws5, 32, 2, 10, "WORKOUT SESSION LOG", bg=GREY_D)
h(ws5, 33, 22)
session_hdrs = ["Date", "Day Type", "Exercises Done", "Avg Weight", "Total Sets", "Cardio (min)", "HR Avg", "Energy (1-10)", "Notes"]
for j, sh in enumerate(session_hdrs, 2):
    hdr(ws5, 33, j, sh, bg=GREY_D, sz=9)

# Pre-fill recent sessions
recent = [
    ("09-Jul", "Push/Cardio", "Treadmill + random strength", "", "", "45", "", "?", "First day back"),
    ("10-Jul", "β€”",           "Session 2",                   "", "", "45", "", "?", ""),
]
for i, sess in enumerate(recent):
    r = 34 + i
    h(ws5, r, 20)
    bg = LT_BLUE if i == 0 else WHITE
    for j, val in enumerate(sess, 2):
        c = ws5.cell(row=r, column=j, value=val)
        c.fill = fill("FFFDE7" if j in [4,5,6,7,8,9] else bg)
        c.font = font(color="0000FF" if j in [4,5,6,7,8,9] else BLACK, size=10)
        c.alignment = align("center" if j > 4 else "left", "center", True)
        c.border = border()

# Add 50 blank log rows
for i in range(50):
    r = 34 + len(recent) + i
    h(ws5, r, 20)
    bg = WHITE if i % 2 == 0 else GREY_L
    for j in range(2, 11):
        c = ws5.cell(row=r, column=j)
        c.fill = fill("FFFDE7" if j in [4,5,6,7,8,9] else bg)
        c.border = border()
        if j in [4,5,6,7,8]:
            c.font = font(color="0000FF", size=10)
        c.alignment = align("center" if j > 4 else "left", "center")

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 6 β€” MILESTONES & MOTIVATION
# ══════════════════════════════════════════════════════════════════════════════
ws6 = wb.create_sheet("πŸ† Milestones")
ws6.sheet_view.showGridLines = False

for i, cw in enumerate([2,18,14,20,18,3], 1): w(ws6, i, cw)

h(ws6, 1, 6)
h(ws6, 2, 36)
merge_hdr(ws6, 2, 2, 5,
    "πŸ†  MILESTONES & GOAL TRACKER  |  From 174 kg back to 98 kg",
    bg=NAVY, sz=14)
h(ws6, 3, 18)
merge_hdr(ws6, 3, 2, 5,
    "You did it before β€” 155 kg β†’ 98 kg. You know the way. "
    "This time, track every milestone. Each one matters.",
    bg=BLUE, sz=10, bold=False)
h(ws6, 4, 6)

# Milestone table
h(ws6, 5, 18)
merge_hdr(ws6, 5, 2, 5, "WEIGHT MILESTONES", bg=GREY_D)
h(ws6, 6, 22)
for j, mh in enumerate(["Milestone", "Target Weight", "Est. Timeline", "Achieved Date", "Notes"], 2):
    hdr(ws6, 6, j, mh, bg=GREY_D)

milestones = [
    ("First 5 kg lost",         "169 kg", "4-5 weeks from start",   "", "Habit formation milestone"),
    ("First 10 kg lost",        "164 kg", "8-10 weeks",              "", "Clothes start to feel looser"),
    ("First 15 kg lost",        "159 kg", "12-15 weeks",             "", "Cardio noticeably easier"),
    ("Back to university entry","155 kg", "4-5 months",              "", "You were this weight starting uni"),
    ("20% of journey done",     "151 kg", "5-6 months",              "", "Real body recomposition visible"),
    ("Halfway to goal",         "136 kg", "8-10 months",             "", "MAJOR milestone β€” celebrate well"),
    ("University best ever",    "120 kg", "12-14 months",            "", "Approaching athletic range"),
    ("Under 110 kg",            "109 kg", "15-18 months",            "", "Significant health improvement"),
    ("GOAL: 98 kg",             "98 kg",  "18-24 months",            "", "You've been here before. You'll be here again."),
]
mile_bgs = [LT_BLUE,LT_BLUE,LT_TEAL,LT_TEAL,LT_GREEN,LT_GREEN,LT_AMBER,LT_AMBER,LT_RED]
for i, (mile, wt, timeline, date, notes) in enumerate(milestones):
    r = 7 + i
    h(ws6, r, 24)
    bg = mile_bgs[i]
    cell(ws6, r, 2, mile,     bg=bg, bold=(i==8), fg=RED if i==8 else NAVY)
    cell(ws6, r, 3, wt,       bg=bg, bold=True, halign="center", fg=GREEN)
    cell(ws6, r, 4, timeline, bg=bg, halign="center")
    c_date = ws6.cell(row=r, column=5, value=date)
    c_date.fill = fill("FFFDE7"); c_date.font = font(color="0000FF", size=10)
    c_date.alignment = align("center"); c_date.border = border("medium", BLUE)
    cell(ws6, r, 6, notes,    bg=bg, fg="555555", italic=True)

h(ws6, 16, 6)

# Non-scale victories
h(ws6, 17, 18)
merge_hdr(ws6, 17, 2, 5, "NON-SCALE VICTORIES β€” TRACK THESE TOO", bg=TEAL)
nscv = [
    ("Cardio endurance",  "Can complete 45 min treadmill without stopping"),
    ("Strength",          "Added weight to any exercise since starting"),
    ("Sleep quality",     "Waking up feeling more rested"),
    ("Energy levels",     "Afternoon energy better than before"),
    ("Resting HR",        "HR monitor showing lower resting HR over weeks"),
    ("Clothes fit",       "Jeans/shirt looser than when you started"),
    ("Confidence",        "Walking into gym feels normal, not daunting"),
    ("Consistency",       "5 sessions in a row without skipping"),
    ("PLAB studies",      "Gym + study both happening simultaneously β€” discipline is discipline"),
]
for i, (nsc, detail) in enumerate(nscv):
    r = 18 + i
    h(ws6, r, 20)
    bg = WHITE if i % 2 == 0 else LT_TEAL
    cell(ws6, r, 2, "☐", bg=bg, bold=True, sz=14, halign="center")
    cell(ws6, r, 3, nsc,    bg=bg, bold=True)
    ws6.merge_cells(start_row=r, start_column=4, end_row=r, end_column=6)
    cell(ws6, r, 4, detail, bg=bg, fg="333333")

h(ws6, 27, 6)

# Motivational note
h(ws6, 28, 36)
merge_hdr(ws6, 28, 2, 5,
    "You went from 155 kg β†’ 98 kg once before. That is 57 kg. "
    "Your body knows how. Your muscles remember. "
    "You are now 3 weeks in and already moving. That is the hardest part β€” and you have already done it. "
    "The only question now is consistency. Trust the process.",
    bg=LT_GREEN, fg=GREEN, sz=10, bold=False)

# ── SAVE ─────────────────────────────────────────────────────────────────────
out = "/tmp/workspace/plab-schedule/Gym_Workout_Plan_2026.xlsx"
wb.save(out)
print(f"Saved: {out}")
Running Command

cd /tmp/workspace/plab-schedule && python3 build_gym_plan.py 2>&1

Running Command

cd /tmp/workspace/plab-schedule && sed -i 's/def cell(ws, row, col, value=None, bg=WHITE, fg=BLACK, sz=10,\n bold=False, halign="left", wrap=True, border_col="CCCCCC"):/def cell(ws, row, col, value=None, bg=WHITE, fg=BLACK, sz=10, bold=False, halign="left", wrap=True, border_col="CCCCCC", italic=False):/' build_gym_plan.py 2>&1 | head -5 grep -n "def cell" build_gym_plan.py

Reading File
Editing File

~/plab-schedule/build_gym_plan.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/plab-schedule/build_gym_plan.py'
Running Command

cd /tmp/workspace/plab-schedule && python3 build_gym_plan.py 2>&1

Running Command

ls -lh /tmp/workspace/plab-schedule/Gym_Workout_Plan_2026.xlsx

Your complete gym plan is ready. Download it here:

Gym Workout Plan 2026

Spreadsheet Β· XLSX


What's Inside β€” 6 Sheets

πŸ“Š Sheet 1 - Overview

  • Your stats and 3-phase programme roadmap
  • Full weekly split (Push/Pull/Legs + cardio structure)
  • Timeline: Phase 1 Foundation β†’ Phase 2 Strength β†’ Phase 3 Cut

πŸ“‹ Sheet 2 - Phase 1 Workouts (full exercise detail)

Every session laid out with:
  • Exercise name, sets Γ— reps, RPE (effort level 1-10), rest time
  • Progression markers (when to increase weight from Week 4/5)
  • Form cues in notes column for every exercise
  • 6 sessions: Push, Pull, Legs, Active Rest, Upper Body, Legs+Core

πŸƒ Sheet 3 - Cardio Plan

  • LISS vs MISS vs HIIT explained with your HR targets
  • Weekly cardio schedule per day with exact duration and intensity
  • 8-week cardio progression (builds from pure LISS to HIIT by Week 8)
  • Your max HR estimate and fat burning zone (115-134 bpm)

πŸ₯— Sheet 4 - Nutrition Guidelines

  • Calorie targets (2,500-2,800/day for your deficit)
  • Protein priority (200-220g/day - most important thing)
  • Meal timing and structure with Indian food examples
  • Hydration targets (4-5L/day minimum)

πŸ“ Sheet 5 - Session Log

  • 24-week weight tracker (every Monday) with auto-calculated loss
  • 50-row session log with pre-filled yellow cells for: exercises, weight used, sets, cardio minutes, HR, energy rating
  • Auto-tracks running total kg lost from 174 kg

πŸ† Sheet 6 - Milestones

  • 9 weight milestones from 174 kg β†’ 98 kg with estimated timelines
  • Non-scale victories checklist (fitness, energy, clothes, sleep)
  • Motivational note at the bottom (because consistency needs reminding)

Key Principles for Your Situation

Why Push/Pull/Legs and not random strength? Your current "random strength" sessions are leaving gains on the table. PPL ensures every muscle gets trained 2x per week with adequate recovery, which is optimal for muscle retention during weight loss.
Why machine-first exercises? At 174 kg, free weight compound lifts (barbell squats, bench press) put enormous stress on joints and require near-perfect form to be safe. Machines allow you to train hard with controlled ROM while your joints adapt over the first 8 weeks. Phase 2 progressively introduces more free weight work.
Your biggest lever right now is the treadmill. At 174 kg, 45 min treadmill at incline burns ~600-700 kcal β€” more than most people burn in an entire workout. Don't skip cardio even on days you feel lazy. Just walk at incline 3-5% if you have no energy. It all counts.

Create a nutrition meal plan template for Indian diet (dal, rice, chicken, vegetables)

Writing File

~/plab-schedule/build_meal_plan.py

from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
from openpyxl.worksheet.datavalidation import DataValidation

wb = Workbook()

# ── COLOURS ───────────────────────────────────────────────────────────────────
NAVY    = "1B3A6B"
BLUE    = "2E86C1"
LT_BLUE = "D6EAF8"
GREEN   = "1E8449"
LT_GRN  = "D5F5E3"
AMBER   = "D4AC0D"
LT_AMB  = "FEF9E7"
RED     = "C0392B"
LT_RED  = "FDEDEC"
PURPLE  = "6C3483"
LT_PUR  = "F4ECF7"
TEAL    = "117A65"
LT_TEAL = "D1F2EB"
ORANGE  = "CA6F1E"
LT_ORG  = "FDEBD0"
GREY_D  = "2C3E50"
GREY_L  = "F2F3F4"
WHITE   = "FFFFFF"
INPUT   = "FFFDE7"
BLACK   = "000000"

# Meal colours
MEAL_CLR = {
    "Breakfast":       ("1A5276", "D6EAF8"),
    "Mid-Morning":     ("117A65", "D1F2EB"),
    "Lunch":           ("1E8449", "D5F5E3"),
    "Pre-Workout":     ("7D6608", "FEF9E7"),
    "Post-Workout":    ("922B21", "FDEDEC"),
    "Dinner":          ("4A235A", "F4ECF7"),
    "Bedtime Snack":   ("2C3E50", "EAECEE"),
}

def fill(c):   return PatternFill("solid", fgColor=c)
def font(bold=False, size=10, color=BLACK, italic=False, name="Calibri"):
    return Font(bold=bold, size=size, color=color, italic=italic, name=name)
def border(style="thin", color="CCCCCC"):
    s = Side(style=style, color=color)
    return Border(left=s, right=s, top=s, bottom=s)
def med_border(color="888888"):
    t = Side(style="medium", color=color)
    return Border(left=t, right=t, top=t, bottom=t)
def align(h="left", v="center", wrap=False):
    return Alignment(horizontal=h, vertical=v, wrap_text=wrap)
def cw(ws, col, width): ws.column_dimensions[get_column_letter(col)].width = width
def rh(ws, row, height): ws.row_dimensions[row].height = height

def hdr(ws, r, c, text, bg=NAVY, fg=WHITE, sz=10, bold=True, ha="center", wrap=True):
    cl = ws.cell(row=r, column=c, value=text)
    cl.fill = fill(bg); cl.font = font(bold=bold, size=sz, color=fg)
    cl.alignment = align(ha, "center", wrap)
    cl.border = border("thin", "999999")
    return cl

def cell(ws, r, c, val=None, bg=WHITE, fg=BLACK, sz=10, bold=False,
         ha="left", wrap=True, bc="CCCCCC", italic=False, num_fmt=None):
    cl = ws.cell(row=r, column=c, value=val)
    cl.fill = fill(bg); cl.font = font(bold=bold, size=sz, color=fg, italic=italic)
    cl.alignment = align(ha, "center", wrap)
    cl.border = border("thin", bc)
    if num_fmt: cl.number_format = num_fmt
    return cl

def mhdr(ws, r, cs, ce, text, bg=NAVY, fg=WHITE, sz=12, bold=True):
    ws.merge_cells(start_row=r, start_column=cs, end_row=r, end_column=ce)
    cl = ws.cell(row=r, column=cs, value=text)
    cl.fill = fill(bg); cl.font = font(bold=bold, size=sz, color=fg)
    cl.alignment = align("center", "center")
    cl.border = border("medium", "888888")
    return cl

def sec_hdr(ws, r, cs, ce, text, bg, fg=WHITE, sz=11):
    ws.merge_cells(start_row=r, start_column=cs, end_row=r, end_column=ce)
    cl = ws.cell(row=r, column=cs, value=f"  {text}")
    cl.fill = fill(bg); cl.font = font(bold=True, size=sz, color=fg)
    cl.alignment = align("left", "center")
    cl.border = border("medium", bg)
    return cl

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 1 β€” OVERVIEW & MACRO TARGETS
# ══════════════════════════════════════════════════════════════════════════════
ws1 = wb.active
ws1.title = "πŸ“Š Overview & Macros"
ws1.sheet_view.showGridLines = False

for i, w_ in enumerate([2,22,12,12,12,12,20,3], 1): cw(ws1, i, w_)

rh(ws1, 1, 6)
rh(ws1, 2, 42)
mhdr(ws1, 2, 2, 7,
    "πŸ›  INDIAN DIET MEAL PLAN  |  Weight Loss & Muscle Preservation",
    bg=NAVY, sz=16)
rh(ws1, 3, 20)
mhdr(ws1, 3, 2, 7,
    "Target: 174kg β†’ 98kg  |  Daily Calories: 2,500–2,800 kcal  |  Protein: 200–220g/day  |  5 meals/day",
    bg=BLUE, sz=10, bold=False)
rh(ws1, 4, 6)

# Daily macro targets
rh(ws1, 5, 18)
mhdr(ws1, 5, 2, 7, "DAILY MACRO TARGETS", bg=GREY_D)
rh(ws1, 6, 22)
for j, h_ in enumerate(["Nutrient","Target","Why This Amount","Best Indian Sources","Avoid","Notes"], 2):
    hdr(ws1, 6, j, h_, bg=GREY_D)

macros = [
    ("Calories",  "2,500–2,800 kcal",
     "~500-700 kcal deficit from maintenance (3,200 kcal). Sustainable fat loss.",
     "Portion control of rice, roti, dal, chicken",
     "Fried snacks, sweets, packaged food, cold drinks",
     "Weigh food for first 2 weeks to calibrate eye."),
    ("Protein",   "200–220g/day",
     "2.2g per kg target body weight (100kg). Preserves muscle while losing fat.",
     "Chicken breast, eggs, dal, paneer, Greek yoghurt, whey",
     "Replacing protein meals with carb-heavy snacks",
     "Most important macro. Hit this EVERY day."),
    ("Carbs",     "250–300g/day",
     "Moderate carbs. Enough for gym energy, not so much it blocks fat loss.",
     "Brown rice, oats, roti (1-2 per meal), fruits, vegetables",
     "White bread, maida, mithai, excess rice",
     "Time carbs around workouts (pre/post training)."),
    ("Fats",      "70–85g/day",
     "Essential for hormones, joint health, vitamin absorption.",
     "Ghee (small amount), almonds, walnuts, eggs, mustard oil",
     "Trans fats, vanaspati, excess coconut oil",
     "Don't eliminate fat β€” it's necessary. Just control quantity."),
    ("Fibre",     "35–40g/day",
     "Keeps you full, supports gut health, slows carb absorption.",
     "Dal, vegetables, salad, whole fruits, oats",
     "Processed foods, fruit juices (eat the fruit, don't drink it)",
     "Add a large salad to lunch and dinner daily."),
    ("Water",     "4–5 litres/day",
     "Kidney health at high body weight. Fat metabolism. Reduces false hunger.",
     "Plain water. Chaas (buttermilk). Green tea.",
     "Soft drinks, packaged juices, excess chai with sugar",
     "1 large glass before every meal and before gym."),
]
macro_bgs = [LT_RED, LT_BLUE, LT_AMB, LT_GRN, LT_TEAL, LT_PUR]
for i, (nut, tgt, why, sources, avoid, notes) in enumerate(macros):
    r = 7 + i
    rh(ws1, r, 20)
    bg = macro_bgs[i]
    cell(ws1, r, 2, nut,     bg=bg, bold=True, ha="center")
    cell(ws1, r, 3, tgt,     bg=bg, bold=True, ha="center", fg=NAVY)
    cell(ws1, r, 4, why,     bg=bg, wrap=True)
    cell(ws1, r, 5, sources, bg=bg, wrap=True, fg=GREEN)
    cell(ws1, r, 6, avoid,   bg=bg, wrap=True, fg=RED)
    cell(ws1, r, 7, notes,   bg=bg, wrap=True, italic=True, fg="444444")

rh(ws1, 13, 6)

# Meal timing
rh(ws1, 14, 18)
mhdr(ws1, 14, 2, 7, "MEAL TIMING & STRUCTURE (5 meals/day)", bg=GREY_D)
rh(ws1, 15, 22)
for j, h_ in enumerate(["Meal", "Ideal Time", "Kcal Target", "Protein Target", "Purpose", "Notes"], 2):
    hdr(ws1, 15, j, h_, bg=GREY_D)

meals_timing = [
    ("Breakfast",      "7:00–8:00 AM",  "450–550 kcal", "35–45g",
     "Break overnight fast. Fuel morning energy and gym (if morning session).",
     "Largest meal for morning gym-goers. Include oats/eggs/yoghurt."),
    ("Mid-Morning Snack","10:00–11:00 AM","150–200 kcal", "15–20g",
     "Prevent mid-morning hunger. Maintain blood sugar.",
     "Fruits + nuts OR chaas. Keep it light."),
    ("Lunch",          "1:00–2:00 PM",  "650–750 kcal", "50–60g",
     "Largest meal of the day for evening gym-goers. Main carb meal.",
     "Rice + dal + chicken + vegetables + salad. This is your fuel."),
    ("Pre-Workout Snack","4:30–5:30 PM", "200–250 kcal", "15–20g",
     "Fuel for gym session. Quick-release energy.",
     "Banana + protein shake OR roti + egg. 45–60 min before gym."),
    ("Post-Workout",   "Within 30–45 min","250–300 kcal","35–45g",
     "Muscle recovery. Glycogen replenishment. Critical window.",
     "Whey protein shake + banana. Don't delay this."),
    ("Dinner",         "8:30–9:30 PM",  "550–650 kcal", "45–55g",
     "Recovery meal. Lighter carbs than lunch. Protein-heavy.",
     "Chicken/dal/paneer + vegetables + 1 roti OR small rice. Less carb than lunch."),
    ("Bedtime (optional)","10:30–11:00 PM","150–200 kcal","15–20g",
     "Slow-release protein during overnight fast. Prevents muscle breakdown.",
     "Greek yoghurt OR paneer + small handful of almonds."),
]
meal_bgs_t = [LT_BLUE, LT_TEAL, LT_GRN, LT_AMB, LT_RED, LT_PUR, GREY_L]
for i, (meal, time, kcal, prot, purpose, notes) in enumerate(meals_timing):
    r = 16 + i
    rh(ws1, r, 28)
    bg = meal_bgs_t[i]
    cell(ws1, r, 2, meal,    bg=bg, bold=True, ha="center")
    cell(ws1, r, 3, time,    bg=bg, ha="center")
    cell(ws1, r, 4, kcal,    bg=bg, bold=True, ha="center", fg=NAVY)
    cell(ws1, r, 5, prot,    bg=bg, bold=True, ha="center", fg=GREEN)
    cell(ws1, r, 6, purpose, bg=bg, wrap=True)
    cell(ws1, r, 7, notes,   bg=bg, wrap=True, italic=True, fg="333333")

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 2 β€” 7-DAY MEAL PLAN
# ══════════════════════════════════════════════════════════════════════════════
ws2 = wb.create_sheet("πŸ—“ 7-Day Meal Plan")
ws2.sheet_view.showGridLines = False

for i, w_ in enumerate([2,14,28,10,10,10,22,3], 1): cw(ws2, i, w_)

rh(ws2, 1, 6)
rh(ws2, 2, 36)
mhdr(ws2, 2, 2, 7,
    "πŸ—“  7-DAY INDIAN MEAL PLAN  |  ~2,600 kcal/day  |  ~210g Protein/day",
    bg=NAVY, sz=14)
rh(ws2, 3, 20)
mhdr(ws2, 3, 2, 7,
    "All quantities given for one serving. Adjust portions up/down to hit your calorie target. "
    "Protein shake = 1 scoop whey (25g protein, 120 kcal) in water.",
    bg=BLUE, sz=10, bold=False)
rh(ws2, 4, 6)

# 7-day plan data: (meal_type, food_item, qty, kcal, protein, carbs, notes)
day_plans = {
    "MONDAY": [
        ("Breakfast",       "Steel-cut oats / rolled oats",                "80g dry",  "290", "10g",  "52g",  "Cook in water or milk"),
        ("Breakfast",       "Full-fat milk",                               "200ml",    "124", "6g",   "10g",  "Or use in oats"),
        ("Breakfast",       "Boiled eggs (whole)",                         "3 eggs",   "234", "18g",  "1g",   "Best breakfast protein"),
        ("Breakfast",       "Banana",                                      "1 medium", "90",  "1g",   "23g",  "Pre-gym energy"),
        ("Mid-Morning",     "Greek yoghurt (plain)",                       "150g",     "130", "15g",  "6g",   "Or chaas (buttermilk)"),
        ("Mid-Morning",     "Mixed nuts (almonds + walnuts)",               "25g",      "155", "4g",   "4g",   "Small handful"),
        ("Lunch",           "Boiled rice / brown rice",                    "150g cooked","195","4g",  "44g",  "Use smaller katori"),
        ("Lunch",           "Dal (moong or masoor, cooked)",               "1 katori (150ml)","130","9g","20g","Use less oil"),
        ("Lunch",           "Chicken breast (grilled/tandoor)",            "200g raw",  "220", "46g",  "0g",   "Remove skin. Grill/bake/tandoor only."),
        ("Lunch",           "Mixed vegetable sabzi (dry)",                 "1 katori",  "80",  "3g",   "12g",  "Use 1 tsp oil only"),
        ("Lunch",           "Cucumber + tomato salad",                     "1 plate",   "40",  "2g",   "8g",   "No dressing β€” lemon + salt only"),
        ("Pre-Workout",     "Banana",                                      "1 large",   "105", "1g",   "27g",  "45-60 min before gym"),
        ("Pre-Workout",     "Whey protein shake",                          "1 scoop",   "120", "25g",  "3g",   "In water, pre or post gym"),
        ("Post-Workout",    "Whey protein shake",                          "1 scoop",   "120", "25g",  "3g",   "Within 30 min of finishing"),
        ("Post-Workout",    "Banana (or apple)",                           "1",         "90",  "1g",   "23g",  "Replenish glycogen"),
        ("Dinner",          "Roti (whole wheat)",                          "2 rotis",   "170", "5g",   "35g",  "Medium size. No butter."),
        ("Dinner",          "Grilled chicken or egg bhurji",               "150g / 3 eggs","165","35g","2g",  "High protein, lower carb"),
        ("Dinner",          "Dal tadka",                                   "1 katori",  "130", "9g",   "20g",  "Moderate oil"),
        ("Dinner",          "Stir-fried vegetables",                       "1 katori",  "60",  "3g",   "10g",  "Spinach, broccoli, beans"),
        ("Bedtime Snack",   "Greek yoghurt or paneer",                     "100g",      "100", "11g",  "4g",   "Slow protein overnight"),
    ],
    "TUESDAY": [
        ("Breakfast",       "Besan chilla (gram flour pancakes)",          "3 medium",  "300", "18g",  "35g",  "Add onion, tomato, green chilli"),
        ("Breakfast",       "Boiled eggs",                                 "2 eggs",    "156", "12g",  "1g",   "Side protein boost"),
        ("Breakfast",       "Green tea",                                   "1 cup",     "2",   "0g",   "0g",   "No sugar"),
        ("Mid-Morning",     "Roasted chana (chickpeas)",                   "40g",       "160", "8g",   "20g",  "High protein snack"),
        ("Mid-Morning",     "Apple or guava",                              "1",         "80",  "0.5g", "20g",  "Fibre + vitamins"),
        ("Lunch",           "Brown rice or 2 rotis",                       "130g / 2 roti","195","4g", "40g", "Choose one"),
        ("Lunch",           "Rajma (kidney beans) curry",                  "1 large katori","220","12g","32g", "Excellent protein+fibre"),
        ("Lunch",           "Paneer tikka / baked paneer",                 "100g",      "265", "18g",  "3g",   "Bake or grill β€” don't fry"),
        ("Lunch",           "Salad (cucumber, carrot, onion)",             "1 large",   "50",  "2g",   "10g",  "Eat before main meal"),
        ("Pre-Workout",     "Dates (khajoor)",                             "4-5",       "120", "1g",   "30g",  "Quick energy, natural sugar"),
        ("Pre-Workout",     "Protein shake",                               "1 scoop",   "120", "25g",  "3g",   ""),
        ("Post-Workout",    "Protein shake + banana",                      "1 + 1",     "210", "26g",  "26g",  ""),
        ("Dinner",          "Egg curry (2 eggs in tomato gravy)",          "2 eggs",    "220", "14g",  "8g",   "Less oil. More tomato."),
        ("Dinner",          "Dal",                                         "1 katori",  "130", "9g",   "20g",  ""),
        ("Dinner",          "Roti",                                        "2",         "170", "5g",   "35g",  ""),
        ("Dinner",          "Palak (spinach) sabzi",                       "1 katori",  "80",  "4g",   "8g",   "Iron + fibre rich"),
        ("Bedtime Snack",   "Almonds + small glass milk",                  "15g + 150ml","185","9g",  "10g",  ""),
    ],
    "WEDNESDAY": [
        ("Breakfast",       "Oats porridge with chia seeds",               "80g + 10g", "340", "12g",  "54g",  "Add chia seeds for omega-3"),
        ("Breakfast",       "Boiled eggs",                                 "3",         "234", "18g",  "1g",   ""),
        ("Breakfast",       "Papaya or orange",                            "1 cup",     "55",  "1g",   "14g",  "Vitamin C post-eggs"),
        ("Mid-Morning",     "Protein shake",                               "1 scoop",   "120", "25g",  "3g",   "Mid-morning protein top-up"),
        ("Mid-Morning",     "Handful of peanuts",                          "30g",       "170", "8g",   "5g",   "Cheap, high protein"),
        ("Lunch",           "Rice + chicken biryani (homemade, light)",    "1 plate",   "450", "38g",  "52g",  "Use less oil. No fried onions."),
        ("Lunch",           "Raita",                                       "1 katori",  "80",  "5g",   "8g",   "Curd + cucumber + jeera"),
        ("Lunch",           "Salad",                                       "1 plate",   "40",  "2g",   "8g",   ""),
        ("Pre-Workout",     "Banana",                                      "1",         "90",  "1g",   "23g",  ""),
        ("Pre-Workout",     "Handful of almonds",                          "15g",       "87",  "3g",   "3g",   ""),
        ("Post-Workout",    "Protein shake + banana",                      "1 + 1",     "210", "26g",  "26g",  ""),
        ("Dinner",          "Grilled fish (surmai/rohu/pomfret)",          "200g",      "220", "40g",  "0g",   "Best dinner protein. Bake/grill."),
        ("Dinner",          "Dal",                                         "1 katori",  "130", "9g",   "20g",  ""),
        ("Dinner",          "1 roti",                                      "1",         "85",  "2.5g", "17g",  "Reduce roti at dinner"),
        ("Dinner",          "Mixed sabzi",                                 "1 katori",  "80",  "3g",   "12g",  ""),
        ("Bedtime Snack",   "Paneer (raw)",                                "80g",       "200", "15g",  "3g",   "Casein protein β€” slow release"),
    ],
    "THURSDAY": [
        ("Breakfast",       "Moong dal chilla (2-3 medium)",               "3 chillas",  "280", "20g",  "32g",  "Best protein breakfast. Add veggies inside."),
        ("Breakfast",       "Curd (plain)",                                "150g",       "100", "8g",   "7g",   "Side with chilla"),
        ("Breakfast",       "Green tea or black coffee (no sugar)",        "1 cup",      "5",   "0g",   "1g",   ""),
        ("Mid-Morning",     "Chaas (buttermilk with jeera)",               "300ml",      "60",  "3g",   "6g",   "Excellent digestion + hydration"),
        ("Mid-Morning",     "Roasted makhana (fox nuts)",                  "30g",        "100", "4g",   "18g",  "Low calorie, filling snack"),
        ("Lunch",           "2 rotis (whole wheat)",                       "2",          "170", "5g",   "35g",  ""),
        ("Lunch",           "Chicken curry (home cooked, less oil)",       "200g chicken","280","42g",  "8g",   "Curry OK but limit oil to 1 tbsp"),
        ("Lunch",           "Chana dal or toor dal",                       "1 katori",   "150", "10g",  "22g",  ""),
        ("Lunch",           "Salad + curd",                                "1 each",     "110", "7g",   "14g",  ""),
        ("Pre-Workout",     "Banana + protein shake",                      "1 + 1 scoop","210","26g",  "26g",  ""),
        ("Post-Workout",    "Protein shake",                               "1 scoop",    "120", "25g",  "3g",   ""),
        ("Dinner",          "Paneer bhurji (scrambled paneer, low oil)",   "150g paneer","350","25g",  "8g",   "Pan with 1 tsp oil, onion, tomato, spices"),
        ("Dinner",          "2 rotis",                                     "2",          "170", "5g",   "35g",  ""),
        ("Dinner",          "Palak or methi sabzi",                        "1 katori",   "80",  "4g",   "8g",   "Iron-rich leafy greens"),
        ("Bedtime Snack",   "Greek yoghurt + 5 walnuts",                   "150g + 5",   "200", "16g",  "8g",   ""),
    ],
    "FRIDAY": [
        ("Breakfast",       "Egg white omelette (4 whites + 1 whole)",     "4W+1 whole", "200", "25g",  "2g",   "Most protein per calorie breakfast"),
        ("Breakfast",       "2 slices whole wheat toast",                  "2 slices",   "160", "6g",   "30g",  ""),
        ("Breakfast",       "Banana",                                      "1",          "90",  "1g",   "23g",  ""),
        ("Mid-Morning",     "Protein shake",                               "1 scoop",    "120", "25g",  "3g",   ""),
        ("Mid-Morning",     "Apple",                                       "1",          "80",  "0.5g", "21g",  "Fibre keeps you full"),
        ("Lunch",           "Brown rice",                                  "150g cooked","195","4g",   "44g",  ""),
        ("Lunch",           "Dal makhani (moderate butter)",               "1 katori",   "200", "10g",  "26g",  "Limit butter. Rich in protein."),
        ("Lunch",           "Tandoori chicken leg/breast",                 "200g",       "280", "40g",  "2g",   "Best restaurant order β€” minimal oil"),
        ("Lunch",           "Salad (large)",                               "1 large",    "50",  "2g",   "10g",  ""),
        ("Pre-Workout",     "Banana + almonds",                            "1 + 20g",    "202", "5g",   "30g",  ""),
        ("Post-Workout",    "Protein shake + banana",                      "1 + 1",      "210", "26g",  "26g",  ""),
        ("Dinner",          "Egg curry (3 eggs)",                          "3 eggs",     "330", "21g",  "12g",  ""),
        ("Dinner",          "2 rotis",                                     "2",          "170", "5g",   "35g",  ""),
        ("Dinner",          "Mixed dal",                                   "1 katori",   "130", "9g",   "20g",  ""),
        ("Dinner",          "Sabzi (any)",                                 "1 katori",   "80",  "3g",   "12g",  ""),
        ("Bedtime Snack",   "Paneer / Greek yoghurt",                      "100g",       "130", "13g",  "4g",   ""),
    ],
    "SATURDAY": [
        ("Breakfast",       "Poha (flattened rice, vegetable poha)",       "1 plate",    "300", "8g",   "52g",  "Add peanuts for protein. Less oil."),
        ("Breakfast",       "Protein shake",                               "1 scoop",    "120", "25g",  "3g",   "Compensate for lower poha protein"),
        ("Breakfast",       "Boiled egg",                                  "2",          "156", "12g",  "1g",   ""),
        ("Mid-Morning",     "Coconut water",                               "1 large",    "60",  "1g",   "14g",  "Natural electrolytes"),
        ("Mid-Morning",     "Mixed nuts",                                  "25g",        "155", "4g",   "4g",   ""),
        ("Lunch",           "Chicken rice (pulao, home cooked)",           "1 plate",    "480", "40g",  "50g",  "Saturday treat meal β€” still clean"),
        ("Lunch",           "Dal",                                         "1 katori",   "130", "9g",   "20g",  ""),
        ("Lunch",           "Raita",                                       "1 katori",   "80",  "5g",   "8g",   ""),
        ("Lunch",           "Salad",                                       "1 plate",    "50",  "2g",   "10g",  ""),
        ("Pre-Workout",     "Banana + protein shake",                      "1 + 1",      "210", "26g",  "26g",  ""),
        ("Post-Workout",    "Protein shake",                               "1 scoop",    "120", "25g",  "3g",   ""),
        ("Dinner",          "Grilled chicken or fish",                     "200g",       "240", "44g",  "0g",   "Light dinner β€” you already had big lunch"),
        ("Dinner",          "1–2 rotis",                                   "1-2",        "85-170","3-5g","17-35g","Just 1 roti if lunch was large"),
        ("Dinner",          "Dal / sabzi",                                 "1 katori each","200","11g", "28g",  ""),
        ("Bedtime Snack",   "Dahi (curd) with jeera",                     "200g",       "120", "8g",   "10g",  "Light and easy to digest"),
    ],
    "SUNDAY": [
        ("Breakfast",       "Masala omelette (3 eggs)",                    "3 eggs",     "280", "20g",  "4g",   "Onion, tomato, green chilli, coriander"),
        ("Breakfast",       "2 rotis or 2 whole wheat toast",              "2",          "170", "5g",   "35g",  ""),
        ("Breakfast",       "Chai (1 cup, less sugar, low fat milk)",      "1 cup",      "60",  "2g",   "8g",   "Limit to 1 cup"),
        ("Mid-Morning",     "Fruit bowl (papaya + banana + apple)",        "1 bowl",     "180", "2g",   "44g",  "Vitamins + fibre + natural sugar"),
        ("Mid-Morning",     "Greek yoghurt",                               "150g",       "130", "15g",  "6g",   ""),
        ("Lunch",           "Mutton / lamb curry (lean cuts)",             "150g cooked","350","32g",  "6g",   "Sunday special β€” lean cuts, less oil"),
        ("Lunch",           "Rice",                                        "150g cooked","195","4g",   "44g",  ""),
        ("Lunch",           "Dal",                                         "1 katori",   "130", "9g",   "20g",  ""),
        ("Lunch",           "Papad (1, roasted not fried)",                "1",          "35",  "1g",   "6g",   "Roasted OK. Fried = avoid."),
        ("Lunch",           "Salad + raita",                               "1 each",     "120", "7g",   "16g",  ""),
        ("Pre-Workout",     "Protein shake + banana",                      "1 + 1",      "210", "26g",  "26g",  "Sunday gym session"),
        ("Post-Workout",    "Protein shake",                               "1 scoop",    "120", "25g",  "3g",   ""),
        ("Dinner",          "Chicken breast (grilled)",                    "200g",       "220", "46g",  "0g",   "Light dinner after Sunday lunch"),
        ("Dinner",          "Dal or rajma",                                "1 katori",   "150", "10g",  "22g",  ""),
        ("Dinner",          "1 roti + sabzi",                              "1 + 1k",     "165", "5g",   "29g",  ""),
        ("Bedtime Snack",   "Paneer + almonds",                            "80g + 15g",  "287", "18g",  "4g",   "Sunday recovery protein"),
    ],
}

day_colors = {
    "MONDAY": (LT_BLUE, BLUE, "1A5276"),
    "TUESDAY": (LT_GRN, GREEN, "1E8449"),
    "WEDNESDAY": (LT_AMB, AMBER, "7D6608"),
    "THURSDAY": (LT_TEAL, TEAL, TEAL),
    "FRIDAY": (LT_ORG, ORANGE, ORANGE),
    "SATURDAY": (LT_PUR, PURPLE, PURPLE),
    "SUNDAY": (LT_RED, RED, RED),
}

row = 5
for day, meals in day_plans.items():
    bg_lite, bg_dark, fg_dark = day_colors[day]

    # Day header
    rh(ws2, row, 26)
    ws2.merge_cells(start_row=row, start_column=2, end_row=row, end_column=7)
    c = ws2.cell(row=row, column=2, value=f"  πŸ“…  {day}")
    c.fill = fill(bg_dark); c.font = font(bold=True, size=12, color=WHITE)
    c.alignment = align("left", "center")
    c.border = med_border(bg_dark)
    row += 1

    # Column headers
    rh(ws2, row, 20)
    for j, ch in enumerate(["Meal", "Food Item", "Quantity", "Kcal", "Protein", "Notes"], 2):
        hdr(ws2, row, j, ch, bg=fg_dark)
    row += 1

    # Meal rows
    prev_meal = ""
    for i, (meal, food, qty, kcal, prot, carbs, notes) in enumerate(meals):
        rh(ws2, row, 24)
        m_dark, m_lite = MEAL_CLR[meal]
        bg = m_lite if meal == prev_meal or meal != prev_meal else m_lite
        # Alternate shade within same meal
        bg = m_lite

        # Meal label (only on first item of each meal)
        if meal != prev_meal:
            ws2.cell(row=row, column=2, value=meal).fill = fill(m_dark)
            ws2.cell(row=row, column=2).font = font(bold=True, size=9, color=WHITE)
            ws2.cell(row=row, column=2).alignment = align("center", "center")
            ws2.cell(row=row, column=2).border = border("medium", m_dark)
            prev_meal = meal
        else:
            ws2.cell(row=row, column=2, value="").fill = fill(m_lite)
            ws2.cell(row=row, column=2).border = border("thin", "CCCCCC")

        cell(ws2, row, 3, food,  bg=bg, bold=True)
        cell(ws2, row, 4, qty,   bg=bg, ha="center")
        cell(ws2, row, 5, int(kcal), bg=bg, ha="center", bold=True, fg=NAVY)
        cell(ws2, row, 6, prot,  bg=bg, ha="center", bold=True, fg=GREEN)
        cell(ws2, row, 7, notes, bg=bg, italic=True, fg="444444")
        row += 1

    # Day total
    rh(ws2, row, 20)
    day_kcal = sum(int(m[3]) for m in meals)
    day_prot = sum(float(m[4].replace("g","").split("/")[0].split("-")[0]) for m in meals)
    ws2.merge_cells(start_row=row, start_column=2, end_row=row, end_column=3)
    c_tot = ws2.cell(row=row, column=2, value=f"  DAILY TOTAL")
    c_tot.fill = fill(bg_dark); c_tot.font = font(bold=True, size=10, color=WHITE)
    c_tot.alignment = align("left", "center"); c_tot.border = med_border(bg_dark)
    cell(ws2, row, 4, "",        bg=bg_dark)
    cell(ws2, row, 5, day_kcal,  bg=bg_dark, bold=True, fg=WHITE, ha="center")
    ws2.cell(row=row, column=5).font = font(bold=True, size=11, color=WHITE)
    cell(ws2, row, 6, f"~{int(day_prot)}g", bg=bg_dark, bold=True, fg=WHITE, ha="center")
    ws2.cell(row=row, column=6).font = font(bold=True, size=11, color=WHITE)
    cell(ws2, row, 7, "βœ… Within target" if 2400 <= day_kcal <= 2900 else "⚠ Check portions",
         bg=LT_GRN if 2400 <= day_kcal <= 2900 else LT_AMB)
    row += 2  # spacer

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 3 β€” FOOD REFERENCE (macros per common Indian food)
# ══════════════════════════════════════════════════════════════════════════════
ws3 = wb.create_sheet("πŸ“– Food Reference")
ws3.sheet_view.showGridLines = False

for i, w_ in enumerate([2,26,14,10,10,10,10,22,3], 1): cw(ws3, i, w_)

rh(ws3, 1, 6)
rh(ws3, 2, 36)
mhdr(ws3, 2, 2, 8,
    "πŸ“–  INDIAN FOOD MACRO REFERENCE  |  Quick lookup for meal planning",
    bg=NAVY, sz=14)
rh(ws3, 3, 18)
mhdr(ws3, 3, 2, 8,
    "All values per standard serving. Use this to swap foods in your meal plan.",
    bg=BLUE, sz=10, bold=False)
rh(ws3, 4, 6)

food_sections = {
    "PROTEINS (High Protein Foods)": (GREEN, [
        ("Chicken breast (grilled, no skin)", "100g",       "165 kcal","31g","0g",  "3.6g", "Best lean protein source"),
        ("Chicken thigh (skinless)",           "100g",       "209 kcal","26g","0g",  "11g",  "More flavour, slightly more fat"),
        ("Egg (whole)",                        "1 large",    "78 kcal", "6g", "1g",  "5g",   "Most complete protein"),
        ("Egg white only",                     "1 white",    "17 kcal", "4g", "0.2g","0g",   "Pure protein"),
        ("Paneer (full fat)",                  "100g",       "265 kcal","18g","3g",  "21g",  "Excellent veg protein + fat"),
        ("Greek yoghurt (plain)",              "100g",       "97 kcal", "10g","4g",  "5g",   "Best dairy protein"),
        ("Dahi (plain curd)",                  "100g",       "60 kcal", "3g", "5g",  "3g",   "Less protein than Greek yoghurt"),
        ("Whey protein (1 scoop, 30g)",        "30g powder", "120 kcal","25g","3g",  "2g",   "Supplement β€” not a replacement for food"),
        ("Dal (moong, cooked)",                "1 katori 150ml","130 kcal","9g","20g","0.5g","High protein, high fibre"),
        ("Dal (masoor/red lentil, cooked)",    "1 katori",   "140 kcal","10g","22g","0.5g", "Highest protein among dals"),
        ("Rajma (cooked)",                     "1 katori",   "220 kcal","12g","32g","0.5g", "Excellent plant protein + fibre"),
        ("Chana (whole chickpeas, cooked)",    "1 katori",   "210 kcal","11g","30g","3g",   ""),
        ("Roasted chana (snack)",              "30g",        "100 kcal","6g", "15g","2g",   "Great snack"),
        ("Fish (surmai, rohu, pomfret 100g)",  "100g",       "110 kcal","20g","0g",  "3g",   "Excellent lean protein"),
        ("Tuna (canned in water)",             "100g",       "116 kcal","26g","0g",  "1g",   "Cheapest high protein"),
    ]),
    "CARBOHYDRATES": (AMBER, [
        ("White rice (cooked)",       "150g cooked", "195 kcal","4g",  "44g","0.3g","Use smaller katori"),
        ("Brown rice (cooked)",       "150g cooked", "185 kcal","4g",  "40g","1.5g","More fibre, similar calories"),
        ("Roti (whole wheat)",        "1 medium",    "85 kcal", "2.5g","17g","0.5g","Better than maida"),
        ("Oats (dry)",                "50g",         "185 kcal","7g",  "33g","3g",  "Best breakfast carb"),
        ("Banana",                    "1 medium",    "90 kcal", "1g",  "23g","0.3g","Pre-workout fuel"),
        ("Sweet potato (cooked)",     "100g",        "86 kcal", "1.6g","20g","0.1g","Better than regular potato"),
        ("Poha (flattened rice)",     "50g dry",     "180 kcal","3.5g","40g","0.2g","Quick energy"),
        ("Besan (gram flour)",        "50g",         "180 kcal","9g",  "28g","2g",  "Higher protein than wheat"),
        ("Whole wheat bread",         "2 slices",    "160 kcal","6g",  "30g","1g",  ""),
        ("Apple",                     "1 medium",    "80 kcal", "0.4g","21g","0.2g",""),
        ("Dates (khajoor)",           "4-5 pieces",  "120 kcal","1g",  "30g","0.1g","Pre-workout energy"),
    ]),
    "VEGETABLES (Low Calorie, High Fibre)": (TEAL, [
        ("Spinach (palak, cooked)",   "1 katori",    "40 kcal", "3g",  "5g", "0.5g","Iron + fibre"),
        ("Broccoli (steamed)",        "100g",        "34 kcal", "2.8g","7g", "0.3g","Best vegetable for diet"),
        ("Beans (French beans)",      "100g cooked", "35 kcal", "2g",  "7g", "0.2g",""),
        ("Cauliflower",               "100g",        "25 kcal", "2g",  "5g", "0.1g",""),
        ("Tomato",                    "1 medium",    "22 kcal", "1g",  "5g", "0.2g","Lycopene β€” good for heart"),
        ("Cucumber",                  "1 cup sliced","14 kcal", "0.7g","3g", "0.1g","Almost zero calorie"),
        ("Carrot",                    "1 medium",    "25 kcal", "0.6g","6g", "0.1g",""),
        ("Bhindi (okra)",             "100g cooked", "30 kcal", "1.5g","7g", "0.1g",""),
        ("Capsicum (bell pepper)",    "1 medium",    "25 kcal", "1g",  "6g", "0.3g","Vitamin C"),
        ("Methi (fenugreek leaves)",  "1 cup",       "13 kcal", "1g",  "3g", "0.2g","Blood sugar control"),
    ]),
    "FATS & NUTS": (ORANGE, [
        ("Almonds",                   "25g (18 nuts)","145 kcal","5g",  "5g", "13g", "Best nut for diet"),
        ("Walnuts",                   "25g",          "165 kcal","4g",  "3g", "16g", "Best omega-3 nut"),
        ("Peanuts (roasted)",         "30g",          "170 kcal","8g",  "5g", "14g", "Cheap high protein nut"),
        ("Ghee",                      "1 tsp (5g)",   "45 kcal", "0g",  "0g", "5g",  "Use sparingly. Good fat."),
        ("Mustard oil",               "1 tsp",        "40 kcal", "0g",  "0g", "5g",  "Better than refined oils"),
        ("Coconut oil",               "1 tsp",        "40 kcal", "0g",  "0g", "4.5g","Use occasionally only"),
        ("Peanut butter (natural)",   "2 tbsp",       "190 kcal","8g",  "7g", "16g", "Excellent fat + protein combo"),
        ("Chia seeds",                "15g",          "70 kcal", "2.5g","6g", "4.5g","Omega-3, fibre, calcium"),
    ]),
    "DRINKS": (PURPLE, [
        ("Water",                     "1 glass 250ml","0 kcal",  "0g",  "0g", "0g",  "DRINK 4-5 litres/day"),
        ("Chaas (buttermilk, plain)", "300ml",        "60 kcal", "3g",  "6g", "2g",  "Excellent digestion aid"),
        ("Coconut water",             "1 glass",      "45 kcal", "1g",  "10g","0.5g","Natural electrolytes"),
        ("Green tea",                 "1 cup",        "2 kcal",  "0g",  "0g", "0g",  "Metabolism boost. No sugar."),
        ("Black coffee (no sugar)",   "1 cup",        "5 kcal",  "0.3g","0.8g","0g", "Pre-workout or morning"),
        ("Whole milk",                "200ml",        "124 kcal","6g",  "10g","7g",  ""),
        ("Low fat milk",              "200ml",        "85 kcal", "7g",  "12g","2g",  "Better for diet"),
        ("Protein shake (whey+water)","1 scoop",      "120 kcal","25g", "3g", "1g",  "Supplement only"),
        ("AVOID: Cold drinks/soda",   "330ml can",    "140 kcal","0g",  "35g","0g",  "Empty calories. No fibre."),
        ("AVOID: Packaged fruit juice","200ml",       "100 kcal","0g",  "24g","0g",  "No fibre. High sugar. Eat the fruit."),
    ]),
}

row = 5
for section_title, (sec_color, foods) in food_sections.items():
    rh(ws3, row, 22)
    ws3.merge_cells(start_row=row, start_column=2, end_row=row, end_column=8)
    c = ws3.cell(row=row, column=2, value=f"  {section_title}")
    c.fill = fill(sec_color); c.font = font(bold=True, size=11, color=WHITE)
    c.alignment = align("left", "center")
    c.border = med_border(sec_color)
    row += 1

    rh(ws3, row, 20)
    for j, ch in enumerate(["Food Item","Serving","Calories","Protein","Carbs","Fats","Notes"], 2):
        hdr(ws3, row, j, ch, bg=sec_color)
    row += 1

    sc_lite = {GREEN: LT_GRN, AMBER: LT_AMB, TEAL: LT_TEAL,
               ORANGE: LT_ORG, PURPLE: LT_PUR}
    bg_l = sc_lite.get(sec_color, GREY_L)

    for i, (food, serving, kcal, prot, carbs, fats, notes) in enumerate(foods):
        rh(ws3, row, 24)
        bg = WHITE if i % 2 == 0 else bg_l
        avoid = "AVOID" in food
        cell(ws3, row, 2, food,   bg=LT_RED if avoid else bg, bold=avoid, fg=RED if avoid else BLACK)
        cell(ws3, row, 3, serving,bg=LT_RED if avoid else bg, ha="center")
        cell(ws3, row, 4, kcal,   bg=LT_RED if avoid else bg, ha="center",
             bold=True, fg=RED if avoid else NAVY)
        cell(ws3, row, 5, prot,   bg=LT_RED if avoid else bg, ha="center", bold=True, fg=GREEN)
        cell(ws3, row, 6, carbs,  bg=LT_RED if avoid else bg, ha="center")
        cell(ws3, row, 7, fats,   bg=LT_RED if avoid else bg, ha="center")
        cell(ws3, row, 8, notes,  bg=LT_RED if avoid else bg, italic=True, fg="444444")
        row += 1
    row += 1

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 4 β€” MEAL TRACKER (daily log)
# ══════════════════════════════════════════════════════════════════════════════
ws4 = wb.create_sheet("πŸ“ Daily Tracker")
ws4.sheet_view.showGridLines = False

for i, w_ in enumerate([2,14,24,12,10,10,10,16,3], 1): cw(ws4, i, w_)

rh(ws4, 1, 6)
rh(ws4, 2, 36)
mhdr(ws4, 2, 2, 8,
    "πŸ“  DAILY MEAL TRACKER  |  Log what you actually eat",
    bg=NAVY, sz=14)
rh(ws4, 3, 18)
mhdr(ws4, 3, 2, 8,
    "Fill in actual food + quantity. Targets: ~2,600 kcal | ~210g protein | 4-5L water",
    bg=BLUE, sz=10, bold=False)
rh(ws4, 4, 6)

# Water + weight header
rh(ws4, 5, 18)
for j, h_ in enumerate(["Date", "Body Weight (kg)", "Water (L)", "Energy (1-10)", "Notes/Mood"], 2):
    hdr(ws4, 5, j, h_, bg=GREY_D)
ws4.merge_cells(start_row=5, start_column=7, end_row=5, end_column=8)

for i in range(7):
    r = 6 + i
    rh(ws4, r, 20)
    bg = WHITE if i % 2 == 0 else GREY_L
    for j in range(2, 7):
        c = ws4.cell(row=r, column=j)
        c.fill = fill(INPUT); c.font = font(color="0000FF", sz=10)
        c.alignment = align("center"); c.border = border("medium", BLUE)
    ws4.merge_cells(start_row=r, start_column=7, end_row=r, end_column=8)
    c2 = ws4.cell(row=r, column=7)
    c2.fill = fill(INPUT); c2.alignment = align(); c2.border = border("thin")

rh(ws4, 13, 6)

# Daily meal log headers
rh(ws4, 14, 18)
mhdr(ws4, 14, 2, 8, "DAILY MEAL LOG β€” Enter below", bg=GREY_D)
rh(ws4, 15, 22)
for j, h_ in enumerate(["Date", "Meal", "Food Item", "Quantity", "Kcal", "Protein (g)", "Carbs (g)", "Notes"], 2):
    hdr(ws4, 15, j, h_, bg=GREY_D)

meals_order = ["Breakfast","Mid-Morning","Lunch","Pre-Workout","Post-Workout","Dinner","Bedtime Snack"]
meal_bgs_log = [LT_BLUE, LT_TEAL, LT_GRN, LT_AMB, LT_RED, LT_PUR, GREY_L]

row = 16
for week in range(1):  # 1 week template
    for day_num in range(7):
        day_label = f"Day {day_num+1}"
        for m_idx, (meal, mbg) in enumerate(zip(meals_order, meal_bgs_log)):
            for entry in range(3):  # 3 rows per meal
                rh(ws4, row, 20)
                if entry == 0 and m_idx == 0:
                    c_date = ws4.cell(row=row, column=2, value=day_label if entry == 0 else "")
                    c_date.fill = fill(INPUT); c_date.font = font(color="0000FF", bold=True)
                    c_date.alignment = align("center"); c_date.border = border("medium", BLUE)
                elif entry == 0:
                    ws4.cell(row=row, column=2).fill = fill(GREY_L)
                    ws4.cell(row=row, column=2).border = border()
                else:
                    ws4.cell(row=row, column=2).fill = fill(GREY_L)
                    ws4.cell(row=row, column=2).border = border()

                if entry == 0:
                    c_m = ws4.cell(row=row, column=3, value=meal)
                    c_m.fill = fill(mbg); c_m.font = font(bold=True, size=9)
                    c_m.alignment = align("center"); c_m.border = border("medium", "888888")
                else:
                    ws4.cell(row=row, column=3).fill = fill(mbg)
                    ws4.cell(row=row, column=3).border = border()

                for j in range(4, 10):
                    c = ws4.cell(row=row, column=j)
                    c.fill = fill(INPUT if j < 9 else mbg)
                    c.font = font(color="0000FF" if j < 9 else BLACK, size=10)
                    c.alignment = align("center" if j >= 5 else "left")
                    c.border = border("thin" if j < 9 else "thin")
                row += 1

        # Day total row
        rh(ws4, row, 20)
        ws4.merge_cells(start_row=row, start_column=2, end_row=row, end_column=4)
        c = ws4.cell(row=row, column=2, value=f"  βœ… DAY {day_num+1} TOTAL")
        c.fill = fill(NAVY); c.font = font(bold=True, color=WHITE, size=10)
        c.alignment = align(); c.border = med_border(NAVY)
        for j, col_label in enumerate(["", "= TOTAL KCAL", "= TOTAL PROTEIN"]):
            col_c = ws4.cell(row=row, column=5+j, value=col_label if col_label else "")
            col_c.fill = fill(NAVY if not col_label else (LT_GRN if "PROTEIN" in col_label else LT_BLUE))
            col_c.font = font(bold=True, color=WHITE if not col_label else NAVY)
            col_c.alignment = align("center")
        ws4.cell(row=row, column=8).fill = fill(NAVY)
        ws4.cell(row=row, column=9).fill = fill(NAVY)
        row += 2

# ══════════════════════════════════════════════════════════════════════════════
# SHEET 5 β€” TIPS & SWAPS
# ══════════════════════════════════════════════════════════════════════════════
ws5 = wb.create_sheet("πŸ’‘ Tips & Swaps")
ws5.sheet_view.showGridLines = False

for i, w_ in enumerate([2,28,50,3], 1): cw(ws5, i, w_)

rh(ws5, 1, 6)
rh(ws5, 2, 36)
mhdr(ws5, 2, 2, 3,
    "πŸ’‘  PRACTICAL TIPS, FOOD SWAPS & MEAL PREP GUIDE",
    bg=NAVY, sz=14)
rh(ws5, 3, 8)

tips_sections = [
    ("SMART FOOD SWAPS (Same Taste, Better Macros)", GREEN, [
        ("Instead of: White rice (150g) β†’ Use: Brown rice",             "Save 10 kcal, gain 1g extra fibre. Negligible calorie change but better for blood sugar."),
        ("Instead of: Fried chicken β†’ Use: Tandoori / grilled chicken", "Save 150-200 kcal per serving. Same protein. Massive difference over a week."),
        ("Instead of: Paneer bhurji in lots of oil β†’ Use: 1 tsp oil only","Save 80-120 kcal. Paneer protein stays the same."),
        ("Instead of: Dahi + sugar β†’ Use: Greek yoghurt plain",          "Double the protein. No added sugar. More filling."),
        ("Instead of: Fruit juice β†’ Use: Whole fruit",                   "Same vitamins. Extra fibre. Half the calories. Slower sugar spike."),
        ("Instead of: Maida roti β†’ Use: Whole wheat roti",              "More fibre, slower digestion, keeps you fuller longer."),
        ("Instead of: Deep fried snacks (pakoda, samosa) β†’ Roasted chana","Save 200+ kcal. 6g protein vs 0g protein."),
        ("Instead of: Chawal ka doodh (sweetened rice milk) β†’ Whey + milk","Triple the protein at same calories."),
        ("Instead of: Mithai as post-workout β†’ Banana + whey shake",     "Same quick energy, far more protein for muscle recovery."),
        ("Instead of: Chips/namkeen β†’ Makhana (roasted fox nuts)",       "Less calories, more fibre, no trans fat."),
    ]),
    ("MEAL PREP TIPS (Save Time, Stay Consistent)", BLUE, [
        ("Cook chicken in bulk (Sunday)",        "Grill or pressure cook 1 kg chicken breast. Store in fridge. Use across 3-4 days in different meals."),
        ("Boil eggs in batches",                 "Boil 8-10 eggs at once. Keep in fridge. Grab-and-go protein for 3-4 days."),
        ("Make dal in large quantity",           "Cook a large pot of dal (moong/masoor). Stays fresh 3-4 days. Add tadka fresh each time."),
        ("Chop vegetables on Sunday",            "Pre-cut cucumber, carrots, tomato for salads. Store in containers. Makes daily salads effortless."),
        ("Pre-portion snacks",                   "Portion nuts (25g per serving) into small containers. Prevents overeating when hungry."),
        ("Keep protein shake ready",             "Pre-measure whey scoop. Keep shaker bottle in gym bag. No excuses post-workout."),
        ("Cook brown rice in large batches",     "Rice stores well. Cook 500g at once, refrigerate. Reheat portions as needed."),
    ]),
    ("RESTAURANT / EATING OUT GUIDE", AMBER, [
        ("Safest order",                         "Tandoori roti + dal makhani + grilled chicken / grilled fish + salad. Skip the naan."),
        ("Dhaba strategy",                        "Dal + 2 rotis + dahi = high protein, moderate calorie meal. Ask for less oil in sabzi."),
        ("Chinese restaurant",                   "Chilli chicken (dry) + rice instead of noodles. Avoid butter naan, avoid fried rice."),
        ("Order protein first, carbs second",    "Ask for extra chicken/dal. Skip one roti or half the rice if you overate at lunch."),
        ("Biryani damage control",               "Remove excess ghee/oil from top. Eat protein pieces first, rice second. Add raita."),
        ("Compensation rule",                    "Big restaurant lunch? Light dinner (egg bhurji + 1 roti + salad). Don't skip the gym."),
        ("Avoid: Butter naan",                   "Single butter naan = 300+ kcal, 15g fat, no protein. Swap for 2 whole wheat rotis."),
    ]),
    ("PROTEIN TIMING β€” CRITICAL RULES", RED, [
        ("Morning (within 1 hr of waking)",      "Eat within 1 hour of waking. Prevents morning muscle breakdown (cortisol spike)."),
        ("Pre-workout (45-60 min before)",       "Banana + protein shake OR 1 roti + 2 eggs. Don't train on completely empty stomach."),
        ("Post-workout (within 30-45 min)",      "MOST IMPORTANT: Protein shake + banana immediately post-workout. Don't shower first."),
        ("Spread protein through day",           "Aim for 40-50g per meal Γ— 4-5 meals. Don't try to eat 200g in 1-2 meals β€” poor absorption."),
        ("Before bed protein",                   "Casein protein (paneer, Greek yoghurt) before sleep = muscle repair overnight. Don't skip."),
        ("Protein after every workout",          "Even on rest days, hit your protein target. Muscles repair on rest days, not gym days."),
    ]),
    ("FOODS TO LIMIT / AVOID", PURPLE, [
        ("Maida (refined flour) products",       "Naan, white bread, biscuits, cake, samosa wrappers. Zero fibre, high glycaemic, no satiety."),
        ("Fried foods (daily)",                  "Pakoda, puri, bhatura, fried chicken, chips. Save for once a week maximum."),
        ("Sugary drinks",                        "Cold drinks, packaged juice, sweetened lassi, masala chai Γ— 4 per day. Liquid calories don't fill you up."),
        ("Excessive ghee / oil",                 "1 tsp per dish = fine. 1 tbsp per dish Γ— 4 dishes = 400+ extra kcal just from cooking fat."),
        ("Late night heavy meals",               "Eating a full dal-rice at 11 PM spikes insulin before sleep. Light dinner or protein + veggies only."),
        ("Mithai and sweets",                    "Gulab jamun, ladoo, barfi β€” pure sugar + fat. Occasional treat OK (1 piece), daily = problem."),
        ("Ultra-processed snacks",               "Maggi, instant noodles, packaged namkeen. Low protein, high sodium, high calories, no satiety."),
    ]),
    ("HYDRATION SCHEDULE", TEAL, [
        ("7:00 AM (wake up)",                    "2 large glasses of water (500ml) before anything else. Rehydrates after overnight fast."),
        ("Before each meal",                      "1 glass before breakfast, lunch, and dinner. Reduces appetite by ~20%."),
        ("During workout",                        "500-750ml during your gym session. Sip every 10-15 min, don't gulp."),
        ("Post-workout",                          "500ml within 30 min of finishing gym. Essential for recovery."),
        ("Add chaas at lunch",                    "300ml chaas (buttermilk) with lunch = hydration + probiotics + electrolytes."),
        ("Green tea (2 cups/day max)",            "One mid-morning, one mid-afternoon. Mild metabolism boost. NO sugar."),
        ("AVOID soda and packaged juice",         "Replace with water, chaas, coconut water, or green tea completely."),
    ]),
]

row = 4
for sec_title, sec_color, items in tips_sections:
    rh(ws5, row, 22)
    ws5.merge_cells(start_row=row, start_column=2, end_row=row, end_column=3)
    c = ws5.cell(row=row, column=2, value=f"  {sec_title}")
    c.fill = fill(sec_color); c.font = font(bold=True, size=11, color=WHITE)
    c.alignment = align("left", "center")
    c.border = med_border(sec_color)
    row += 1

    sc_lite = {GREEN: LT_GRN, BLUE: LT_BLUE, AMBER: LT_AMB,
               RED: LT_RED, PURPLE: LT_PUR, TEAL: LT_TEAL}
    bg_l = sc_lite.get(sec_color, GREY_L)

    for i, (lbl, detail) in enumerate(items):
        rh(ws5, row, 28)
        bg = WHITE if i % 2 == 0 else bg_l
        cell(ws5, row, 2, lbl,    bg=bg, bold=True)
        cell(ws5, row, 3, detail, bg=bg, wrap=True)
        row += 1
    row += 1

# ── SAVE ─────────────────────────────────────────────────────────────────────
out = "/tmp/workspace/plab-schedule/Indian_Meal_Plan_2026.xlsx"
wb.save(out)
print(f"Saved: {out}")
Running Command

cd /tmp/workspace/plab-schedule && python3 build_meal_plan.py 2>&1

Editing File

~/plab-schedule/build_meal_plan.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/plab-schedule/build_meal_plan.py'
Running Command

cd /tmp/workspace/plab-schedule && python3 build_meal_plan.py 2>&1

Editing File

~/plab-schedule/build_meal_plan.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/plab-schedule/build_meal_plan.py'
Running Command

cd /tmp/workspace/plab-schedule && python3 build_meal_plan.py 2>&1

Editing File

~/plab-schedule/build_meal_plan.py

Error: String not found in file: '                c.fill = fill(INPUT); c.font = font(color="0000FF", sz=10)'
Running Command

grep -n "sz=10" /tmp/workspace/plab-schedule/build_meal_plan.py | head -20

Reading File
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.