roncc13 commited on
Commit
075ee77
·
verified ·
1 Parent(s): b0fac1b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +80 -231
app.py CHANGED
@@ -1,6 +1,8 @@
1
  import gradio as gr
2
  from transformers import AutoTokenizer, AutoModelForSequenceClassification
3
  import torch
 
 
4
 
5
  # --------------------------
6
  # Model setup
@@ -10,7 +12,8 @@ MODEL_ID = "roncc13/trainCMDBERT-sample"
10
  tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
11
  model = AutoModelForSequenceClassification.from_pretrained(MODEL_ID)
12
 
13
- label_names = ["fake", "real"] # adjust to match your label order
 
14
 
15
 
16
  def classify(text: str):
@@ -21,7 +24,7 @@ def classify(text: str):
21
  return_tensors="pt",
22
  truncation=True,
23
  padding=True,
24
- max_length=256
25
  )
26
  with torch.no_grad():
27
  outputs = model(**inputs)
@@ -29,210 +32,18 @@ def classify(text: str):
29
  return {label_names[i]: float(probs[i]) for i in range(len(label_names))}
30
 
31
 
32
- # --------------------------
33
- # CSS
34
- # --------------------------
35
- custom_css = """
36
- body {
37
- /* Tailwind: bg-gradient-to-br from-indigo-950 via-purple-950 to-slate-900 */
38
- background: linear-gradient(135deg, #020617 0%, #1e0b45 50%, #020617 100%);
39
- }
40
- .gradio-container {
41
- font-family: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text",
42
- "Segoe UI", sans-serif;
43
- max-width: 1200px !important;
44
- margin: 0 auto !important;
45
- padding: 32px 32px 40px 32px !important;
46
- color: #f9fafb;
47
- }
48
-
49
- /* make default blocks transparent so our glass style shows */
50
- .gradio-container .block {
51
- background: transparent !important;
52
- border: none !important;
53
- box-shadow: none !important;
54
- }
55
-
56
- /* optional: constrain generic rows a bit */
57
- .gradio-container .row {
58
- max-width: 1120px;
59
- margin-left: auto;
60
- margin-right: auto;
61
- }
62
-
63
- /* Main cards */
64
- .glass-card {
65
- background: rgba(15, 23, 42, 0.96); /* near indigo-950 */
66
- border-radius: 24px;
67
- border: 1px solid rgba(148, 163, 184, 0.30); /* slate-400 */
68
- box-shadow: 0 24px 60px rgba(15, 23, 42, 0.9);
69
- padding: 18px 20px;
70
- }
71
-
72
- /* Header bar: CMD-BERT left, About CMD-BERT right */
73
- .cmd-header {
74
- display: flex;
75
- align-items: center;
76
- justify-content: space-between;
77
- margin-bottom: 24px;
78
- }
79
- .cmd-header-left {
80
- display: flex;
81
- align-items: center;
82
- gap: 10px;
83
- }
84
- .cmd-logo {
85
- width: 32px;
86
- height: 32px;
87
- border-radius: 999px;
88
- background: radial-gradient(circle at 30% 30%, #a5b4fc, #6366f1);
89
- display: flex;
90
- align-items: center;
91
- justify-content: center;
92
- color: #020617;
93
- font-weight: 700;
94
- font-size: 16px;
95
- }
96
- .cmd-title {
97
- font-weight: 600;
98
- font-size: 14px;
99
- letter-spacing: 0.08em;
100
- text-transform: uppercase;
101
- color: #e5e7eb;
102
- }
103
- .cmd-subtitle {
104
- font-size: 11px;
105
- color: #9ca3af;
106
- }
107
- .cmd-header-right {
108
- display: flex;
109
- align-items: center;
110
- gap: 8px;
111
- font-size: 13px;
112
- color: #e5e7eb;
113
- opacity: 0.9;
114
- }
115
- .cmd-header-pill {
116
- width: 24px;
117
- height: 24px;
118
- border-radius: 999px;
119
- background: rgba(148, 163, 184, 0.35);
120
- }
121
-
122
- /* Hero text */
123
- .hero-title {
124
- font-size: 34px;
125
- font-weight: 700;
126
- letter-spacing: 0.02em;
127
- line-height: 1.2;
128
- color: #e5e7eb;
129
- margin-bottom: 8px;
130
- }
131
- .hero-subtitle {
132
- font-size: 14px;
133
- max-width: 640px;
134
- opacity: 0.9;
135
- color: #cbd5f5;
136
- }
137
-
138
- /* Buttons */
139
- .btn-primary-custom {
140
- background: linear-gradient(135deg, #22c55e, #16a34a) !important;
141
- color: #020617 !important;
142
- border-radius: 999px !important;
143
- border: none !important;
144
- padding: 10px 26px !important;
145
- font-weight: 600 !important;
146
- box-shadow: 0 18px 40px rgba(22, 163, 74, 0.7);
147
- }
148
- .btn-secondary-custom {
149
- background: transparent !important;
150
- color: #e5e7eb !important;
151
- border-radius: 999px !important;
152
- border: 1px solid rgba(148, 163, 184, 0.6) !important;
153
- padding: 10px 22px !important;
154
- font-size: 11px !important;
155
- }
156
-
157
- /* Confidence bar + label pill */
158
- .conf-bar-bg {
159
- margin-top: 14px;
160
- width: 100%;
161
- height: 8px;
162
- border-radius: 999px;
163
- background: rgba(148, 163, 184, 0.35);
164
- }
165
- .conf-bar-fill {
166
- height: 100%;
167
- border-radius: inherit;
168
- background: linear-gradient(90deg, #f97316, #fb923c);
169
- }
170
- .badge-pill {
171
- display: inline-flex;
172
- align-items: center;
173
- padding: 4px 14px;
174
- border-radius: 999px;
175
- font-size: 11px;
176
- font-weight: 600;
177
- }
178
- .badge-fake {
179
- background: #f97316;
180
- color: #111827;
181
- }
182
- .badge-real {
183
- background: #22c55e;
184
- color: #022c22;
185
- }
186
-
187
- /* Textbox styling */
188
- textarea {
189
- background: #020617 !important;
190
- border-radius: 18px !important;
191
- border: 1px solid rgba(148, 163, 184, 0.35) !important;
192
- color: #f9fafb !important;
193
- font-size: 13px !important;
194
- }
195
-
196
- /* Two-column responsiveness */
197
- @media (max-width: 900px) {
198
- .two-col {
199
- flex-direction: column !important;
200
- }
201
- }
202
- """
203
-
204
-
205
- def header_html():
206
- return gr.HTML(
207
- """
208
- <div style="max-width:1120px;margin:0 auto;">
209
- <header class="cmd-header">
210
- <div class="cmd-header-left">
211
- <div class="cmd-logo">C</div>
212
- <div>
213
- <div class="cmd-title">CMD‑BERT</div>
214
- <div class="cmd-subtitle">Cebuano Misinformation Detector</div>
215
- </div>
216
- </div>
217
- <div class="cmd-header-right">
218
- <span>About CMD‑BERT</span>
219
- <div class="cmd-header-pill"></div>
220
- </div>
221
- </header>
222
- </div>
223
- """
224
- )
225
-
226
-
227
  # --------------------------
