CarlyneB commited on
Commit
9253c67
·
1 Parent(s): b5483eb

[FEAT] Objective 6 - Finalising the interface: change the look and feel of the tool to bring it into line with the CRRA design

Browse files
.streamlit/config.toml CHANGED
@@ -1,7 +1,10 @@
1
- [theme]
2
- base = "light"
3
- primaryColor = "#3d86e9"
4
- backgroundColor = "#ffffff"
5
- secondaryBackgroundColor = "#c6d7f0e1"
6
- textColor = "#262730"
7
- font = "monospace"
 
 
 
 
1
+ [theme]
2
+ base = "light"
3
+ primaryColor = "#298231"
4
+ backgroundColor = "#ffffff"
5
+ secondaryBackgroundColor = "#eae9d8"
6
+ textColor = "#000000"
7
+ font = "sans-serif"
8
+
9
+ [server]
10
+ enableStaticServing = true
assets/styles.css CHANGED
@@ -1,106 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  :root {
2
- --off-white: #eae9d8;
3
- --red: #ff5f4d;
4
- --green-dark: #298231;
5
- --green: #55c95b;
6
- --green-light: #92ef9b;
7
- --yellow: #d9e210;
8
- --white: #ffffff;
9
- --brown-grey: #665d5b;
10
- --grey: #a0a0a0;
11
- --black: #000000;
12
- --font: 'Segoe UI', sans-serif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  }
14
 
15
- .cdr-table, .cdr-table-res, .resources-summary {
16
- width: 100%;
17
- border-collapse: separate;
18
- border-spacing: 0;
19
- border-radius: 10px;
20
- overflow: hidden;
21
- font-family: var(--font);
22
  }
23
 
24
- .cdr-table thead th, .cdr-table-res thead th, .resources-summary thead th{
25
- background-color: var(--green);
26
- color: white;
27
- font-weight: bold;
28
- padding: 8px 12px;
29
- text-align: center;
30
  }
31
-
32
- .cdr-table tbody th, .cdr-table-res tbody th, .resources-summary tbody th{
33
- text-align: center;
34
- color: var(--grey);
35
- font-weight: 100;
 
 
 
 
 
 
 
 
 
36
  }
37
 
38
- .cdr-table tbody td:nth-child(3) {
39
- text-align: right;
 
 
 
40
  }
41
 
42
- .cdr-table-res tbody {
43
- text-align: center;
 
 
 
44
  }
45
 
46
- .cdr-table tbody tr:hover, .cdr-table-res tbody tr:hover, .resources-summary tbody tr:hover {
47
- background-color: #f5f5f5;
 
 
 
 
 
48
  }
49
 
50
- /* tab2 */
51
- [class*="st-key-toggle_type_"] p {
52
- font-weight: 600;
53
- font-family: var(--font);
54
- text-transform: uppercase;
55
- letter-spacing: 1.25px;
56
- color: var(--brown-grey);
 
57
  }
58
 
59
- [class*="cap_type_"] p {
60
- font-family: var(--font);
 
 
 
 
 
 
61
  }
62
 
63
- [class*="slider_"] p, [class*="manual_"] p{
64
- font-family: var(--font);
65
  font-style: italic;
66
- color: var(--grey);
 
67
  }
68
 
69
- /* tab2 end */
 
 
 
 
 
 
70
 
71
- h1.custom-title {
72
- font-family: var(--font);
73
- font-size: 60px;
74
  font-style: italic;
75
- line-height: 0.95em;
76
  }
77
 
78
- .crra-hl {
79
- background: linear-gradient(90deg, var(--yellow) 0%, var(--green-light) 99%);
80
- -webkit-text-fill-color: transparent;
81
- background-clip: text;
82
- padding-right: 6px;
83
  }
84
 
85
- div.custom-corps {
86
- font-family: var(--font);
87
  font-size: 18px;
88
  }
89
 
90
- .resources-summary tbody {
91
- text-align: center
92
- }
93
-
94
- .text_with_border {
95
- border-left: 3px solid var(--red);
96
- padding-left: 16px;
97
- font-family: var(--font);
98
- font-size: 18px !important;
99
  }
100
 
