GoogleCalc commited on
Commit
86cc7c3
Β·
verified Β·
1 Parent(s): e50cebd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +82 -356
app.py CHANGED
@@ -571,7 +571,7 @@ def get_share_links(app_url="https://howmuchfeed.com"):
571
 
572
 
573
  # ============================================================
574
- # FEED CALCULATION - MOBILE-FRIENDLY WITH FIXED COLORS
575
  # ============================================================
576
 
577
  def calculate_feed(birds, weeks, days, bag_size, price_per_bag, email, country_code, breed, request: gr.Request):
@@ -645,7 +645,7 @@ def calculate_feed(birds, weeks, days, bag_size, price_per_bag, email, country_c
645
  # Generate share links
646
  share_links = get_share_links()
647
 
648
- # Output HTML - COMPLETELY REWRITTEN FOR MOBILE
649
  starter_bags_display = f"{starter_bags:.1f}"
650
  finisher_bags_display = f"{finisher_bags:.1f}"
651
 
@@ -654,340 +654,102 @@ def calculate_feed(birds, weeks, days, bag_size, price_per_bag, email, country_c
654
  age_text += f" + {days} day{'s' if days != 1 else ''}"
655
 
656
  html = f"""
657
- <div style="
658
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
659
- font-size: 15px;
660
- line-height: 1.5;
661
- color: #1a1a1a;
662
- background: #ffffff;
663
- border-radius: 12px;
664
- padding: 12px 14px;
665
- border: 1px solid #e5e7eb;
666
- box-shadow: 0 1px 3px rgba(0,0,0,0.05);
667
- margin: 0;
668
- width: 100%;
669
- box-sizing: border-box;
670
- ">
671
-
672
- <!-- Quick Answer Box -->
673
- <div style="
674
- background: #f0fdf4;
675
- border: 2px solid #22c55e;
676
- border-radius: 10px;
677
- padding: 14px 16px;
678
- margin-bottom: 14px;
679
- background-color: #f0fdf4 !important;
680
- ">
681
- <div style="
682
- font-size: 12px;
683
- font-weight: 700;
684
- color: #16a34a;
685
- text-transform: uppercase;
686
- letter-spacing: 0.5px;
687
- margin-bottom: 4px;
688
- background: transparent;
689
- ">Here Is Your Result</div>
690
 
691
- <div style="
692
- font-size: 16px;
693
- font-weight: 700;
694
- color: #166534;
695
- background: transparent;
696
- ">
697
  You need
698
- <span style="
699
- display: inline-block;
700
- background: #166534;
701
- color: #ffffff;
702
- border-radius: 6px;
703
- padding: 2px 12px;
704
- font-size: 20px;
705
- font-weight: 800;
706
- background-color: #166534 !important;
707
- ">{total_bags:.1f}</span>
708
- bags of {bag_size} kg poultry feed to raise <strong>{birds:,}</strong> {breed} broilers from day-old to <strong>{age_text} ({total_days} days)</strong>
709
  </div>
710
-
711
- <div style="
712
- margin-top: 6px;
713
- font-size: 16px;
714
- font-weight: 700;
715
- color: #1e40af;
716
- background: transparent;
717
- ">
718
- Buy
719
- <span style="
720
- display: inline-block;
721
- background: #1e40af;
722
- color: #ffffff;
723
- border-radius: 6px;
724
- padding: 2px 12px;
725
- font-size: 18px;
726
- font-weight: 800;
727
- background-color: #1e40af !important;
728
- ">{rounded_bags}</span>
729
- bags to make sure you have enough feed.
730
- </div>
731
-
732
- <div style="
733
- margin-top: 6px;
734
- display: flex;
735
- gap: 10px;
736
- flex-wrap: wrap;
737
- font-size: 13px;
738
- color: #4b5563;
739
- background: transparent;
740
- ">
741
- <span>πŸ” <strong>{birds:,}</strong></span>
742
- <span>πŸ“… <strong>{age_text}</strong></span>
743
- <span>🏷️ <strong>{breed}</strong></span>
744
  </div>
745
-
746
- <div style="
747
- margin-top: 6px;
748
- display: flex;
749
- gap: 6px;
750
- font-size: 12px;
751
- background: transparent;
752
- ">
753
- <span style="
754
- background: {breed_color};
755
- color: #ffffff;
756
- padding: 2px 10px;
757
- border-radius: 10px;
758
- font-weight: 600;
759
- background-color: {breed_color} !important;
760
- ">{breed}</span>
761
- <span style="
762
- background: #6b7280;
763
- color: #ffffff;
764
- padding: 2px 10px;
765
- border-radius: 10px;
766
- font-weight: 600;
767
- background-color: #6b7280 !important;
768
- ">{country_name}</span>
769
  </div>
770
  </div>
771
 
772
- <!-- Feed Breakdown Table -->
773
- <div style="margin-bottom: 12px; background: transparent;">
774
- <div style="
775
- font-size: 12px;
776
- font-weight: 700;
777
- color: #6b7280;
778
- text-transform: uppercase;
779
- letter-spacing: 0.5px;
780
- margin-bottom: 6px;
781
- background: transparent;
782
- ">Feed Breakdown</div>
783
-
784
- <table style="
785
- width: 100%;
786
- border-collapse: collapse;
787
- font-size: 14px;
788
- background: #ffffff;
789
- border: 1px solid #e5e7eb;
790
- border-radius: 8px;
791
- overflow: hidden;
792
- ">
793
  <thead>
794
  <tr style="background: #f8fafc; border-bottom: 2px solid #e5e7eb;">
795
- <th style="padding: 8px 10px; text-align: left; font-weight: 600; color: #1a1a1a; background: #f8fafc;">Stage</th>
796
- <th style="padding: 8px 10px; text-align: right; font-weight: 600; color: #1a1a1a; background: #f8fafc;">Feed (kg)</th>
797
- <th style="padding: 8px 10px; text-align: right; font-weight: 600; color: #1a1a1a; background: #f8fafc;">Bags (25 kg)</th>
798
  </tr>
799
  </thead>
800
  <tbody>
801
  <tr style="border-bottom: 1px solid #e5e7eb;">
802
- <td style="padding: 8px 10px; color: #1a1a1a; background: #ffffff;"><strong>Starter</strong></td>
803
- <td style="padding: 8px 10px; text-align: right; color: #1a1a1a; background: #ffffff;">{starter_kg:,.1f}</td>
804
- <td style="padding: 8px 10px; text-align: right; font-weight: 700; color: #1e293b; background: #ffffff;">{starter_bags_display}</td>
805
  </tr>
806
  <tr style="border-bottom: 1px solid #e5e7eb;">
807
- <td style="padding: 8px 10px; color: #1a1a1a; background: #ffffff;"><strong>Finisher</strong></td>
808
- <td style="padding: 8px 10px; text-align: right; color: #1a1a1a; background: #ffffff;">{finisher_kg:,.1f}</td>
809
- <td style="padding: 8px 10px; text-align: right; font-weight: 700; color: #1e293b; background: #ffffff;">{finisher_bags_display}</td>
810
  </tr>
811
  <tr style="background: #f8fafc; font-weight: 700;">
812
- <td style="padding: 8px 10px; color: #1a1a1a; background: #f8fafc;">TOTAL</td>
813
- <td style="padding: 8px 10px; text-align: right; color: #1a1a1a; background: #f8fafc;">{total_feed_kg:,.1f}</td>
814
- <td style="padding: 8px 10px; text-align: right; font-weight: 800; font-size: 16px; color: #166534; background: #f8fafc;">{total_bags:.1f}</td>
815
  </tr>
816
  </tbody>
817
  </table>
818
  </div>
819
 
820
- <!-- Feed Cost Box -->
821
- <div style="
822
- background: #eff6ff;
823
- border: 2px solid #3b82f6;
824
- border-radius: 10px;
825
- padding: 12px 16px;
826
- margin-bottom: 12px;
827
- background-color: #eff6ff !important;
828
- ">
829
- <div style="
830
- font-size: 12px;
831
- font-weight: 700;
832
- color: #6b7280;
833
- text-transform: uppercase;
834
- letter-spacing: 0.5px;
835
- margin-bottom: 2px;
836
- background: transparent;
837
- ">Feed Cost Estimate</div>
838
-
839
- <div style="
840
- display: flex;
841
- align-items: baseline;
842
- gap: 6px;
843
- flex-wrap: wrap;
844
- font-size: 14px;
845
- background: transparent;
846
- ">
847
- <span style="color: #4b5563; background: transparent;">Approx</span>
848
- <span style="
849
- display: inline-block;
850
- background: #1e40af;
851
- color: #ffffff;
852
- border-radius: 6px;
853
- padding: 2px 12px;
854
- font-size: 18px;
855
- font-weight: 700;
856
- background-color: #1e40af !important;
857
- ">{rounded_bags}</span>
858
- <span style="color: #4b5563; background: transparent;">bags Γ— {currency_symbol}{price_per_bag:,.0f}</span>
859
  </div>
860
-
861
- <div style="
862
- font-size: 24px;
863
- font-weight: 800;
864
- color: #1e40af;
865
- margin-top: 2px;
866
- background: transparent;
867
- ">
868
  {currency_symbol}{total_cost:,.0f}
869
  </div>
870
  </div>
871
 
872
- <!-- Expected Performance -->
873
- <div style="
874
- border-top: 1px solid #e5e7eb;
875
- padding-top: 10px;
876
- margin-bottom: 12px;
877
- background: transparent;
878
- ">
879
- <div style="
880
- font-size: 12px;
881
- font-weight: 700;
882
- color: #6b7280;
883
- text-transform: uppercase;
884
- letter-spacing: 0.5px;
885
- margin-bottom: 4px;
886
- background: transparent;
887
- ">Expected Performance</div>
888
-
889
- <div style="
890
- display: flex;
891
- gap: 16px;
892
- flex-wrap: wrap;
893
- font-size: 14px;
894
- background: transparent;
895
- ">
896
- <div style="background: transparent;">
897
- Body weight: <span style="font-weight: 700; color: #1a1a1a;">{expected_weight:.3f} kg</span>
898
- </div>
899
- <div style="background: transparent;">
900
- Flock weight: <span style="font-weight: 700; color: #1a1a1a;">{flock_weight:,.1f} kg</span>
901
- </div>
902
  </div>
903
  </div>
904
 
905
- <!-- Share Buttons -->
906
- <div style="
907
- border-top: 1px solid #e5e7eb;
908
- padding-top: 12px;
909
- background: transparent;
910
- ">
911
- <div style="
912
- font-size: 12px;
913
- font-weight: 700;
914
- color: #6b7280;
915
- text-transform: uppercase;
916
- letter-spacing: 0.5px;
917
- margin-bottom: 6px;
918
- background: transparent;
919
- ">Share Results</div>
920
-
921
- <div style="
922
- display: flex;
923
- gap: 8px;
924
- flex-wrap: wrap;
925
- background: transparent;
926
- ">
927
  <a href="{share_links['whatsapp']}" target="_blank" style="text-decoration: none;">
928
- <span style="
929
- display: inline-block;
930
- background: #25D366;
931
- color: #ffffff;
932
- padding: 6px 14px;
933
- border-radius: 6px;
934
- font-size: 13px;
935
- font-weight: 600;
936
- cursor: pointer;
937
- background-color: #25D366 !important;
938
- ">WhatsApp</span>
939
  </a>
940
  <a href="{share_links['email']}" target="_blank" style="text-decoration: none;">
941
- <span style="
942
- display: inline-block;
943
- background: #ea4335;
944
- color: #ffffff;
945
- padding: 6px 14px;
946
- border-radius: 6px;
947
- font-size: 13px;
948
- font-weight: 600;
949
- cursor: pointer;
950
- background-color: #ea4335 !important;
951
- ">Email</span>
952
  </a>
953
  <a href="{share_links['facebook']}" target="_blank" style="text-decoration: none;">
954
- <span style="
955
- display: inline-block;
956
- background: #1877f2;
957
- color: #ffffff;
958
- padding: 6px 14px;
959
- border-radius: 6px;
960
- font-size: 13px;
961
- font-weight: 600;
962
- cursor: pointer;
963
- background-color: #1877f2 !important;
964
- ">Facebook</span>
965
  </a>
966
  <a href="{share_links['twitter']}" target="_blank" style="text-decoration: none;">
967
- <span style="
968
- display: inline-block;
969
- background: #000000;
970
- color: #ffffff;
971
- padding: 6px 14px;
972
- border-radius: 6px;
973
- font-size: 13px;
974
- font-weight: 600;
975
- cursor: pointer;
976
- background-color: #000000 !important;
977
- ">Twitter/X</span>
978
  </a>
979
  </div>
980
  </div>
981
 
982
- <div style="
983
- margin-top: 10px;
984
- font-size: 11px;
985
- color: #9ca3af;
986
- border-top: 1px solid #f3f4f6;
987
- padding-top: 8px;
988
- background: transparent;
989
- ">
990
- Estimate based on {breed} benchmark. Actual results may vary.
991
  </div>
992
  </div>
993
  """
@@ -1206,15 +968,11 @@ def export_calculations_wrapper():
1206
 
1207
 
1208
  # ============================================================
1209
- # CSS - FORCE LIGHT THEME
1210
  # ============================================================
1211
 
1212
  CSS = """
1213
- /* Reset everything - force light theme */
1214
- * {
1215
- color: #1a1a1a !important;
1216
- }
1217
-
1218
  body, .gradio-container, .gradio-row, .gradio-column, .gradio-group {
1219
  background-color: #f6f8f4 !important;
1220
  color: #1a1a1a !important;
@@ -1223,7 +981,6 @@ body, .gradio-container, .gradio-row, .gradio-column, .gradio-group {
1223
  .gradio-container {
1224
  max-width: 100% !important;
1225
  padding: 6px 8px !important;
1226
- background-color: #f6f8f4 !important;
1227
  }
1228
 
1229
  .main-title {
@@ -1231,7 +988,7 @@ body, .gradio-container, .gradio-row, .gradio-column, .gradio-group {
1231
  padding: 8px 4px 4px 4px;
1232
  }
1233
  .main-title h1 {
1234
- font-size: 22px;
1235
  margin-bottom: 2px;
1236
  color: #1a1a1a !important;
1237
  }
@@ -1245,7 +1002,6 @@ body, .gradio-container, .gradio-row, .gradio-column, .gradio-group {
1245
  border-radius: 12px !important;
1246
  padding: 8px 10px !important;
1247
  border: 1px solid #e5e7eb !important;
1248
- box-shadow: 0 1px 3px rgba(0,0,0,0.05) !important;
1249
  }
1250
 
1251
  .email-box {
@@ -1264,50 +1020,29 @@ button { min-height: 44px !important; }
1264
  background: #fffbeb !important;
1265
  }
1266
 
1267
- /* Fix all text inputs */
1268
- input, select, textarea {
1269
- background: #ffffff !important;
1270
- color: #1a1a1a !important;
1271
- border: 1px solid #d1d5db !important;
1272
- }
1273
-
1274
- /* Fix labels */
1275
  label, span, p, div, h1, h2, h3, h4 {
1276
  color: #1a1a1a !important;
1277
- background: transparent !important;
1278
  }
1279
 
1280
- /* Fix dropdowns */
1281
- select option {
1282
  background: #ffffff !important;
1283
  color: #1a1a1a !important;
 
1284
  }
1285
 
1286
- /* Force white background on all containers */
1287
- .gradio-container, .gradio-row, .gradio-column, .gradio-group {
1288
- background: transparent !important;
1289
- }
1290
-
1291
- /* Mobile specific */
1292
- @media screen and (max-width: 768px) {
1293
- .gradio-container {
1294
- padding: 4px !important;
1295
- }
1296
-
1297
- .main-title h1 {
1298
- font-size: 20px !important;
1299
- }
1300
-
1301
- .answer-box {
1302
- padding: 6px 8px !important;
1303
- }
1304
- }
1305
-
1306
- /* Override dark mode completely */
1307
  .dark, .dark * {
1308
  background-color: #f6f8f4 !important;
1309
  color: #1a1a1a !important;
1310
  }
 
 
 
 
 
 
 
1311
  """
1312
 
1313
  # ============================================================
@@ -1359,7 +1094,7 @@ with gr.Blocks(title=APP_NAME, css=CSS, theme=gr.themes.Default()) as demo:
1359
  label="+ Days",
1360
  )
1361
 
1362
- gr.Markdown("## 🌾 Feed")
1363
 
1364
  with gr.Row():
1365
  bag_size = gr.Dropdown(
@@ -1369,11 +1104,11 @@ with gr.Blocks(title=APP_NAME, css=CSS, theme=gr.themes.Default()) as demo:
1369
  )
1370
  price = gr.Number(
1371
  value=24000,
1372
- label="Price per bag",
1373
  minimum=0,
1374
  )
1375
 
1376
- gr.Markdown("## 🌍 Country")
1377
 
1378
  detected_country = detect_country_from_env() or "NG"
1379
  country_choices = ["Auto-Detect"] + [code for code, _ in COUNTRY_LIST]
@@ -1381,19 +1116,19 @@ with gr.Blocks(title=APP_NAME, css=CSS, theme=gr.themes.Default()) as demo:
1381
  country_dropdown = gr.Dropdown(
1382
  choices=country_choices,
1383
  value="Auto-Detect",
1384
- label="Select your country (Auto-Detect uses your IP)",
1385
  )
1386
 
1387
  country_code_state = gr.State(detected_country)
1388
 
1389
- gr.Markdown("## πŸ“§ Your email")
1390
 
1391
  email = gr.Textbox(
1392
  label="",
1393
  placeholder="farmer@example.com",
1394
  type="email",
1395
  elem_classes=["email-box"],
1396
- info="Required β€” we'll send you future updates",
1397
  )
1398
 
1399
  calculate_btn = gr.Button(
@@ -1406,13 +1141,13 @@ with gr.Blocks(title=APP_NAME, css=CSS, theme=gr.themes.Default()) as demo:
1406
 
1407
  # RIGHT COLUMN: RESULTS
1408
  with gr.Column(scale=1, min_width=300):
1409
- gr.Markdown("## 🎯 Quick Answer")
1410
  result = gr.HTML(
1411
  value="""
1412
  <div style="font-size: 14px; color: #9ca3af; text-align: center; padding: 30px 10px; background: #ffffff; border-radius: 12px;">
1413
  <div style="font-size: 40px; margin-bottom: 10px;">πŸ”</div>
1414
- <div>Select your breed, enter details and click <strong>Calculate</strong></div>
1415
- <div style="font-size: 13px; margin-top: 6px;">Your feed estimate will appear here</div>
1416
  </div>
1417
  """,
1418
  elem_classes=["answer-box"],
@@ -1426,7 +1161,7 @@ with gr.Blocks(title=APP_NAME, css=CSS, theme=gr.themes.Default()) as demo:
1426
 
1427
  with gr.Tabs():
1428
  with gr.TabItem("πŸ“Š Selected Breed Benchmark"):
1429
- gr.Markdown("### πŸ“Š Weekly Benchmark for Selected Breed")
1430
  initial_table, _ = get_benchmark_table("Ross 308")
1431
 
1432
  benchmark_table = gr.Dataframe(
@@ -1437,7 +1172,7 @@ with gr.Blocks(title=APP_NAME, css=CSS, theme=gr.themes.Default()) as demo:
1437
  )
1438
 
1439
  with gr.TabItem("πŸ“ˆ Breed Comparison"):
1440
- gr.Markdown("### πŸ“Š Breed Comparison (As-Hatched Performance)")
1441
  comp_data, breeds_list, days_list = get_breed_comparison_table()
1442
 
1443
  headers = ["Days"]
@@ -1453,10 +1188,6 @@ with gr.Blocks(title=APP_NAME, css=CSS, theme=gr.themes.Default()) as demo:
1453
  wrap=True,
1454
  )
1455
 
1456
- # ============================================================
1457
- # ADMIN-ONLY ANALYTICS TAB
1458
- # ============================================================
1459
-
1460
  with gr.TabItem("πŸ”’ Admin"):
1461
  gr.Markdown("### πŸ”’ Admin Dashboard")
1462
 
@@ -1483,7 +1214,6 @@ with gr.Blocks(title=APP_NAME, css=CSS, theme=gr.themes.Default()) as demo:
1483
  export_calc_btn = gr.Button("πŸ“₯ Export Calculations (CSV)", variant="secondary")
1484
  export_calc_output = gr.File(label="Download Calculations CSV")
1485
 
1486
- # Login/Logout events
1487
  login_btn.click(
1488
  fn=admin_login,
1489
  inputs=[admin_password_input],
@@ -1583,21 +1313,18 @@ with gr.Blocks(title=APP_NAME, css=CSS, theme=gr.themes.Default()) as demo:
1583
  # EVENTS
1584
  # ============================================================
1585
 
1586
- # Update benchmark table when breed changes
1587
  breed.change(
1588
  fn=update_benchmark,
1589
  inputs=[breed],
1590
  outputs=[benchmark_table],
1591
  )
1592
 
1593
- # Update price and currency when country changes
1594
  country_dropdown.change(
1595
  fn=on_country_change_handler,
1596
  inputs=[country_dropdown],
1597
  outputs=[country_code_state, price, price],
1598
  )
1599
 
1600
- # Calculate button
1601
  def calculate_wrapper(birds, weeks, days, bag_size, price_per_bag, email, country_code_state, breed, request: gr.Request):
1602
  if not country_code_state:
1603
  country_code_state = "NG"
@@ -1615,7 +1342,6 @@ with gr.Blocks(title=APP_NAME, css=CSS, theme=gr.themes.Default()) as demo:
1615
  outputs=[question_answer],
1616
  )
1617
 
1618
- # Track visits on page load
1619
  demo.load(
1620
  fn=track_visit,
1621
  inputs=None,
 
571
 
572
 
573
  # ============================================================
574
+ # FEED CALCULATION
575
  # ============================================================
576
 
577
  def calculate_feed(birds, weeks, days, bag_size, price_per_bag, email, country_code, breed, request: gr.Request):
 
645
  # Generate share links
646
  share_links = get_share_links()
647
 
648
+ # Output HTML
649
  starter_bags_display = f"{starter_bags:.1f}"
650
  finisher_bags_display = f"{finisher_bags:.1f}"
651
 
 
654
  age_text += f" + {days} day{'s' if days != 1 else ''}"
655
 
656
  html = f"""
657
+ <div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 15px; line-height: 1.5; color: #1a1a1a; background: #ffffff; border-radius: 12px; padding: 12px 14px; border: 1px solid #e5e7eb; width: 100%; box-sizing: border-box;">
658
+
659
+ <div style="background: #f0fdf4; border: 2px solid #22c55e; border-radius: 10px; padding: 14px 16px; margin-bottom: 14px;">
660
+ <div style="font-size: 12px; font-weight: 700; color: #16a34a; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 4px;">πŸ“Š Your Feed Estimate</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
661
 
662
+ <div style="font-size: 16px; font-weight: 700; color: #166534;">
 
 
 
 
 
663
  You need
664
+ <span style="display: inline-block; background: #166534; color: #ffffff; border-radius: 6px; padding: 2px 12px; font-size: 20px; font-weight: 800;">
665
+ {total_bags:.1f}
666
+ </span>
667
+ bags of {bag_size} kg feed
 
 
 
 
 
 
 
668
  </div>
669
+ <div style="font-size: 14px; color: #374151; margin-top: 4px;">
670
+ To raise <strong>{birds:,}</strong> {breed} broilers from day-old to <strong>{age_text} ({total_days} days)</strong>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
671
  </div>
672
+ <div style="margin-top: 6px; font-size: 15px; font-weight: 700; color: #1e40af;">
673
+ Buy
674
+ <span style="display: inline-block; background: #1e40af; color: #ffffff; border-radius: 6px; padding: 2px 12px; font-size: 18px; font-weight: 800;">
675
+ {rounded_bags}
676
+ </span>
677
+ bags (rounded up for safety)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
678
  </div>
679
  </div>
680
 
681
+ <div style="margin-bottom: 12px;">
682
+ <div style="font-size: 12px; font-weight: 700; color: #6b7280; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px;">🌾 Feed Breakdown</div>
683
+ <table style="width: 100%; border-collapse: collapse; font-size: 14px; background: #ffffff; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden;">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
684
  <thead>
685
  <tr style="background: #f8fafc; border-bottom: 2px solid #e5e7eb;">
686
+ <th style="padding: 8px 10px; text-align: left; font-weight: 600; color: #1a1a1a;">Stage</th>
687
+ <th style="padding: 8px 10px; text-align: right; font-weight: 600; color: #1a1a1a;">Feed (kg)</th>
688
+ <th style="padding: 8px 10px; text-align: right; font-weight: 600; color: #1a1a1a;">Bags (25 kg)</th>
689
  </tr>
690
  </thead>
691
  <tbody>
692
  <tr style="border-bottom: 1px solid #e5e7eb;">
693
+ <td style="padding: 8px 10px; color: #1a1a1a;"><strong>🌱 Starter</strong></td>
694
+ <td style="padding: 8px 10px; text-align: right; color: #1a1a1a;">{starter_kg:,.1f}</td>
695
+ <td style="padding: 8px 10px; text-align: right; font-weight: 700; color: #1e293b;">{starter_bags_display}</td>
696
  </tr>
697
  <tr style="border-bottom: 1px solid #e5e7eb;">
698
+ <td style="padding: 8px 10px; color: #1a1a1a;"><strong>🌽 Finisher</strong></td>
699
+ <td style="padding: 8px 10px; text-align: right; color: #1a1a1a;">{finisher_kg:,.1f}</td>
700
+ <td style="padding: 8px 10px; text-align: right; font-weight: 700; color: #1e293b;">{finisher_bags_display}</td>
701
  </tr>
702
  <tr style="background: #f8fafc; font-weight: 700;">
703
+ <td style="padding: 8px 10px; color: #1a1a1a;">TOTAL</td>
704
+ <td style="padding: 8px 10px; text-align: right; color: #1a1a1a;">{total_feed_kg:,.1f}</td>
705
+ <td style="padding: 8px 10px; text-align: right; font-weight: 800; font-size: 16px; color: #166534;">{total_bags:.1f}</td>
706
  </tr>
707
  </tbody>
708
  </table>
709
  </div>
710
 
711
+ <div style="background: #eff6ff; border: 2px solid #3b82f6; border-radius: 10px; padding: 12px 16px; margin-bottom: 12px;">
712
+ <div style="font-size: 12px; font-weight: 700; color: #6b7280; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 2px;">πŸ’° Feed Cost</div>
713
+ <div style="display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; font-size: 14px;">
714
+ <span style="color: #4b5563;">Approximately</span>
715
+ <span style="display: inline-block; background: #1e40af; color: #ffffff; border-radius: 6px; padding: 2px 12px; font-size: 18px; font-weight: 700;">
716
+ {rounded_bags}
717
+ </span>
718
+ <span style="color: #4b5563;">bags Γ— {currency_symbol}{price_per_bag:,.0f}</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
719
  </div>
720
+ <div style="font-size: 24px; font-weight: 800; color: #1e40af; margin-top: 2px;">
 
 
 
 
 
 
 
721
  {currency_symbol}{total_cost:,.0f}
722
  </div>
723
  </div>
724
 
725
+ <div style="border-top: 1px solid #e5e7eb; padding-top: 10px; margin-bottom: 12px;">
726
+ <div style="font-size: 12px; font-weight: 700; color: #6b7280; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 4px;">πŸ“ˆ Expected Performance</div>
727
+ <div style="display: flex; gap: 16px; flex-wrap: wrap; font-size: 14px;">
728
+ <div>Body weight: <span style="font-weight: 700; color: #1a1a1a;">{expected_weight:.3f} kg</span></div>
729
+ <div>Flock weight: <span style="font-weight: 700; color: #1a1a1a;">{flock_weight:,.1f} kg</span></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
730
  </div>
731
  </div>
732
 
733
+ <div style="border-top: 1px solid #e5e7eb; padding-top: 12px;">
734
+ <div style="font-size: 12px; font-weight: 700; color: #6b7280; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px;">πŸ“€ Share Results</div>
735
+ <div style="display: flex; gap: 8px; flex-wrap: wrap;">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
736
  <a href="{share_links['whatsapp']}" target="_blank" style="text-decoration: none;">
737
+ <span style="display: inline-block; background: #25D366; color: #ffffff; padding: 6px 14px; border-radius: 6px; font-size: 13px; font-weight: 600; cursor: pointer;">WhatsApp</span>
 
 
 
 
 
 
 
 
 
 
738
  </a>
739
  <a href="{share_links['email']}" target="_blank" style="text-decoration: none;">
740
+ <span style="display: inline-block; background: #ea4335; color: #ffffff; padding: 6px 14px; border-radius: 6px; font-size: 13px; font-weight: 600; cursor: pointer;">Email</span>
 
 
 
 
 
 
 
 
 
 
741
  </a>
742
  <a href="{share_links['facebook']}" target="_blank" style="text-decoration: none;">
743
+ <span style="display: inline-block; background: #1877f2; color: #ffffff; padding: 6px 14px; border-radius: 6px; font-size: 13px; font-weight: 600; cursor: pointer;">Facebook</span>
 
 
 
 
 
 
 
 
 
 
744
  </a>
745
  <a href="{share_links['twitter']}" target="_blank" style="text-decoration: none;">
746
+ <span style="display: inline-block; background: #000000; color: #ffffff; padding: 6px 14px; border-radius: 6px; font-size: 13px; font-weight: 600; cursor: pointer;">Twitter/X</span>
 
 
 
 
 
 
 
 
 
 
747
  </a>
748
  </div>
749
  </div>
750
 
751
+ <div style="margin-top: 10px; font-size: 11px; color: #9ca3af; border-top: 1px solid #f3f4f6; padding-top: 8px;">
752
+ ⚠️ Estimate based on {breed} benchmark. Actual results may vary.
 
 
 
 
 
 
 
753
  </div>
754
  </div>
755
  """
 
968
 
969
 
970
  # ============================================================
971
+ # CSS
972
  # ============================================================
973
 
974
  CSS = """
975
+ /* Force light theme */
 
 
 
 
976
  body, .gradio-container, .gradio-row, .gradio-column, .gradio-group {
977
  background-color: #f6f8f4 !important;
978
  color: #1a1a1a !important;
 
981
  .gradio-container {
982
  max-width: 100% !important;
983
  padding: 6px 8px !important;
 
984
  }
985
 
986
  .main-title {
 
988
  padding: 8px 4px 4px 4px;
989
  }
990
  .main-title h1 {
991
+ font-size: 24px;
992
  margin-bottom: 2px;
993
  color: #1a1a1a !important;
994
  }
 
1002
  border-radius: 12px !important;
1003
  padding: 8px 10px !important;
1004
  border: 1px solid #e5e7eb !important;
 
1005
  }
1006
 
1007
  .email-box {
 
1020
  background: #fffbeb !important;
1021
  }
1022
 
1023
+ /* Fix all text */
 
 
 
 
 
 
 
1024
  label, span, p, div, h1, h2, h3, h4 {
1025
  color: #1a1a1a !important;
 
1026
  }
1027
 
1028
+ input, select, textarea {
 
1029
  background: #ffffff !important;
1030
  color: #1a1a1a !important;
1031
+ border: 1px solid #d1d5db !important;
1032
  }
1033
 
1034
+ /* Force dark mode override */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1035
  .dark, .dark * {
1036
  background-color: #f6f8f4 !important;
1037
  color: #1a1a1a !important;
1038
  }
1039
+
1040
+ /* Mobile */
1041
+ @media screen and (max-width: 768px) {
1042
+ .gradio-container { padding: 4px !important; }
1043
+ .main-title h1 { font-size: 20px !important; }
1044
+ .answer-box { padding: 6px 8px !important; }
1045
+ }
1046
  """
1047
 
1048
  # ============================================================
 
1094
  label="+ Days",
1095
  )
1096
 
1097
+ gr.Markdown("## 🌾 Feed Details")
1098
 
1099
  with gr.Row():
1100
  bag_size = gr.Dropdown(
 
1104
  )
1105
  price = gr.Number(
1106
  value=24000,
1107
+ label="Price per bag (₦)",
1108
  minimum=0,
1109
  )
1110
 
1111
+ gr.Markdown("## 🌍 Your Location")
1112
 
1113
  detected_country = detect_country_from_env() or "NG"
1114
  country_choices = ["Auto-Detect"] + [code for code, _ in COUNTRY_LIST]
 
1116
  country_dropdown = gr.Dropdown(
1117
  choices=country_choices,
1118
  value="Auto-Detect",
1119
+ label="Country (Auto-Detect uses your IP)",
1120
  )
1121
 
1122
  country_code_state = gr.State(detected_country)
1123
 
1124
+ gr.Markdown("## πŸ“§ Your Email")
1125
 
1126
  email = gr.Textbox(
1127
  label="",
1128
  placeholder="farmer@example.com",
1129
  type="email",
1130
  elem_classes=["email-box"],
1131
+ info="We'll send you future updates",
1132
  )
1133
 
1134
  calculate_btn = gr.Button(
 
1141
 
1142
  # RIGHT COLUMN: RESULTS
1143
  with gr.Column(scale=1, min_width=300):
1144
+ gr.Markdown("## πŸ“Š Quick Answer")
1145
  result = gr.HTML(
1146
  value="""
1147
  <div style="font-size: 14px; color: #9ca3af; text-align: center; padding: 30px 10px; background: #ffffff; border-radius: 12px;">
1148
  <div style="font-size: 40px; margin-bottom: 10px;">πŸ”</div>
1149
+ <div>Enter your details and click <strong>Calculate</strong></div>
1150
+ <div style="font-size: 13px; margin-top: 6px;">Your feed estimate appears here</div>
1151
  </div>
1152
  """,
1153
  elem_classes=["answer-box"],
 
1161
 
1162
  with gr.Tabs():
1163
  with gr.TabItem("πŸ“Š Selected Breed Benchmark"):
1164
+ gr.Markdown("### Weekly Benchmark for Selected Breed")
1165
  initial_table, _ = get_benchmark_table("Ross 308")
1166
 
1167
  benchmark_table = gr.Dataframe(
 
1172
  )
1173
 
1174
  with gr.TabItem("πŸ“ˆ Breed Comparison"):
1175
+ gr.Markdown("### Breed Comparison (As-Hatched Performance)")
1176
  comp_data, breeds_list, days_list = get_breed_comparison_table()
1177
 
1178
  headers = ["Days"]
 
1188
  wrap=True,
1189
  )
1190
 
 
 
 
 
1191
  with gr.TabItem("πŸ”’ Admin"):
1192
  gr.Markdown("### πŸ”’ Admin Dashboard")
1193
 
 
1214
  export_calc_btn = gr.Button("πŸ“₯ Export Calculations (CSV)", variant="secondary")
1215
  export_calc_output = gr.File(label="Download Calculations CSV")
1216
 
 
1217
  login_btn.click(
1218
  fn=admin_login,
1219
  inputs=[admin_password_input],
 
1313
  # EVENTS
1314
  # ============================================================
1315
 
 
1316
  breed.change(
1317
  fn=update_benchmark,
1318
  inputs=[breed],
1319
  outputs=[benchmark_table],
1320
  )
1321
 
 
1322
  country_dropdown.change(
1323
  fn=on_country_change_handler,
1324
  inputs=[country_dropdown],
1325
  outputs=[country_code_state, price, price],
1326
  )
1327
 
 
1328
  def calculate_wrapper(birds, weeks, days, bag_size, price_per_bag, email, country_code_state, breed, request: gr.Request):
1329
  if not country_code_state:
1330
  country_code_state = "NG"
 
1342
  outputs=[question_answer],
1343
  )
1344
 
 
1345
  demo.load(
1346
  fn=track_visit,
1347
  inputs=None,