228
  # UI with Tabs
229
  # --------------------------
230
  with gr.Blocks(fill_height=True) as demo:
231
- # small spacer so tabs are not glued to the top
232
  gr.HTML("<div style='height:8px;'></div>")
233
 
 
234
  with gr.Tab("Analyzer"):
235
- header_html()
 
 
236
  gr.HTML(
237
  """
238
  <section style="margin:0 auto 22px auto; max-width:1120px;">
@@ -248,11 +59,13 @@ with gr.Blocks(fill_height=True) as demo:
248
  """
249
  )
250
 
 
251
  gr.HTML("<div style='max-width:1120px;margin:0 auto;'>")
252
  with gr.Row(elem_classes=["two-col"], equal_height=True):
253
  # Left: input card
254
  with gr.Column(scale=3):
255
  with gr.Group(elem_classes=["glass-card"]):
 
256
  gr.Markdown(
257
  "#### Text input\n"
258
  "Cebuano only. This tool checks linguistic patterns; it does not verify facts."
@@ -265,43 +78,57 @@ with gr.Blocks(fill_height=True) as demo:
265
  news_text = gr.Textbox(
266
  lines=7,
267
  label="",
268
- placeholder="Paste Cebuano news text here..."
269
  )
270
  with gr.Row():
271
- analyze_btn = gr.Button("Analyze", elem_classes=["btn-primary-custom"])
272
- clear_btn = gr.Button("Clear", elem_classes=["btn-secondary-custom"])
 
 
 
 
273
  gr.Markdown(
274
  "<span style='font-size:11px;opacity:0.8;'>"
275
  "Tip: Keep inputs under 1,000 characters for faster results."
276
- "</span>"
 
277
  )
 
278
 
279
  # Right: result card
280
  with gr.Column(scale=2):
281
  with gr.Group(elem_classes=["glass-card"]):
 
282
  gr.Markdown("#### Result")
283
  result_label_html = gr.HTML(
284
  '<span class="badge-pill badge-fake">FAKE</span>'
285
  )
286
  conf_text = gr.HTML(
287
- '<div style="display:flex;align-items:flex-end;gap:6px;margin-top:10px;">'
288
- '<span style="font-size:28px;font-weight:600;" id="conf-val">0.00</span>'
289
- '<span style="font-size:12px;opacity:0.8;">confidence</span>'
290
- '</div>'
 
 
291
  )
292
  conf_bar = gr.HTML(
293
- '<div class="conf-bar-bg">'
294
- '<div class="conf-bar-fill" style="width:0%;"></div>'
295
- '</div>'
 
 
296
  )
297
  gr.Markdown(
298
  "<span style='font-size:11px;opacity:0.85;'>"
299
  "Model: CMD‑BERT (fine‑tuned BERT‑base). "
300
  "Output: Label and confidence score for the submitted text."
301
- "</span>"
 
302
  )
 
303
  gr.HTML("</div>")
304
 
 
305
  def analyze_ui(text):
306
  probs = classify(text)
307
  fake_p = probs.get("fake", 0.0)
@@ -317,15 +144,15 @@ with gr.Blocks(fill_height=True) as demo:
317
  conf_pct = int(conf * 100)
318
  label_html = f'<span class="{css_class}">{label}</span>'
319
  conf_html = (
320
- '<div style="display:flex;align-items:flex-end;gap:6px;margin-top:10px;">'
321
- f'<span style="font-size:28px;font-weight:600;" id="conf-val">{conf:.2f}</span>'
322
- '<span style="font-size:12px;opacity:0.8;">confidence</span>'
323
- '</div>'
324
  )
325
  bar_html = (
326
- '<div class="conf-bar-bg">'
327
- f'<div class="conf-bar-fill" style="width:{conf_pct}%;"></div>'
328
- '</div>'
329
  )
330
  return label_html, conf_html, bar_html
331
 
@@ -336,48 +163,57 @@ with gr.Blocks(fill_height=True) as demo:
336
  )