101
  .objective_title {
102
  color: var(--red);
103
- font-family: var(--font);
104
  font-size: 14px;
105
  font-weight: 700;
106
  text-transform: uppercase;
@@ -108,330 +166,445 @@ div.custom-corps {
108
  margin: 0 0 8px 0;
109
  }
110
 
111
- .st-key-warning_resources [data-testid="stAlert"] {
112
- /* background-color: var(--off-white)!important; */
113
- clip-path: polygon(1.5% 0, 100% 0, 98.5% 100%, 0 100%);
 
114
  }
115
 
116
- .st-key-warning_resources p{
117
- font-family: var(--font);
 
 
 
 
 
 
 
 
 
 
 
 
118
  font-style: italic;
119
- font-size: 16px;
120
  line-height: 1.5;
121
  letter-spacing: 0.01em;
122
- /* font-weight: 600; */
123
- /* color: #var(--brown-grey); */
124
  }
125
 
126
- .st-key-warning_resources span{
 
 
 
 
127
  font-style: italic;
128
- /* color: #var(--brown-grey); */
 
 
129
  }
130
 
131
- .crra-heading {
132
- font-family: var(--font) !important;
133
- font-weight: 700;
134
  font-style: italic;
135
- font-size: clamp(32px, 2.5vw, 55px) !important;
136
- line-height: 1.25em;
137
- margin: 0;
138
- padding: 0;
139
  }
140
 
141
- .et_pb_module {
142
- margin-bottom: 30px
 
 
 
143
  }
144
 
145
- .crra-sub-heading {
146
- font-family: var(--font) !important;
147
- font-weight: 700;
148
- font-style: italic;
149
- font-size: clamp(25px, 2vw, 32px) !important;
150
- line-height: 1.25em;
151
  color: var(--brown-grey) !important;
152
- margin-bottom: 15px;
153
- margin-top: 30px !important;
154
  }
155
 
156
- .dialog-title {
157
- font-family: var(--font) !important;
158
- font-weight: 600;
159
- font-size: clamp(20px, 1vw, 30px) !important;
160
- line-height: 1.25em;
161
- margin-bottom: 15px;
162
- margin-top: 10px !important;
163
  }
164
 
165
- .crra-sub-sub-heading {
166
- font-family: var(--font) !important;
167
- font-weight: 600;
168
- font-style: italic;
169
- font-size: clamp(20px, 1.5vw, 30px) !important;
170
- line-height: 1.25em;
171
- color: var(--grey) !important;
172
- margin-bottom: 15px;
173
- margin-top: 20px !important;
174
- }
175
 
176
- .crra-group-name{
177
- font-family: var(--font) !important;
178
- font-weight: 600;
179
- font-style: italic;
180
- font-size: clamp(20px, 1.5vw, 30px) !important;
181
- line-height: 1.25em;
 
 
 
 
 
 
 
 
 
182
  }
183
 
184
- /* buttons */
185
- .st-key-btn-reset button, [class*="btn-reset-"] button {
186
- border-width: 0px !important;
 
 
187
  border-radius: 0px;
188
  color: var(--white);
189
  background: var(--red);
190
- height: clamp(52px, 3.125vw, 74px);
191
- padding: 0 clamp(20px, 2vw, 42px);
192
- clip-path: polygon(16px 0, 100% 0, calc(100% - 16px) 100%, 0 100%);
193
  transition: background 0.25s ease;
194
  }
195
-
196
- .st-key-btn-reset button p, [class*="btn-reset-"] button p{
197
- font-weight: 700 !important;
198
- font-family: var(--font);
199
- font-size: clamp(13px, 0.800vw, 18px);
200
- letter-spacing: 0.094444em;
201
- text-decoration: none;
202
- text-transform: uppercase;
203
- line-height: 1.2;
204
- }
205
-
206
- .st-key-btn-reset button:hover, [class*="btn-reset-"] button:hover{
207
- background: #E5503F;
208
- color: #FFFFFF;
209
  }
210
 
 
211
  .st-key-btn-load button {
 
212
  border-radius: 0px;
213
  color: var(--off-white);
214
  background: var(--green-dark);
215
- height: clamp(52px, 3.125vw, 74px);
216
- padding: 0 clamp(20px, 2vw, 42px);
217
- clip-path: polygon(16px 0, 100% 0, calc(100% - 16px) 100%, 0 100%);
218
  transition: background 0.25s ease;
219
  }
220
- .st-key-btn-load button p{
221
- font-weight: 700 !important;
222
- font-family: var(--font);
223
- font-size: clamp(13px, 0.800vw, 18px);
224
- letter-spacing: 0.094444em;
225
- text-decoration: none;
226
- text-transform: uppercase;
227
- line-height: 1.2;
228
- }
229
-
230
- .st-key-btn-load button:hover{
231
  background: #01740f;
232
  color: var(--off-white);
233
  }
234
 
235
- [class*="btn-new-"] button {
 
 
 
236
  border-radius: 0px;
237
- border: 0px;
238
  color: var(--red);
239
  background: var(--off-white);
240
- height: clamp(52px, 3.125vw, 74px);
241
- padding: 0 clamp(20px, 2vw, 42px);
242
- clip-path: polygon(16px 0, 100% 0, calc(100% - 16px) 100%, 0 100%);
243
  transition: background 0.25s ease;
244
  }
245
-
246
- [class*="btn-new-"] button p{
247
- font-weight: 700 !important;
248
- font-family: var(--font);
249
- font-size: clamp(13px, 0.800vw, 18px);
250
- letter-spacing: 0.094444em;
251
- text-decoration: none;
252
- text-transform: uppercase;
253
- line-height: 1.2;
254
  }
255
 
256
- [class*="btn-new-"] button:hover{
257
- background: #D8D7C4;
258
- color: var(--red);
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  }
260
 
 
261
  [class*="simple-btn-"] button {
262
  color: var(--red);
263
  }
264
-
265
- [class*="simple-btn-"] button p{
266
  text-transform: uppercase;
267
  font-weight: bold;
268
  }
269
-
270
- [class*="simple-btn-"] button:hover{
271
- color: #E5503F;
272
  font-style: italic;
273
  }
274
 
275
- .stSidebar {
276
- background: var(--off-white) !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  }
278
 
279
- section[data-testid="stExpander"] summary {
280
- font-family: var(--font);
281
- font-weight: 600;
282
- text-transform: uppercase;
283
  }
284
 
285
- [class*="crra-select"] [data-baseweb="select"], .st-key-crra-select p{
286
- font-family: var(--font);
287
- font-size: 16px;
 
 
 
 
 
 
 
 
 
 
 
288
  }
289
 
290
- [class*="crra-select"] p{
291
- font-style: italic;
292
- font-weight: bold;
 
 
293
  }
294
 
295
- /* .st-key-crra-select [data-baseweb="select"] div{
296
- background-color: var(--off-white);
297
- } */
 
 
 
 
 
 
 
 
 
 
 
 
 
298
 
299
- /* tab1 */
300
- [class*="_dlg_edit_existing_"] p {
301
- font-family: var(--font);
302
- color: var(--red);
303
- font-weight: bold;
304
- font-style: italic;
 
 
305
  }
306
 
307
- .caption {
308
- font-family: var(--font);
309
- color:#a0a0a0;
310
- font-size: 0.88rem !important;
311
- letter-spacing: 0.05rem;
 
 
312
  }
313
 
314
- .caption-input {
315
- font-family: var(--font);
316
- font-style: italic;
317
- letter-spacing: 0.05rem;
318
  }
319
 
320
- [class*="_dlg_existing_enable_"] p, [class*="label-input-"] p {
321
- font-family: var(--font);
 
322
  }
323
 
324
- .st-key-container-btn {
325
- display: flex;
326
- flex-direction: row;
327
- gap: 1.125%;
 
 
328
  }
329
 
 
 
 
 
 
 
 
 
330
  [class*="cap_"] p {
331
  font-family: var(--font);
332
  }
333
 
334
- /* .stDownloadButton {
335
- place-self: center;
336
- } */
337
-
338
- [class*="st-key-download_button_"] button {
339
- border-width: 0px !important;
340
- border-radius: 0px;
341
- color: var(--green-dark);
342
- background: var(--off-white);
343
- height: clamp(52px, 3.125vw, 74px);
344
- padding: 0 clamp(20px, 2vw, 42px);
345
- clip-path: polygon(16px 0, 100% 0, calc(100% - 16px) 100%, 0 100%);
346
- transition: background 0.25s ease;
347
- }
348
-
349
- [class*="st-key-download_button_save_custom"] button {
350
- float:inline-end;
351
  }
352
-
353
- [class*="st-key-download_button_"] button p{
354
- font-weight: 700 !important;
355
- font-family: var(--font);
356
- font-size: clamp(13px, 0.800vw, 18px);
357
- letter-spacing: 0.094444em;
358
- text-decoration: none;
359
  text-transform: uppercase;
360
- line-height: 1.2;
 
361
  }
362
-
363
- [class*="st-key-download_button_"] button:hover{
364
- background: #D8D7C4;
365
- color: #FFFFFF;
366
  }
367
 
368
- [class*="resource_uploader_"],
 
369
  [class*="inputs_loader_"],
370
  [class*="scenario_loader_"],
371
- .st-key-comparison_loader
372
- {
373
  font-family: var(--font);
374
  }
375
 
376
- [class*="resource_uploader_"] p,
377
  [class*="inputs_loader_"] p,
378
  [class*="scenario_loader_"] p,
379
- .st-key-comparison_loader p
380
- {
381
- font-family: var(--font);
382
  color: var(--grey);
383
  font-style: italic;
384
  }
385
 
386
- [class*="resource_uploader_"] [data-testid="stFileUploaderDropzone"],
387
  [class*="inputs_loader_"] [data-testid="stFileUploaderDropzone"],
388
  [class*="scenario_loader_"] [data-testid="stFileUploaderDropzone"],
389
- .st-key-comparison_loader [data-testid="stFileUploaderDropzone"]
390
- {
391
  background-color: var(--off-white);
392
- /* clip-path: polygon(16px 0, 100% 0, calc(100% - 16px) 100%, 0 100%); */
393
- /* border-radius: 0px; */
394
- /* box-shadow: 0px 4px 20px 0px rgba(0, 0, 0, 0.20); */
395
  color: var(--green-dark);
396
- font-weight: 700 !important;
397
  letter-spacing: 0.094444em;
398
- text-decoration: none;
399
  text-transform: uppercase;
400
- /* justify-self: center; */
 
401
  width: stretch;
402
- font-size: 0.875rem;;
403
  }
404
 
405
- /* [class*="resource_uploader_"] [data-testid="stFileUploaderDropzone"] [data-testid="stFileUploaderDropzoneInstructions"]{
406
- padding-inline: 3rem;
407
- } */
408
-
409
- [class*="resource_uploader_"] [data-testid="stFileUploaderDropzone"] [data-testid="stFileUploaderDropzoneInstructions"] span svg,
410
- [class*="inputs_loader_"] [data-testid="stFileUploaderDropzone"] [data-testid="stFileUploaderDropzoneInstructions"] span svg,
411
- [class*="scenario_loader_"] [data-testid="stFileUploaderDropzone"] [data-testid="stFileUploaderDropzoneInstructions"] span svg,
412
- .st-key-comparison_loader [data-testid="stFileUploaderDropzone"] [data-testid="stFileUploaderDropzoneInstructions"] span svg
413
- {
414
  color: var(--green-dark);
415
  }
416
 
417
  [class*="resource_uploader_"] [data-testid="stBaseButton-secondary"],
418
  [class*="inputs_loader_"] [data-testid="stBaseButton-secondary"],
419
- [class*="scenario_loader_"] [data-testid="stBaseButton-secondary"]{
420
- color: var(--off-white);
421
  font-weight: 700 !important;
422
  text-transform: uppercase;
423
  font-style: italic;
424
- background-color: var(--green-dark);
425
- border: none;
426
  }
427
 
428
- div.custom-corps-light {
429
- font-family: var(--font);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
  color: var(--grey);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  font-style: italic;
432
- font-weight: 400;
433
- font-size: 0.875rem;
434
- /* margin-bottom: 0.25rem; */
435
  }
436
 
437
- /* tab1 end */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* ═══════════════════════════════════════════════════════════
2
+ CRRA Design System — styles.css
3
+ Palette : off-white #eae9d8 red #ff5f4d green-dark #298231
4
+ green #55c95b green-light #92ef9b yellow #d9e210
5
+ brown-grey #665d5b grey #a0a0a0
6
+ Font : Segoe UI, sans-serif
7
+ ═══════════════════════════════════════════════════════════ */
8
+
9
+ /* ── 0. Font face ──────────────────────────────────────────── */
10
+ /* @font-face {
11
+ font-family: 'Segoe UI';
12
+ src: url(/app/static/fonts/segoe-ui-400.woff2) format('woff2'),
13
+ url(/app/static/fonts/segoe-ui-400.woff) format('woff');
14
+ font-weight: 400;
15
+ font-style: normal;
16
+ font-display: swap;
17
+ } */
18
+
19
+ /* ── 1. Variables ──────────────────────────────────────────── */
20
  :root {
21
+ --off-white: #eae9d8;
22
+ --red: #ff5f4d;
23
+ --green-dark: #298231;
24
+ --green: #55c95b;
25
+ --green-light: #92ef9b;
26
+ --yellow: #d9e210;
27
+ --white: #ffffff;
28
+ --brown-grey: #665d5b;
29
+ --grey: #a0a0a0;
30
+ --black: #000000;
31
+ --font: 'Segoe UI', system-ui, -apple-system, sans-serif;
32
+
33
+ --btn-height: clamp(52px, 3.125vw, 74px);
34
+ --btn-padding: 0 clamp(20px, 2vw, 42px);
35
+ --btn-clip: polygon(16px 0, 100% 0, calc(100% - 16px) 100%, 0 100%);
36
+ --btn-fs: clamp(13px, 0.8vw, 18px);
37
+ }
38
+
39
+ /* ── 2. Global font ────────────────────────────────────────── */
40
+ /* span and div are excluded — too broad and breaks Material icon fonts */
41
+ html, body,
42
+ .stApp, .main, .block-container,
43
+ p, label, li,
44
+ h1, h2, h3, h4, h5, h6,
45
+ input, textarea, select,
46
+ .stMarkdown, .stAlert,
47
+ [data-testid="stWidgetLabel"] {
48
+ font-family: var(--font) !important;
49
  }
50
 
51
+ /* ── 3. Sidebar ────────────────────────────────────────────── */
52
+ .stSidebar {
53
+ background: var(--off-white) !important;
 
 
 
 
54
  }
55
 
56
+ /* ── 4. Markdown headings (st.markdown "### …") ────────────── */
57
+ .stMarkdown h1 {
58
+ font-weight: 700;
59
+ font-style: italic;
60
+ color: var(--black);
61
+ line-height: 1.2em;
62
  }
63
+ .stMarkdown h2 {
64
+ font-weight: 700;
65
+ font-style: italic;
66
+ color: var(--brown-grey);
67
+ line-height: 1.2em;
68
+ }
69
+ .stMarkdown h3 {
70
+ font-weight: 600;
71
+ color: var(--black);
72
+ line-height: 1.3em;
73
+ }
74
+ .stMarkdown h4 {
75
+ font-weight: 600;
76
+ color: var(--brown-grey);
77
  }
78
 
79
+ /* ── 5. Custom typography classes ──────────────────────────── */
80
+ h1.custom-title {
81
+ font-size: 60px;
82
+ font-style: italic;
83
+ line-height: 0.95em;
84
  }
85
 
86
+ .crra-hl {
87
+ background: linear-gradient(90deg, var(--yellow) 0%, var(--green-light) 99%);
88
+ -webkit-text-fill-color: transparent;
89
+ background-clip: text;
90
+ padding-right: 6px;
91
  }
92
 
93
+ .crra-heading {
94
+ font-weight: 700;
95
+ font-style: italic;
96
+ font-size: clamp(32px, 2.5vw, 55px) !important;
97
+ line-height: 1.25em;
98
+ margin: 0;
99
+ padding: 0;
100
  }
101
 
102
+ .crra-sub-heading {
103
+ font-weight: 700;
104
+ font-style: italic;
105
+ font-size: clamp(25px, 2vw, 32px) !important;
106
+ line-height: 1.25em;
107
+ color: var(--brown-grey) !important;
108
+ margin-bottom: 15px;
109
+ margin-top: 30px !important;
110
  }
111
 
112
+ .crra-sub-sub-heading {
113
+ font-weight: 600;
114
+ font-style: italic;
115
+ font-size: clamp(20px, 1.5vw, 30px) !important;
116
+ line-height: 1.25em;
117
+ color: var(--grey) !important;
118
+ margin-bottom: 15px;
119
+ margin-top: 20px !important;
120
  }
121
 
122
+ .crra-group-name {
123
+ font-weight: 600;
124
  font-style: italic;
125
+ font-size: clamp(20px, 1.5vw, 30px) !important;
126
+ line-height: 1.25em;
127
  }
128
 
129
+ .dialog-title {
130
+ font-weight: 600;
131
+ font-size: clamp(20px, 1vw, 30px) !important;
132
+ line-height: 1.25em;
133
+ margin-bottom: 15px;
134
+ margin-top: 10px !important;
135
+ }
136
 
137
+ .caption {
138
+ color: var(--grey);
139
+ font-size: 0.88rem !important;
140
  font-style: italic;
141
+ letter-spacing: 0.05rem;
142
  }
143
 
144
+ .caption-input {
145
+ font-style: italic;
146
+ letter-spacing: 0.05rem;
 
 
147
  }
148
 
149
+ .custom-corps {
 
150
  font-size: 18px;
151
  }
152
 
153
+ .custom-corps-light {
154
+ color: var(--grey);
155
+ font-style: italic;
156
+ font-weight: 400;
157
+ font-size: 0.875rem;
 
 
 
 
158
  }
159
 
160
  .objective_title {
161
  color: var(--red);
 
162
  font-size: 14px;
163
  font-weight: 700;
164
  text-transform: uppercase;
 
166
  margin: 0 0 8px 0;
167
  }
168
 
169
+ .text_with_border {
170
+ border-left: 3px solid var(--red);
171
+ padding-left: 16px;
172
+ font-size: 18px !important;
173
  }
174
 
175
+ /* ── 6. Colorful bar divider ───────────────────────────────── */
176
+ .et_pb_module {
177
+ margin-bottom: 30px;
178
+ }
179
+
180
+ /* ── 7. Alerts ─────────────────────────────────────────────── */
181
+ [data-testid="stAlert"],
182
+ [data-testid="stAlert"] > div,
183
+ [data-testid="stAlert"] > div > div {
184
+ border-radius: 0px !important;
185
+ clip-path: polygon(1% 0, 100% 0, 99% 100%, 0 100%);
186
+ }
187
+
188
+ [data-testid="stAlert"] p {
189
  font-style: italic;
190
+ font-size: 0.95rem;
191
  line-height: 1.5;
192
  letter-spacing: 0.01em;
193
+ padding-left: 1%;
 
194
  }
195
 
196
+ /* Special warning box (tab1) */
197
+ .st-key-warning_resources [data-testid="stAlert"] {
198
+ clip-path: polygon(1.5% 0, 100% 0, 98.5% 100%, 0 100%);
199
+ }
200
+ .st-key-warning_resources p {
201
  font-style: italic;
202
+ font-size: 16px;
203
+ line-height: 1.5;
204
+ letter-spacing: 0.01em;
205
  }
206
 
207
+ .st-key-warning_resources span,
208
+ [data-testid="stAlert"] span {
 
209
  font-style: italic;
210
+ padding-left: 1%;
 
 
 
211
  }
212
 
213
+ /* ── 8. Metrics ────────────────────────────────────────────── */
214
+ [data-testid="metric-container"] {
215
+ background-color: var(--off-white);
216
+ padding: 16px 20px;
217
+ clip-path: polygon(10px 0, 100% 0, calc(100% - 10px) 100%, 0 100%);
218
  }
219
 
220
+ [data-testid="stMetricLabel"] p {
221
+ font-weight: 600 !important;
222
+ text-transform: uppercase !important;
223
+ letter-spacing: 0.08em !important;
224
+ font-size: 0.72rem !important;
 
225
  color: var(--brown-grey) !important;
 
 
226
  }
227
 
228
+ [data-testid="stMetricValue"] {
229
+ font-weight: 700 !important;
230
+ color: var(--green-dark) !important;
 
 
 
 
231
  }
232
 
233
+ /* ── 9. Buttons ────────────────────────────────────────────── */
 
 
 
 
 
 
 
 
 
234
 
235
+ /* Shared button text style */
236
+ [class*="edit_batch_"] [data-testid="stBaseButton-secondary"] p,
237
+ .st-key-btn-reset button p,
238
+ [class*="btn-reset-"] button p,
239
+ .st-key-btn-load button p,
240
+ [class*="btn-new-"] button p,
241
+ [class*="st-key-download_button_"] button p,
242
+ [data-testid="stDownloadButton"] button p,
243
+ [data-testid="stBaseButton-primary"] p {
244
+ font-weight: 700 !important;
245
+ font-size: var(--btn-fs);
246
+ letter-spacing: 0.094444em;
247
+ text-transform: uppercase;
248
+ line-height: 1.2;
249
+ text-decoration: none;
250
  }
251
 
252
+ /* Reset — red */
253
+ .st-key-btn-reset button,
254
+ [class*="btn-reset-"] button,
255
+ [data-testid="stBaseButton-primary"] {
256
+ border: none !important;
257
  border-radius: 0px;
258
  color: var(--white);
259
  background: var(--red);
260
+ height: var(--btn-height);
261
+ padding: var(--btn-padding);
262
+ clip-path: var(--btn-clip);
263
  transition: background 0.25s ease;
264
  }
265
+ .st-key-btn-reset button:hover,
266
+ [class*="btn-reset-"] button:hover,
267
+ [data-testid="stBaseButton-primary"]:hover {
268
+ background: #e5503f;
269
+ color: var(--white);
 
 
 
 
 
 
 
 
 
270
  }
271
 
272
+ /* Load — green-dark */
273
  .st-key-btn-load button {
274
+ border: none !important;
275
  border-radius: 0px;
276
  color: var(--off-white);
277
  background: var(--green-dark);
278
+ height: var(--btn-height);
279
+ padding: var(--btn-padding);
280
+ clip-path: var(--btn-clip);
281
  transition: background 0.25s ease;
282
  }
283
+ .st-key-btn-load button:hover {
 
 
 
 
 
 
 
 
 
 
284
  background: #01740f;
285
  color: var(--off-white);
286
  }
287
 
288
+ /* New — off-white / red text */
289
+ [class*="btn-new-"] button,
290
+ [class*="edit_batch_"] [data-testid="stBaseButton-secondary"] {
291
+ border: none !important;
292
  border-radius: 0px;
 
293
  color: var(--red);
294
  background: var(--off-white);
295
+ height: var(--btn-height);
296
+ padding: var(--btn-padding);
297
+ clip-path: var(--btn-clip);
298
  transition: background 0.25s ease;
299
  }
300
+ [class*="edit_batch_"] [data-testid="stBaseButton-secondary"]:hover,
301
+ [class*="btn-new-"] button:hover {
302
+ background: #d8d7c4;
303
+ color: var(--red);
 
 
 
 
 
304
  }
305
 
306
+ /* Download — off-white / green-dark text — covers all download buttons */
307
+ [class*="st-key-download_button_"] button,
308
+ [data-testid="stDownloadButton"] button {
309
+ border: none !important;
310
+ border-radius: 0px;
311
+ color: var(--green-dark);
312
+ background: var(--off-white);
313
+ height: var(--btn-height);
314
+ padding: var(--btn-padding);
315
+ clip-path: var(--btn-clip);
316
+ transition: background 0.25s ease, color 0.25s ease;
317
+ }
318
+ [class*="st-key-download_button_"] button:hover,
319
+ [data-testid="stDownloadButton"] button:hover {
320
+ background: #d8d7c4;
321
+ color: var(--green-dark);
322
  }
323
 
324
+ /* Simple text button */
325
  [class*="simple-btn-"] button {
326
  color: var(--red);
327
  }
328
+ [class*="simple-btn-"] button p {
 
329
  text-transform: uppercase;
330
  font-weight: bold;
331
  }
332
+ [class*="simple-btn-"] button:hover {
333
+ color: #e5503f;
 
334
  font-style: italic;
335
  }
336
 
337
+ /* Run Optimization — CTA principal */
338
+ .st-key-btn-run-optimization button {
339
+ border: none !important;
340
+ border-radius: 0px;
341
+ background: var(--green-dark) !important;
342
+ color: var(--off-white) !important;
343
+ clip-path: var(--btn-clip);
344
+ height: clamp(64px, 3vw, 90px) !important;
345
+ padding: 0 clamp(28px, 3vw, 56px) !important;
346
+ transition: background 0.25s ease, transform 0.15s ease;
347
+ box-shadow: 4px 4px 0px var(--green);
348
+ }
349
+ .st-key-btn-run-optimization button:hover {
350
+ background: #01740f !important;
351
+ color: var(--off-white) !important;
352
+ transform: translate(-2px, -2px);
353
+ box-shadow: 6px 6px 0px var(--green);
354
+ }
355
+ .st-key-btn-run-optimization button:active {
356
+ transform: translate(0px, 0px);
357
+ box-shadow: 2px 2px 0px var(--green);
358
+ }
359
+ .st-key-btn-run-optimization button p {
360
+ font-weight: 700 !important;
361
+ font-size: clamp(15px, 1vw, 20px) !important;
362
+ letter-spacing: 0.12em !important;
363
+ text-transform: uppercase !important;
364
  }
365
 
366
+ /* Default secondary Streamlit button */
367
+ [data-testid="stBaseButton-secondary"] {
368
+ border-radius: 0px;
 
369
  }
370
 
371
+ /* ── 10. Form elements ──────────────────────────────────────── */
372
+
373
+ /* Input / number labels */
374
+ [data-testid="stTextInput"] label p,
375
+ [data-testid="stNumberInput"] label p,
376
+ [data-testid="stTextArea"] label p,
377
+ [data-testid="stSelectbox"] label p,
378
+ [data-testid="stMultiSelect"] label p,
379
+ [data-testid="stSlider"] label p {
380
+ font-weight: 600 !important;
381
+ color: var(--brown-grey) !important;
382
+ font-size: 0.85rem !important;
383
+ text-transform: uppercase !important;
384
+ letter-spacing: 0.06em !important;
385
  }
386
 
387
+ /* [data-testid="stRadio"] label p, */
388
+ [data-testid="stCheckbox"] label p{
389
+ font-weight: 600 !important;
390
+ font-size: 1rem !important;
391
+ text-transform: uppercase !important;
392
  }
393
 
394
+ /* Text & number input fields */
395
+ [data-testid="stTextInput"] input,
396
+ [data-testid="stNumberInput"] input {
397
+ border-radius: 0px !important;
398
+ border-top: none !important;
399
+ border-left: none !important;
400
+ border-right: none !important;
401
+ background: transparent !important;
402
+ box-shadow: none !important;
403
+ transition: border-color 0.2s ease;
404
+ }
405
+ [data-testid="stTextInput"] input:focus,
406
+ [data-testid="stNumberInput"] input:focus {
407
+ border-bottom-color: var(--green-dark) !important;
408
+ box-shadow: none !important;
409
+ }
410
 
411
+ /* Text area */
412
+ [data-testid="stTextArea"] textarea {
413
+ border-radius: 0px !important;
414
+ border-color: var(--grey) !important;
415
+ }
416
+ [data-testid="stTextArea"] textarea:focus {
417
+ border-color: var(--green-dark) !important;
418
+ box-shadow: none !important;
419
  }
420
 
421
+ /* Select box */
422
+ [data-baseweb="select"] {
423
+ font-family: var(--font) !important;
424
+ font-size: 16px;
425
+ }
426
+ [data-baseweb="select"] > div {
427
+ border: none !important;
428
  }
429
 
430
+ [data-baseweb="select"] > div:focus-within {
431
+ border-bottom-color: var(--green-dark) !important;
432
+ box-shadow: none !important;
 
433
  }
434
 
435
+ /* Radio value text */
436
+ [data-testid="stRadio"] p {
437
+ font-size: 0.9rem;
438
  }
439
 
440
+ /* Slider help text */
441
+ [class*="slider_"] p,
442
+ [class*="manual_"] p,
443
+ [data-testid="stSlider"] p {
444
+ font-style: italic;
445
+ color: var(--grey);
446
  }
447
 
448
+ /* Tab2 constraint toggles */
449
+ [class*="st-key-toggle_type_"] p {
450
+ font-weight: 600;
451
+ text-transform: uppercase;
452
+ letter-spacing: 1.25px;
453
+ color: var(--brown-grey);
454
+ }
455
+ [class*="cap_type_"] p,
456
  [class*="cap_"] p {
457
  font-family: var(--font);
458
  }
459
 
460
+ /* ── 11. Expanders ──────────────────────────────────────────── */
461
+ section[data-testid="stExpander"] {
462
+ border-radius: 0px !important;
463
+ border: 1px solid rgba(0, 0, 0, 0.08);
 
 
 
 
 
 
 
 
 
 
 
 
 
464
  }
465
+ section[data-testid="stExpander"] summary {
466
+ font-weight: 600;
 
 
 
 
 
467
  text-transform: uppercase;
468
+ letter-spacing: 0.08em;
469
+ font-size: 0.83rem;
470
  }
471
+ section[data-testid="stExpander"] summary p {
472
+ font-weight: 600;
473
+ text-transform: uppercase;
 
474
  }
475
 
476
+ /* ── 12. File uploaders ─────────────────────────────────────── */
477
+ [class*="resource_uploader_"],
478
  [class*="inputs_loader_"],
479
  [class*="scenario_loader_"],
480
+ .st-key-comparison_loader {
 
481
  font-family: var(--font);
482
  }
483
 
484
+ [class*="resource_uploader_"] p,
485
  [class*="inputs_loader_"] p,
486
  [class*="scenario_loader_"] p,
487
+ .st-key-comparison_loader p {
 
 
488
  color: var(--grey);
489
  font-style: italic;
490
  }
491
 
492
+ [class*="resource_uploader_"] [data-testid="stFileUploaderDropzone"],
493
  [class*="inputs_loader_"] [data-testid="stFileUploaderDropzone"],
494
  [class*="scenario_loader_"] [data-testid="stFileUploaderDropzone"],
495
+ .st-key-comparison_loader [data-testid="stFileUploaderDropzone"] {
 
496
  background-color: var(--off-white);
 
 
 
497
  color: var(--green-dark);
498
+ font-weight: 700;
499
  letter-spacing: 0.094444em;
 
500
  text-transform: uppercase;
501
+ font-size: 0.875rem;
502
+ border-radius: 0px !important;
503
  width: stretch;
 
504
  }
505
 
506
+ [class*="resource_uploader_"] [data-testid="stFileUploaderDropzone"] span svg,
507
+ [class*="inputs_loader_"] [data-testid="stFileUploaderDropzone"] span svg,
508
+ [class*="scenario_loader_"] [data-testid="stFileUploaderDropzone"] span svg,
509
+ .st-key-comparison_loader [data-testid="stFileUploaderDropzone"] span svg {
 
 
 
 
 
510
  color: var(--green-dark);
511
  }
512
 
513
  [class*="resource_uploader_"] [data-testid="stBaseButton-secondary"],
514
  [class*="inputs_loader_"] [data-testid="stBaseButton-secondary"],
515
+ [class*="scenario_loader_"] [data-testid="stBaseButton-secondary"] {
516
+ color: var(--off-white) !important;
517
  font-weight: 700 !important;
518
  text-transform: uppercase;
519
  font-style: italic;
520
+ background-color: var(--green-dark) !important;
521
+ border: none !important;
522
  }
523
 
524
+ /* ── 13. Custom HTML tables ─────────────────────────────────── */
525
+ .cdr-table, .cdr-table-res, .resources-summary {
526
+ width: 100%;
527
+ border-collapse: separate;
528
+ border-spacing: 0;
529
+ border-radius: 10px;
530
+ overflow: hidden;
531
+ }
532
+
533
+ .cdr-table thead th,
534
+ .cdr-table-res thead th,
535
+ .resources-summary thead th {
536
+ background-color: var(--green);
537
+ color: var(--white);
538
+ font-weight: bold;
539
+ padding: 8px 12px;
540
+ text-align: center;
541
+ }
542
+
543
+ .cdr-table tbody th,
544
+ .cdr-table-res tbody th,
545
+ .resources-summary tbody th {
546
+ text-align: center;
547
  color: var(--grey);
548
+ font-weight: 100;
549
+ }
550
+
551
+ .cdr-table tbody td:nth-child(3) {
552
+ text-align: right;
553
+ }
554
+
555
+ .cdr-table-res tbody,
556
+ .resources-summary tbody {
557
+ text-align: center;
558
+ }
559
+
560
+ .cdr-table tbody tr:hover,
561
+ .cdr-table-res tbody tr:hover,
562
+ .resources-summary tbody tr:hover {
563
+ background-color: #f5f5f5;
564
+ }
565
+
566
+ /* ── 14. Tab1 dialogs ───────────────────────────────────────── */
567
+ [class*="_dlg_edit_existing_"] p {
568
+ color: var(--red);
569
+ font-weight: bold;
570
  font-style: italic;
 
 
 
571
  }
572
 
573
+ [class*="_dlg_existing_enable_"] p,
574
+ [class*="label-input-"] p {
575
+ font-family: var(--font);
576
+ }
577
+
578
+ /* ── 15. Select (crra-select variant) ───────────────────────── */
579
+ [class*="crra-select"] [data-baseweb="select"],
580
+ .st-key-crra-select p {
581
+ font-size: 16px;
582
+ }
583
+ [class*="crra-select"] p {
584
+ font-style: italic;
585
+ font-weight: bold;
586
+ }
587
+
588
+ /* ── 16. Button container layout ────────────────────────────── */
589
+ .st-key-container-btn {
590
+ display: flex;
591
+ flex-direction: row;
592
+ gap: 1.125%;
593
+ }
594
+
595
+ /* ── 17. Scrollbar ──────────────────────────────────────────── */
596
+ ::-webkit-scrollbar {
597
+ width: 6px;
598
+ height: 6px;
599
+ }
600
+ ::-webkit-scrollbar-track {
601
+ background: var(--off-white);
602
+ border-radius: 3px;
603
+ }
604
+ ::-webkit-scrollbar-thumb {
605
+ background: var(--grey);
606
+ border-radius: 3px;
607
+ }
608
+ ::-webkit-scrollbar-thumb:hover {
609
+ background: var(--brown-grey);
610
+ }
tabs/tab1_inputs.py CHANGED
@@ -4,7 +4,7 @@ import streamlit as st
4
  from config.config import (resource_groups, resource_order, resource_tooltips,
5
  resource_unit_map, method_groups, CO2_UNIT, secondary_resources)
6
  from utils.data_utils import render_resource_input, strip_unit_suffix
7
- from utils.ui_helpers import format_resource_summary, show_bar
8
  from io import BytesIO
9
 
10
  # ─── Custom resource helpers ──────────────────────────────────────────────────
@@ -439,7 +439,8 @@ def _render_custom_batch_inline(global_idx, batch, cost_sliders, unit):
439
 
440
  col_edit, col_del = st.columns([1, 1])
441
  with col_edit:
442
- if st.button("Edit", key=f"edit_batch_{global_idx}", use_container_width=True, icon=":material/edit:"):
 
443
  dialog_add_custom_resource(cost_sliders, edit_index=global_idx)
444
  with col_del:
445
  if st.button("Delete", key=f"del_batch_{global_idx}", use_container_width=True,
@@ -767,7 +768,19 @@ def render_tab(cost_sliders):
767
  df_custom = pd.DataFrame(custom_rows).set_index("Resource")
768
  df_custom["Type"] = "New"
769
  dfs.append(df_custom)
770
- df_all = pd.concat(dfs)[["Available Amount", "Type"]]
771
- st.markdown(format_resource_summary(df_all), unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
772
  else:
773
  st.error("No resources defined yet. Set availability above to populate the summary table.")
 
4
  from config.config import (resource_groups, resource_order, resource_tooltips,
5
  resource_unit_map, method_groups, CO2_UNIT, secondary_resources)
6
  from utils.data_utils import render_resource_input, strip_unit_suffix
7
+ from utils.ui_helpers import show_bar
8
  from io import BytesIO
9
 
10
  # ─── Custom resource helpers ──────────────────────────────────────────────────
 
439
 
440
  col_edit, col_del = st.columns([1, 1])
441
  with col_edit:
442
+ if st.button("Edit", key=f"edit_batch_{global_idx}", use_container_width=True,
443
+ type="secondary", icon=":material/edit:"):
444
  dialog_add_custom_resource(cost_sliders, edit_index=global_idx)
445
  with col_del:
446
  if st.button("Delete", key=f"del_batch_{global_idx}", use_container_width=True,
 
768
  df_custom = pd.DataFrame(custom_rows).set_index("Resource")
769
  df_custom["Type"] = "New"
770
  dfs.append(df_custom)
771
+ df_all = pd.concat(dfs)[["Available Amount", "Type"]].reset_index().rename(columns={"index": "Resource"})
772
+ num_rows = len(df_all)
773
+ st.dataframe(
774
+ df_all,
775
+ use_container_width=True,
776
+ hide_index=True,
777
+ height=(num_rows * 36 + 36),
778
+ column_config={
779
+ "Resource": st.column_config.TextColumn("Resource", width="large"),
780
+ "Available Amount": st.column_config.TextColumn("Available Amount"),
781
+ "Type": st.column_config.TextColumn("Type"),
782
+ },
783
+ row_height=36
784
+ )
785
  else:
786
  st.error("No resources defined yet. Set availability above to populate the summary table.")
tabs/tab2_constraints.py CHANGED
@@ -175,11 +175,12 @@ def render_tab():
175
  df_display,
176
  use_container_width=True,
177
  hide_index=True,
178
- height=(num_rows * 36 + 10),
179
  column_config={
180
  "CDR Method": st.column_config.TextColumn("CDR Method", width="large"),
181
  "Cap value": st.column_config.TextColumn("Cap value"),
182
  },
 
183
  )
184
  else:
185
  st.warning("No active methods selected.")
 
175
  df_display,
176
  use_container_width=True,
177
  hide_index=True,
178
+ height=(num_rows * 36 + 36),
179
  column_config={
180
  "CDR Method": st.column_config.TextColumn("CDR Method", width="large"),
181
  "Cap value": st.column_config.TextColumn("Cap value"),
182
  },
183
+ row_height=36
184
  )
185
  else:
186
  st.warning("No active methods selected.")
tabs/tab3_coefficients.py CHANGED
@@ -393,11 +393,12 @@ def render_tab(cost_sliders):
393
  num_rows = len(df_current)
394
  st.dataframe(
395
  data=styled,
396
- height=(num_rows*36 + 15),
397
  use_container_width=True,
398
  column_config={
399
  "_index": st.column_config.TextColumn(
400
  "Method",
401
  width="large"
402
- ),
403
- })
 
 
393
  num_rows = len(df_current)
394
  st.dataframe(
395
  data=styled,
396
+ height=(num_rows * 36 + 37),
397
  use_container_width=True,
398
  column_config={
399
  "_index": st.column_config.TextColumn(
400
  "Method",
401
  width="large"
402
+ )},
403
+ row_height=36
404
+ )
utils/ui_helpers.py CHANGED
@@ -1,238 +1,80 @@
1
- import numpy as np
2
- import pandas as pd
3
- import streamlit as st
4
- from config.config import CO2_UNIT, CRRA_COLORS, CRRA_FONT
5
- import plotly.express as px
6
- from config.config import PLOTLY_TEMPLATE, PLOTLY_COLOR_SEQUENCE
7
-
8
- # params
9
- background_color = "#3d86e9"
10
-
11
- def apply_chart_style(fig, height=None):
12
- """Style cohérent CRRA pour tous les graphiques Plotly."""
13
- fig.update_layout(
14
- template=PLOTLY_TEMPLATE,
15
- font=dict(family=CRRA_FONT, size=12, color=CRRA_COLORS["black"]),
16
- margin=dict(t=40, b=40, l=40, r=20),
17
- legend=dict(
18
- orientation="v",
19
- yanchor="middle", y=0.5,
20
- xanchor="left", x=1.02,
21
- ),
22
- )
23
- if height:
24
- fig.update_layout(height=height)
25
- return fig
26
-
27
- def register_chart_data(name: str, df: pd.DataFrame):
28
- """Enregistre les données d'un graphique pour l'export consolidé final."""
29
- st.session_state.setdefault("chart_data_registry", {})
30
- st.session_state["chart_data_registry"][name] = df.copy()
31
-
32
- def show_bar():
33
- st.markdown(
34
- """
35
- <div class="et_pb_module">
36
- <div style="display:flex;width:100%;height:12px;margin:0 auto 0 0;">
37
- <span style="flex:1;background:#FF5F4D;"></span>
38
- <span style="flex:1;background:#A0A0A0;"></span>
39
- <span style="flex:1;background:#D9E210;"></span>
40
- <span style="flex:1;background:#55C95B;"></span>
41
- </div>
42
- </div>
43
- """,
44
- unsafe_allow_html=True,
45
- )
46
-
47
- def styled_table(df):
48
- return (
49
- df.style
50
- .set_table_styles([{"selector": "th", "props": [("background-color", background_color), ("font-weight", "bold")]}])
51
- )
52
-
53
- def highlight_differences(data, df_default):
54
- styled = pd.DataFrame('', index=data.index, columns=data.columns)
55
- for m in data.index:
56
- for r in data.columns:
57
- if m in df_default.index and r in df_default.columns:
58
- current = data.at[m, r]
59
- default = df_default.at[m, r]
60
- if abs(current - default) > 1e-6:
61
- styled.at[m, r] = 'background-color: #cce5ff'
62
- return styled
63
-
64
- def smart_format(val):
65
- return f"{val:.2e}" if abs(val) < 0.01 else f"{val:.2f}"
66
-
67
- def inputs_table_style(df):
68
- return(
69
- df.style
70
- .set_properties(**{'text-align': 'right'})
71
- .format(smart_format)
72
- .set_table_styles([{"selector": "th", "props": [("background-color", background_color), ("font-weight", "bold")]}])
73
- )
74
-
75
- def format_table_summary(df: pd.DataFrame, class_name: str):
76
- df = df.copy()
77
- df.index.name = "CDR Method"
78
- df = df.reset_index()
79
-
80
- html = df.to_html(index=True, classes=class_name, border=0)
81
- return html
82
-
83
- def format_efficiency_summary(df: pd.DataFrame, df_default: pd.DataFrame):
84
- df_numeric = df.copy()
85
- df_display = df_numeric.map(smart_display_format)
86
-
87
- # Highlight modified values
88
- def highlight_changes(data):
89
- styled = pd.DataFrame('', index=data.index, columns=data.columns)
90
- for m in data.index:
91
- for r in data.columns:
92
- try:
93
- current = df_numeric.at[m, r]
94
- default = df_default.at[m, r]
95
- if abs(current - default) > 1e-6:
96
- styled.at[m, r] = 'background-color: #ff5f4d'
97
- except:
98
- continue
99
- return styled
100
-
101
- # Table style
102
- return (
103
- df_display
104
- .style
105
- .apply(highlight_changes, axis=None)
106
- )
107
-
108
- # -----------------------------
109
- # SLIDER GROUP HELPER
110
- # -----------------------------
111
- def render_slider_group(method_name, resource, slider_data, key_prefix="", enable_toggle=False, toggle_default=True, store_path=None):
112
- """
113
- Renders a slider for a method/resource pair with optional enable toggle.
114
- Reads from and writes to st.session_state using store_path: ("costs", method, resource).
115
- Avoids value reset issues by separating widget key from storage.
116
- """
117
- base_key = f"{key_prefix}{method_name}_{resource}".replace(" ", "_").lower()
118
- toggle_key = f"{base_key}_toggle"
119
- widget_key = f"{base_key}_slider"
120
-
121
- # Validate data availability
122
- if method_name not in slider_data or resource not in slider_data[method_name]:
123
- return None
124
-
125
- values = slider_data[method_name][resource]
126
- min_val = values.get("min", 0.0)
127
- med_val = values.get("median", 0.5)
128
- max_val = values.get("max", 1.0)
129
-
130
- # Handle toggle for secondary resources
131
- enabled = True
132
- if enable_toggle:
133
- if toggle_key not in st.session_state:
134
- st.session_state[toggle_key] = toggle_default
135
- enabled = st.checkbox(f"Use {resource}", key=toggle_key)
136
-
137
- if not enabled:
138
- return None
139
-
140
- # Determine slider format and step size
141
- step = 0.01 if max_val < 100 else 1.0
142
- fmt = "%.2f" if max_val < 100 else "%.0f"
143
-
144
- # Ensure session state value exists
145
- if store_path:
146
- base, m, r = store_path
147
- if base not in st.session_state:
148
- st.session_state[base] = {}
149
- if m not in st.session_state[base]:
150
- st.session_state[base][m] = {}
151
- if r not in st.session_state[base][m]:
152
- st.session_state[base][m][r] = med_val
153
-
154
- current_val = st.session_state[base][m][r]
155
- else:
156
- if base_key not in st.session_state:
157
- st.session_state[base_key] = med_val
158
- current_val = st.session_state[base_key]
159
-
160
- # Render slider with separate widget key
161
- val = st.slider(
162
- label=f"{method_name} – {resource}",
163
- min_value=float(min_val),
164
- max_value=float(max_val),
165
- value=float(current_val),
166
- step=step,
167
- format=fmt,
168
- key=widget_key
169
- )
170
-
171
- # Sync slider output to session state
172
- if store_path:
173
- st.session_state[base][m][r] = val
174
- else:
175
- st.session_state[base_key] = val
176
-
177
- return val
178
-
179
- # -----------------------------
180
- # TABLE FORMATTING HELPER
181
- # -----------------------------
182
- def format_results_table(df, highlight_changes=False, precision=2):
183
- """
184
- Applies consistent styling to result tables.
185
- """
186
- def bold_methods(val):
187
- return f"font-weight: bold" if isinstance(val, str) else ""
188
-
189
- styled = df.style.format(
190
- {col: f"{{:.{precision}f}}" for col in df.select_dtypes(include=[np.number]).columns}
191
- ).applymap(bold_methods, subset=[df.columns[0]])
192
-
193
- if highlight_changes:
194
- styled = styled.highlight_max(axis=0, color='lightgreen')
195
-
196
- return styled
197
-
198
- def format_resource_summary(df: pd.DataFrame):
199
- df = df.copy()
200
- df.index.name = "Resource"
201
- df = df.reset_index()
202
-
203
- def highlight_new(row):
204
- if row.get("Type") == "New":
205
- return ["color: #ff5f4d; font-weight: bold"] * len(row)
206
- return [""] * len(row)
207
-
208
- styled = (
209
- df.style
210
- .apply(highlight_new, axis=1)
211
- .set_table_attributes('class="resources-summary"')
212
- )
213
-
214
- return styled.to_html()
215
-
216
- def smart_display_format(val):
217
- if abs(val) < 1e-8:
218
- return ""
219
- elif abs(val) < 0.001:
220
- return f"{val:,.5f}"
221
- elif abs(val) < 0.01:
222
- return f"{val:,.4f}"
223
- elif abs(val) < 1:
224
- return f"{val:,.3f}"
225
- elif abs(val) < 100:
226
- return f"{val:,.2f}"
227
- else:
228
- return f"{val:,.0f}"
229
-
230
- # -----------------------------
231
- # CHART LABEL CLEANER (Optional)
232
- # -----------------------------
233
- def clean_labels(labels):
234
- """
235
- Cleans labels to remove zero-allocation items or clutter.
236
- """
237
- return [label for label in labels if label and label.strip() != ""]
238
-
 
1
+ import pandas as pd
2
+ import streamlit as st
3
+ from config.config import CRRA_COLORS, CRRA_FONT, PLOTLY_TEMPLATE
4
+
5
+
6
+ def apply_chart_style(fig, height=None):
7
+ fig.update_layout(
8
+ template=PLOTLY_TEMPLATE,
9
+ font=dict(family=CRRA_FONT, size=12, color=CRRA_COLORS["black"]),
10
+ margin=dict(t=40, b=40, l=40, r=20),
11
+ legend=dict(
12
+ orientation="v",
13
+ yanchor="middle", y=0.5,
14
+ xanchor="left", x=1.02,
15
+ ),
16
+ )
17
+ if height:
18
+ fig.update_layout(height=height)
19
+ return fig
20
+
21
+
22
+ def register_chart_data(name: str, df: pd.DataFrame):
23
+ """Enregistre les données d'un graphique pour l'export consolidé final."""
24
+ st.session_state.setdefault("chart_data_registry", {})
25
+ st.session_state["chart_data_registry"][name] = df.copy()
26
+
27
+
28
+ def show_bar():
29
+ st.markdown(
30
+ """
31
+ <div class="et_pb_module">
32
+ <div style="display:flex;width:100%;height:12px;margin:0 auto 0 0;">
33
+ <span style="flex:1;background:#FF5F4D;"></span>
34
+ <span style="flex:1;background:#A0A0A0;"></span>
35
+ <span style="flex:1;background:#D9E210;"></span>
36
+ <span style="flex:1;background:#55C95B;"></span>
37
+ </div>
38
+ </div>
39
+ """,
40
+ unsafe_allow_html=True,
41
+ )
42
+
43
+
44
+ def smart_display_format(val):
45
+ if abs(val) < 1e-8:
46
+ return ""
47
+ elif abs(val) < 0.001:
48
+ return f"{val:,.5f}"
49
+ elif abs(val) < 0.01:
50
+ return f"{val:,.4f}"
51
+ elif abs(val) < 1:
52
+ return f"{val:,.3f}"
53
+ elif abs(val) < 100:
54
+ return f"{val:,.2f}"
55
+ else:
56
+ return f"{val:,.0f}"
57
+
58
+
59
+ def format_efficiency_summary(df: pd.DataFrame, df_default: pd.DataFrame):
60
+ df_numeric = df.copy()
61
+ df_display = df_numeric.map(smart_display_format)
62
+
63
+ def highlight_changes(data):
64
+ styled = pd.DataFrame('', index=data.index, columns=data.columns)
65
+ for m in data.index:
66
+ for r in data.columns:
67
+ try:
68
+ current = df_numeric.at[m, r]
69
+ default = df_default.at[m, r]
70
+ if abs(current - default) > 1e-6:
71
+ styled.at[m, r] = 'background-color: #ff5f4d'
72
+ except:
73
+ continue
74
+ return styled
75
+
76
+ return (
77
+ df_display
78
+ .style
79
+ .apply(highlight_changes, axis=None)
80
+ )