337
  clear_btn.click(fn=lambda: "", inputs=None, outputs=[news_text])
338
 
339
- # -------- How it works tab --------
340
  with gr.Tab("How it works"):
341
- header_html()
342
  gr.HTML("<div style='max-width:1120px;margin:0 auto;'>")
343
  with gr.Group(elem_classes=["glass-card"]):
 
344
  gr.Markdown(
345
  "## How CMD‑BERT works\n"
346
  "CMD‑BERT is an AI‑augmented linguistic model that focuses on writing style, "
347
  "not literal truth. It looks for patterns such as exaggerated wording, "
348
  "over‑confident claims, and framing that often appear in misleading content."
349
  )
 
350
  with gr.Row():
351
  with gr.Column():
352
  with gr.Group(elem_classes=["glass-card"]):
 
353
  gr.Markdown(
354
  "### 1. Input and preprocessing\n"
355
  "- User pastes a Cebuano headline, post, or short article.\n"
356
  "- The text is tokenized and trimmed to a safe maximum length.\n"
357
  "- Inputs are processed in memory and not stored permanently."
358
  )
 
359
  with gr.Column():
360
  with gr.Group(elem_classes=["glass-card"]):
 
361
  gr.Markdown(
362
  "### 2. CMD‑BERT analysis\n"
363
  "- CMD‑BERT is a fine‑tuned BERT‑base model trained on Cebuano news.\n"
364
  "- It computes probabilities for two classes: **Fake** and **Legit**.\n"
365
  "- The highest‑probability class becomes the predicted label."
366
  )
 
367
  with gr.Group(elem_classes=["glass-card"]):
 
368
  gr.Markdown(
369
  "### 3. Result and interpretation\n"
370
  "- The interface shows the predicted label and confidence bar.\n"
371
  "- Users are reminded that this is a screening tool only.\n"
372
  "- Final judgment should always involve human critical thinking."
373
  )
 
374
  gr.HTML("</div>")
375
 
376
- # -------- About tab --------
377
  with gr.Tab("About"):
378
- header_html()
379
  gr.HTML("<div style='max-width:1120px;margin:0 auto;'>")
380
  with gr.Group(elem_classes=["glass-card"]):
 
381
  gr.Markdown(
382
  "## About CMD‑BERT\n"
383
  "**CMD‑BERT: An AI Augmented Linguistic Recognition Model for Cebuano Fake News Detection**\n\n"
@@ -385,7 +221,9 @@ with gr.Blocks(fill_height=True) as demo:
385
  "Cebu Technological University–Main Campus. The tool aims to support Cebuano readers "
386
  "by highlighting potentially misleading writing patterns in online news and posts."
387
  )
 
388
  with gr.Group(elem_classes=["glass-card"]):
 
389
  gr.Markdown(
390
  "### Thesis information\n"
391
  "_A Thesis Project presented to the Faculty of the Department of Computer Engineering_\n\n"
@@ -400,43 +238,53 @@ with gr.Blocks(fill_height=True) as demo:
400
  "**Adviser:** Engr. Jueco, M.Eng. \n"
401
  "January 2026"
402
  )
 
403
  gr.HTML("</div>")
404
 
405
- # -------- Feedback tab --------
406
  with gr.Tab("Feedback"):
407
- header_html()
408
  gr.HTML("<div style='max-width:1120px;margin:0 auto;'>")
409
  with gr.Group(elem_classes=["glass-card"]):
 
410
  gr.Markdown(
411
  "## Feedback and model improvement\n"
412
  "CMD‑BERT is experimental and continuously improving. Your feedback can help "
413
  "identify model mistakes, usability issues, and opportunities to refine the dataset."
414
  )
 
415
 
416
  with gr.Row():
417
  with gr.Column():
418
  with gr.Group(elem_classes=["glass-card"]):
 
419
  fb_type = gr.Dropdown(
420
  ["Bug / technical issue", "Model mistake", "UI suggestion", "Other"],
421
- label="Feedback type"
422
  )
423
  fb_text = gr.Textbox(
424
  lines=6,
425
  label="Your message or example text",
426
- placeholder="Describe the issue or paste an example of text the model misclassified."
427
  )
428
  fb_email = gr.Textbox(
429
  label="Email (optional, for follow‑up)",
430
- placeholder="you@example.com"
431
  )
432
  fb_checkbox = gr.Checkbox(
433
- label="Allow us to use this text anonymously for future model improvements.",
434
- value=True
 
 
 
435
  )
436
- fb_submit = gr.Button("Submit feedback", elem_classes=["btn-primary-custom"])
437
-
 
 
438
  with gr.Column():
439
  with gr.Group(elem_classes=["glass-card"]):
 
440
  fb_status = gr.Markdown("No feedback submitted yet.")
441
  gr.Markdown(
442
  "### FAQ\n"
@@ -447,6 +295,7 @@ with gr.Blocks(fill_height=True) as demo:
447
  "**Who maintains this tool?** \n"
448
  "The CMD‑BERT thesis team at Cebu Technological University–Main Campus."
449
  )
 
450
 
451
  def save_feedback(ftype, text, email, consent):
452
  if not text.strip():
 
1
  import gradio as gr
2
  from transformers import AutoTokenizer, AutoModelForSequenceClassification
3
  import torch
4
+ from theme import custom_css, header, card_start, card_end
5
+
6
 
7
  # --------------------------
8
  # Model setup
 
12
  tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
13
  model = AutoModelForSequenceClassification.from_pretrained(MODEL_ID)
14
 
15
+ # Adjust if your label order is different
16
+ label_names = ["fake", "real"]
17
 
18
 
19
  def classify(text: str):
 
24
  return_tensors="pt",
25
  truncation=True,
26
  padding=True,
27
+ max_length=256,
28
  )
29
  with torch.no_grad():
30
  outputs = model(**inputs)
 
32
  return {label_names[i]: float(probs[i]) for i in range(len(label_names))}
33
 
34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  # --------------------------
36
  # UI with Tabs
37
  # --------------------------
38
  with gr.Blocks(fill_height=True) as demo:
39
+ # small spacer so tabs are not glued to the very top
40
  gr.HTML("<div style='height:8px;'></div>")
41
 
42
+ # ===== Analyzer tab =====
43
  with gr.Tab("Analyzer"):
44
+ header()
45
+
46
+ # Hero section
47
  gr.HTML(
48
  """
49
  <section style="margin:0 auto 22px auto; max-width:1120px;">
 
59
  """
60
  )
61
 
62
+ # Main two-column layout
63
  gr.HTML("<div style='max-width:1120px;margin:0 auto;'>")
64
  with gr.Row(elem_classes=["two-col"], equal_height=True):
65
  # Left: input card
66
  with gr.Column(scale=3):
67
  with gr.Group(elem_classes=["glass-card"]):
68
+ card_start()
69
  gr.Markdown(
70
  "#### Text input\n"
71
  "Cebuano only. This tool checks linguistic patterns; it does not verify facts."
 
78
  news_text = gr.Textbox(
79
  lines=7,
80
  label="",
81
+ placeholder="Paste Cebuano news text here...",
82
  )
83
  with gr.Row():
84
+ analyze_btn = gr.Button(
85
+ "Analyze", elem_classes=["btn-primary-custom"]
86
+ )
87
+ clear_btn = gr.Button(
88
+ "Clear", elem_classes=["btn-secondary-custom"]
89
+ )
90
  gr.Markdown(
91
  "<span style='font-size:11px;opacity:0.8;'>"
92
  "Tip: Keep inputs under 1,000 characters for faster results."
93
+ "</span>",
94
+ container=False,
95
  )
96
+ card_end()
97
 
98
  # Right: result card
99
  with gr.Column(scale=2):
100
  with gr.Group(elem_classes=["glass-card"]):
101
+ card_start()
102
  gr.Markdown("#### Result")
103
  result_label_html = gr.HTML(
104
  '<span class="badge-pill badge-fake">FAKE</span>'
105
  )
106
  conf_text = gr.HTML(
107
+ """
108
+ <div style="display:flex;align-items:flex-end;gap:6px;margin-top:10px;">
109
+ <span style="font-size:28px;font-weight:600;" id="conf-val">0.00</span>
110
+ <span style="font-size:12px;opacity:0.8;">confidence</span>
111
+ </div>
112
+ """
113
  )
114
  conf_bar = gr.HTML(
115
+ """
116
+ <div class="conf-bar-bg">
117
+ <div class="conf-bar-fill" style="width:0%;"></div>
118
+ </div>
119
+ """
120
  )
121
  gr.Markdown(
122
  "<span style='font-size:11px;opacity:0.85;'>"
123
  "Model: CMD‑BERT (fine‑tuned BERT‑base). "
124
  "Output: Label and confidence score for the submitted text."
125
+ "</span>",
126
+ container=False,
127
  )
128
+ card_end()
129
  gr.HTML("</div>")
130
 
131
+ # backend → UI glue
132
  def analyze_ui(text):
133
  probs = classify(text)
134
  fake_p = probs.get("fake", 0.0)
 
144
  conf_pct = int(conf * 100)
145
  label_html = f'<span class="{css_class}">{label}</span>'
146
  conf_html = (
147
+ "<div style='display:flex;align-items:flex-end;gap:6px;margin-top:10px;'>"
148
+ f"<span style='font-size:28px;font-weight:600;' id='conf-val'>{conf:.2f}</span>"
149
+ "<span style='font-size:12px;opacity:0.8;'>confidence</span>"
150
+ "</div>"
151
  )
152
  bar_html = (
153
+ "<div class='conf-bar-bg'>"
154
+ f"<div class='conf-bar-fill' style='width:{conf_pct}%;'></div>"
155
+ "</div>"
156
  )
157
  return label_html, conf_html, bar_html
158
 
 
163
  )
164
  clear_btn.click(fn=lambda: "", inputs=None, outputs=[news_text])
165
 
166
+ # ===== How it works tab =====
167
  with gr.Tab("How it works"):
168
+ header()
169
  gr.HTML("<div style='max-width:1120px;margin:0 auto;'>")
170
  with gr.Group(elem_classes=["glass-card"]):
171
+ card_start()
172
  gr.Markdown(
173
  "## How CMD‑BERT works\n"
174
  "CMD‑BERT is an AI‑augmented linguistic model that focuses on writing style, "
175
  "not literal truth. It looks for patterns such as exaggerated wording, "
176
  "over‑confident claims, and framing that often appear in misleading content."
177
  )
178
+ card_end()
179
  with gr.Row():
180
  with gr.Column():
181
  with gr.Group(elem_classes=["glass-card"]):
182
+ card_start()
183
  gr.Markdown(
184
  "### 1. Input and preprocessing\n"
185
  "- User pastes a Cebuano headline, post, or short article.\n"
186
  "- The text is tokenized and trimmed to a safe maximum length.\n"
187
  "- Inputs are processed in memory and not stored permanently."
188
  )
189
+ card_end()
190
  with gr.Column():
191
  with gr.Group(elem_classes=["glass-card"]):
192
+ card_start()
193
  gr.Markdown(
194
  "### 2. CMD‑BERT analysis\n"
195
  "- CMD‑BERT is a fine‑tuned BERT‑base model trained on Cebuano news.\n"
196
  "- It computes probabilities for two classes: **Fake** and **Legit**.\n"
197
  "- The highest‑probability class becomes the predicted label."
198
  )
199
+ card_end()
200
  with gr.Group(elem_classes=["glass-card"]):
201
+ card_start()
202
  gr.Markdown(
203
  "### 3. Result and interpretation\n"
204
  "- The interface shows the predicted label and confidence bar.\n"
205
  "- Users are reminded that this is a screening tool only.\n"
206
  "- Final judgment should always involve human critical thinking."
207
  )
208
+ card_end()
209
  gr.HTML("</div>")
210
 
211
+ # ===== About tab =====
212
  with gr.Tab("About"):
213
+ header()
214
  gr.HTML("<div style='max-width:1120px;margin:0 auto;'>")
215
  with gr.Group(elem_classes=["glass-card"]):
216
+ card_start()
217
  gr.Markdown(
218
  "## About CMD‑BERT\n"
219
  "**CMD‑BERT: An AI Augmented Linguistic Recognition Model for Cebuano Fake News Detection**\n\n"
 
221
  "Cebu Technological University–Main Campus. The tool aims to support Cebuano readers "
222
  "by highlighting potentially misleading writing patterns in online news and posts."
223
  )
224
+ card_end()
225
  with gr.Group(elem_classes=["glass-card"]):
226
+ card_start()
227
  gr.Markdown(
228
  "### Thesis information\n"
229
  "_A Thesis Project presented to the Faculty of the Department of Computer Engineering_\n\n"
 
238
  "**Adviser:** Engr. Jueco, M.Eng. \n"
239
  "January 2026"
240
  )
241
+ card_end()
242
  gr.HTML("</div>")
243
 
244
+ # ===== Feedback tab =====
245
  with gr.Tab("Feedback"):
246
+ header()
247
  gr.HTML("<div style='max-width:1120px;margin:0 auto;'>")
248
  with gr.Group(elem_classes=["glass-card"]):
249
+ card_start()
250
  gr.Markdown(
251
  "## Feedback and model improvement\n"
252
  "CMD‑BERT is experimental and continuously improving. Your feedback can help "
253
  "identify model mistakes, usability issues, and opportunities to refine the dataset."
254
  )
255
+ card_end()
256
 
257
  with gr.Row():
258
  with gr.Column():
259
  with gr.Group(elem_classes=["glass-card"]):
260
+ card_start()
261
  fb_type = gr.Dropdown(
262
  ["Bug / technical issue", "Model mistake", "UI suggestion", "Other"],
263
+ label="Feedback type",
264
  )
265
  fb_text = gr.Textbox(
266
  lines=6,
267
  label="Your message or example text",
268
+ placeholder="Describe the issue or paste an example of text the model misclassified.",
269
  )
270
  fb_email = gr.Textbox(
271
  label="Email (optional, for follow‑up)",
272
+ placeholder="you@example.com",
273
  )
274
  fb_checkbox = gr.Checkbox(
275
+ label=(
276
+ "Allow us to use this text anonymously for future "
277
+ "model improvements."
278
+ ),
279
+ value=True,
280
  )
281
+ fb_submit = gr.Button(
282
+ "Submit feedback", elem_classes=["btn-primary-custom"]
283
+ )
284
+ card_end()
285
  with gr.Column():
286
  with gr.Group(elem_classes=["glass-card"]):
287
+ card_start()
288
  fb_status = gr.Markdown("No feedback submitted yet.")
289
  gr.Markdown(
290
  "### FAQ\n"
 
295
  "**Who maintains this tool?** \n"
296
  "The CMD‑BERT thesis team at Cebu Technological University–Main Campus."
297
  )
298
+ card_end()
299
 
300
  def save_feedback(ftype, text, email, consent):
301
  if not text.strip():